##// END OF EJS Templates
Upstream merges.
gvaroquaux -
r1298:92012389 merge
parent child Browse files
Show More
@@ -1,386 +1,391 b''
1 1
2 2 """ Implementations for various useful completers
3 3
4 4 See Extensions/ipy_stock_completers.py on examples of how to enable a completer,
5 5 but the basic idea is to do:
6 6
7 7 ip.set_hook('complete_command', svn_completer, str_key = 'svn')
8 8
9 9 """
10 10 import IPython.ipapi
11 11 import glob,os,shlex,sys
12 12 import inspect
13 13 from time import time
14 14 from zipimport import zipimporter
15 15 ip = IPython.ipapi.get()
16 16
17 17 try:
18 18 set
19 19 except:
20 20 from sets import Set as set
21 21
22 22 TIMEOUT_STORAGE = 3 #Time in seconds after which the rootmodules will be stored
23 23 TIMEOUT_GIVEUP = 20 #Time in seconds after which we give up
24 24
25 25 def quick_completer(cmd, completions):
26 26 """ Easily create a trivial completer for a command.
27 27
28 28 Takes either a list of completions, or all completions in string
29 29 (that will be split on whitespace)
30 30
31 31 Example::
32 32
33 33 [d:\ipython]|1> import ipy_completers
34 34 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
35 35 [d:\ipython]|3> foo b<TAB>
36 36 bar baz
37 37 [d:\ipython]|3> foo ba
38 38 """
39 39 if isinstance(completions, basestring):
40 40
41 41 completions = completions.split()
42 42 def do_complete(self,event):
43 43 return completions
44 44
45 45 ip.set_hook('complete_command',do_complete, str_key = cmd)
46 46
47 47 def getRootModules():
48 48 """
49 49 Returns a list containing the names of all the modules available in the
50 50 folders of the pythonpath.
51 51 """
52 52 modules = []
53 53 if ip.db.has_key('rootmodules'):
54 54 return ip.db['rootmodules']
55 55 t = time()
56 56 store = False
57 57 for path in sys.path:
58 58 modules += moduleList(path)
59 59 if time() - t >= TIMEOUT_STORAGE and not store:
60 60 store = True
61 61 print "\nCaching the list of root modules, please wait!"
62 62 print "(This will only be done once - type '%rehashx' to " + \
63 63 "reset cache!)"
64 64 print
65 65 if time() - t > TIMEOUT_GIVEUP:
66 66 print "This is taking too long, we give up."
67 67 print
68 68 ip.db['rootmodules'] = []
69 69 return []
70 70
71 71 modules += sys.builtin_module_names
72 72
73 73 modules = list(set(modules))
74 74 if '__init__' in modules:
75 75 modules.remove('__init__')
76 76 modules = list(set(modules))
77 77 if store:
78 78 ip.db['rootmodules'] = modules
79 79 return modules
80 80
81 81 def moduleList(path):
82 82 """
83 83 Return the list containing the names of the modules available in the given
84 84 folder.
85 85 """
86 86
87 87 if os.path.isdir(path):
88 88 folder_list = os.listdir(path)
89 89 elif path.endswith('.egg'):
90 90 try:
91 91 folder_list = [f for f in zipimporter(path)._files]
92 92 except:
93 93 folder_list = []
94 94 else:
95 95 folder_list = []
96 96 #folder_list = glob.glob(os.path.join(path,'*'))
97 97 folder_list = [p for p in folder_list \
98 98 if os.path.exists(os.path.join(path, p,'__init__.py'))\
99 99 or p[-3:] in ('.py','.so')\
100 100 or p[-4:] in ('.pyc','.pyo','.pyd')]
101 101
102 102 folder_list = [os.path.basename(p).split('.')[0] for p in folder_list]
103 103 return folder_list
104 104
105 105 def moduleCompletion(line):
106 106 """
107 107 Returns a list containing the completion possibilities for an import line.
108 108 The line looks like this :
109 109 'import xml.d'
110 110 'from xml.dom import'
111 111 """
112 112 def tryImport(mod, only_modules=False):
113 113 def isImportable(module, attr):
114 114 if only_modules:
115 115 return inspect.ismodule(getattr(module, attr))
116 116 else:
117 117 return not(attr[:2] == '__' and attr[-2:] == '__')
118 118 try:
119 119 m = __import__(mod)
120 120 except:
121 121 return []
122 122 mods = mod.split('.')
123 123 for module in mods[1:]:
124 124 m = getattr(m,module)
125 125 if (not hasattr(m, '__file__')) or (not only_modules) or\
126 126 (hasattr(m, '__file__') and '__init__' in m.__file__):
127 127 completion_list = [attr for attr in dir(m) if isImportable(m, attr)]
128 128 completion_list.extend(getattr(m,'__all__',[]))
129 129 if hasattr(m, '__file__') and '__init__' in m.__file__:
130 130 completion_list.extend(moduleList(os.path.dirname(m.__file__)))
131 131 completion_list = list(set(completion_list))
132 132 if '__init__' in completion_list:
133 133 completion_list.remove('__init__')
134 134 return completion_list
135 135
136 136 words = line.split(' ')
137 137 if len(words) == 3 and words[0] == 'from':
138 138 return ['import ']
139 139 if len(words) < 3 and (words[0] in ['import','from']) :
140 140 if len(words) == 1:
141 141 return getRootModules()
142 142 mod = words[1].split('.')
143 143 if len(mod) < 2:
144 144 return getRootModules()
145 145 completion_list = tryImport('.'.join(mod[:-1]), True)
146 146 completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
147 147 return completion_list
148 148 if len(words) >= 3 and words[0] == 'from':
149 149 mod = words[1]
150 150 return tryImport(mod)
151 151
152 152 def vcs_completer(commands, event):
153 153 """ utility to make writing typical version control app completers easier
154 154
155 155 VCS command line apps typically have the format:
156 156
157 157 [sudo ]PROGNAME [help] [command] file file...
158 158
159 159 """
160 160
161 161
162 162 cmd_param = event.line.split()
163 163 if event.line.endswith(' '):
164 164 cmd_param.append('')
165 165
166 166 if cmd_param[0] == 'sudo':
167 167 cmd_param = cmd_param[1:]
168 168
169 169 if len(cmd_param) == 2 or 'help' in cmd_param:
170 170 return commands.split()
171 171
172 172 return ip.IP.Completer.file_matches(event.symbol)
173 173
174 174
175 175 pkg_cache = None
176 176
177 177 def module_completer(self,event):
178 178 """ Give completions after user has typed 'import ...' or 'from ...'"""
179 179
180 180 # This works in all versions of python. While 2.5 has
181 181 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
182 182 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
183 183 # of possibly problematic side effects.
184 184 # This search the folders in the sys.path for available modules.
185 185
186 186 return moduleCompletion(event.line)
187 187
188 188
189 189 svn_commands = """\
190 190 add blame praise annotate ann cat checkout co cleanup commit ci copy
191 191 cp delete del remove rm diff di export help ? h import info list ls
192 192 lock log merge mkdir move mv rename ren propdel pdel pd propedit pedit
193 193 pe propget pget pg proplist plist pl propset pset ps resolved revert
194 194 status stat st switch sw unlock update
195 195 """
196 196
197 197 def svn_completer(self,event):
198 198 return vcs_completer(svn_commands, event)
199 199
200 200
201 201 hg_commands = """
202 202 add addremove annotate archive backout branch branches bundle cat
203 203 clone commit copy diff export grep heads help identify import incoming
204 204 init locate log manifest merge outgoing parents paths pull push
205 205 qapplied qclone qcommit qdelete qdiff qfold qguard qheader qimport
206 206 qinit qnew qnext qpop qprev qpush qrefresh qrename qrestore qsave
207 207 qselect qseries qtop qunapplied recover remove rename revert rollback
208 208 root serve showconfig status strip tag tags tip unbundle update verify
209 209 version
210 210 """
211 211
212 212 def hg_completer(self,event):
213 213 """ Completer for mercurial commands """
214 214
215 215 return vcs_completer(hg_commands, event)
216 216
217 217
218 218
219 219 __bzr_commands = None
220 220
221 221 def bzr_commands():
222 222 global __bzr_commands
223 223 if __bzr_commands is not None:
224 224 return __bzr_commands
225 225 out = os.popen('bzr help commands')
226 226 __bzr_commands = [l.split()[0] for l in out]
227 227 return __bzr_commands
228 228
229 229 def bzr_completer(self,event):
230 230 """ Completer for bazaar commands """
231 231 cmd_param = event.line.split()
232 232 if event.line.endswith(' '):
233 233 cmd_param.append('')
234 234
235 235 if len(cmd_param) > 2:
236 236 cmd = cmd_param[1]
237 237 param = cmd_param[-1]
238 238 output_file = (param == '--output=')
239 239 if cmd == 'help':
240 240 return bzr_commands()
241 241 elif cmd in ['bundle-revisions','conflicts',
242 242 'deleted','nick','register-branch',
243 243 'serve','unbind','upgrade','version',
244 244 'whoami'] and not output_file:
245 245 return []
246 246 else:
247 247 # the rest are probably file names
248 248 return ip.IP.Completer.file_matches(event.symbol)
249 249
250 250 return bzr_commands()
251 251
252 252
253 253 def shlex_split(x):
254 254 """Helper function to split lines into segments."""
255 255 #shlex.split raise exception if syntax error in sh syntax
256 256 #for example if no closing " is found. This function keeps dropping
257 257 #the last character of the line until shlex.split does not raise
258 258 #exception. Adds end of the line to the result of shlex.split
259 259 #example: %run "c:/python -> ['%run','"c:/python']
260 260 endofline=[]
261 261 while x!="":
262 262 try:
263 263 comps=shlex.split(x)
264 264 if len(endofline)>=1:
265 265 comps.append("".join(endofline))
266 266 return comps
267 267 except ValueError:
268 268 endofline=[x[-1:]]+endofline
269 269 x=x[:-1]
270 270 return ["".join(endofline)]
271 271
272 272 def runlistpy(self, event):
273 273 comps = shlex_split(event.line)
274 274 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
275 275
276 276 #print "\nev=",event # dbg
277 277 #print "rp=",relpath # dbg
278 278 #print 'comps=',comps # dbg
279 279
280 280 lglob = glob.glob
281 281 isdir = os.path.isdir
282 282 if relpath.startswith('~'):
283 283 relpath = os.path.expanduser(relpath)
284 284 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*')
285 285 if isdir(f)]
286 286
287 287 # Find if the user has already typed the first filename, after which we
288 288 # should complete on all files, since after the first one other files may
289 289 # be arguments to the input script.
290 290 #filter(
291 291 if filter(lambda f: f.endswith('.py') or f.endswith('.ipy') or
292 292 f.endswith('.pyw'),comps):
293 293 pys = [f.replace('\\','/') for f in lglob('*')]
294 294 else:
295 295 pys = [f.replace('\\','/')
296 296 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
297 297 lglob(relpath + '*.pyw')]
298 298 return dirs + pys
299 299
300 300
301 greedy_cd_completer = False
302
301 303 def cd_completer(self, event):
302 304 relpath = event.symbol
303 305 #print event # dbg
304 306 if '-b' in event.line:
305 307 # return only bookmark completions
306 308 bkms = self.db.get('bookmarks',{})
307 309 return bkms.keys()
308 310
309 311
310 312 if event.symbol == '-':
311 313 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
312 314 # jump in directory history by number
313 315 fmt = '-%0' + width_dh +'d [%s]'
314 316 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
315 317 if len(ents) > 1:
316 318 return ents
317 319 return []
318 320
319 321 if relpath.startswith('~'):
320 322 relpath = os.path.expanduser(relpath).replace('\\','/')
321 323 found = []
322 324 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
323 325 if os.path.isdir(f)]:
324 326 if ' ' in d:
325 327 # we don't want to deal with any of that, complex code
326 328 # for this is elsewhere
327 329 raise IPython.ipapi.TryNext
328 330 found.append( d )
329 331
330 332 if not found:
331 333 if os.path.isdir(relpath):
332 334 return [relpath]
333 335 raise IPython.ipapi.TryNext
334 336
335 337
336 338 def single_dir_expand(matches):
337 339 "Recursively expand match lists containing a single dir."
338 340
339 341 if len(matches) == 1 and os.path.isdir(matches[0]):
340 342 # Takes care of links to directories also. Use '/'
341 343 # explicitly, even under Windows, so that name completions
342 344 # don't end up escaped.
343 345 d = matches[0]
344 346 if d[-1] in ['/','\\']:
345 347 d = d[:-1]
346 348
347 349 subdirs = [p for p in os.listdir(d) if os.path.isdir( d + '/' + p) and not p.startswith('.')]
348 350 if subdirs:
349 351 matches = [ (d + '/' + p) for p in subdirs ]
350 352 return single_dir_expand(matches)
351 353 else:
352 354 return matches
353 355 else:
354 356 return matches
355 357
356 return single_dir_expand(found)
358 if greedy_cd_completer:
359 return single_dir_expand(found)
360 else:
361 return found
357 362
358 363 def apt_get_packages(prefix):
359 364 out = os.popen('apt-cache pkgnames')
360 365 for p in out:
361 366 if p.startswith(prefix):
362 367 yield p.rstrip()
363 368
364 369
365 370 apt_commands = """\
366 371 update upgrade install remove purge source build-dep dist-upgrade
367 372 dselect-upgrade clean autoclean check"""
368 373
369 374 def apt_completer(self, event):
370 375 """ Completer for apt-get (uses apt-cache internally)
371 376
372 377 """
373 378
374 379
375 380 cmd_param = event.line.split()
376 381 if event.line.endswith(' '):
377 382 cmd_param.append('')
378 383
379 384 if cmd_param[0] == 'sudo':
380 385 cmd_param = cmd_param[1:]
381 386
382 387 if len(cmd_param) == 2 or 'help' in cmd_param:
383 388 return apt_commands.split()
384 389
385 390 return list(apt_get_packages(event.symbol))
386 391
@@ -1,251 +1,258 b''
1 1 """Shell mode for IPython.
2 2
3 3 Start ipython in shell mode by invoking "ipython -p sh"
4 4
5 5 (the old version, "ipython -p pysh" still works but this is the more "modern"
6 6 shell mode and is recommended for users who don't care about pysh-mode
7 7 compatibility)
8 8 """
9 9
10 10 from IPython import ipapi
11 11 import os,textwrap
12 12
13 13 # The import below effectively obsoletes your old-style ipythonrc[.ini],
14 14 # so consider yourself warned!
15 15
16 16 import ipy_defaults
17 17
18 18 def main():
19 19 ip = ipapi.get()
20 20 o = ip.options
21 21 # autocall to "full" mode (smart mode is default, I like full mode)
22 22
23 23 o.autocall = 2
24 24
25 25 # Jason Orendorff's path class is handy to have in user namespace
26 26 # if you are doing shell-like stuff
27 27 try:
28 28 ip.ex("from IPython.external.path import path" )
29 29 except ImportError:
30 30 pass
31 31
32 32 # beefed up %env is handy in shell mode
33 33 import envpersist
34 34
35 35 # To see where mycmd resides (in path/aliases), do %which mycmd
36 36 import ipy_which
37 37
38 38 # tab completers for hg, svn, ...
39 39 import ipy_app_completers
40 40
41 41 # To make executables foo and bar in mybin usable without PATH change, do:
42 42 # %rehashdir c:/mybin
43 43 # %store foo
44 44 # %store bar
45 45 import ipy_rehashdir
46 46
47 47 # does not work without subprocess module!
48 48 #import ipy_signals
49 49
50 50 ip.ex('import os')
51 51 ip.ex("def up(): os.chdir('..')")
52 52 ip.user_ns['LA'] = LastArgFinder()
53 53 # Nice prompt
54 54
55 55 o.prompt_in1= r'\C_LightBlue[\C_LightCyan\Y2\C_LightBlue]\C_Green|\#> '
56 56 o.prompt_in2= r'\C_Green|\C_LightGreen\D\C_Green> '
57 57 o.prompt_out= '<\#> '
58 58
59 59 from IPython import Release
60 60
61 61 import sys
62 62 # Non-chatty banner
63 63 o.banner = "IPython %s [on Py %s]\n" % (Release.version,sys.version.split(None,1)[0])
64 64
65 65
66 66 ip.IP.default_option('cd','-q')
67 67 ip.IP.default_option('macro', '-r')
68 68 # If you only rarely want to execute the things you %edit...
69 69 #ip.IP.default_option('edit','-x')
70 70
71 71
72 72 o.prompts_pad_left="1"
73 73 # Remove all blank lines in between prompts, like a normal shell.
74 74 o.separate_in="0"
75 75 o.separate_out="0"
76 76 o.separate_out2="0"
77 77
78 78 # now alias all syscommands
79 79
80 80 db = ip.db
81 81
82 82 syscmds = db.get("syscmdlist",[] )
83 83 if not syscmds:
84 84 print textwrap.dedent("""
85 85 System command list not initialized, probably the first run...
86 86 running %rehashx to refresh the command list. Run %rehashx
87 87 again to refresh command list (after installing new software etc.)
88 88 """)
89 89 ip.magic('rehashx')
90 90 syscmds = db.get("syscmdlist")
91 91
92 92 # lowcase aliases on win32 only
93 93 if os.name == 'posix':
94 94 mapper = lambda s:s
95 95 else:
96 96 def mapper(s): return s.lower()
97 97
98 98 for cmd in syscmds:
99 99 # print "sys",cmd #dbg
100 100 noext, ext = os.path.splitext(cmd)
101 101 key = mapper(noext)
102 102 if key not in ip.IP.alias_table:
103 103 ip.defalias(key, cmd)
104 104
105 105 # mglob combines 'find', recursion, exclusion... '%mglob?' to learn more
106 106 ip.load("IPython.external.mglob")
107 107
108 108 # win32 is crippled w/o cygwin, try to help it a little bit
109 109 if sys.platform == 'win32':
110 110 if 'cygwin' in os.environ['PATH'].lower():
111 111 # use the colors of cygwin ls (recommended)
112 112 ip.defalias('d', 'ls -F --color=auto')
113 113 else:
114 114 # get icp, imv, imkdir, igrep, irm,...
115 115 ip.load('ipy_fsops')
116 116
117 117 # and the next best thing to real 'ls -F'
118 118 ip.defalias('d','dir /w /og /on')
119 119
120 ip.set_hook('input_prefilter', dotslash_prefilter_f)
120 121 extend_shell_behavior(ip)
121 122
122 123 class LastArgFinder:
123 124 """ Allow $LA to work as "last argument of previous command", like $! in bash
124 125
125 126 To call this in normal IPython code, do LA()
126 127 """
127 128 def __call__(self, hist_idx = None):
128 129 ip = ipapi.get()
129 130 if hist_idx is None:
130 131 return str(self)
131 132 return ip.IP.input_hist_raw[hist_idx].strip().split()[-1]
132 133 def __str__(self):
133 134 ip = ipapi.get()
134 135 for cmd in reversed(ip.IP.input_hist_raw):
135 136 parts = cmd.strip().split()
136 137 if len(parts) < 2 or parts[-1] in ['$LA', 'LA()']:
137 138 continue
138 139 return parts[-1]
139 140 return ""
140 141
141
142
143
142 def dotslash_prefilter_f(self,line):
143 """ ./foo now runs foo as system command
144
145 Removes the need for doing !./foo
146 """
147 import IPython.genutils
148 if line.startswith("./"):
149 return "_ip.system(" + IPython.genutils.make_quoted_expr(line)+")"
150 raise ipapi.TryNext
144 151
145 152 # XXX You do not need to understand the next function!
146 153 # This should probably be moved out of profile
147 154
148 155 def extend_shell_behavior(ip):
149 156
150 157 # Instead of making signature a global variable tie it to IPSHELL.
151 158 # In future if it is required to distinguish between different
152 159 # shells we can assign a signature per shell basis
153 160 ip.IP.__sig__ = 0xa005
154 161 # mark the IPSHELL with this signature
155 162 ip.IP.user_ns['__builtins__'].__dict__['__sig__'] = ip.IP.__sig__
156 163
157 164 from IPython.Itpl import ItplNS
158 165 from IPython.genutils import shell
159 166 # utility to expand user variables via Itpl
160 167 # xxx do something sensible with depth?
161 168 ip.IP.var_expand = lambda cmd, lvars=None, depth=2: \
162 169 str(ItplNS(cmd, ip.IP.user_ns, get_locals()))
163 170
164 171 def get_locals():
165 172 """ Substituting a variable through Itpl deep inside the IPSHELL stack
166 173 requires the knowledge of all the variables in scope upto the last
167 174 IPSHELL frame. This routine simply merges all the local variables
168 175 on the IPSHELL stack without worrying about their scope rules
169 176 """
170 177 import sys
171 178 # note lambda expression constitues a function call
172 179 # hence fno should be incremented by one
173 180 getsig = lambda fno: sys._getframe(fno+1).f_globals \
174 181 ['__builtins__'].__dict__['__sig__']
175 182 getlvars = lambda fno: sys._getframe(fno+1).f_locals
176 183 # trackback until we enter the IPSHELL
177 184 frame_no = 1
178 185 sig = ip.IP.__sig__
179 186 fsig = ~sig
180 187 while fsig != sig :
181 188 try:
182 189 fsig = getsig(frame_no)
183 190 except (AttributeError, KeyError):
184 191 frame_no += 1
185 192 except ValueError:
186 193 # stack is depleted
187 194 # call did not originate from IPSHELL
188 195 return {}
189 196 first_frame = frame_no
190 197 # walk further back until we exit from IPSHELL or deplete stack
191 198 try:
192 199 while(sig == getsig(frame_no+1)):
193 200 frame_no += 1
194 201 except (AttributeError, KeyError, ValueError):
195 202 pass
196 203 # merge the locals from top down hence overriding
197 204 # any re-definitions of variables, functions etc.
198 205 lvars = {}
199 206 for fno in range(frame_no, first_frame-1, -1):
200 207 lvars.update(getlvars(fno))
201 208 #print '\n'*5, first_frame, frame_no, '\n', lvars, '\n'*5 #dbg
202 209 return lvars
203 210
204 211 def _runlines(lines):
205 212 """Run a string of one or more lines of source.
206 213
207 214 This method is capable of running a string containing multiple source
208 215 lines, as if they had been entered at the IPython prompt. Since it
209 216 exposes IPython's processing machinery, the given strings can contain
210 217 magic calls (%magic), special shell access (!cmd), etc."""
211 218
212 219 # We must start with a clean buffer, in case this is run from an
213 220 # interactive IPython session (via a magic, for example).
214 221 ip.IP.resetbuffer()
215 222 lines = lines.split('\n')
216 223 more = 0
217 224 command = ''
218 225 for line in lines:
219 226 # skip blank lines so we don't mess up the prompt counter, but do
220 227 # NOT skip even a blank line if we are in a code block (more is
221 228 # true)
222 229 # if command is not empty trim the line
223 230 if command != '' :
224 231 line = line.strip()
225 232 # add the broken line to the command
226 233 if line and line[-1] == '\\' :
227 234 command += line[0:-1] + ' '
228 235 more = True
229 236 continue
230 237 else :
231 238 # add the last (current) line to the command
232 239 command += line
233 240 if command or more:
234 241 # push to raw history, so hist line numbers stay in sync
235 242 ip.IP.input_hist_raw.append("# " + command + "\n")
236 243
237 244 more = ip.IP.push(ip.IP.prefilter(command,more))
238 245 command = ''
239 246 # IPython's runsource returns None if there was an error
240 247 # compiling the code. This allows us to stop processing right
241 248 # away, so the user gets the error message at the right place.
242 249 if more is None:
243 250 break
244 251 # final newline in case the input didn't have it, so that the code
245 252 # actually does get executed
246 253 if more:
247 254 ip.IP.push('\n')
248 255
249 256 ip.IP.runlines = _runlines
250 257
251 258 main()
@@ -1,107 +1,116 b''
1 1 """ User configuration file for IPython
2 2
3 3 This is a more flexible and safe way to configure ipython than *rc files
4 4 (ipythonrc, ipythonrc-pysh etc.)
5 5
6 6 This file is always imported on ipython startup. You can import the
7 7 ipython extensions you need here (see IPython/Extensions directory).
8 8
9 9 Feel free to edit this file to customize your ipython experience.
10 10
11 11 Note that as such this file does nothing, for backwards compatibility.
12 12 Consult e.g. file 'ipy_profile_sh.py' for an example of the things
13 13 you can do here.
14 14
15 15 See http://ipython.scipy.org/moin/IpythonExtensionApi for detailed
16 16 description on what you could do here.
17 17 """
18 18
19 19 # Most of your config files and extensions will probably start with this import
20 20
21 21 import IPython.ipapi
22 22 ip = IPython.ipapi.get()
23 23
24 24 # You probably want to uncomment this if you did %upgrade -nolegacy
25 25 # import ipy_defaults
26 26
27 27 import os
28 28
29 29 def main():
30 30
31 31 # uncomment if you want to get ipython -p sh behaviour
32 32 # without having to use command line switches
33 33 # import ipy_profile_sh
34 34
35 35 # Configure your favourite editor?
36 36 # Good idea e.g. for %edit os.path.isfile
37 37
38 38 #import ipy_editors
39 39
40 40 # Choose one of these:
41 41
42 42 #ipy_editors.scite()
43 43 #ipy_editors.scite('c:/opt/scite/scite.exe')
44 44 #ipy_editors.komodo()
45 45 #ipy_editors.idle()
46 46 # ... or many others, try 'ipy_editors??' after import to see them
47 47
48 48 # Or roll your own:
49 49 #ipy_editors.install_editor("c:/opt/jed +$line $file")
50 50
51 51
52 52 o = ip.options
53 53 # An example on how to set options
54 54 #o.autocall = 1
55 55 o.system_verbose = 0
56 56
57 57 #import_all("os sys")
58 58 #execf('~/_ipython/ns.py')
59 59
60 60
61 61 # -- prompt
62 62 # A different, more compact set of prompts from the default ones, that
63 63 # always show your current location in the filesystem:
64 64
65 65 #o.prompt_in1 = r'\C_LightBlue[\C_LightCyan\Y2\C_LightBlue]\C_Normal\n\C_Green|\#>'
66 66 #o.prompt_in2 = r'.\D: '
67 67 #o.prompt_out = r'[\#] '
68 68
69 69 # Try one of these color settings if you can't read the text easily
70 70 # autoexec is a list of IPython commands to execute on startup
71 71 #o.autoexec.append('%colors LightBG')
72 72 #o.autoexec.append('%colors NoColor')
73 73 #o.autoexec.append('%colors Linux')
74 74
75 75 # for sane integer division that converts to float (1/2 == 0.5)
76 76 #o.autoexec.append('from __future__ import division')
77 77
78 78 # For %tasks and %kill
79 79 #import jobctrl
80 80
81 81 # For autoreloading of modules (%autoreload, %aimport)
82 82 #import ipy_autoreload
83 83
84 84 # For winpdb support (%wdb)
85 85 #import ipy_winpdb
86 86
87 87 # For bzr completer, requires bzrlib (the python installation of bzr)
88 88 #ip.load('ipy_bzr')
89 89
90 90 # Tab completer that is not quite so picky (i.e.
91 91 # "foo".<TAB> and str(2).<TAB> will work). Complete
92 92 # at your own risk!
93 93 #import ipy_greedycompleter
94 94
95 # If you are on Linux, you may be annoyed by
96 # "Display all N possibilities? (y or n)" on tab completion,
97 # as well as the paging through "more". Uncomment the following
98 # lines to disable that behaviour
99 #import readline
100 #readline.parse_and_bind('set completion-query-items 1000')
101 #readline.parse_and_bind('set page-completions no')
102
103
95 104
96 105
97 106 # some config helper functions you can use
98 107 def import_all(modules):
99 108 """ Usage: import_all("os sys") """
100 109 for m in modules.split():
101 110 ip.ex("from %s import *" % m)
102 111
103 112 def execf(fname):
104 113 """ Execute a file in user namespace """
105 114 ip.ex('execfile("%s")' % os.path.expanduser(fname))
106 115
107 116 main()
@@ -1,395 +1,425 b''
1 1 # encoding: utf-8
2 # -*- test-case-name: ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
2 # -*- test-case-name: IPython.frontend.cocoa.tests.test_cocoa_frontend -*-
3 3
4 """PyObjC classes to provide a Cocoa frontend to the ipython1.kernel.engineservice.EngineService.
4 """PyObjC classes to provide a Cocoa frontend to the
5 IPython.kernel.engineservice.IEngineBase.
5 6
6 The Cocoa frontend is divided into two classes:
7 - IPythonCocoaController
8 - IPythonCLITextViewDelegate
7 To add an IPython interpreter to a cocoa app, instantiate an
8 IPythonCocoaController in a XIB and connect its textView outlet to an
9 NSTextView instance in your UI. That's it.
9 10
10 To add an IPython interpreter to a cocoa app, instantiate both of these classes in an XIB...[FINISH]
11 Author: Barry Wark
11 12 """
12 13
13 14 __docformat__ = "restructuredtext en"
14 15
15 #-------------------------------------------------------------------------------
16 # Copyright (C) 2008 Barry Wark <barrywark@gmail.com>
16 #-----------------------------------------------------------------------------
17 # Copyright (C) 2008 The IPython Development Team
17 18 #
18 19 # Distributed under the terms of the BSD License. The full license is in
19 20 # the file COPYING, distributed as part of this software.
20 #-------------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
21 22
22 #-------------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
23 24 # Imports
24 #-------------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
25 26
26 27 import objc
27 28 import uuid
28 29
29 30 from Foundation import NSObject, NSMutableArray, NSMutableDictionary,\
30 31 NSLog, NSNotificationCenter, NSMakeRange,\
31 32 NSLocalizedString, NSIntersectionRange
32 33
33 34 from AppKit import NSApplicationWillTerminateNotification, NSBeep,\
34 35 NSTextView, NSRulerView, NSVerticalRuler
35 36
36 37 from pprint import saferepr
37 38
38 39 import IPython
39 from IPython.kernel.engineservice import EngineService, ThreadedEngineService
40 from IPython.kernel.engineservice import ThreadedEngineService
40 41 from IPython.frontend.frontendbase import FrontEndBase
41 42
42 43 from twisted.internet.threads import blockingCallFromThread
43 44 from twisted.python.failure import Failure
44 45
45 #-------------------------------------------------------------------------------
46 #------------------------------------------------------------------------------
46 47 # Classes to implement the Cocoa frontend
47 #-------------------------------------------------------------------------------
48 #------------------------------------------------------------------------------
48 49
49 50 # TODO:
50 # 1. use MultiEngineClient and out-of-process engine rather than ThreadedEngineService?
51 # 1. use MultiEngineClient and out-of-process engine rather than
52 # ThreadedEngineService?
51 53 # 2. integrate Xgrid launching of engines
52 54
53 55
54 56
55 57
56 58 class IPythonCocoaController(NSObject, FrontEndBase):
57 59 userNS = objc.ivar() #mirror of engine.user_ns (key=>str(value))
58 60 waitingForEngine = objc.ivar().bool()
59 61 textView = objc.IBOutlet()
60 62
61 63 def init(self):
62 64 self = super(IPythonCocoaController, self).init()
63 65 FrontEndBase.__init__(self, engine=ThreadedEngineService())
64 66 if(self != None):
65 67 self._common_init()
66 68
67 69 return self
68 70
69 71 def _common_init(self):
70 72 """_common_init"""
71 73
72 74 self.userNS = NSMutableDictionary.dictionary()
73 75 self.waitingForEngine = False
74 76
75 77 self.lines = {}
76 78 self.tabSpaces = 4
77 79 self.tabUsesSpaces = True
78 self.currentBlockID = self.nextBlockID()
80 self.currentBlockID = self.next_block_ID()
79 81 self.blockRanges = {} # blockID=>NSRange
80 82
81 83
82 84 def awakeFromNib(self):
83 85 """awakeFromNib"""
84 86
85 87 self._common_init()
86 88
87 89 # Start the IPython engine
88 90 self.engine.startService()
89 91 NSLog('IPython engine started')
90 92
91 93 # Register for app termination
92 NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self,
93 'appWillTerminate:',
94 NSApplicationWillTerminateNotification,
95 None)
94 nc = NSNotificationCenter.defaultCenter()
95 nc.addObserver_selector_name_object_(
96 self,
97 'appWillTerminate:',
98 NSApplicationWillTerminateNotification,
99 None)
96 100
97 101 self.textView.setDelegate_(self)
98 102 self.textView.enclosingScrollView().setHasVerticalRuler_(True)
99 self.verticalRulerView = NSRulerView.alloc().initWithScrollView_orientation_(
100 self.textView.enclosingScrollView(),
101 NSVerticalRuler)
103 r = NSRulerView.alloc().initWithScrollView_orientation_(
104 self.textView.enclosingScrollView(),
105 NSVerticalRuler)
106 self.verticalRulerView = r
102 107 self.verticalRulerView.setClientView_(self.textView)
103 self.startCLIForTextView()
108 self._start_cli_banner()
104 109
105 110
106 111 def appWillTerminate_(self, notification):
107 112 """appWillTerminate"""
108 113
109 114 self.engine.stopService()
110 115
111 116
112 117 def complete(self, token):
113 118 """Complete token in engine's user_ns
114 119
115 120 Parameters
116 121 ----------
117 122 token : string
118 123
119 124 Result
120 125 ------
121 Deferred result of ipython1.kernel.engineservice.IEngineInteractive.complete
126 Deferred result of
127 IPython.kernel.engineservice.IEngineBase.complete
122 128 """
123 129
124 130 return self.engine.complete(token)
125 131
126 132
127 133 def execute(self, block, blockID=None):
128 134 self.waitingForEngine = True
129 135 self.willChangeValueForKey_('commandHistory')
130 136 d = super(IPythonCocoaController, self).execute(block, blockID)
131 d.addBoth(self._engineDone)
132 d.addCallback(self._updateUserNS)
137 d.addBoth(self._engine_done)
138 d.addCallback(self._update_user_ns)
133 139
134 140 return d
135 141
136 142
137 def _engineDone(self, x):
143 def _engine_done(self, x):
138 144 self.waitingForEngine = False
139 145 self.didChangeValueForKey_('commandHistory')
140 146 return x
141 147
142 def _updateUserNS(self, result):
148 def _update_user_ns(self, result):
143 149 """Update self.userNS from self.engine's namespace"""
144 150 d = self.engine.keys()
145 d.addCallback(self._getEngineNamepsaceValuesForKeys)
151 d.addCallback(self._get_engine_namespace_values_for_keys)
146 152
147 153 return result
148 154
149 155
150 def _getEngineNamepsaceValuesForKeys(self, keys):
156 def _get_engine_namespace_values_for_keys(self, keys):
151 157 d = self.engine.pull(keys)
152 d.addCallback(self._storeEngineNamespaceValues, keys=keys)
158 d.addCallback(self._store_engine_namespace_values, keys=keys)
153 159
154 160
155 def _storeEngineNamespaceValues(self, values, keys=[]):
161 def _store_engine_namespace_values(self, values, keys=[]):
156 162 assert(len(values) == len(keys))
157 163 self.willChangeValueForKey_('userNS')
158 164 for (k,v) in zip(keys,values):
159 165 self.userNS[k] = saferepr(v)
160 166 self.didChangeValueForKey_('userNS')
161 167
162 168
163 def startCLIForTextView(self):
169 def update_cell_prompt(self, result):
170 if(isinstance(result, Failure)):
171 blockID = result.blockID
172 else:
173 blockID = result['blockID']
174
175
176 self.insert_text(self.input_prompt(result=result),
177 textRange=NSMakeRange(self.blockRanges[blockID].location,0),
178 scrollToVisible=False
179 )
180
181 return result
182
183
184 def render_result(self, result):
185 blockID = result['blockID']
186 inputRange = self.blockRanges[blockID]
187 del self.blockRanges[blockID]
188
189 #print inputRange,self.current_block_range()
190 self.insert_text('\n' +
191 self.output_prompt(result) +
192 result.get('display',{}).get('pprint','') +
193 '\n\n',
194 textRange=NSMakeRange(inputRange.location+inputRange.length,
195 0))
196 return result
197
198
199 def render_error(self, failure):
200 self.insert_text('\n\n'+str(failure)+'\n\n')
201 self.start_new_block()
202 return failure
203
204
205 def _start_cli_banner(self):
164 206 """Print banner"""
165 207
166 banner = """IPython1 %s -- An enhanced Interactive Python.""" % IPython.__version__
208 banner = """IPython1 %s -- An enhanced Interactive Python.""" % \
209 IPython.__version__
167 210
168 211 self.insert_text(banner + '\n\n')
169 212
170 # NSTextView/IPythonTextView delegate methods
213
214 def start_new_block(self):
215 """"""
216
217 self.currentBlockID = self.next_block_ID()
218
219
220
221 def next_block_ID(self):
222
223 return uuid.uuid4()
224
225 def current_block_range(self):
226 return self.blockRanges.get(self.currentBlockID,
227 NSMakeRange(self.textView.textStorage().length(),
228 0))
229
230 def current_block(self):
231 """The current block's text"""
232
233 return self.text_for_range(self.current_block_range())
234
235 def text_for_range(self, textRange):
236 """text_for_range"""
237
238 ts = self.textView.textStorage()
239 return ts.string().substringWithRange_(textRange)
240
241 def current_line(self):
242 block = self.text_for_range(self.current_block_range())
243 block = block.split('\n')
244 return block[-1]
245
246
247 def insert_text(self, string=None, textRange=None, scrollToVisible=True):
248 """Insert text into textView at textRange, updating blockRanges
249 as necessary
250 """
251
252 if(textRange == None):
253 #range for end of text
254 textRange = NSMakeRange(self.textView.textStorage().length(), 0)
255
256 for r in self.blockRanges.itervalues():
257 intersection = NSIntersectionRange(r,textRange)
258 if(intersection.length == 0): #ranges don't intersect
259 if r.location >= textRange.location:
260 r.location += len(string)
261 else: #ranges intersect
262 if(r.location <= textRange.location):
263 assert(intersection.length == textRange.length)
264 r.length += textRange.length
265 else:
266 r.location += intersection.length
267
268 self.textView.replaceCharactersInRange_withString_(
269 textRange, string)
270 self.textView.setSelectedRange_(
271 NSMakeRange(textRange.location+len(string), 0))
272 if(scrollToVisible):
273 self.textView.scrollRangeToVisible_(textRange)
274
275
276
277
278 def replace_current_block_with_string(self, textView, string):
279 textView.replaceCharactersInRange_withString_(
280 self.current_block_range(),
281 string)
282 self.current_block_range().length = len(string)
283 r = NSMakeRange(textView.textStorage().length(), 0)
284 textView.scrollRangeToVisible_(r)
285 textView.setSelectedRange_(r)
286
287
288 def current_indent_string(self):
289 """returns string for indent or None if no indent"""
290
291 if(len(self.current_block()) > 0):
292 lines = self.current_block().split('\n')
293 currentIndent = len(lines[-1]) - len(lines[-1])
294 if(currentIndent == 0):
295 currentIndent = self.tabSpaces
296
297 if(self.tabUsesSpaces):
298 result = ' ' * currentIndent
299 else:
300 result = '\t' * (currentIndent/self.tabSpaces)
301 else:
302 result = None
303
304 return result
305
306
307 # NSTextView delegate methods...
171 308 def textView_doCommandBySelector_(self, textView, selector):
172 309 assert(textView == self.textView)
173 310 NSLog("textView_doCommandBySelector_: "+selector)
174 311
175 312
176 313 if(selector == 'insertNewline:'):
177 indent = self.currentIndentString()
314 indent = self.current_indent_string()
178 315 if(indent):
179 line = indent + self.currentLine()
316 line = indent + self.current_line()
180 317 else:
181 line = self.currentLine()
318 line = self.current_line()
182 319
183 if(self.is_complete(self.currentBlock())):
184 self.execute(self.currentBlock(),
320 if(self.is_complete(self.current_block())):
321 self.execute(self.current_block(),
185 322 blockID=self.currentBlockID)
186 self.startNewBlock()
323 self.start_new_block()
187 324
188 325 return True
189 326
190 327 return False
191 328
192 329 elif(selector == 'moveUp:'):
193 prevBlock = self.get_history_previous(self.currentBlock())
330 prevBlock = self.get_history_previous(self.current_block())
194 331 if(prevBlock != None):
195 self.replaceCurrentBlockWithString(textView, prevBlock)
332 self.replace_current_block_with_string(textView, prevBlock)
196 333 else:
197 334 NSBeep()
198 335 return True
199 336
200 337 elif(selector == 'moveDown:'):
201 338 nextBlock = self.get_history_next()
202 339 if(nextBlock != None):
203 self.replaceCurrentBlockWithString(textView, nextBlock)
340 self.replace_current_block_with_string(textView, nextBlock)
204 341 else:
205 342 NSBeep()
206 343 return True
207 344
208 345 elif(selector == 'moveToBeginningOfParagraph:'):
209 textView.setSelectedRange_(NSMakeRange(self.currentBlockRange().location, 0))
346 textView.setSelectedRange_(NSMakeRange(
347 self.current_block_range().location,
348 0))
210 349 return True
211 350 elif(selector == 'moveToEndOfParagraph:'):
212 textView.setSelectedRange_(NSMakeRange(self.currentBlockRange().location + \
213 self.currentBlockRange().length, 0))
351 textView.setSelectedRange_(NSMakeRange(
352 self.current_block_range().location + \
353 self.current_block_range().length, 0))
214 354 return True
215 355 elif(selector == 'deleteToEndOfParagraph:'):
216 if(textView.selectedRange().location <= self.currentBlockRange().location):
356 if(textView.selectedRange().location <= \
357 self.current_block_range().location):
217 358 # Intersect the selected range with the current line range
218 if(self.currentBlockRange().length < 0):
359 if(self.current_block_range().length < 0):
219 360 self.blockRanges[self.currentBlockID].length = 0
220 361
221 362 r = NSIntersectionRange(textView.rangesForUserTextChange()[0],
222 self.currentBlockRange())
363 self.current_block_range())
223 364
224 365 if(r.length > 0): #no intersection
225 366 textView.setSelectedRange_(r)
226 367
227 368 return False # don't actually handle the delete
228 369
229 370 elif(selector == 'insertTab:'):
230 if(len(self.currentLine().strip()) == 0): #only white space
371 if(len(self.current_line().strip()) == 0): #only white space
231 372 return False
232 373 else:
233 374 self.textView.complete_(self)
234 375 return True
235 376
236 377 elif(selector == 'deleteBackward:'):
237 378 #if we're at the beginning of the current block, ignore
238 if(textView.selectedRange().location == self.currentBlockRange().location):
379 if(textView.selectedRange().location == \
380 self.current_block_range().location):
239 381 return True
240 382 else:
241 self.currentBlockRange().length-=1
383 self.current_block_range().length-=1
242 384 return False
243 385 return False
244 386
245 387
246 def textView_shouldChangeTextInRanges_replacementStrings_(self, textView, ranges, replacementStrings):
388 def textView_shouldChangeTextInRanges_replacementStrings_(self,
389 textView, ranges, replacementStrings):
247 390 """
248 391 Delegate method for NSTextView.
249 392
250 Refuse change text in ranges not at end, but make those changes at end.
393 Refuse change text in ranges not at end, but make those changes at
394 end.
251 395 """
252 396
253 #print 'textView_shouldChangeTextInRanges_replacementStrings_:',ranges,replacementStrings
254 397 assert(len(ranges) == len(replacementStrings))
255 398 allow = True
256 399 for r,s in zip(ranges, replacementStrings):
257 400 r = r.rangeValue()
258 401 if(textView.textStorage().length() > 0 and
259 r.location < self.currentBlockRange().location):
402 r.location < self.current_block_range().location):
260 403 self.insert_text(s)
261 404 allow = False
262 405
263 406
264 self.blockRanges.setdefault(self.currentBlockID, self.currentBlockRange()).length += len(s)
407 self.blockRanges.setdefault(self.currentBlockID,
408 self.current_block_range()).length +=\
409 len(s)
265 410
266 411 return allow
267 412
268 def textView_completions_forPartialWordRange_indexOfSelectedItem_(self, textView, words, charRange, index):
413 def textView_completions_forPartialWordRange_indexOfSelectedItem_(self,
414 textView, words, charRange, index):
269 415 try:
270 token = textView.textStorage().string().substringWithRange_(charRange)
416 ts = textView.textStorage()
417 token = ts.string().substringWithRange_(charRange)
271 418 completions = blockingCallFromThread(self.complete, token)
272 419 except:
273 420 completions = objc.nil
274 421 NSBeep()
275 422
276 423 return (completions,0)
277 424
278
279 def startNewBlock(self):
280 """"""
281
282 self.currentBlockID = self.nextBlockID()
283
284
285
286 def nextBlockID(self):
287
288 return uuid.uuid4()
289
290 def currentBlockRange(self):
291 return self.blockRanges.get(self.currentBlockID, NSMakeRange(self.textView.textStorage().length(), 0))
292
293 def currentBlock(self):
294 """The current block's text"""
295
296 return self.textForRange(self.currentBlockRange())
297
298 def textForRange(self, textRange):
299 """textForRange"""
300
301 return self.textView.textStorage().string().substringWithRange_(textRange)
302
303 def currentLine(self):
304 block = self.textForRange(self.currentBlockRange())
305 block = block.split('\n')
306 return block[-1]
307
308 def update_cell_prompt(self, result):
309 if(isinstance(result, Failure)):
310 blockID = result.blockID
311 else:
312 blockID = result['blockID']
313
314
315 self.insert_text(self.input_prompt(result=result),
316 textRange=NSMakeRange(self.blockRanges[blockID].location,0),
317 scrollToVisible=False
318 )
319
320 return result
321
322
323 def render_result(self, result):
324 blockID = result['blockID']
325 inputRange = self.blockRanges[blockID]
326 del self.blockRanges[blockID]
327
328 #print inputRange,self.currentBlockRange()
329 self.insert_text('\n' +
330 self.output_prompt(result) +
331 result.get('display',{}).get('pprint','') +
332 '\n\n',
333 textRange=NSMakeRange(inputRange.location+inputRange.length, 0))
334 return result
335
336
337 def render_error(self, failure):
338 self.insert_text('\n\n'+str(failure)+'\n\n')
339 self.startNewBlock()
340 return failure
341
342
343 def insert_text(self, string=None, textRange=None, scrollToVisible=True):
344 """Insert text into textView at textRange, updating blockRanges as necessary"""
345
346 if(textRange == None):
347 textRange = NSMakeRange(self.textView.textStorage().length(), 0) #range for end of text
348
349 for r in self.blockRanges.itervalues():
350 intersection = NSIntersectionRange(r,textRange)
351 if(intersection.length == 0): #ranges don't intersect
352 if r.location >= textRange.location:
353 r.location += len(string)
354 else: #ranges intersect
355 if(r.location <= textRange.location):
356 assert(intersection.length == textRange.length)
357 r.length += textRange.length
358 else:
359 r.location += intersection.length
360
361 self.textView.replaceCharactersInRange_withString_(textRange, string) #textStorage().string()
362 self.textView.setSelectedRange_(NSMakeRange(textRange.location+len(string), 0))
363 if(scrollToVisible):
364 self.textView.scrollRangeToVisible_(textRange)
365
366
367
368 def replaceCurrentBlockWithString(self, textView, string):
369 textView.replaceCharactersInRange_withString_(self.currentBlockRange(),
370 string)
371 self.currentBlockRange().length = len(string)
372 r = NSMakeRange(textView.textStorage().length(), 0)
373 textView.scrollRangeToVisible_(r)
374 textView.setSelectedRange_(r)
375
376
377 def currentIndentString(self):
378 """returns string for indent or None if no indent"""
379
380 if(len(self.currentBlock()) > 0):
381 lines = self.currentBlock().split('\n')
382 currentIndent = len(lines[-1]) - len(lines[-1])
383 if(currentIndent == 0):
384 currentIndent = self.tabSpaces
385
386 if(self.tabUsesSpaces):
387 result = ' ' * currentIndent
388 else:
389 result = '\t' * (currentIndent/self.tabSpaces)
390 else:
391 result = None
392
393 return result
394
395 425
@@ -1,77 +1,72 b''
1 1 # encoding: utf-8
2 """This file contains unittests for the ipython1.frontend.cocoa.cocoa_frontend module.
3
4 Things that should be tested:
5
6 - IPythonCocoaController instantiates an IEngineInteractive
7 - IPythonCocoaController executes code on the engine
8 - IPythonCocoaController mirrors engine's user_ns
2 """This file contains unittests for the
3 IPython.frontend.cocoa.cocoa_frontend module.
9 4 """
10 5 __docformat__ = "restructuredtext en"
11
12 #-------------------------------------------------------------------------------
13 # Copyright (C) 2005 Fernando Perez <fperez@colorado.edu>
14 # Brian E Granger <ellisonbg@gmail.com>
15 # Benjamin Ragan-Kelley <benjaminrk@gmail.com>
16 #
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
19 #-------------------------------------------------------------------------------
20
21 #-------------------------------------------------------------------------------
22 # Imports
23 #-------------------------------------------------------------------------------
24 from IPython.kernel.core.interpreter import Interpreter
6
7 #---------------------------------------------------------------------------
8 # Copyright (C) 2005 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #---------------------------------------------------------------------------
13
14 #---------------------------------------------------------------------------
15 # Imports
16 #---------------------------------------------------------------------------
17 from IPython.kernel.core.interpreter import Interpreter
25 18 import IPython.kernel.engineservice as es
26 19 from IPython.testing.util import DeferredTestCase
27 20 from twisted.internet.defer import succeed
28 21 from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController
29 22
30 23 from Foundation import NSMakeRect
31 24 from AppKit import NSTextView, NSScrollView
32 25
33 26 class TestIPythonCocoaControler(DeferredTestCase):
34 27 """Tests for IPythonCocoaController"""
35 28
36 29 def setUp(self):
37 30 self.controller = IPythonCocoaController.alloc().init()
38 31 self.engine = es.EngineService()
39 32 self.engine.startService()
40 33
41 34
42 35 def tearDown(self):
43 36 self.controller = None
44 37 self.engine.stopService()
45 38
46 39 def testControllerExecutesCode(self):
47 40 code ="""5+5"""
48 41 expected = Interpreter().execute(code)
49 42 del expected['number']
50 43 def removeNumberAndID(result):
51 44 del result['number']
52 45 del result['id']
53 46 return result
54 self.assertDeferredEquals(self.controller.execute(code).addCallback(removeNumberAndID), expected)
47 self.assertDeferredEquals(
48 self.controller.execute(code).addCallback(removeNumberAndID),
49 expected)
55 50
56 51 def testControllerMirrorsUserNSWithValuesAsStrings(self):
57 52 code = """userns1=1;userns2=2"""
58 53 def testControllerUserNS(result):
59 54 self.assertEquals(self.controller.userNS['userns1'], 1)
60 55 self.assertEquals(self.controller.userNS['userns2'], 2)
61 56
62 57 self.controller.execute(code).addCallback(testControllerUserNS)
63 58
64 59
65 60 def testControllerInstantiatesIEngine(self):
66 61 self.assert_(es.IEngineBase.providedBy(self.controller.engine))
67 62
68 63 def testControllerCompletesToken(self):
69 64 code = """longNameVariable=10"""
70 65 def testCompletes(result):
71 66 self.assert_("longNameVariable" in result)
72 67
73 68 def testCompleteToken(result):
74 69 self.controller.complete("longNa").addCallback(testCompletes)
75 70
76 71 self.controller.execute(code).addCallback(testCompletes)
77 72
@@ -1,326 +1,352 b''
1 1 # encoding: utf-8
2 # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*-
2 3 """
3 frontendbase provides an interface and base class for GUI frontends for IPython.kernel/IPython.kernel.core.
4 frontendbase provides an interface and base class for GUI frontends for
5 IPython.kernel/IPython.kernel.core.
4 6
5 7 Frontend implementations will likely want to subclass FrontEndBase.
6 8
7 9 Author: Barry Wark
8 10 """
9 11 __docformat__ = "restructuredtext en"
10 12
11 13 #-------------------------------------------------------------------------------
12 14 # Copyright (C) 2008 The IPython Development Team
13 15 #
14 16 # Distributed under the terms of the BSD License. The full license is in
15 17 # the file COPYING, distributed as part of this software.
16 18 #-------------------------------------------------------------------------------
17 19
18 20 #-------------------------------------------------------------------------------
19 21 # Imports
20 22 #-------------------------------------------------------------------------------
21 23 import string
22 24 import uuid
23 25 import _ast
24 26
25 27 import zope.interface as zi
26 28
27 29 from IPython.kernel.core.history import FrontEndHistory
28 30 from IPython.kernel.core.util import Bunch
29 31 from IPython.kernel.engineservice import IEngineCore
30 32
31 33 from twisted.python.failure import Failure
32 34
33 35 ##############################################################################
34 36 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
35 37 # not
36 38
37 39 rc = Bunch()
38 40 rc.prompt_in1 = r'In [$number]: '
39 41 rc.prompt_in2 = r'...'
40 42 rc.prompt_out = r'Out [$number]: '
41 43
42 44 ##############################################################################
43 45
44 46 class IFrontEndFactory(zi.Interface):
45 47 """Factory interface for frontends."""
46 48
47 49 def __call__(engine=None, history=None):
48 50 """
49 51 Parameters:
50 52 interpreter : IPython.kernel.engineservice.IEngineCore
51 53 """
52 54
53 55 pass
54 56
55 57
56 58
57 59 class IFrontEnd(zi.Interface):
58 60 """Interface for frontends. All methods return t.i.d.Deferred"""
59 61
60 zi.Attribute("input_prompt_template", "string.Template instance substituteable with execute result.")
61 zi.Attribute("output_prompt_template", "string.Template instance substituteable with execute result.")
62 zi.Attribute("continuation_prompt_template", "string.Template instance substituteable with execute result.")
62 zi.Attribute("input_prompt_template", "string.Template instance\
63 substituteable with execute result.")
64 zi.Attribute("output_prompt_template", "string.Template instance\
65 substituteable with execute result.")
66 zi.Attribute("continuation_prompt_template", "string.Template instance\
67 substituteable with execute result.")
63 68
64 69 def update_cell_prompt(self, result):
65 70 """Subclass may override to update the input prompt for a block.
66 Since this method will be called as a twisted.internet.defer.Deferred's callback,
67 implementations should return result when finished."""
71 Since this method will be called as a
72 twisted.internet.defer.Deferred's callback,
73 implementations should return result when finished.
74
75 NB: result is a failure if the execute returned a failre.
76 To get the blockID, you should do something like::
77 if(isinstance(result, twisted.python.failure.Failure)):
78 blockID = result.blockID
79 else:
80 blockID = result['blockID']
81 """
68 82
69 83 pass
70 84
71 85 def render_result(self, result):
72 """Render the result of an execute call. Implementors may choose the method of rendering.
73 For example, a notebook-style frontend might render a Chaco plot inline.
86 """Render the result of an execute call. Implementors may choose the
87 method of rendering.
88 For example, a notebook-style frontend might render a Chaco plot
89 inline.
74 90
75 91 Parameters:
76 92 result : dict (result of IEngineBase.execute )
77 93
78 94 Result:
79 95 Output of frontend rendering
80 96 """
81 97
82 98 pass
83 99
84 100 def render_error(self, failure):
85 """Subclasses must override to render the failure. Since this method will be called as a
86 twisted.internet.defer.Deferred's callback, implementations should return result
87 when finished."""
101 """Subclasses must override to render the failure. Since this method
102 ill be called as a twisted.internet.defer.Deferred's callback,
103 implementations should return result when finished.
104 """
88 105
89 106 pass
90 107
91 108
92 109 def input_prompt(result={}):
93 """Returns the input prompt by subsituting into self.input_prompt_template"""
110 """Returns the input prompt by subsituting into
111 self.input_prompt_template
112 """
94 113 pass
95 114
96 115 def output_prompt(result):
97 """Returns the output prompt by subsituting into self.output_prompt_template"""
116 """Returns the output prompt by subsituting into
117 self.output_prompt_template
118 """
98 119
99 120 pass
100 121
101 122 def continuation_prompt():
102 """Returns the continuation prompt by subsituting into self.continuation_prompt_template"""
123 """Returns the continuation prompt by subsituting into
124 self.continuation_prompt_template
125 """
103 126
104 127 pass
105 128
106 129 def is_complete(block):
107 130 """Returns True if block is complete, False otherwise."""
108 131
109 132 pass
110 133
111 134 def compile_ast(block):
112 135 """Compiles block to an _ast.AST"""
113 136
114 137 pass
115 138
116 139
117 140 def get_history_previous(currentBlock):
118 141 """Returns the block previous in the history. Saves currentBlock if
119 142 the history_cursor is currently at the end of the input history"""
120 143 pass
121 144
122 145 def get_history_next():
123 146 """Returns the next block in the history."""
124 147
125 148 pass
126 149
127 150
128 151 class FrontEndBase(object):
129 152 """
130 153 FrontEndBase manages the state tasks for a CLI frontend:
131 154 - Input and output history management
132 155 - Input/continuation and output prompt generation
133 156
134 157 Some issues (due to possibly unavailable engine):
135 158 - How do we get the current cell number for the engine?
136 159 - How do we handle completions?
137 160 """
138 161
139 162 zi.implements(IFrontEnd)
140 163 zi.classProvides(IFrontEndFactory)
141 164
142 165 history_cursor = 0
143 166
144 167 current_indent_level = 0
145 168
146 169
147 170 input_prompt_template = string.Template(rc.prompt_in1)
148 171 output_prompt_template = string.Template(rc.prompt_out)
149 172 continuation_prompt_template = string.Template(rc.prompt_in2)
150 173
151 174 def __init__(self, engine=None, history=None):
152 175 assert(engine==None or IEngineCore.providedBy(engine))
153 176 self.engine = IEngineCore(engine)
154 177 if history is None:
155 178 self.history = FrontEndHistory(input_cache=[''])
156 179 else:
157 180 self.history = history
158 181
159 182
160 183 def input_prompt(self, result={}):
161 184 """Returns the current input prompt
162 185
163 186 It would be great to use ipython1.core.prompts.Prompt1 here
164 187 """
165 188
166 189 result.setdefault('number','')
167 190
168 191 return self.input_prompt_template.safe_substitute(result)
169 192
170 193
171 194 def continuation_prompt(self):
172 195 """Returns the current continuation prompt"""
173 196
174 197 return self.continuation_prompt_template.safe_substitute()
175 198
176 199 def output_prompt(self, result):
177 200 """Returns the output prompt for result"""
178 201
179 202 return self.output_prompt_template.safe_substitute(result)
180 203
181 204
182 205 def is_complete(self, block):
183 206 """Determine if block is complete.
184 207
185 208 Parameters
186 209 block : string
187 210
188 211 Result
189 212 True if block can be sent to the engine without compile errors.
190 213 False otherwise.
191 214 """
192 215
193 216 try:
194 217 ast = self.compile_ast(block)
195 218 except:
196 219 return False
197 220
198 221 lines = block.split('\n')
199 222 return (len(lines)==1 or str(lines[-1])=='')
200 223
201 224
202 225 def compile_ast(self, block):
203 226 """Compile block to an AST
204 227
205 228 Parameters:
206 229 block : str
207 230
208 231 Result:
209 232 AST
210 233
211 234 Throws:
212 235 Exception if block cannot be compiled
213 236 """
214 237
215 238 return compile(block, "<string>", "exec", _ast.PyCF_ONLY_AST)
216 239
217 240
218 241 def execute(self, block, blockID=None):
219 242 """Execute the block and return result.
220 243
221 244 Parameters:
222 245 block : {str, AST}
223 246 blockID : any
224 Caller may provide an ID to identify this block. result['blockID'] := blockID
247 Caller may provide an ID to identify this block.
248 result['blockID'] := blockID
225 249
226 250 Result:
227 251 Deferred result of self.interpreter.execute
228 252 """
229 253
230 254 if(not self.is_complete(block)):
231 255 return Failure(Exception("Block is not compilable"))
232 256
233 257 if(blockID == None):
234 258 blockID = uuid.uuid4() #random UUID
235 259
236 260 d = self.engine.execute(block)
237 261 d.addCallback(self._add_history, block=block)
238 262 d.addBoth(self._add_block_id, blockID)
239 263 d.addBoth(self.update_cell_prompt)
240 264 d.addCallbacks(self.render_result, errback=self.render_error)
241 265
242 266 return d
243 267
244 268
245 269 def _add_block_id(self, result, blockID):
246 """Add the blockID to result or failure. Unfortunatley, we have to treat failures
247 differently than result dicts
270 """Add the blockID to result or failure. Unfortunatley, we have to
271 treat failures differently than result dicts.
248 272 """
249 273
250 274 if(isinstance(result, Failure)):
251 275 result.blockID = blockID
252 276 else:
253 277 result['blockID'] = blockID
254 278
255 279 return result
256 280
257 281 def _add_history(self, result, block=None):
258 282 """Add block to the history"""
259 283
260 284 assert(block != None)
261 285 self.history.add_items([block])
262 286 self.history_cursor += 1
263 287
264 288 return result
265 289
266 290
267 291 def get_history_previous(self, currentBlock):
268 292 """ Returns previous history string and decrement history cursor.
269 293 """
270 294 command = self.history.get_history_item(self.history_cursor - 1)
271 295
272 296 if command is not None:
273 297 if(self.history_cursor == len(self.history.input_cache)):
274 298 self.history.input_cache[self.history_cursor] = currentBlock
275 299 self.history_cursor -= 1
276 300 return command
277 301
278 302
279 303 def get_history_next(self):
280 304 """ Returns next history string and increment history cursor.
281 305 """
282 306 command = self.history.get_history_item(self.history_cursor+1)
283 307
284 308 if command is not None:
285 309 self.history_cursor += 1
286 310 return command
287 311
288 312 ###
289 313 # Subclasses probably want to override these methods...
290 314 ###
291 315
292 316 def update_cell_prompt(self, result):
293 317 """Subclass may override to update the input prompt for a block.
294 Since this method will be called as a twisted.internet.defer.Deferred's callback,
295 implementations should return result when finished.
318 Since this method will be called as a
319 twisted.internet.defer.Deferred's callback, implementations should
320 return result when finished.
296 321
297 NP: result is a failure if the execute returned a failre. To get the blockID, you should
298 do something like::
322 NB: result is a failure if the execute returned a failre.
323 To get the blockID, you should do something like::
299 324 if(isinstance(result, twisted.python.failure.Failure)):
300 325 blockID = result.blockID
301 326 else:
302 327 blockID = result['blockID']
303 328
304 329
305 330 """
306 331
307 332 return result
308 333
309 334
310 335 def render_result(self, result):
311 """Subclasses must override to render result. Since this method will be called as a
312 twisted.internet.defer.Deferred's callback, implementations should return result
313 when finished."""
336 """Subclasses must override to render result. Since this method will
337 be called as a twisted.internet.defer.Deferred's callback,
338 implementations should return result when finished.
339 """
314 340
315 341 return result
316 342
317 343
318 344 def render_error(self, failure):
319 """Subclasses must override to render the failure. Since this method will be called as a
320 twisted.internet.defer.Deferred's callback, implementations should return result
321 when finished."""
345 """Subclasses must override to render the failure. Since this method
346 will be called as a twisted.internet.defer.Deferred's callback,
347 implementations should return result when finished."""
322 348
323 349 return failure
324 350
325 351
326 352
@@ -1,149 +1,151 b''
1 1 # encoding: utf-8
2 2
3 3 """This file contains unittests for the frontendbase module."""
4 4
5 5 __docformat__ = "restructuredtext en"
6 6
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
13
14 #-------------------------------------------------------------------------------
15 # Imports
16 #-------------------------------------------------------------------------------
7 #---------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #---------------------------------------------------------------------------
13
14 #---------------------------------------------------------------------------
15 # Imports
16 #---------------------------------------------------------------------------
17 17
18 18 import unittest
19 19 from IPython.frontend import frontendbase
20 20 from IPython.kernel.engineservice import EngineService
21 21
22 22 class FrontEndCallbackChecker(frontendbase.FrontEndBase):
23 23 """FrontEndBase subclass for checking callbacks"""
24 24 def __init__(self, engine=None, history=None):
25 super(FrontEndCallbackChecker, self).__init__(engine=engine, history=history)
25 super(FrontEndCallbackChecker, self).__init__(engine=engine,
26 history=history)
26 27 self.updateCalled = False
27 28 self.renderResultCalled = False
28 29 self.renderErrorCalled = False
29 30
30 31 def update_cell_prompt(self, result):
31 32 self.updateCalled = True
32 33 return result
33 34
34 35 def render_result(self, result):
35 36 self.renderResultCalled = True
36 37 return result
37 38
38 39
39 40 def render_error(self, failure):
40 41 self.renderErrorCalled = True
41 42 return failure
42 43
43 44
44 45
45 46
46 47 class TestFrontendBase(unittest.TestCase):
47 48 def setUp(self):
48 49 """Setup the EngineService and FrontEndBase"""
49 50
50 51 self.fb = FrontEndCallbackChecker(engine=EngineService())
51 52
52 53
53 54 def test_implements_IFrontEnd(self):
54 assert(frontendbase.IFrontEnd.implementedBy(frontendbase.FrontEndBase))
55 assert(frontendbase.IFrontEnd.implementedBy(
56 frontendbase.FrontEndBase))
55 57
56 58
57 59 def test_is_complete_returns_False_for_incomplete_block(self):
58 60 """"""
59 61
60 62 block = """def test(a):"""
61 63
62 64 assert(self.fb.is_complete(block) == False)
63 65
64 66 def test_is_complete_returns_True_for_complete_block(self):
65 67 """"""
66 68
67 69 block = """def test(a): pass"""
68 70
69 71 assert(self.fb.is_complete(block))
70 72
71 73 block = """a=3"""
72 74
73 75 assert(self.fb.is_complete(block))
74 76
75 77
76 78 def test_blockID_added_to_result(self):
77 79 block = """3+3"""
78 80
79 81 d = self.fb.execute(block, blockID='TEST_ID')
80 82
81 83 d.addCallback(self.checkBlockID, expected='TEST_ID')
82 84
83 85 def test_blockID_added_to_failure(self):
84 86 block = "raise Exception()"
85 87
86 88 d = self.fb.execute(block,blockID='TEST_ID')
87 89 d.addErrback(self.checkFailureID, expected='TEST_ID')
88 90
89 91 def checkBlockID(self, result, expected=""):
90 92 assert(result['blockID'] == expected)
91 93
92 94
93 95 def checkFailureID(self, failure, expected=""):
94 96 assert(failure.blockID == expected)
95 97
96 98
97 99 def test_callbacks_added_to_execute(self):
98 100 """test that
99 101 update_cell_prompt
100 102 render_result
101 103
102 104 are added to execute request
103 105 """
104 106
105 107 d = self.fb.execute("10+10")
106 108 d.addCallback(self.checkCallbacks)
107 109
108 110
109 111 def checkCallbacks(self, result):
110 112 assert(self.fb.updateCalled)
111 113 assert(self.fb.renderResultCalled)
112 114
113 115
114 116 def test_error_callback_added_to_execute(self):
115 117 """test that render_error called on execution error"""
116 118
117 119 d = self.fb.execute("raise Exception()")
118 120 d.addCallback(self.checkRenderError)
119 121
120 122 def checkRenderError(self, result):
121 123 assert(self.fb.renderErrorCalled)
122 124
123 125 def test_history_returns_expected_block(self):
124 126 """Make sure history browsing doesn't fail"""
125 127
126 128 blocks = ["a=1","a=2","a=3"]
127 129 for b in blocks:
128 130 d = self.fb.execute(b)
129 131
130 132 # d is now the deferred for the last executed block
131 133 d.addCallback(self.historyTests, blocks)
132 134
133 135
134 136 def historyTests(self, result, blocks):
135 137 """historyTests"""
136 138
137 139 assert(len(blocks) >= 3)
138 140 assert(self.fb.get_history_previous("") == blocks[-2])
139 141 assert(self.fb.get_history_previous("") == blocks[-3])
140 142 assert(self.fb.get_history_next() == blocks[-2])
141 143
142 144
143 145 def test_history_returns_none_at_startup(self):
144 146 """test_history_returns_none_at_startup"""
145 147
146 148 assert(self.fb.get_history_previous("")==None)
147 149 assert(self.fb.get_history_next()==None)
148 150
149 151
@@ -1,2046 +1,2097 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 General purpose utilities.
4 4
5 5 This is a grab-bag of stuff I find useful in most programs I write. Some of
6 6 these things are also convenient when working at the command line.
7 7
8 8 $Id: genutils.py 2998 2008-01-31 10:06:04Z vivainio $"""
9 9
10 10 #*****************************************************************************
11 11 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
12 12 #
13 13 # Distributed under the terms of the BSD License. The full license is in
14 14 # the file COPYING, distributed as part of this software.
15 15 #*****************************************************************************
16 16
17 17 from IPython import Release
18 18 __author__ = '%s <%s>' % Release.authors['Fernando']
19 19 __license__ = Release.license
20 20
21 21 #****************************************************************************
22 22 # required modules from the Python standard library
23 23 import __main__
24 24 import commands
25 25 try:
26 26 import doctest
27 27 except ImportError:
28 28 pass
29 29 import os
30 import platform
30 31 import re
31 32 import shlex
32 33 import shutil
34 import subprocess
33 35 import sys
34 36 import tempfile
35 37 import time
36 38 import types
37 39 import warnings
38 40
39 41 # Curses and termios are Unix-only modules
40 42 try:
41 43 import curses
42 44 # We need termios as well, so if its import happens to raise, we bail on
43 45 # using curses altogether.
44 46 import termios
45 47 except ImportError:
46 48 USE_CURSES = False
47 49 else:
48 50 # Curses on Solaris may not be complete, so we can't use it there
49 51 USE_CURSES = hasattr(curses,'initscr')
50 52
51 53 # Other IPython utilities
52 54 import IPython
53 55 from IPython.Itpl import Itpl,itpl,printpl
54 56 from IPython import DPyGetOpt, platutils
55 57 from IPython.generics import result_display
56 58 import IPython.ipapi
57 59 from IPython.external.path import path
58 60 if os.name == "nt":
59 61 from IPython.winconsole import get_console_size
60 62
61 63 try:
62 64 set
63 65 except:
64 66 from sets import Set as set
65 67
66 68
67 69 #****************************************************************************
68 70 # Exceptions
69 71 class Error(Exception):
70 72 """Base class for exceptions in this module."""
71 73 pass
72 74
73 75 #----------------------------------------------------------------------------
74 76 class IOStream:
75 77 def __init__(self,stream,fallback):
76 78 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
77 79 stream = fallback
78 80 self.stream = stream
79 81 self._swrite = stream.write
80 82 self.flush = stream.flush
81 83
82 84 def write(self,data):
83 85 try:
84 86 self._swrite(data)
85 87 except:
86 88 try:
87 89 # print handles some unicode issues which may trip a plain
88 90 # write() call. Attempt to emulate write() by using a
89 91 # trailing comma
90 92 print >> self.stream, data,
91 93 except:
92 94 # if we get here, something is seriously broken.
93 95 print >> sys.stderr, \
94 96 'ERROR - failed to write data to stream:', self.stream
95 97
96 98 def close(self):
97 99 pass
98 100
99 101
100 102 class IOTerm:
101 103 """ Term holds the file or file-like objects for handling I/O operations.
102 104
103 105 These are normally just sys.stdin, sys.stdout and sys.stderr but for
104 106 Windows they can can replaced to allow editing the strings before they are
105 107 displayed."""
106 108
107 109 # In the future, having IPython channel all its I/O operations through
108 110 # this class will make it easier to embed it into other environments which
109 111 # are not a normal terminal (such as a GUI-based shell)
110 112 def __init__(self,cin=None,cout=None,cerr=None):
111 113 self.cin = IOStream(cin,sys.stdin)
112 114 self.cout = IOStream(cout,sys.stdout)
113 115 self.cerr = IOStream(cerr,sys.stderr)
114 116
115 117 # Global variable to be used for all I/O
116 118 Term = IOTerm()
117 119
118 120 import IPython.rlineimpl as readline
119 121 # Remake Term to use the readline i/o facilities
120 122 if sys.platform == 'win32' and readline.have_readline:
121 123
122 124 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
123 125
124 126
125 127 #****************************************************************************
126 128 # Generic warning/error printer, used by everything else
127 129 def warn(msg,level=2,exit_val=1):
128 130 """Standard warning printer. Gives formatting consistency.
129 131
130 132 Output is sent to Term.cerr (sys.stderr by default).
131 133
132 134 Options:
133 135
134 136 -level(2): allows finer control:
135 137 0 -> Do nothing, dummy function.
136 138 1 -> Print message.
137 139 2 -> Print 'WARNING:' + message. (Default level).
138 140 3 -> Print 'ERROR:' + message.
139 141 4 -> Print 'FATAL ERROR:' + message and trigger a sys.exit(exit_val).
140 142
141 143 -exit_val (1): exit value returned by sys.exit() for a level 4
142 144 warning. Ignored for all other levels."""
143 145
144 146 if level>0:
145 147 header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
146 148 print >> Term.cerr, '%s%s' % (header[level],msg)
147 149 if level == 4:
148 150 print >> Term.cerr,'Exiting.\n'
149 151 sys.exit(exit_val)
150 152
151 153 def info(msg):
152 154 """Equivalent to warn(msg,level=1)."""
153 155
154 156 warn(msg,level=1)
155 157
156 158 def error(msg):
157 159 """Equivalent to warn(msg,level=3)."""
158 160
159 161 warn(msg,level=3)
160 162
161 163 def fatal(msg,exit_val=1):
162 164 """Equivalent to warn(msg,exit_val=exit_val,level=4)."""
163 165
164 166 warn(msg,exit_val=exit_val,level=4)
165 167
166 168 #---------------------------------------------------------------------------
167 169 # Debugging routines
168 170 #
169 171 def debugx(expr,pre_msg=''):
170 172 """Print the value of an expression from the caller's frame.
171 173
172 174 Takes an expression, evaluates it in the caller's frame and prints both
173 175 the given expression and the resulting value (as well as a debug mark
174 176 indicating the name of the calling function. The input must be of a form
175 177 suitable for eval().
176 178
177 179 An optional message can be passed, which will be prepended to the printed
178 180 expr->value pair."""
179 181
180 182 cf = sys._getframe(1)
181 183 print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
182 184 eval(expr,cf.f_globals,cf.f_locals))
183 185
184 186 # deactivate it by uncommenting the following line, which makes it a no-op
185 187 #def debugx(expr,pre_msg=''): pass
186 188
187 189 #----------------------------------------------------------------------------
188 190 StringTypes = types.StringTypes
189 191
190 192 # Basic timing functionality
191 193
192 194 # If possible (Unix), use the resource module instead of time.clock()
193 195 try:
194 196 import resource
195 197 def clocku():
196 198 """clocku() -> floating point number
197 199
198 200 Return the *USER* CPU time in seconds since the start of the process.
199 201 This is done via a call to resource.getrusage, so it avoids the
200 202 wraparound problems in time.clock()."""
201 203
202 204 return resource.getrusage(resource.RUSAGE_SELF)[0]
203 205
204 206 def clocks():
205 207 """clocks() -> floating point number
206 208
207 209 Return the *SYSTEM* CPU time in seconds since the start of the process.
208 210 This is done via a call to resource.getrusage, so it avoids the
209 211 wraparound problems in time.clock()."""
210 212
211 213 return resource.getrusage(resource.RUSAGE_SELF)[1]
212 214
213 215 def clock():
214 216 """clock() -> floating point number
215 217
216 218 Return the *TOTAL USER+SYSTEM* CPU time in seconds since the start of
217 219 the process. This is done via a call to resource.getrusage, so it
218 220 avoids the wraparound problems in time.clock()."""
219 221
220 222 u,s = resource.getrusage(resource.RUSAGE_SELF)[:2]
221 223 return u+s
222 224
223 225 def clock2():
224 226 """clock2() -> (t_user,t_system)
225 227
226 228 Similar to clock(), but return a tuple of user/system times."""
227 229 return resource.getrusage(resource.RUSAGE_SELF)[:2]
228 230
229 231 except ImportError:
230 232 # There is no distinction of user/system time under windows, so we just use
231 233 # time.clock() for everything...
232 234 clocku = clocks = clock = time.clock
233 235 def clock2():
234 236 """Under windows, system CPU time can't be measured.
235 237
236 238 This just returns clock() and zero."""
237 239 return time.clock(),0.0
238 240
239 241 def timings_out(reps,func,*args,**kw):
240 242 """timings_out(reps,func,*args,**kw) -> (t_total,t_per_call,output)
241 243
242 244 Execute a function reps times, return a tuple with the elapsed total
243 245 CPU time in seconds, the time per call and the function's output.
244 246
245 247 Under Unix, the return value is the sum of user+system time consumed by
246 248 the process, computed via the resource module. This prevents problems
247 249 related to the wraparound effect which the time.clock() function has.
248 250
249 251 Under Windows the return value is in wall clock seconds. See the
250 252 documentation for the time module for more details."""
251 253
252 254 reps = int(reps)
253 255 assert reps >=1, 'reps must be >= 1'
254 256 if reps==1:
255 257 start = clock()
256 258 out = func(*args,**kw)
257 259 tot_time = clock()-start
258 260 else:
259 261 rng = xrange(reps-1) # the last time is executed separately to store output
260 262 start = clock()
261 263 for dummy in rng: func(*args,**kw)
262 264 out = func(*args,**kw) # one last time
263 265 tot_time = clock()-start
264 266 av_time = tot_time / reps
265 267 return tot_time,av_time,out
266 268
267 269 def timings(reps,func,*args,**kw):
268 270 """timings(reps,func,*args,**kw) -> (t_total,t_per_call)
269 271
270 272 Execute a function reps times, return a tuple with the elapsed total CPU
271 273 time in seconds and the time per call. These are just the first two values
272 274 in timings_out()."""
273 275
274 276 return timings_out(reps,func,*args,**kw)[0:2]
275 277
276 278 def timing(func,*args,**kw):
277 279 """timing(func,*args,**kw) -> t_total
278 280
279 281 Execute a function once, return the elapsed total CPU time in
280 282 seconds. This is just the first value in timings_out()."""
281 283
282 284 return timings_out(1,func,*args,**kw)[0]
283 285
284 286 #****************************************************************************
285 287 # file and system
286 288
287 289 def arg_split(s,posix=False):
288 290 """Split a command line's arguments in a shell-like manner.
289 291
290 292 This is a modified version of the standard library's shlex.split()
291 293 function, but with a default of posix=False for splitting, so that quotes
292 294 in inputs are respected."""
293 295
294 296 # XXX - there may be unicode-related problems here!!! I'm not sure that
295 297 # shlex is truly unicode-safe, so it might be necessary to do
296 298 #
297 299 # s = s.encode(sys.stdin.encoding)
298 300 #
299 301 # first, to ensure that shlex gets a normal string. Input from anyone who
300 302 # knows more about unicode and shlex than I would be good to have here...
301 303 lex = shlex.shlex(s, posix=posix)
302 304 lex.whitespace_split = True
303 305 return list(lex)
304 306
305 307 def system(cmd,verbose=0,debug=0,header=''):
306 308 """Execute a system command, return its exit status.
307 309
308 310 Options:
309 311
310 312 - verbose (0): print the command to be executed.
311 313
312 314 - debug (0): only print, do not actually execute.
313 315
314 316 - header (''): Header to print on screen prior to the executed command (it
315 317 is only prepended to the command, no newlines are added).
316 318
317 319 Note: a stateful version of this function is available through the
318 320 SystemExec class."""
319 321
320 322 stat = 0
321 323 if verbose or debug: print header+cmd
322 324 sys.stdout.flush()
323 325 if not debug: stat = os.system(cmd)
324 326 return stat
325 327
326 328 def abbrev_cwd():
327 329 """ Return abbreviated version of cwd, e.g. d:mydir """
328 330 cwd = os.getcwd().replace('\\','/')
329 331 drivepart = ''
330 332 tail = cwd
331 333 if sys.platform == 'win32':
332 334 if len(cwd) < 4:
333 335 return cwd
334 336 drivepart,tail = os.path.splitdrive(cwd)
335 337
336 338
337 339 parts = tail.split('/')
338 340 if len(parts) > 2:
339 341 tail = '/'.join(parts[-2:])
340 342
341 343 return (drivepart + (
342 344 cwd == '/' and '/' or tail))
343 345
344 346
345 347 # This function is used by ipython in a lot of places to make system calls.
346 348 # We need it to be slightly different under win32, due to the vagaries of
347 349 # 'network shares'. A win32 override is below.
348 350
349 351 def shell(cmd,verbose=0,debug=0,header=''):
350 352 """Execute a command in the system shell, always return None.
351 353
352 354 Options:
353 355
354 356 - verbose (0): print the command to be executed.
355 357
356 358 - debug (0): only print, do not actually execute.
357 359
358 360 - header (''): Header to print on screen prior to the executed command (it
359 361 is only prepended to the command, no newlines are added).
360 362
361 363 Note: this is similar to genutils.system(), but it returns None so it can
362 364 be conveniently used in interactive loops without getting the return value
363 365 (typically 0) printed many times."""
364 366
365 367 stat = 0
366 368 if verbose or debug: print header+cmd
367 369 # flush stdout so we don't mangle python's buffering
368 370 sys.stdout.flush()
369 371
370 372 if not debug:
371 373 platutils.set_term_title("IPy " + cmd)
372 374 os.system(cmd)
373 375 platutils.set_term_title("IPy " + abbrev_cwd())
374 376
375 377 # override shell() for win32 to deal with network shares
376 378 if os.name in ('nt','dos'):
377 379
378 380 shell_ori = shell
379 381
380 382 def shell(cmd,verbose=0,debug=0,header=''):
381 383 if os.getcwd().startswith(r"\\"):
382 384 path = os.getcwd()
383 385 # change to c drive (cannot be on UNC-share when issuing os.system,
384 386 # as cmd.exe cannot handle UNC addresses)
385 387 os.chdir("c:")
386 388 # issue pushd to the UNC-share and then run the command
387 389 try:
388 390 shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header)
389 391 finally:
390 392 os.chdir(path)
391 393 else:
392 394 shell_ori(cmd,verbose,debug,header)
393 395
394 396 shell.__doc__ = shell_ori.__doc__
395 397
396 398 def getoutput(cmd,verbose=0,debug=0,header='',split=0):
397 399 """Dummy substitute for perl's backquotes.
398 400
399 401 Executes a command and returns the output.
400 402
401 403 Accepts the same arguments as system(), plus:
402 404
403 405 - split(0): if true, the output is returned as a list split on newlines.
404 406
405 407 Note: a stateful version of this function is available through the
406 408 SystemExec class.
407 409
408 410 This is pretty much deprecated and rarely used,
409 411 genutils.getoutputerror may be what you need.
410 412
411 413 """
412 414
413 415 if verbose or debug: print header+cmd
414 416 if not debug:
415 417 output = os.popen(cmd).read()
416 418 # stipping last \n is here for backwards compat.
417 419 if output.endswith('\n'):
418 420 output = output[:-1]
419 421 if split:
420 422 return output.split('\n')
421 423 else:
422 424 return output
423 425
424 426 def getoutputerror(cmd,verbose=0,debug=0,header='',split=0):
425 427 """Return (standard output,standard error) of executing cmd in a shell.
426 428
427 429 Accepts the same arguments as system(), plus:
428 430
429 431 - split(0): if true, each of stdout/err is returned as a list split on
430 432 newlines.
431 433
432 434 Note: a stateful version of this function is available through the
433 435 SystemExec class."""
434 436
435 437 if verbose or debug: print header+cmd
436 438 if not cmd:
437 439 if split:
438 440 return [],[]
439 441 else:
440 442 return '',''
441 443 if not debug:
442 444 pin,pout,perr = os.popen3(cmd)
443 445 tout = pout.read().rstrip()
444 446 terr = perr.read().rstrip()
445 447 pin.close()
446 448 pout.close()
447 449 perr.close()
448 450 if split:
449 451 return tout.split('\n'),terr.split('\n')
450 452 else:
451 453 return tout,terr
452 454
453 455 # for compatibility with older naming conventions
454 456 xsys = system
455 457 bq = getoutput
456 458
457 459 class SystemExec:
458 460 """Access the system and getoutput functions through a stateful interface.
459 461
460 462 Note: here we refer to the system and getoutput functions from this
461 463 library, not the ones from the standard python library.
462 464
463 465 This class offers the system and getoutput functions as methods, but the
464 466 verbose, debug and header parameters can be set for the instance (at
465 467 creation time or later) so that they don't need to be specified on each
466 468 call.
467 469
468 470 For efficiency reasons, there's no way to override the parameters on a
469 471 per-call basis other than by setting instance attributes. If you need
470 472 local overrides, it's best to directly call system() or getoutput().
471 473
472 474 The following names are provided as alternate options:
473 475 - xsys: alias to system
474 476 - bq: alias to getoutput
475 477
476 478 An instance can then be created as:
477 479 >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ')
478 480
479 481 And used as:
480 482 >>> sysexec.xsys('pwd')
481 483 >>> dirlist = sysexec.bq('ls -l')
482 484 """
483 485
484 486 def __init__(self,verbose=0,debug=0,header='',split=0):
485 487 """Specify the instance's values for verbose, debug and header."""
486 488 setattr_list(self,'verbose debug header split')
487 489
488 490 def system(self,cmd):
489 491 """Stateful interface to system(), with the same keyword parameters."""
490 492
491 493 system(cmd,self.verbose,self.debug,self.header)
492 494
493 495 def shell(self,cmd):
494 496 """Stateful interface to shell(), with the same keyword parameters."""
495 497
496 498 shell(cmd,self.verbose,self.debug,self.header)
497 499
498 500 xsys = system # alias
499 501
500 502 def getoutput(self,cmd):
501 503 """Stateful interface to getoutput()."""
502 504
503 505 return getoutput(cmd,self.verbose,self.debug,self.header,self.split)
504 506
505 507 def getoutputerror(self,cmd):
506 508 """Stateful interface to getoutputerror()."""
507 509
508 510 return getoutputerror(cmd,self.verbose,self.debug,self.header,self.split)
509 511
510 512 bq = getoutput # alias
511 513
512 514 #-----------------------------------------------------------------------------
513 515 def mutex_opts(dict,ex_op):
514 516 """Check for presence of mutually exclusive keys in a dict.
515 517
516 518 Call: mutex_opts(dict,[[op1a,op1b],[op2a,op2b]...]"""
517 519 for op1,op2 in ex_op:
518 520 if op1 in dict and op2 in dict:
519 521 raise ValueError,'\n*** ERROR in Arguments *** '\
520 522 'Options '+op1+' and '+op2+' are mutually exclusive.'
521 523
522 524 #-----------------------------------------------------------------------------
523 525 def get_py_filename(name):
524 526 """Return a valid python filename in the current directory.
525 527
526 528 If the given name is not a file, it adds '.py' and searches again.
527 529 Raises IOError with an informative message if the file isn't found."""
528 530
529 531 name = os.path.expanduser(name)
530 532 if not os.path.isfile(name) and not name.endswith('.py'):
531 533 name += '.py'
532 534 if os.path.isfile(name):
533 535 return name
534 536 else:
535 537 raise IOError,'File `%s` not found.' % name
536 538
537 539 #-----------------------------------------------------------------------------
538 540 def filefind(fname,alt_dirs = None):
539 541 """Return the given filename either in the current directory, if it
540 542 exists, or in a specified list of directories.
541 543
542 544 ~ expansion is done on all file and directory names.
543 545
544 546 Upon an unsuccessful search, raise an IOError exception."""
545 547
546 548 if alt_dirs is None:
547 549 try:
548 550 alt_dirs = get_home_dir()
549 551 except HomeDirError:
550 552 alt_dirs = os.getcwd()
551 553 search = [fname] + list_strings(alt_dirs)
552 554 search = map(os.path.expanduser,search)
553 555 #print 'search list for',fname,'list:',search # dbg
554 556 fname = search[0]
555 557 if os.path.isfile(fname):
556 558 return fname
557 559 for direc in search[1:]:
558 560 testname = os.path.join(direc,fname)
559 561 #print 'testname',testname # dbg
560 562 if os.path.isfile(testname):
561 563 return testname
562 564 raise IOError,'File' + `fname` + \
563 565 ' not found in current or supplied directories:' + `alt_dirs`
564 566
565 567 #----------------------------------------------------------------------------
566 568 def file_read(filename):
567 569 """Read a file and close it. Returns the file source."""
568 570 fobj = open(filename,'r');
569 571 source = fobj.read();
570 572 fobj.close()
571 573 return source
572 574
573 575 def file_readlines(filename):
574 576 """Read a file and close it. Returns the file source using readlines()."""
575 577 fobj = open(filename,'r');
576 578 lines = fobj.readlines();
577 579 fobj.close()
578 580 return lines
579 581
580 582 #----------------------------------------------------------------------------
581 583 def target_outdated(target,deps):
582 584 """Determine whether a target is out of date.
583 585
584 586 target_outdated(target,deps) -> 1/0
585 587
586 588 deps: list of filenames which MUST exist.
587 589 target: single filename which may or may not exist.
588 590
589 591 If target doesn't exist or is older than any file listed in deps, return
590 592 true, otherwise return false.
591 593 """
592 594 try:
593 595 target_time = os.path.getmtime(target)
594 596 except os.error:
595 597 return 1
596 598 for dep in deps:
597 599 dep_time = os.path.getmtime(dep)
598 600 if dep_time > target_time:
599 601 #print "For target",target,"Dep failed:",dep # dbg
600 602 #print "times (dep,tar):",dep_time,target_time # dbg
601 603 return 1
602 604 return 0
603 605
604 606 #-----------------------------------------------------------------------------
605 607 def target_update(target,deps,cmd):
606 608 """Update a target with a given command given a list of dependencies.
607 609
608 610 target_update(target,deps,cmd) -> runs cmd if target is outdated.
609 611
610 612 This is just a wrapper around target_outdated() which calls the given
611 613 command if target is outdated."""
612 614
613 615 if target_outdated(target,deps):
614 616 xsys(cmd)
615 617
616 618 #----------------------------------------------------------------------------
617 619 def unquote_ends(istr):
618 620 """Remove a single pair of quotes from the endpoints of a string."""
619 621
620 622 if not istr:
621 623 return istr
622 624 if (istr[0]=="'" and istr[-1]=="'") or \
623 625 (istr[0]=='"' and istr[-1]=='"'):
624 626 return istr[1:-1]
625 627 else:
626 628 return istr
627 629
628 630 #----------------------------------------------------------------------------
629 631 def process_cmdline(argv,names=[],defaults={},usage=''):
630 632 """ Process command-line options and arguments.
631 633
632 634 Arguments:
633 635
634 636 - argv: list of arguments, typically sys.argv.
635 637
636 638 - names: list of option names. See DPyGetOpt docs for details on options
637 639 syntax.
638 640
639 641 - defaults: dict of default values.
640 642
641 643 - usage: optional usage notice to print if a wrong argument is passed.
642 644
643 645 Return a dict of options and a list of free arguments."""
644 646
645 647 getopt = DPyGetOpt.DPyGetOpt()
646 648 getopt.setIgnoreCase(0)
647 649 getopt.parseConfiguration(names)
648 650
649 651 try:
650 652 getopt.processArguments(argv)
651 653 except DPyGetOpt.ArgumentError, exc:
652 654 print usage
653 655 warn('"%s"' % exc,level=4)
654 656
655 657 defaults.update(getopt.optionValues)
656 658 args = getopt.freeValues
657 659
658 660 return defaults,args
659 661
660 662 #----------------------------------------------------------------------------
661 663 def optstr2types(ostr):
662 664 """Convert a string of option names to a dict of type mappings.
663 665
664 666 optstr2types(str) -> {None:'string_opts',int:'int_opts',float:'float_opts'}
665 667
666 668 This is used to get the types of all the options in a string formatted
667 669 with the conventions of DPyGetOpt. The 'type' None is used for options
668 670 which are strings (they need no further conversion). This function's main
669 671 use is to get a typemap for use with read_dict().
670 672 """
671 673
672 674 typeconv = {None:'',int:'',float:''}
673 675 typemap = {'s':None,'i':int,'f':float}
674 676 opt_re = re.compile(r'([\w]*)([^:=]*:?=?)([sif]?)')
675 677
676 678 for w in ostr.split():
677 679 oname,alias,otype = opt_re.match(w).groups()
678 680 if otype == '' or alias == '!': # simple switches are integers too
679 681 otype = 'i'
680 682 typeconv[typemap[otype]] += oname + ' '
681 683 return typeconv
682 684
683 685 #----------------------------------------------------------------------------
684 686 def read_dict(filename,type_conv=None,**opt):
685 687
686 688 """Read a dictionary of key=value pairs from an input file, optionally
687 689 performing conversions on the resulting values.
688 690
689 691 read_dict(filename,type_conv,**opt) -> dict
690 692
691 693 Only one value per line is accepted, the format should be
692 694 # optional comments are ignored
693 695 key value\n
694 696
695 697 Args:
696 698
697 699 - type_conv: A dictionary specifying which keys need to be converted to
698 700 which types. By default all keys are read as strings. This dictionary
699 701 should have as its keys valid conversion functions for strings
700 702 (int,long,float,complex, or your own). The value for each key
701 703 (converter) should be a whitespace separated string containing the names
702 704 of all the entries in the file to be converted using that function. For
703 705 keys to be left alone, use None as the conversion function (only needed
704 706 with purge=1, see below).
705 707
706 708 - opt: dictionary with extra options as below (default in parens)
707 709
708 710 purge(0): if set to 1, all keys *not* listed in type_conv are purged out
709 711 of the dictionary to be returned. If purge is going to be used, the
710 712 set of keys to be left as strings also has to be explicitly specified
711 713 using the (non-existent) conversion function None.
712 714
713 715 fs(None): field separator. This is the key/value separator to be used
714 716 when parsing the file. The None default means any whitespace [behavior
715 717 of string.split()].
716 718
717 719 strip(0): if 1, strip string values of leading/trailinig whitespace.
718 720
719 721 warn(1): warning level if requested keys are not found in file.
720 722 - 0: silently ignore.
721 723 - 1: inform but proceed.
722 724 - 2: raise KeyError exception.
723 725
724 726 no_empty(0): if 1, remove keys with whitespace strings as a value.
725 727
726 728 unique([]): list of keys (or space separated string) which can't be
727 729 repeated. If one such key is found in the file, each new instance
728 730 overwrites the previous one. For keys not listed here, the behavior is
729 731 to make a list of all appearances.
730 732
731 733 Example:
732 734 If the input file test.ini has:
733 735 i 3
734 736 x 4.5
735 737 y 5.5
736 738 s hi ho
737 739 Then:
738 740
739 741 >>> type_conv={int:'i',float:'x',None:'s'}
740 742 >>> read_dict('test.ini')
741 743 {'i': '3', 's': 'hi ho', 'x': '4.5', 'y': '5.5'}
742 744 >>> read_dict('test.ini',type_conv)
743 745 {'i': 3, 's': 'hi ho', 'x': 4.5, 'y': '5.5'}
744 746 >>> read_dict('test.ini',type_conv,purge=1)
745 747 {'i': 3, 's': 'hi ho', 'x': 4.5}
746 748 """
747 749
748 750 # starting config
749 751 opt.setdefault('purge',0)
750 752 opt.setdefault('fs',None) # field sep defaults to any whitespace
751 753 opt.setdefault('strip',0)
752 754 opt.setdefault('warn',1)
753 755 opt.setdefault('no_empty',0)
754 756 opt.setdefault('unique','')
755 757 if type(opt['unique']) in StringTypes:
756 758 unique_keys = qw(opt['unique'])
757 759 elif type(opt['unique']) in (types.TupleType,types.ListType):
758 760 unique_keys = opt['unique']
759 761 else:
760 762 raise ValueError, 'Unique keys must be given as a string, List or Tuple'
761 763
762 764 dict = {}
763 765 # first read in table of values as strings
764 766 file = open(filename,'r')
765 767 for line in file.readlines():
766 768 line = line.strip()
767 769 if len(line) and line[0]=='#': continue
768 770 if len(line)>0:
769 771 lsplit = line.split(opt['fs'],1)
770 772 try:
771 773 key,val = lsplit
772 774 except ValueError:
773 775 key,val = lsplit[0],''
774 776 key = key.strip()
775 777 if opt['strip']: val = val.strip()
776 778 if val == "''" or val == '""': val = ''
777 779 if opt['no_empty'] and (val=='' or val.isspace()):
778 780 continue
779 781 # if a key is found more than once in the file, build a list
780 782 # unless it's in the 'unique' list. In that case, last found in file
781 783 # takes precedence. User beware.
782 784 try:
783 785 if dict[key] and key in unique_keys:
784 786 dict[key] = val
785 787 elif type(dict[key]) is types.ListType:
786 788 dict[key].append(val)
787 789 else:
788 790 dict[key] = [dict[key],val]
789 791 except KeyError:
790 792 dict[key] = val
791 793 # purge if requested
792 794 if opt['purge']:
793 795 accepted_keys = qwflat(type_conv.values())
794 796 for key in dict.keys():
795 797 if key in accepted_keys: continue
796 798 del(dict[key])
797 799 # now convert if requested
798 800 if type_conv==None: return dict
799 801 conversions = type_conv.keys()
800 802 try: conversions.remove(None)
801 803 except: pass
802 804 for convert in conversions:
803 805 for val in qw(type_conv[convert]):
804 806 try:
805 807 dict[val] = convert(dict[val])
806 808 except KeyError,e:
807 809 if opt['warn'] == 0:
808 810 pass
809 811 elif opt['warn'] == 1:
810 812 print >>sys.stderr, 'Warning: key',val,\
811 813 'not found in file',filename
812 814 elif opt['warn'] == 2:
813 815 raise KeyError,e
814 816 else:
815 817 raise ValueError,'Warning level must be 0,1 or 2'
816 818
817 819 return dict
818 820
819 821 #----------------------------------------------------------------------------
820 822 def flag_calls(func):
821 823 """Wrap a function to detect and flag when it gets called.
822 824
823 825 This is a decorator which takes a function and wraps it in a function with
824 826 a 'called' attribute. wrapper.called is initialized to False.
825 827
826 828 The wrapper.called attribute is set to False right before each call to the
827 829 wrapped function, so if the call fails it remains False. After the call
828 830 completes, wrapper.called is set to True and the output is returned.
829 831
830 832 Testing for truth in wrapper.called allows you to determine if a call to
831 833 func() was attempted and succeeded."""
832 834
833 835 def wrapper(*args,**kw):
834 836 wrapper.called = False
835 837 out = func(*args,**kw)
836 838 wrapper.called = True
837 839 return out
838 840
839 841 wrapper.called = False
840 842 wrapper.__doc__ = func.__doc__
841 843 return wrapper
842 844
843 845 #----------------------------------------------------------------------------
844 846 def dhook_wrap(func,*a,**k):
845 847 """Wrap a function call in a sys.displayhook controller.
846 848
847 849 Returns a wrapper around func which calls func, with all its arguments and
848 850 keywords unmodified, using the default sys.displayhook. Since IPython
849 851 modifies sys.displayhook, it breaks the behavior of certain systems that
850 852 rely on the default behavior, notably doctest.
851 853 """
852 854
853 855 def f(*a,**k):
854 856
855 857 dhook_s = sys.displayhook
856 858 sys.displayhook = sys.__displayhook__
857 859 try:
858 860 out = func(*a,**k)
859 861 finally:
860 862 sys.displayhook = dhook_s
861 863
862 864 return out
863 865
864 866 f.__doc__ = func.__doc__
865 867 return f
866 868
867 869 #----------------------------------------------------------------------------
868 870 def doctest_reload():
869 871 """Properly reload doctest to reuse it interactively.
870 872
871 873 This routine:
872 874
873 875 - reloads doctest
874 876
875 877 - resets its global 'master' attribute to None, so that multiple uses of
876 878 the module interactively don't produce cumulative reports.
877 879
878 880 - Monkeypatches its core test runner method to protect it from IPython's
879 881 modified displayhook. Doctest expects the default displayhook behavior
880 882 deep down, so our modification breaks it completely. For this reason, a
881 883 hard monkeypatch seems like a reasonable solution rather than asking
882 884 users to manually use a different doctest runner when under IPython."""
883 885
884 886 import doctest
885 887 reload(doctest)
886 888 doctest.master=None
887 889
888 890 try:
889 891 doctest.DocTestRunner
890 892 except AttributeError:
891 893 # This is only for python 2.3 compatibility, remove once we move to
892 894 # 2.4 only.
893 895 pass
894 896 else:
895 897 doctest.DocTestRunner.run = dhook_wrap(doctest.DocTestRunner.run)
896 898
897 899 #----------------------------------------------------------------------------
898 900 class HomeDirError(Error):
899 901 pass
900 902
901 903 def get_home_dir():
902 904 """Return the closest possible equivalent to a 'home' directory.
903 905
904 906 We first try $HOME. Absent that, on NT it's $HOMEDRIVE\$HOMEPATH.
905 907
906 908 Currently only Posix and NT are implemented, a HomeDirError exception is
907 909 raised for all other OSes. """
908 910
909 911 isdir = os.path.isdir
910 912 env = os.environ
911 913
912 914 # first, check py2exe distribution root directory for _ipython.
913 915 # This overrides all. Normally does not exist.
914 916
915 917 if '\\library.zip\\' in IPython.__file__.lower():
916 918 root, rest = IPython.__file__.lower().split('library.zip')
917 919 if isdir(root + '_ipython'):
918 920 os.environ["IPYKITROOT"] = root.rstrip('\\')
919 921 return root
920 922
921 923 try:
922 924 homedir = env['HOME']
923 925 if not isdir(homedir):
924 926 # in case a user stuck some string which does NOT resolve to a
925 927 # valid path, it's as good as if we hadn't foud it
926 928 raise KeyError
927 929 return homedir
928 930 except KeyError:
929 931 if os.name == 'posix':
930 932 raise HomeDirError,'undefined $HOME, IPython can not proceed.'
931 933 elif os.name == 'nt':
932 934 # For some strange reason, win9x returns 'nt' for os.name.
933 935 try:
934 936 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
935 937 if not isdir(homedir):
936 938 homedir = os.path.join(env['USERPROFILE'])
937 939 if not isdir(homedir):
938 940 raise HomeDirError
939 941 return homedir
940 942 except:
941 943 try:
942 944 # Use the registry to get the 'My Documents' folder.
943 945 import _winreg as wreg
944 946 key = wreg.OpenKey(wreg.HKEY_CURRENT_USER,
945 947 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
946 948 homedir = wreg.QueryValueEx(key,'Personal')[0]
947 949 key.Close()
948 950 if not isdir(homedir):
949 951 e = ('Invalid "Personal" folder registry key '
950 952 'typically "My Documents".\n'
951 953 'Value: %s\n'
952 954 'This is not a valid directory on your system.' %
953 955 homedir)
954 956 raise HomeDirError(e)
955 957 return homedir
956 958 except HomeDirError:
957 959 raise
958 960 except:
959 961 return 'C:\\'
960 962 elif os.name == 'dos':
961 963 # Desperate, may do absurd things in classic MacOS. May work under DOS.
962 964 return 'C:\\'
963 965 else:
964 966 raise HomeDirError,'support for your operating system not implemented.'
965 967
966 968 #****************************************************************************
967 969 # strings and text
968 970
969 971 class LSString(str):
970 972 """String derivative with a special access attributes.
971 973
972 974 These are normal strings, but with the special attributes:
973 975
974 976 .l (or .list) : value as list (split on newlines).
975 977 .n (or .nlstr): original value (the string itself).
976 978 .s (or .spstr): value as whitespace-separated string.
977 979 .p (or .paths): list of path objects
978 980
979 981 Any values which require transformations are computed only once and
980 982 cached.
981 983
982 984 Such strings are very useful to efficiently interact with the shell, which
983 985 typically only understands whitespace-separated options for commands."""
984 986
985 987 def get_list(self):
986 988 try:
987 989 return self.__list
988 990 except AttributeError:
989 991 self.__list = self.split('\n')
990 992 return self.__list
991 993
992 994 l = list = property(get_list)
993 995
994 996 def get_spstr(self):
995 997 try:
996 998 return self.__spstr
997 999 except AttributeError:
998 1000 self.__spstr = self.replace('\n',' ')
999 1001 return self.__spstr
1000 1002
1001 1003 s = spstr = property(get_spstr)
1002 1004
1003 1005 def get_nlstr(self):
1004 1006 return self
1005 1007
1006 1008 n = nlstr = property(get_nlstr)
1007 1009
1008 1010 def get_paths(self):
1009 1011 try:
1010 1012 return self.__paths
1011 1013 except AttributeError:
1012 1014 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
1013 1015 return self.__paths
1014 1016
1015 1017 p = paths = property(get_paths)
1016 1018
1017 1019 def print_lsstring(arg):
1018 1020 """ Prettier (non-repr-like) and more informative printer for LSString """
1019 1021 print "LSString (.p, .n, .l, .s available). Value:"
1020 1022 print arg
1021 1023
1022 1024 print_lsstring = result_display.when_type(LSString)(print_lsstring)
1023 1025
1024 1026 #----------------------------------------------------------------------------
1025 1027 class SList(list):
1026 1028 """List derivative with a special access attributes.
1027 1029
1028 1030 These are normal lists, but with the special attributes:
1029 1031
1030 1032 .l (or .list) : value as list (the list itself).
1031 1033 .n (or .nlstr): value as a string, joined on newlines.
1032 1034 .s (or .spstr): value as a string, joined on spaces.
1033 1035 .p (or .paths): list of path objects
1034 1036
1035 1037 Any values which require transformations are computed only once and
1036 1038 cached."""
1037 1039
1038 1040 def get_list(self):
1039 1041 return self
1040 1042
1041 1043 l = list = property(get_list)
1042 1044
1043 1045 def get_spstr(self):
1044 1046 try:
1045 1047 return self.__spstr
1046 1048 except AttributeError:
1047 1049 self.__spstr = ' '.join(self)
1048 1050 return self.__spstr
1049 1051
1050 1052 s = spstr = property(get_spstr)
1051 1053
1052 1054 def get_nlstr(self):
1053 1055 try:
1054 1056 return self.__nlstr
1055 1057 except AttributeError:
1056 1058 self.__nlstr = '\n'.join(self)
1057 1059 return self.__nlstr
1058 1060
1059 1061 n = nlstr = property(get_nlstr)
1060 1062
1061 1063 def get_paths(self):
1062 1064 try:
1063 1065 return self.__paths
1064 1066 except AttributeError:
1065 1067 self.__paths = [path(p) for p in self if os.path.exists(p)]
1066 1068 return self.__paths
1067 1069
1068 1070 p = paths = property(get_paths)
1069 1071
1070 1072 def grep(self, pattern, prune = False, field = None):
1071 1073 """ Return all strings matching 'pattern' (a regex or callable)
1072 1074
1073 1075 This is case-insensitive. If prune is true, return all items
1074 1076 NOT matching the pattern.
1075 1077
1076 1078 If field is specified, the match must occur in the specified
1077 1079 whitespace-separated field.
1078 1080
1079 1081 Examples::
1080 1082
1081 1083 a.grep( lambda x: x.startswith('C') )
1082 1084 a.grep('Cha.*log', prune=1)
1083 1085 a.grep('chm', field=-1)
1084 1086 """
1085 1087
1086 1088 def match_target(s):
1087 1089 if field is None:
1088 1090 return s
1089 1091 parts = s.split()
1090 1092 try:
1091 1093 tgt = parts[field]
1092 1094 return tgt
1093 1095 except IndexError:
1094 1096 return ""
1095 1097
1096 1098 if isinstance(pattern, basestring):
1097 1099 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
1098 1100 else:
1099 1101 pred = pattern
1100 1102 if not prune:
1101 1103 return SList([el for el in self if pred(match_target(el))])
1102 1104 else:
1103 1105 return SList([el for el in self if not pred(match_target(el))])
1104 1106 def fields(self, *fields):
1105 1107 """ Collect whitespace-separated fields from string list
1106 1108
1107 1109 Allows quick awk-like usage of string lists.
1108 1110
1109 1111 Example data (in var a, created by 'a = !ls -l')::
1110 1112 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
1111 1113 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
1112 1114
1113 1115 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
1114 1116 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
1115 1117 (note the joining by space).
1116 1118 a.fields(-1) is ['ChangeLog', 'IPython']
1117 1119
1118 1120 IndexErrors are ignored.
1119 1121
1120 1122 Without args, fields() just split()'s the strings.
1121 1123 """
1122 1124 if len(fields) == 0:
1123 1125 return [el.split() for el in self]
1124 1126
1125 1127 res = SList()
1126 1128 for el in [f.split() for f in self]:
1127 1129 lineparts = []
1128 1130
1129 1131 for fd in fields:
1130 1132 try:
1131 1133 lineparts.append(el[fd])
1132 1134 except IndexError:
1133 1135 pass
1134 1136 if lineparts:
1135 1137 res.append(" ".join(lineparts))
1136 1138
1137 1139 return res
1138 1140
1139 1141
1140 1142
1141 1143
1142 1144
1143 1145 def print_slist(arg):
1144 1146 """ Prettier (non-repr-like) and more informative printer for SList """
1145 1147 print "SList (.p, .n, .l, .s, .grep(), .fields() available). Value:"
1146 1148 nlprint(arg)
1147 1149
1148 1150 print_slist = result_display.when_type(SList)(print_slist)
1149 1151
1150 1152
1151 1153
1152 1154 #----------------------------------------------------------------------------
1153 1155 def esc_quotes(strng):
1154 1156 """Return the input string with single and double quotes escaped out"""
1155 1157
1156 1158 return strng.replace('"','\\"').replace("'","\\'")
1157 1159
1158 1160 #----------------------------------------------------------------------------
1159 1161 def make_quoted_expr(s):
1160 1162 """Return string s in appropriate quotes, using raw string if possible.
1161 1163
1162 1164 Effectively this turns string: cd \ao\ao\
1163 1165 to: r"cd \ao\ao\_"[:-1]
1164 1166
1165 1167 Note the use of raw string and padding at the end to allow trailing backslash.
1166 1168
1167 1169 """
1168 1170
1169 1171 tail = ''
1170 1172 tailpadding = ''
1171 1173 raw = ''
1172 1174 if "\\" in s:
1173 1175 raw = 'r'
1174 1176 if s.endswith('\\'):
1175 1177 tail = '[:-1]'
1176 1178 tailpadding = '_'
1177 1179 if '"' not in s:
1178 1180 quote = '"'
1179 1181 elif "'" not in s:
1180 1182 quote = "'"
1181 1183 elif '"""' not in s and not s.endswith('"'):
1182 1184 quote = '"""'
1183 1185 elif "'''" not in s and not s.endswith("'"):
1184 1186 quote = "'''"
1185 1187 else:
1186 1188 # give up, backslash-escaped string will do
1187 1189 return '"%s"' % esc_quotes(s)
1188 1190 res = raw + quote + s + tailpadding + quote + tail
1189 1191 return res
1190 1192
1191 1193
1192 1194 #----------------------------------------------------------------------------
1193 1195 def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):
1194 1196 """Take multiple lines of input.
1195 1197
1196 1198 A list with each line of input as a separate element is returned when a
1197 1199 termination string is entered (defaults to a single '.'). Input can also
1198 1200 terminate via EOF (^D in Unix, ^Z-RET in Windows).
1199 1201
1200 1202 Lines of input which end in \\ are joined into single entries (and a
1201 1203 secondary continuation prompt is issued as long as the user terminates
1202 1204 lines with \\). This allows entering very long strings which are still
1203 1205 meant to be treated as single entities.
1204 1206 """
1205 1207
1206 1208 try:
1207 1209 if header:
1208 1210 header += '\n'
1209 1211 lines = [raw_input(header + ps1)]
1210 1212 except EOFError:
1211 1213 return []
1212 1214 terminate = [terminate_str]
1213 1215 try:
1214 1216 while lines[-1:] != terminate:
1215 1217 new_line = raw_input(ps1)
1216 1218 while new_line.endswith('\\'):
1217 1219 new_line = new_line[:-1] + raw_input(ps2)
1218 1220 lines.append(new_line)
1219 1221
1220 1222 return lines[:-1] # don't return the termination command
1221 1223 except EOFError:
1222 1224 print
1223 1225 return lines
1224 1226
1225 1227 #----------------------------------------------------------------------------
1226 1228 def raw_input_ext(prompt='', ps2='... '):
1227 1229 """Similar to raw_input(), but accepts extended lines if input ends with \\."""
1228 1230
1229 1231 line = raw_input(prompt)
1230 1232 while line.endswith('\\'):
1231 1233 line = line[:-1] + raw_input(ps2)
1232 1234 return line
1233 1235
1234 1236 #----------------------------------------------------------------------------
1235 1237 def ask_yes_no(prompt,default=None):
1236 1238 """Asks a question and returns a boolean (y/n) answer.
1237 1239
1238 1240 If default is given (one of 'y','n'), it is used if the user input is
1239 1241 empty. Otherwise the question is repeated until an answer is given.
1240 1242
1241 1243 An EOF is treated as the default answer. If there is no default, an
1242 1244 exception is raised to prevent infinite loops.
1243 1245
1244 1246 Valid answers are: y/yes/n/no (match is not case sensitive)."""
1245 1247
1246 1248 answers = {'y':True,'n':False,'yes':True,'no':False}
1247 1249 ans = None
1248 1250 while ans not in answers.keys():
1249 1251 try:
1250 1252 ans = raw_input(prompt+' ').lower()
1251 1253 if not ans: # response was an empty string
1252 1254 ans = default
1253 1255 except KeyboardInterrupt:
1254 1256 pass
1255 1257 except EOFError:
1256 1258 if default in answers.keys():
1257 1259 ans = default
1258 1260 print
1259 1261 else:
1260 1262 raise
1261 1263
1262 1264 return answers[ans]
1263 1265
1264 1266 #----------------------------------------------------------------------------
1265 1267 def marquee(txt='',width=78,mark='*'):
1266 1268 """Return the input string centered in a 'marquee'."""
1267 1269 if not txt:
1268 1270 return (mark*width)[:width]
1269 1271 nmark = (width-len(txt)-2)/len(mark)/2
1270 1272 if nmark < 0: nmark =0
1271 1273 marks = mark*nmark
1272 1274 return '%s %s %s' % (marks,txt,marks)
1273 1275
1274 1276 #----------------------------------------------------------------------------
1275 1277 class EvalDict:
1276 1278 """
1277 1279 Emulate a dict which evaluates its contents in the caller's frame.
1278 1280
1279 1281 Usage:
1280 1282 >>>number = 19
1281 1283 >>>text = "python"
1282 1284 >>>print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
1283 1285 """
1284 1286
1285 1287 # This version is due to sismex01@hebmex.com on c.l.py, and is basically a
1286 1288 # modified (shorter) version of:
1287 1289 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66018 by
1288 1290 # Skip Montanaro (skip@pobox.com).
1289 1291
1290 1292 def __getitem__(self, name):
1291 1293 frame = sys._getframe(1)
1292 1294 return eval(name, frame.f_globals, frame.f_locals)
1293 1295
1294 1296 EvalString = EvalDict # for backwards compatibility
1295 1297 #----------------------------------------------------------------------------
1296 1298 def qw(words,flat=0,sep=None,maxsplit=-1):
1297 1299 """Similar to Perl's qw() operator, but with some more options.
1298 1300
1299 1301 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
1300 1302
1301 1303 words can also be a list itself, and with flat=1, the output will be
1302 1304 recursively flattened. Examples:
1303 1305
1304 1306 >>> qw('1 2')
1305 1307 ['1', '2']
1306 1308 >>> qw(['a b','1 2',['m n','p q']])
1307 1309 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
1308 1310 >>> qw(['a b','1 2',['m n','p q']],flat=1)
1309 1311 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q'] """
1310 1312
1311 1313 if type(words) in StringTypes:
1312 1314 return [word.strip() for word in words.split(sep,maxsplit)
1313 1315 if word and not word.isspace() ]
1314 1316 if flat:
1315 1317 return flatten(map(qw,words,[1]*len(words)))
1316 1318 return map(qw,words)
1317 1319
1318 1320 #----------------------------------------------------------------------------
1319 1321 def qwflat(words,sep=None,maxsplit=-1):
1320 1322 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
1321 1323 return qw(words,1,sep,maxsplit)
1322 1324
1323 1325 #----------------------------------------------------------------------------
1324 1326 def qw_lol(indata):
1325 1327 """qw_lol('a b') -> [['a','b']],
1326 1328 otherwise it's just a call to qw().
1327 1329
1328 1330 We need this to make sure the modules_some keys *always* end up as a
1329 1331 list of lists."""
1330 1332
1331 1333 if type(indata) in StringTypes:
1332 1334 return [qw(indata)]
1333 1335 else:
1334 1336 return qw(indata)
1335 1337
1336 1338 #-----------------------------------------------------------------------------
1337 1339 def list_strings(arg):
1338 1340 """Always return a list of strings, given a string or list of strings
1339 1341 as input."""
1340 1342
1341 1343 if type(arg) in StringTypes: return [arg]
1342 1344 else: return arg
1343 1345
1344 1346 #----------------------------------------------------------------------------
1345 1347 def grep(pat,list,case=1):
1346 1348 """Simple minded grep-like function.
1347 1349 grep(pat,list) returns occurrences of pat in list, None on failure.
1348 1350
1349 1351 It only does simple string matching, with no support for regexps. Use the
1350 1352 option case=0 for case-insensitive matching."""
1351 1353
1352 1354 # This is pretty crude. At least it should implement copying only references
1353 1355 # to the original data in case it's big. Now it copies the data for output.
1354 1356 out=[]
1355 1357 if case:
1356 1358 for term in list:
1357 1359 if term.find(pat)>-1: out.append(term)
1358 1360 else:
1359 1361 lpat=pat.lower()
1360 1362 for term in list:
1361 1363 if term.lower().find(lpat)>-1: out.append(term)
1362 1364
1363 1365 if len(out): return out
1364 1366 else: return None
1365 1367
1366 1368 #----------------------------------------------------------------------------
1367 1369 def dgrep(pat,*opts):
1368 1370 """Return grep() on dir()+dir(__builtins__).
1369 1371
1370 1372 A very common use of grep() when working interactively."""
1371 1373
1372 1374 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
1373 1375
1374 1376 #----------------------------------------------------------------------------
1375 1377 def idgrep(pat):
1376 1378 """Case-insensitive dgrep()"""
1377 1379
1378 1380 return dgrep(pat,0)
1379 1381
1380 1382 #----------------------------------------------------------------------------
1381 1383 def igrep(pat,list):
1382 1384 """Synonym for case-insensitive grep."""
1383 1385
1384 1386 return grep(pat,list,case=0)
1385 1387
1386 1388 #----------------------------------------------------------------------------
1387 1389 def indent(str,nspaces=4,ntabs=0):
1388 1390 """Indent a string a given number of spaces or tabstops.
1389 1391
1390 1392 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
1391 1393 """
1392 1394 if str is None:
1393 1395 return
1394 1396 ind = '\t'*ntabs+' '*nspaces
1395 1397 outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
1396 1398 if outstr.endswith(os.linesep+ind):
1397 1399 return outstr[:-len(ind)]
1398 1400 else:
1399 1401 return outstr
1400 1402
1401 1403 #-----------------------------------------------------------------------------
1402 1404 def native_line_ends(filename,backup=1):
1403 1405 """Convert (in-place) a file to line-ends native to the current OS.
1404 1406
1405 1407 If the optional backup argument is given as false, no backup of the
1406 1408 original file is left. """
1407 1409
1408 1410 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
1409 1411
1410 1412 bak_filename = filename + backup_suffixes[os.name]
1411 1413
1412 1414 original = open(filename).read()
1413 1415 shutil.copy2(filename,bak_filename)
1414 1416 try:
1415 1417 new = open(filename,'wb')
1416 1418 new.write(os.linesep.join(original.splitlines()))
1417 1419 new.write(os.linesep) # ALWAYS put an eol at the end of the file
1418 1420 new.close()
1419 1421 except:
1420 1422 os.rename(bak_filename,filename)
1421 1423 if not backup:
1422 1424 try:
1423 1425 os.remove(bak_filename)
1424 1426 except:
1425 1427 pass
1426 1428
1427 1429 #----------------------------------------------------------------------------
1428 1430 def get_pager_cmd(pager_cmd = None):
1429 1431 """Return a pager command.
1430 1432
1431 1433 Makes some attempts at finding an OS-correct one."""
1432 1434
1433 1435 if os.name == 'posix':
1434 1436 default_pager_cmd = 'less -r' # -r for color control sequences
1435 1437 elif os.name in ['nt','dos']:
1436 1438 default_pager_cmd = 'type'
1437 1439
1438 1440 if pager_cmd is None:
1439 1441 try:
1440 1442 pager_cmd = os.environ['PAGER']
1441 1443 except:
1442 1444 pager_cmd = default_pager_cmd
1443 1445 return pager_cmd
1444 1446
1445 1447 #-----------------------------------------------------------------------------
1446 1448 def get_pager_start(pager,start):
1447 1449 """Return the string for paging files with an offset.
1448 1450
1449 1451 This is the '+N' argument which less and more (under Unix) accept.
1450 1452 """
1451 1453
1452 1454 if pager in ['less','more']:
1453 1455 if start:
1454 1456 start_string = '+' + str(start)
1455 1457 else:
1456 1458 start_string = ''
1457 1459 else:
1458 1460 start_string = ''
1459 1461 return start_string
1460 1462
1461 1463 #----------------------------------------------------------------------------
1462 1464 # (X)emacs on W32 doesn't like to be bypassed with msvcrt.getch()
1463 1465 if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
1464 1466 import msvcrt
1465 1467 def page_more():
1466 1468 """ Smart pausing between pages
1467 1469
1468 1470 @return: True if need print more lines, False if quit
1469 1471 """
1470 1472 Term.cout.write('---Return to continue, q to quit--- ')
1471 1473 ans = msvcrt.getch()
1472 1474 if ans in ("q", "Q"):
1473 1475 result = False
1474 1476 else:
1475 1477 result = True
1476 1478 Term.cout.write("\b"*37 + " "*37 + "\b"*37)
1477 1479 return result
1478 1480 else:
1479 1481 def page_more():
1480 1482 ans = raw_input('---Return to continue, q to quit--- ')
1481 1483 if ans.lower().startswith('q'):
1482 1484 return False
1483 1485 else:
1484 1486 return True
1485 1487
1486 1488 esc_re = re.compile(r"(\x1b[^m]+m)")
1487 1489
1488 1490 def page_dumb(strng,start=0,screen_lines=25):
1489 1491 """Very dumb 'pager' in Python, for when nothing else works.
1490 1492
1491 1493 Only moves forward, same interface as page(), except for pager_cmd and
1492 1494 mode."""
1493 1495
1494 1496 out_ln = strng.splitlines()[start:]
1495 1497 screens = chop(out_ln,screen_lines-1)
1496 1498 if len(screens) == 1:
1497 1499 print >>Term.cout, os.linesep.join(screens[0])
1498 1500 else:
1499 1501 last_escape = ""
1500 1502 for scr in screens[0:-1]:
1501 1503 hunk = os.linesep.join(scr)
1502 1504 print >>Term.cout, last_escape + hunk
1503 1505 if not page_more():
1504 1506 return
1505 1507 esc_list = esc_re.findall(hunk)
1506 1508 if len(esc_list) > 0:
1507 1509 last_escape = esc_list[-1]
1508 1510 print >>Term.cout, last_escape + os.linesep.join(screens[-1])
1509 1511
1510 1512 #----------------------------------------------------------------------------
1511 1513 def page(strng,start=0,screen_lines=0,pager_cmd = None):
1512 1514 """Print a string, piping through a pager after a certain length.
1513 1515
1514 1516 The screen_lines parameter specifies the number of *usable* lines of your
1515 1517 terminal screen (total lines minus lines you need to reserve to show other
1516 1518 information).
1517 1519
1518 1520 If you set screen_lines to a number <=0, page() will try to auto-determine
1519 1521 your screen size and will only use up to (screen_size+screen_lines) for
1520 1522 printing, paging after that. That is, if you want auto-detection but need
1521 1523 to reserve the bottom 3 lines of the screen, use screen_lines = -3, and for
1522 1524 auto-detection without any lines reserved simply use screen_lines = 0.
1523 1525
1524 1526 If a string won't fit in the allowed lines, it is sent through the
1525 1527 specified pager command. If none given, look for PAGER in the environment,
1526 1528 and ultimately default to less.
1527 1529
1528 1530 If no system pager works, the string is sent through a 'dumb pager'
1529 1531 written in python, very simplistic.
1530 1532 """
1531 1533
1532 1534 # Some routines may auto-compute start offsets incorrectly and pass a
1533 1535 # negative value. Offset to 0 for robustness.
1534 1536 start = max(0,start)
1535 1537
1536 1538 # first, try the hook
1537 1539 ip = IPython.ipapi.get()
1538 1540 if ip:
1539 1541 try:
1540 1542 ip.IP.hooks.show_in_pager(strng)
1541 1543 return
1542 1544 except IPython.ipapi.TryNext:
1543 1545 pass
1544 1546
1545 1547 # Ugly kludge, but calling curses.initscr() flat out crashes in emacs
1546 1548 TERM = os.environ.get('TERM','dumb')
1547 1549 if TERM in ['dumb','emacs'] and os.name != 'nt':
1548 1550 print strng
1549 1551 return
1550 1552 # chop off the topmost part of the string we don't want to see
1551 1553 str_lines = strng.split(os.linesep)[start:]
1552 1554 str_toprint = os.linesep.join(str_lines)
1553 1555 num_newlines = len(str_lines)
1554 1556 len_str = len(str_toprint)
1555 1557
1556 1558 # Dumb heuristics to guesstimate number of on-screen lines the string
1557 1559 # takes. Very basic, but good enough for docstrings in reasonable
1558 1560 # terminals. If someone later feels like refining it, it's not hard.
1559 1561 numlines = max(num_newlines,int(len_str/80)+1)
1560 1562
1561 1563 if os.name == "nt":
1562 1564 screen_lines_def = get_console_size(defaulty=25)[1]
1563 1565 else:
1564 1566 screen_lines_def = 25 # default value if we can't auto-determine
1565 1567
1566 1568 # auto-determine screen size
1567 1569 if screen_lines <= 0:
1568 1570 if TERM=='xterm':
1569 1571 use_curses = USE_CURSES
1570 1572 else:
1571 1573 # curses causes problems on many terminals other than xterm.
1572 1574 use_curses = False
1573 1575 if use_curses:
1574 1576 # There is a bug in curses, where *sometimes* it fails to properly
1575 1577 # initialize, and then after the endwin() call is made, the
1576 1578 # terminal is left in an unusable state. Rather than trying to
1577 1579 # check everytime for this (by requesting and comparing termios
1578 1580 # flags each time), we just save the initial terminal state and
1579 1581 # unconditionally reset it every time. It's cheaper than making
1580 1582 # the checks.
1581 1583 term_flags = termios.tcgetattr(sys.stdout)
1582 1584 scr = curses.initscr()
1583 1585 screen_lines_real,screen_cols = scr.getmaxyx()
1584 1586 curses.endwin()
1585 1587 # Restore terminal state in case endwin() didn't.
1586 1588 termios.tcsetattr(sys.stdout,termios.TCSANOW,term_flags)
1587 1589 # Now we have what we needed: the screen size in rows/columns
1588 1590 screen_lines += screen_lines_real
1589 1591 #print '***Screen size:',screen_lines_real,'lines x',\
1590 1592 #screen_cols,'columns.' # dbg
1591 1593 else:
1592 1594 screen_lines += screen_lines_def
1593 1595
1594 1596 #print 'numlines',numlines,'screenlines',screen_lines # dbg
1595 1597 if numlines <= screen_lines :
1596 1598 #print '*** normal print' # dbg
1597 1599 print >>Term.cout, str_toprint
1598 1600 else:
1599 1601 # Try to open pager and default to internal one if that fails.
1600 1602 # All failure modes are tagged as 'retval=1', to match the return
1601 1603 # value of a failed system command. If any intermediate attempt
1602 1604 # sets retval to 1, at the end we resort to our own page_dumb() pager.
1603 1605 pager_cmd = get_pager_cmd(pager_cmd)
1604 1606 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
1605 1607 if os.name == 'nt':
1606 1608 if pager_cmd.startswith('type'):
1607 1609 # The default WinXP 'type' command is failing on complex strings.
1608 1610 retval = 1
1609 1611 else:
1610 1612 tmpname = tempfile.mktemp('.txt')
1611 1613 tmpfile = file(tmpname,'wt')
1612 1614 tmpfile.write(strng)
1613 1615 tmpfile.close()
1614 1616 cmd = "%s < %s" % (pager_cmd,tmpname)
1615 1617 if os.system(cmd):
1616 1618 retval = 1
1617 1619 else:
1618 1620 retval = None
1619 1621 os.remove(tmpname)
1620 1622 else:
1621 1623 try:
1622 1624 retval = None
1623 1625 # if I use popen4, things hang. No idea why.
1624 1626 #pager,shell_out = os.popen4(pager_cmd)
1625 1627 pager = os.popen(pager_cmd,'w')
1626 1628 pager.write(strng)
1627 1629 pager.close()
1628 1630 retval = pager.close() # success returns None
1629 1631 except IOError,msg: # broken pipe when user quits
1630 1632 if msg.args == (32,'Broken pipe'):
1631 1633 retval = None
1632 1634 else:
1633 1635 retval = 1
1634 1636 except OSError:
1635 1637 # Other strange problems, sometimes seen in Win2k/cygwin
1636 1638 retval = 1
1637 1639 if retval is not None:
1638 1640 page_dumb(strng,screen_lines=screen_lines)
1639 1641
1640 1642 #----------------------------------------------------------------------------
1641 1643 def page_file(fname,start = 0, pager_cmd = None):
1642 1644 """Page a file, using an optional pager command and starting line.
1643 1645 """
1644 1646
1645 1647 pager_cmd = get_pager_cmd(pager_cmd)
1646 1648 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
1647 1649
1648 1650 try:
1649 1651 if os.environ['TERM'] in ['emacs','dumb']:
1650 1652 raise EnvironmentError
1651 1653 xsys(pager_cmd + ' ' + fname)
1652 1654 except:
1653 1655 try:
1654 1656 if start > 0:
1655 1657 start -= 1
1656 1658 page(open(fname).read(),start)
1657 1659 except:
1658 1660 print 'Unable to show file',`fname`
1659 1661
1660 1662
1661 1663 #----------------------------------------------------------------------------
1662 1664 def snip_print(str,width = 75,print_full = 0,header = ''):
1663 1665 """Print a string snipping the midsection to fit in width.
1664 1666
1665 1667 print_full: mode control:
1666 1668 - 0: only snip long strings
1667 1669 - 1: send to page() directly.
1668 1670 - 2: snip long strings and ask for full length viewing with page()
1669 1671 Return 1 if snipping was necessary, 0 otherwise."""
1670 1672
1671 1673 if print_full == 1:
1672 1674 page(header+str)
1673 1675 return 0
1674 1676
1675 1677 print header,
1676 1678 if len(str) < width:
1677 1679 print str
1678 1680 snip = 0
1679 1681 else:
1680 1682 whalf = int((width -5)/2)
1681 1683 print str[:whalf] + ' <...> ' + str[-whalf:]
1682 1684 snip = 1
1683 1685 if snip and print_full == 2:
1684 1686 if raw_input(header+' Snipped. View (y/n)? [N]').lower() == 'y':
1685 1687 page(str)
1686 1688 return snip
1687 1689
1688 1690 #****************************************************************************
1689 1691 # lists, dicts and structures
1690 1692
1691 1693 def belong(candidates,checklist):
1692 1694 """Check whether a list of items appear in a given list of options.
1693 1695
1694 1696 Returns a list of 1 and 0, one for each candidate given."""
1695 1697
1696 1698 return [x in checklist for x in candidates]
1697 1699
1698 1700 #----------------------------------------------------------------------------
1699 1701 def uniq_stable(elems):
1700 1702 """uniq_stable(elems) -> list
1701 1703
1702 1704 Return from an iterable, a list of all the unique elements in the input,
1703 1705 but maintaining the order in which they first appear.
1704 1706
1705 1707 A naive solution to this problem which just makes a dictionary with the
1706 1708 elements as keys fails to respect the stability condition, since
1707 1709 dictionaries are unsorted by nature.
1708 1710
1709 1711 Note: All elements in the input must be valid dictionary keys for this
1710 1712 routine to work, as it internally uses a dictionary for efficiency
1711 1713 reasons."""
1712 1714
1713 1715 unique = []
1714 1716 unique_dict = {}
1715 1717 for nn in elems:
1716 1718 if nn not in unique_dict:
1717 1719 unique.append(nn)
1718 1720 unique_dict[nn] = None
1719 1721 return unique
1720 1722
1721 1723 #----------------------------------------------------------------------------
1722 1724 class NLprinter:
1723 1725 """Print an arbitrarily nested list, indicating index numbers.
1724 1726
1725 1727 An instance of this class called nlprint is available and callable as a
1726 1728 function.
1727 1729
1728 1730 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
1729 1731 and using 'sep' to separate the index from the value. """
1730 1732
1731 1733 def __init__(self):
1732 1734 self.depth = 0
1733 1735
1734 1736 def __call__(self,lst,pos='',**kw):
1735 1737 """Prints the nested list numbering levels."""
1736 1738 kw.setdefault('indent',' ')
1737 1739 kw.setdefault('sep',': ')
1738 1740 kw.setdefault('start',0)
1739 1741 kw.setdefault('stop',len(lst))
1740 1742 # we need to remove start and stop from kw so they don't propagate
1741 1743 # into a recursive call for a nested list.
1742 1744 start = kw['start']; del kw['start']
1743 1745 stop = kw['stop']; del kw['stop']
1744 1746 if self.depth == 0 and 'header' in kw.keys():
1745 1747 print kw['header']
1746 1748
1747 1749 for idx in range(start,stop):
1748 1750 elem = lst[idx]
1749 1751 if type(elem)==type([]):
1750 1752 self.depth += 1
1751 1753 self.__call__(elem,itpl('$pos$idx,'),**kw)
1752 1754 self.depth -= 1
1753 1755 else:
1754 1756 printpl(kw['indent']*self.depth+'$pos$idx$kw["sep"]$elem')
1755 1757
1756 1758 nlprint = NLprinter()
1757 1759 #----------------------------------------------------------------------------
1758 1760 def all_belong(candidates,checklist):
1759 1761 """Check whether a list of items ALL appear in a given list of options.
1760 1762
1761 1763 Returns a single 1 or 0 value."""
1762 1764
1763 1765 return 1-(0 in [x in checklist for x in candidates])
1764 1766
1765 1767 #----------------------------------------------------------------------------
1766 1768 def sort_compare(lst1,lst2,inplace = 1):
1767 1769 """Sort and compare two lists.
1768 1770
1769 1771 By default it does it in place, thus modifying the lists. Use inplace = 0
1770 1772 to avoid that (at the cost of temporary copy creation)."""
1771 1773 if not inplace:
1772 1774 lst1 = lst1[:]
1773 1775 lst2 = lst2[:]
1774 1776 lst1.sort(); lst2.sort()
1775 1777 return lst1 == lst2
1776 1778
1777 1779 #----------------------------------------------------------------------------
1778 1780 def mkdict(**kwargs):
1779 1781 """Return a dict from a keyword list.
1780 1782
1781 1783 It's just syntactic sugar for making ditcionary creation more convenient:
1782 1784 # the standard way
1783 1785 >>>data = { 'red' : 1, 'green' : 2, 'blue' : 3 }
1784 1786 # a cleaner way
1785 1787 >>>data = dict(red=1, green=2, blue=3)
1786 1788
1787 1789 If you need more than this, look at the Struct() class."""
1788 1790
1789 1791 return kwargs
1790 1792
1791 1793 #----------------------------------------------------------------------------
1792 1794 def list2dict(lst):
1793 1795 """Takes a list of (key,value) pairs and turns it into a dict."""
1794 1796
1795 1797 dic = {}
1796 1798 for k,v in lst: dic[k] = v
1797 1799 return dic
1798 1800
1799 1801 #----------------------------------------------------------------------------
1800 1802 def list2dict2(lst,default=''):
1801 1803 """Takes a list and turns it into a dict.
1802 1804 Much slower than list2dict, but more versatile. This version can take
1803 1805 lists with sublists of arbitrary length (including sclars)."""
1804 1806
1805 1807 dic = {}
1806 1808 for elem in lst:
1807 1809 if type(elem) in (types.ListType,types.TupleType):
1808 1810 size = len(elem)
1809 1811 if size == 0:
1810 1812 pass
1811 1813 elif size == 1:
1812 1814 dic[elem] = default
1813 1815 else:
1814 1816 k,v = elem[0], elem[1:]
1815 1817 if len(v) == 1: v = v[0]
1816 1818 dic[k] = v
1817 1819 else:
1818 1820 dic[elem] = default
1819 1821 return dic
1820 1822
1821 1823 #----------------------------------------------------------------------------
1822 1824 def flatten(seq):
1823 1825 """Flatten a list of lists (NOT recursive, only works for 2d lists)."""
1824 1826
1825 1827 return [x for subseq in seq for x in subseq]
1826 1828
1827 1829 #----------------------------------------------------------------------------
1828 1830 def get_slice(seq,start=0,stop=None,step=1):
1829 1831 """Get a slice of a sequence with variable step. Specify start,stop,step."""
1830 1832 if stop == None:
1831 1833 stop = len(seq)
1832 1834 item = lambda i: seq[i]
1833 1835 return map(item,xrange(start,stop,step))
1834 1836
1835 1837 #----------------------------------------------------------------------------
1836 1838 def chop(seq,size):
1837 1839 """Chop a sequence into chunks of the given size."""
1838 1840 chunk = lambda i: seq[i:i+size]
1839 1841 return map(chunk,xrange(0,len(seq),size))
1840 1842
1841 1843 #----------------------------------------------------------------------------
1842 1844 # with is a keyword as of python 2.5, so this function is renamed to withobj
1843 1845 # from its old 'with' name.
1844 1846 def with_obj(object, **args):
1845 1847 """Set multiple attributes for an object, similar to Pascal's with.
1846 1848
1847 1849 Example:
1848 1850 with_obj(jim,
1849 1851 born = 1960,
1850 1852 haircolour = 'Brown',
1851 1853 eyecolour = 'Green')
1852 1854
1853 1855 Credit: Greg Ewing, in
1854 1856 http://mail.python.org/pipermail/python-list/2001-May/040703.html.
1855 1857
1856 1858 NOTE: up until IPython 0.7.2, this was called simply 'with', but 'with'
1857 1859 has become a keyword for Python 2.5, so we had to rename it."""
1858 1860
1859 1861 object.__dict__.update(args)
1860 1862
1861 1863 #----------------------------------------------------------------------------
1862 1864 def setattr_list(obj,alist,nspace = None):
1863 1865 """Set a list of attributes for an object taken from a namespace.
1864 1866
1865 1867 setattr_list(obj,alist,nspace) -> sets in obj all the attributes listed in
1866 1868 alist with their values taken from nspace, which must be a dict (something
1867 1869 like locals() will often do) If nspace isn't given, locals() of the
1868 1870 *caller* is used, so in most cases you can omit it.
1869 1871
1870 1872 Note that alist can be given as a string, which will be automatically
1871 1873 split into a list on whitespace. If given as a list, it must be a list of
1872 1874 *strings* (the variable names themselves), not of variables."""
1873 1875
1874 1876 # this grabs the local variables from the *previous* call frame -- that is
1875 1877 # the locals from the function that called setattr_list().
1876 1878 # - snipped from weave.inline()
1877 1879 if nspace is None:
1878 1880 call_frame = sys._getframe().f_back
1879 1881 nspace = call_frame.f_locals
1880 1882
1881 1883 if type(alist) in StringTypes:
1882 1884 alist = alist.split()
1883 1885 for attr in alist:
1884 1886 val = eval(attr,nspace)
1885 1887 setattr(obj,attr,val)
1886 1888
1887 1889 #----------------------------------------------------------------------------
1888 1890 def getattr_list(obj,alist,*args):
1889 1891 """getattr_list(obj,alist[, default]) -> attribute list.
1890 1892
1891 1893 Get a list of named attributes for an object. When a default argument is
1892 1894 given, it is returned when the attribute doesn't exist; without it, an
1893 1895 exception is raised in that case.
1894 1896
1895 1897 Note that alist can be given as a string, which will be automatically
1896 1898 split into a list on whitespace. If given as a list, it must be a list of
1897 1899 *strings* (the variable names themselves), not of variables."""
1898 1900
1899 1901 if type(alist) in StringTypes:
1900 1902 alist = alist.split()
1901 1903 if args:
1902 1904 if len(args)==1:
1903 1905 default = args[0]
1904 1906 return map(lambda attr: getattr(obj,attr,default),alist)
1905 1907 else:
1906 1908 raise ValueError,'getattr_list() takes only one optional argument'
1907 1909 else:
1908 1910 return map(lambda attr: getattr(obj,attr),alist)
1909 1911
1910 1912 #----------------------------------------------------------------------------
1911 1913 def map_method(method,object_list,*argseq,**kw):
1912 1914 """map_method(method,object_list,*args,**kw) -> list
1913 1915
1914 1916 Return a list of the results of applying the methods to the items of the
1915 1917 argument sequence(s). If more than one sequence is given, the method is
1916 1918 called with an argument list consisting of the corresponding item of each
1917 1919 sequence. All sequences must be of the same length.
1918 1920
1919 1921 Keyword arguments are passed verbatim to all objects called.
1920 1922
1921 1923 This is Python code, so it's not nearly as fast as the builtin map()."""
1922 1924
1923 1925 out_list = []
1924 1926 idx = 0
1925 1927 for object in object_list:
1926 1928 try:
1927 1929 handler = getattr(object, method)
1928 1930 except AttributeError:
1929 1931 out_list.append(None)
1930 1932 else:
1931 1933 if argseq:
1932 1934 args = map(lambda lst:lst[idx],argseq)
1933 1935 #print 'ob',object,'hand',handler,'ar',args # dbg
1934 1936 out_list.append(handler(args,**kw))
1935 1937 else:
1936 1938 out_list.append(handler(**kw))
1937 1939 idx += 1
1938 1940 return out_list
1939 1941
1940 1942 #----------------------------------------------------------------------------
1941 1943 def get_class_members(cls):
1942 1944 ret = dir(cls)
1943 1945 if hasattr(cls,'__bases__'):
1944 1946 for base in cls.__bases__:
1945 1947 ret.extend(get_class_members(base))
1946 1948 return ret
1947 1949
1948 1950 #----------------------------------------------------------------------------
1949 1951 def dir2(obj):
1950 1952 """dir2(obj) -> list of strings
1951 1953
1952 1954 Extended version of the Python builtin dir(), which does a few extra
1953 1955 checks, and supports common objects with unusual internals that confuse
1954 1956 dir(), such as Traits and PyCrust.
1955 1957
1956 1958 This version is guaranteed to return only a list of true strings, whereas
1957 1959 dir() returns anything that objects inject into themselves, even if they
1958 1960 are later not really valid for attribute access (many extension libraries
1959 1961 have such bugs).
1960 1962 """
1961 1963
1962 1964 # Start building the attribute list via dir(), and then complete it
1963 1965 # with a few extra special-purpose calls.
1964 1966 words = dir(obj)
1965 1967
1966 1968 if hasattr(obj,'__class__'):
1967 1969 words.append('__class__')
1968 1970 words.extend(get_class_members(obj.__class__))
1969 1971 #if '__base__' in words: 1/0
1970 1972
1971 1973 # Some libraries (such as traits) may introduce duplicates, we want to
1972 1974 # track and clean this up if it happens
1973 1975 may_have_dupes = False
1974 1976
1975 1977 # this is the 'dir' function for objects with Enthought's traits
1976 1978 if hasattr(obj, 'trait_names'):
1977 1979 try:
1978 1980 words.extend(obj.trait_names())
1979 1981 may_have_dupes = True
1980 1982 except TypeError:
1981 1983 # This will happen if `obj` is a class and not an instance.
1982 1984 pass
1983 1985
1984 1986 # Support for PyCrust-style _getAttributeNames magic method.
1985 1987 if hasattr(obj, '_getAttributeNames'):
1986 1988 try:
1987 1989 words.extend(obj._getAttributeNames())
1988 1990 may_have_dupes = True
1989 1991 except TypeError:
1990 1992 # `obj` is a class and not an instance. Ignore
1991 1993 # this error.
1992 1994 pass
1993 1995
1994 1996 if may_have_dupes:
1995 1997 # eliminate possible duplicates, as some traits may also
1996 1998 # appear as normal attributes in the dir() call.
1997 1999 words = list(set(words))
1998 2000 words.sort()
1999 2001
2000 2002 # filter out non-string attributes which may be stuffed by dir() calls
2001 2003 # and poor coding in third-party modules
2002 2004 return [w for w in words if isinstance(w, basestring)]
2003 2005
2004 2006 #----------------------------------------------------------------------------
2005 2007 def import_fail_info(mod_name,fns=None):
2006 2008 """Inform load failure for a module."""
2007 2009
2008 2010 if fns == None:
2009 2011 warn("Loading of %s failed.\n" % (mod_name,))
2010 2012 else:
2011 2013 warn("Loading of %s from %s failed.\n" % (fns,mod_name))
2012 2014
2013 2015 #----------------------------------------------------------------------------
2014 2016 # Proposed popitem() extension, written as a method
2015 2017
2016 2018
2017 2019 class NotGiven: pass
2018 2020
2019 2021 def popkey(dct,key,default=NotGiven):
2020 2022 """Return dct[key] and delete dct[key].
2021 2023
2022 2024 If default is given, return it if dct[key] doesn't exist, otherwise raise
2023 2025 KeyError. """
2024 2026
2025 2027 try:
2026 2028 val = dct[key]
2027 2029 except KeyError:
2028 2030 if default is NotGiven:
2029 2031 raise
2030 2032 else:
2031 2033 return default
2032 2034 else:
2033 2035 del dct[key]
2034 2036 return val
2035 2037
2036 2038 def wrap_deprecated(func, suggest = '<nothing>'):
2037 2039 def newFunc(*args, **kwargs):
2038 2040 warnings.warn("Call to deprecated function %s, use %s instead" %
2039 2041 ( func.__name__, suggest),
2040 2042 category=DeprecationWarning,
2041 2043 stacklevel = 2)
2042 2044 return func(*args, **kwargs)
2043 2045 return newFunc
2044
2045 #*************************** end of file <genutils.py> **********************
2046 2046
2047
2048 def _num_cpus_unix():
2049 """Return the number of active CPUs on a Unix system."""
2050 return os.sysconf("SC_NPROCESSORS_ONLN")
2051
2052
2053 def _num_cpus_darwin():
2054 """Return the number of active CPUs on a Darwin system."""
2055 p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE)
2056 return p.stdout.read()
2057
2058
2059 def _num_cpus_windows():
2060 """Return the number of active CPUs on a Windows system."""
2061 return os.environ.get("NUMBER_OF_PROCESSORS")
2062
2063
2064 def num_cpus():
2065 """Return the effective number of CPUs in the system as an integer.
2066
2067 This cross-platform function makes an attempt at finding the total number of
2068 available CPUs in the system, as returned by various underlying system and
2069 python calls.
2070
2071 If it can't find a sensible answer, it returns 1 (though an error *may* make
2072 it return a large positive number that's actually incorrect).
2073 """
2074
2075 # Many thanks to the Parallel Python project (http://www.parallelpython.com)
2076 # for the names of the keys we needed to look up for this function. This
2077 # code was inspired by their equivalent function.
2078
2079 ncpufuncs = {'Linux':_num_cpus_unix,
2080 'Darwin':_num_cpus_darwin,
2081 'Windows':_num_cpus_windows,
2082 # On Vista, python < 2.5.2 has a bug and returns 'Microsoft'
2083 # See http://bugs.python.org/issue1082 for details.
2084 'Microsoft':_num_cpus_windows,
2085 }
2086
2087 ncpufunc = ncpufuncs.get(platform.system(),
2088 # default to unix version (Solaris, AIX, etc)
2089 _num_cpus_unix)
2090
2091 try:
2092 ncpus = max(1,int(ncpufunc()))
2093 except:
2094 ncpus = 1
2095 return ncpus
2096
2097 #*************************** end of file <genutils.py> **********************
@@ -1,394 +1,406 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Mimic C structs with lots of extra functionality.
3 3
4 4 $Id: ipstruct.py 1950 2006-11-28 19:15:35Z vivainio $"""
5 5
6 6 #*****************************************************************************
7 7 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #*****************************************************************************
12 12
13 13 from IPython import Release
14 14 __author__ = '%s <%s>' % Release.authors['Fernando']
15 15 __license__ = Release.license
16 16
17 17 __all__ = ['Struct']
18 18
19 19 import types
20 20 import pprint
21 21
22 22 from IPython.genutils import list2dict2
23 23
24 24 class Struct:
25 25 """Class to mimic C structs but also provide convenient dictionary-like
26 26 functionality.
27 27
28 28 Instances can be initialized with a dictionary, a list of key=value pairs
29 29 or both. If both are present, the dictionary must come first.
30 30
31 31 Because Python classes provide direct assignment to their members, it's
32 32 easy to overwrite normal methods (S.copy = 1 would destroy access to
33 33 S.copy()). For this reason, all builtin method names are protected and
34 34 can't be assigned to. An attempt to do s.copy=1 or s['copy']=1 will raise
35 35 a KeyError exception. If you really want to, you can bypass this
36 36 protection by directly assigning to __dict__: s.__dict__['copy']=1 will
37 37 still work. Doing this will break functionality, though. As in most of
38 38 Python, namespace protection is weakly enforced, so feel free to shoot
39 39 yourself if you really want to.
40 40
41 41 Note that this class uses more memory and is *much* slower than a regular
42 42 dictionary, so be careful in situations where memory or performance are
43 43 critical. But for day to day use it should behave fine. It is particularly
44 44 convenient for storing configuration data in programs.
45 45
46 46 +,+=,- and -= are implemented. +/+= do merges (non-destructive updates),
47 47 -/-= remove keys from the original. See the method descripitions.
48 48
49 49 This class allows a quick access syntax: both s.key and s['key'] are
50 50 valid. This syntax has a limitation: each 'key' has to be explicitly
51 51 accessed by its original name. The normal s.key syntax doesn't provide
52 52 access to the keys via variables whose values evaluate to the desired
53 53 keys. An example should clarify this:
54 54
55 55 Define a dictionary and initialize both with dict and k=v pairs:
56 56 >>> d={'a':1,'b':2}
57 57 >>> s=Struct(d,hi=10,ho=20)
58
58 59 The return of __repr__ can be used to create a new instance:
59 60 >>> s
60 Struct({'ho': 20, 'b': 2, 'hi': 10, 'a': 1})
61 Struct({'__allownew': True, 'a': 1, 'b': 2, 'hi': 10, 'ho': 20})
62
63 Note: the special '__allownew' key is used for internal purposes.
64
61 65 __str__ (called by print) shows it's not quite a regular dictionary:
62 66 >>> print s
63 Struct {a: 1, b: 2, hi: 10, ho: 20}
67 Struct({'__allownew': True, 'a': 1, 'b': 2, 'hi': 10, 'ho': 20})
68
64 69 Access by explicitly named key with dot notation:
65 70 >>> s.a
66 71 1
72
67 73 Or like a dictionary:
68 74 >>> s['a']
69 75 1
76
70 77 If you want a variable to hold the key value, only dictionary access works:
71 78 >>> key='hi'
72 79 >>> s.key
73 80 Traceback (most recent call last):
74 81 File "<stdin>", line 1, in ?
75 82 AttributeError: Struct instance has no attribute 'key'
83
76 84 >>> s[key]
77 85 10
78 86
79 87 Another limitation of the s.key syntax (and Struct(key=val)
80 88 initialization): keys can't be numbers. But numeric keys can be used and
81 89 accessed using the dictionary syntax. Again, an example:
82 90
83 91 This doesn't work:
84 >>> s=Struct(4='hi')
92 >>> s=Struct(4='hi') #doctest: +IGNORE_EXCEPTION_DETAIL
93 Traceback (most recent call last):
94 ...
85 95 SyntaxError: keyword can't be an expression
96
86 97 But this does:
87 98 >>> s=Struct()
88 99 >>> s[4]='hi'
89 100 >>> s
90 Struct({4: 'hi'})
101 Struct({4: 'hi', '__allownew': True})
91 102 >>> s[4]
92 103 'hi'
93 104 """
94 105
95 106 # Attributes to which __setitem__ and __setattr__ will block access.
96 107 # Note: much of this will be moot in Python 2.2 and will be done in a much
97 108 # cleaner way.
98 109 __protected = ('copy dict dictcopy get has_attr has_key items keys '
99 110 'merge popitem setdefault update values '
100 111 '__make_dict __dict_invert ').split()
101 112
102 113 def __init__(self,dict=None,**kw):
103 114 """Initialize with a dictionary, another Struct, or by giving
104 115 explicitly the list of attributes.
105 116
106 117 Both can be used, but the dictionary must come first:
107 118 Struct(dict), Struct(k1=v1,k2=v2) or Struct(dict,k1=v1,k2=v2).
108 119 """
109 120 self.__dict__['__allownew'] = True
110 121 if dict is None:
111 122 dict = {}
112 123 if isinstance(dict,Struct):
113 124 dict = dict.dict()
114 125 elif dict and type(dict) is not types.DictType:
115 126 raise TypeError,\
116 127 'Initialize with a dictionary or key=val pairs.'
117 128 dict.update(kw)
118 129 # do the updating by hand to guarantee that we go through the
119 130 # safety-checked __setitem__
120 131 for k,v in dict.items():
121 132 self[k] = v
122 133
123 134
124 135 def __setitem__(self,key,value):
125 136 """Used when struct[key] = val calls are made."""
126 137 if key in Struct.__protected:
127 138 raise KeyError,'Key '+`key`+' is a protected key of class Struct.'
128 139 if not self['__allownew'] and key not in self.__dict__:
129 140 raise KeyError(
130 141 "Can't create unknown attribute %s - Check for typos, or use allow_new_attr to create new attributes!" %
131 142 key)
132 143
133 144 self.__dict__[key] = value
134 145
135 146 def __setattr__(self, key, value):
136 147 """Used when struct.key = val calls are made."""
137 148 self.__setitem__(key,value)
138 149
139 150 def __str__(self):
140 151 """Gets called by print."""
141 152
142 153 return 'Struct('+ pprint.pformat(self.__dict__)+')'
143 154
144 155 def __repr__(self):
145 156 """Gets called by repr.
146 157
147 158 A Struct can be recreated with S_new=eval(repr(S_old))."""
148 159 return self.__str__()
149 160
150 161 def __getitem__(self,key):
151 162 """Allows struct[key] access."""
152 163 return self.__dict__[key]
153 164
154 165 def __contains__(self,key):
155 166 """Allows use of the 'in' operator."""
156 167 return self.__dict__.has_key(key)
157 168
158 169 def __iadd__(self,other):
159 170 """S += S2 is a shorthand for S.merge(S2)."""
160 171 self.merge(other)
161 172 return self
162 173
163 174 def __add__(self,other):
164 175 """S + S2 -> New Struct made form S and S.merge(S2)"""
165 176 Sout = self.copy()
166 177 Sout.merge(other)
167 178 return Sout
168 179
169 180 def __sub__(self,other):
170 181 """Return S1-S2, where all keys in S2 have been deleted (if present)
171 182 from S1."""
172 183 Sout = self.copy()
173 184 Sout -= other
174 185 return Sout
175 186
176 187 def __isub__(self,other):
177 188 """Do in place S = S - S2, meaning all keys in S2 have been deleted
178 189 (if present) from S1."""
179 190
180 191 for k in other.keys():
181 192 if self.has_key(k):
182 193 del self.__dict__[k]
183 194
184 195 def __make_dict(self,__loc_data__,**kw):
185 196 "Helper function for update and merge. Return a dict from data."
186 197
187 198 if __loc_data__ == None:
188 199 dict = {}
189 200 elif type(__loc_data__) is types.DictType:
190 201 dict = __loc_data__
191 202 elif isinstance(__loc_data__,Struct):
192 203 dict = __loc_data__.__dict__
193 204 else:
194 205 raise TypeError, 'Update with a dict, a Struct or key=val pairs.'
195 206 if kw:
196 207 dict.update(kw)
197 208 return dict
198 209
199 210 def __dict_invert(self,dict):
200 211 """Helper function for merge. Takes a dictionary whose values are
201 212 lists and returns a dict. with the elements of each list as keys and
202 213 the original keys as values."""
203 214
204 215 outdict = {}
205 216 for k,lst in dict.items():
206 217 if type(lst) is types.StringType:
207 218 lst = lst.split()
208 219 for entry in lst:
209 220 outdict[entry] = k
210 221 return outdict
211 222
212 223 def clear(self):
213 224 """Clear all attributes."""
214 225 self.__dict__.clear()
215 226
216 227 def copy(self):
217 228 """Return a (shallow) copy of a Struct."""
218 229 return Struct(self.__dict__.copy())
219 230
220 231 def dict(self):
221 232 """Return the Struct's dictionary."""
222 233 return self.__dict__
223 234
224 235 def dictcopy(self):
225 236 """Return a (shallow) copy of the Struct's dictionary."""
226 237 return self.__dict__.copy()
227 238
228 239 def popitem(self):
229 240 """S.popitem() -> (k, v), remove and return some (key, value) pair as
230 241 a 2-tuple; but raise KeyError if S is empty."""
231 242 return self.__dict__.popitem()
232 243
233 244 def update(self,__loc_data__=None,**kw):
234 245 """Update (merge) with data from another Struct or from a dictionary.
235 246 Optionally, one or more key=value pairs can be given at the end for
236 247 direct update."""
237 248
238 249 # The funny name __loc_data__ is to prevent a common variable name which
239 250 # could be a fieled of a Struct to collide with this parameter. The problem
240 251 # would arise if the function is called with a keyword with this same name
241 252 # that a user means to add as a Struct field.
242 253 newdict = Struct.__make_dict(self,__loc_data__,**kw)
243 254 for k,v in newdict.items():
244 255 self[k] = v
245 256
246 257 def merge(self,__loc_data__=None,__conflict_solve=None,**kw):
247 258 """S.merge(data,conflict,k=v1,k=v2,...) -> merge data and k=v into S.
248 259
249 260 This is similar to update(), but much more flexible. First, a dict is
250 261 made from data+key=value pairs. When merging this dict with the Struct
251 262 S, the optional dictionary 'conflict' is used to decide what to do.
252 263
253 264 If conflict is not given, the default behavior is to preserve any keys
254 265 with their current value (the opposite of the update method's
255 266 behavior).
256 267
257 268 conflict is a dictionary of binary functions which will be used to
258 269 solve key conflicts. It must have the following structure:
259 270
260 271 conflict == { fn1 : [Skey1,Skey2,...], fn2 : [Skey3], etc }
261 272
262 273 Values must be lists or whitespace separated strings which are
263 274 automatically converted to lists of strings by calling string.split().
264 275
265 276 Each key of conflict is a function which defines a policy for
266 277 resolving conflicts when merging with the input data. Each fn must be
267 278 a binary function which returns the desired outcome for a key
268 279 conflict. These functions will be called as fn(old,new).
269 280
270 281 An example is probably in order. Suppose you are merging the struct S
271 282 with a dict D and the following conflict policy dict:
272 283
273 284 S.merge(D,{fn1:['a','b',4], fn2:'key_c key_d'})
274 285
275 286 If the key 'a' is found in both S and D, the merge method will call:
276 287
277 288 S['a'] = fn1(S['a'],D['a'])
278 289
279 290 As a convenience, merge() provides five (the most commonly needed)
280 291 pre-defined policies: preserve, update, add, add_flip and add_s. The
281 292 easiest explanation is their implementation:
282 293
283 294 preserve = lambda old,new: old
284 295 update = lambda old,new: new
285 296 add = lambda old,new: old + new
286 297 add_flip = lambda old,new: new + old # note change of order!
287 298 add_s = lambda old,new: old + ' ' + new # only works for strings!
288 299
289 300 You can use those four words (as strings) as keys in conflict instead
290 301 of defining them as functions, and the merge method will substitute
291 302 the appropriate functions for you. That is, the call
292 303
293 304 S.merge(D,{'preserve':'a b c','add':[4,5,'d'],my_function:[6]})
294 305
295 306 will automatically substitute the functions preserve and add for the
296 307 names 'preserve' and 'add' before making any function calls.
297 308
298 309 For more complicated conflict resolution policies, you still need to
299 310 construct your own functions. """
300 311
301 312 data_dict = Struct.__make_dict(self,__loc_data__,**kw)
302 313
303 314 # policies for conflict resolution: two argument functions which return
304 315 # the value that will go in the new struct
305 316 preserve = lambda old,new: old
306 317 update = lambda old,new: new
307 318 add = lambda old,new: old + new
308 319 add_flip = lambda old,new: new + old # note change of order!
309 320 add_s = lambda old,new: old + ' ' + new
310 321
311 322 # default policy is to keep current keys when there's a conflict
312 323 conflict_solve = list2dict2(self.keys(),default = preserve)
313 324
314 325 # the conflict_solve dictionary is given by the user 'inverted': we
315 326 # need a name-function mapping, it comes as a function -> names
316 327 # dict. Make a local copy (b/c we'll make changes), replace user
317 328 # strings for the three builtin policies and invert it.
318 329 if __conflict_solve:
319 330 inv_conflict_solve_user = __conflict_solve.copy()
320 331 for name, func in [('preserve',preserve), ('update',update),
321 ('add',add), ('add_flip',add_flip), ('add_s',add_s)]:
332 ('add',add), ('add_flip',add_flip),
333 ('add_s',add_s)]:
322 334 if name in inv_conflict_solve_user.keys():
323 335 inv_conflict_solve_user[func] = inv_conflict_solve_user[name]
324 336 del inv_conflict_solve_user[name]
325 337 conflict_solve.update(Struct.__dict_invert(self,inv_conflict_solve_user))
326 338 #print 'merge. conflict_solve: '; pprint(conflict_solve) # dbg
327 339 #print '*'*50,'in merger. conflict_solver:'; pprint(conflict_solve)
328 340 for key in data_dict:
329 341 if key not in self:
330 342 self[key] = data_dict[key]
331 343 else:
332 344 self[key] = conflict_solve[key](self[key],data_dict[key])
333 345
334 346 def has_key(self,key):
335 347 """Like has_key() dictionary method."""
336 348 return self.__dict__.has_key(key)
337 349
338 350 def hasattr(self,key):
339 351 """hasattr function available as a method.
340 352
341 353 Implemented like has_key, to make sure that all available keys in the
342 354 internal dictionary of the Struct appear also as attributes (even
343 355 numeric keys)."""
344 356 return self.__dict__.has_key(key)
345 357
346 358 def items(self):
347 359 """Return the items in the Struct's dictionary, in the same format
348 360 as a call to {}.items()."""
349 361 return self.__dict__.items()
350 362
351 363 def keys(self):
352 364 """Return the keys in the Struct's dictionary, in the same format
353 365 as a call to {}.keys()."""
354 366 return self.__dict__.keys()
355 367
356 368 def values(self,keys=None):
357 369 """Return the values in the Struct's dictionary, in the same format
358 370 as a call to {}.values().
359 371
360 372 Can be called with an optional argument keys, which must be a list or
361 373 tuple of keys. In this case it returns only the values corresponding
362 374 to those keys (allowing a form of 'slicing' for Structs)."""
363 375 if not keys:
364 376 return self.__dict__.values()
365 377 else:
366 378 ret=[]
367 379 for k in keys:
368 380 ret.append(self[k])
369 381 return ret
370 382
371 383 def get(self,attr,val=None):
372 """S.get(k[,d]) -> S[k] if S.has_key(k), else d. d defaults to None."""
384 """S.get(k[,d]) -> S[k] if k in S, else d. d defaults to None."""
373 385 try:
374 386 return self[attr]
375 387 except KeyError:
376 388 return val
377 389
378 390 def setdefault(self,attr,val=None):
379 """S.setdefault(k[,d]) -> S.get(k,d), also set S[k]=d if not S.has_key(k)"""
391 """S.setdefault(k[,d]) -> S.get(k,d), also set S[k]=d if k not in S"""
380 392 if not self.has_key(attr):
381 393 self[attr] = val
382 394 return self.get(attr,val)
383 395
384 396 def allow_new_attr(self, allow = True):
385 397 """ Set whether new attributes can be created inside struct
386 398
387 This can be used to catch typos by verifying that the attribute user tries to
388 change already exists in this Struct.
399 This can be used to catch typos by verifying that the attribute user
400 tries to change already exists in this Struct.
389 401 """
390 402 self['__allownew'] = allow
391 403
392 404
393 405 # end class Struct
394 406
@@ -1,874 +1,876 b''
1 1 # encoding: utf-8
2 2 # -*- test-case-name: IPython.kernel.tests.test_engineservice -*-
3 3
4 4 """A Twisted Service Representation of the IPython core.
5 5
6 6 The IPython Core exposed to the network is called the Engine. Its
7 7 representation in Twisted in the EngineService. Interfaces and adapters
8 8 are used to abstract out the details of the actual network protocol used.
9 9 The EngineService is an Engine that knows nothing about the actual protocol
10 10 used.
11 11
12 12 The EngineService is exposed with various network protocols in modules like:
13 13
14 14 enginepb.py
15 15 enginevanilla.py
16 16
17 17 As of 12/12/06 the classes in this module have been simplified greatly. It was
18 18 felt that we had over-engineered things. To improve the maintainability of the
19 19 code we have taken out the ICompleteEngine interface and the completeEngine
20 20 method that automatically added methods to engines.
21 21
22 22 """
23 23
24 24 __docformat__ = "restructuredtext en"
25 25
26 26 #-------------------------------------------------------------------------------
27 27 # Copyright (C) 2008 The IPython Development Team
28 28 #
29 29 # Distributed under the terms of the BSD License. The full license is in
30 30 # the file COPYING, distributed as part of this software.
31 31 #-------------------------------------------------------------------------------
32 32
33 33 #-------------------------------------------------------------------------------
34 34 # Imports
35 35 #-------------------------------------------------------------------------------
36 36
37 37 import os, sys, copy
38 38 import cPickle as pickle
39 39 from new import instancemethod
40 40
41 41 from twisted.application import service
42 42 from twisted.internet import defer, reactor
43 43 from twisted.python import log, failure, components
44 44 import zope.interface as zi
45 45
46 46 from IPython.kernel.core.interpreter import Interpreter
47 47 from IPython.kernel import newserialized, error, util
48 48 from IPython.kernel.util import printer
49 49 from IPython.kernel.twistedutil import gatherBoth, DeferredList
50 50 from IPython.kernel import codeutil
51 51
52 52
53 53 #-------------------------------------------------------------------------------
54 54 # Interface specification for the Engine
55 55 #-------------------------------------------------------------------------------
56 56
57 57 class IEngineCore(zi.Interface):
58 58 """The minimal required interface for the IPython Engine.
59 59
60 60 This interface provides a formal specification of the IPython core.
61 61 All these methods should return deferreds regardless of what side of a
62 62 network connection they are on.
63 63
64 64 In general, this class simply wraps a shell class and wraps its return
65 65 values as Deferred objects. If the underlying shell class method raises
66 66 an exception, this class should convert it to a twisted.failure.Failure
67 67 that will be propagated along the Deferred's errback chain.
68 68
69 69 In addition, Failures are aggressive. By this, we mean that if a method
70 70 is performing multiple actions (like pulling multiple object) if any
71 71 single one fails, the entire method will fail with that Failure. It is
72 72 all or nothing.
73 73 """
74 74
75 75 id = zi.interface.Attribute("the id of the Engine object")
76 76 properties = zi.interface.Attribute("A dict of properties of the Engine")
77 77
78 78 def execute(lines):
79 79 """Execute lines of Python code.
80 80
81 81 Returns a dictionary with keys (id, number, stdin, stdout, stderr)
82 82 upon success.
83 83
84 84 Returns a failure object if the execution of lines raises an exception.
85 85 """
86 86
87 87 def push(namespace):
88 88 """Push dict namespace into the user's namespace.
89 89
90 90 Returns a deferred to None or a failure.
91 91 """
92 92
93 93 def pull(keys):
94 94 """Pulls values out of the user's namespace by keys.
95 95
96 96 Returns a deferred to a tuple objects or a single object.
97 97
98 98 Raises NameError if any one of objects doess not exist.
99 99 """
100 100
101 101 def push_function(namespace):
102 102 """Push a dict of key, function pairs into the user's namespace.
103 103
104 104 Returns a deferred to None or a failure."""
105 105
106 106 def pull_function(keys):
107 107 """Pulls functions out of the user's namespace by keys.
108 108
109 109 Returns a deferred to a tuple of functions or a single function.
110 110
111 111 Raises NameError if any one of the functions does not exist.
112 112 """
113 113
114 114 def get_result(i=None):
115 115 """Get the stdin/stdout/stderr of command i.
116 116
117 117 Returns a deferred to a dict with keys
118 118 (id, number, stdin, stdout, stderr).
119 119
120 120 Raises IndexError if command i does not exist.
121 121 Raises TypeError if i in not an int.
122 122 """
123 123
124 124 def reset():
125 125 """Reset the shell.
126 126
127 127 This clears the users namespace. Won't cause modules to be
128 128 reloaded. Should also re-initialize certain variables like id.
129 129 """
130 130
131 131 def kill():
132 132 """Kill the engine by stopping the reactor."""
133 133
134 134 def keys():
135 135 """Return the top level variables in the users namspace.
136 136
137 137 Returns a deferred to a dict."""
138 138
139 139
140 140 class IEngineSerialized(zi.Interface):
141 141 """Push/Pull methods that take Serialized objects.
142 142
143 143 All methods should return deferreds.
144 144 """
145 145
146 146 def push_serialized(namespace):
147 147 """Push a dict of keys and Serialized objects into the user's namespace."""
148 148
149 149 def pull_serialized(keys):
150 150 """Pull objects by key from the user's namespace as Serialized.
151 151
152 152 Returns a list of or one Serialized.
153 153
154 154 Raises NameError is any one of the objects does not exist.
155 155 """
156 156
157 157
158 158 class IEngineProperties(zi.Interface):
159 159 """Methods for access to the properties object of an Engine"""
160 160
161 161 properties = zi.Attribute("A StrictDict object, containing the properties")
162 162
163 163 def set_properties(properties):
164 164 """set properties by key and value"""
165 165
166 166 def get_properties(keys=None):
167 167 """get a list of properties by `keys`, if no keys specified, get all"""
168 168
169 169 def del_properties(keys):
170 170 """delete properties by `keys`"""
171 171
172 172 def has_properties(keys):
173 173 """get a list of bool values for whether `properties` has `keys`"""
174 174
175 175 def clear_properties():
176 176 """clear the properties dict"""
177 177
178 178 class IEngineBase(IEngineCore, IEngineSerialized, IEngineProperties):
179 179 """The basic engine interface that EngineService will implement.
180 180
181 181 This exists so it is easy to specify adapters that adapt to and from the
182 182 API that the basic EngineService implements.
183 183 """
184 184 pass
185 185
186 186 class IEngineQueued(IEngineBase):
187 187 """Interface for adding a queue to an IEngineBase.
188 188
189 189 This interface extends the IEngineBase interface to add methods for managing
190 190 the engine's queue. The implicit details of this interface are that the
191 191 execution of all methods declared in IEngineBase should appropriately be
192 192 put through a queue before execution.
193 193
194 194 All methods should return deferreds.
195 195 """
196 196
197 197 def clear_queue():
198 198 """Clear the queue."""
199 199
200 200 def queue_status():
201 201 """Get the queued and pending commands in the queue."""
202 202
203 203 def register_failure_observer(obs):
204 204 """Register an observer of pending Failures.
205 205
206 206 The observer must implement IFailureObserver.
207 207 """
208 208
209 209 def unregister_failure_observer(obs):
210 210 """Unregister an observer of pending Failures."""
211 211
212 212
213 213 class IEngineThreaded(zi.Interface):
214 214 """A place holder for threaded commands.
215 215
216 216 All methods should return deferreds.
217 217 """
218 218 pass
219 219
220 220
221 221 #-------------------------------------------------------------------------------
222 222 # Functions and classes to implement the EngineService
223 223 #-------------------------------------------------------------------------------
224 224
225 225
226 226 class StrictDict(dict):
227 227 """This is a strict copying dictionary for use as the interface to the
228 228 properties of an Engine.
229 229 :IMPORTANT:
230 230 This object copies the values you set to it, and returns copies to you
231 231 when you request them. The only way to change properties os explicitly
232 232 through the setitem and getitem of the dictionary interface.
233 233 Example:
234 234 >>> e = kernel.get_engine(id)
235 235 >>> L = someList
236 236 >>> e.properties['L'] = L
237 237 >>> L == e.properties['L']
238 238 ... True
239 239 >>> L.append(something Else)
240 240 >>> L == e.properties['L']
241 241 ... False
242 242
243 243 getitem copies, so calls to methods of objects do not affect the
244 244 properties, as in the following example:
245 245 >>> e.properties[1] = range(2)
246 246 >>> print e.properties[1]
247 247 ... [0, 1]
248 248 >>> e.properties[1].append(2)
249 249 >>> print e.properties[1]
250 250 ... [0, 1]
251 251
252 252 """
253 253 def __init__(self, *args, **kwargs):
254 254 dict.__init__(self, *args, **kwargs)
255 255 self.modified = True
256 256
257 257 def __getitem__(self, key):
258 258 return copy.deepcopy(dict.__getitem__(self, key))
259 259
260 260 def __setitem__(self, key, value):
261 261 # check if this entry is valid for transport around the network
262 262 # and copying
263 263 try:
264 264 pickle.dumps(key, 2)
265 265 pickle.dumps(value, 2)
266 266 newvalue = copy.deepcopy(value)
267 267 except:
268 268 raise error.InvalidProperty(value)
269 269 dict.__setitem__(self, key, newvalue)
270 270 self.modified = True
271 271
272 272 def __delitem__(self, key):
273 273 dict.__delitem__(self, key)
274 274 self.modified = True
275 275
276 276 def update(self, dikt):
277 277 for k,v in dikt.iteritems():
278 278 self[k] = v
279 279
280 280 def pop(self, key):
281 281 self.modified = True
282 282 return dict.pop(self, key)
283 283
284 284 def popitem(self):
285 285 self.modified = True
286 286 return dict.popitem(self)
287 287
288 288 def clear(self):
289 289 self.modified = True
290 290 dict.clear(self)
291 291
292 292 def subDict(self, *keys):
293 293 d = {}
294 294 for key in keys:
295 295 d[key] = self[key]
296 296 return d
297 297
298 298
299 299
300 300 class EngineAPI(object):
301 301 """This is the object through which the user can edit the `properties`
302 302 attribute of an Engine.
303 303 The Engine Properties object copies all object in and out of itself.
304 304 See the EngineProperties object for details.
305 305 """
306 306 _fix=False
307 307 def __init__(self, id):
308 308 self.id = id
309 309 self.properties = StrictDict()
310 310 self._fix=True
311 311
312 312 def __setattr__(self, k,v):
313 313 if self._fix:
314 314 raise error.KernelError("I am protected!")
315 315 else:
316 316 object.__setattr__(self, k, v)
317 317
318 318 def __delattr__(self, key):
319 319 raise error.KernelError("I am protected!")
320 320
321 321
322 322 _apiDict = {}
323 323
324 324 def get_engine(id):
325 325 """Get the Engine API object, whcih currently just provides the properties
326 326 object, by ID"""
327 327 global _apiDict
328 328 if not _apiDict.get(id):
329 329 _apiDict[id] = EngineAPI(id)
330 330 return _apiDict[id]
331 331
332 332 def drop_engine(id):
333 333 """remove an engine"""
334 334 global _apiDict
335 335 if _apiDict.has_key(id):
336 336 del _apiDict[id]
337 337
338 338 class EngineService(object, service.Service):
339 339 """Adapt a IPython shell into a IEngine implementing Twisted Service."""
340 340
341 341 zi.implements(IEngineBase)
342 342 name = 'EngineService'
343 343
344 344 def __init__(self, shellClass=Interpreter, mpi=None):
345 345 """Create an EngineService.
346 346
347 347 shellClass: something that implements IInterpreter or core1
348 348 mpi: an mpi module that has rank and size attributes
349 349 """
350 350 self.shellClass = shellClass
351 351 self.shell = self.shellClass()
352 352 self.mpi = mpi
353 353 self.id = None
354 354 self.properties = get_engine(self.id).properties
355 355 if self.mpi is not None:
356 356 log.msg("MPI started with rank = %i and size = %i" %
357 357 (self.mpi.rank, self.mpi.size))
358 358 self.id = self.mpi.rank
359 359 self._seedNamespace()
360 360
361 361 # Make id a property so that the shell can get the updated id
362 362
363 363 def _setID(self, id):
364 364 self._id = id
365 365 self.properties = get_engine(id).properties
366 366 self.shell.push({'id': id})
367 367
368 368 def _getID(self):
369 369 return self._id
370 370
371 371 id = property(_getID, _setID)
372 372
373 373 def _seedNamespace(self):
374 374 self.shell.push({'mpi': self.mpi, 'id' : self.id})
375 375
376 376 def executeAndRaise(self, msg, callable, *args, **kwargs):
377 377 """Call a method of self.shell and wrap any exception."""
378 378 d = defer.Deferred()
379 379 try:
380 380 result = callable(*args, **kwargs)
381 381 except:
382 382 # This gives the following:
383 383 # et=exception class
384 384 # ev=exception class instance
385 385 # tb=traceback object
386 386 et,ev,tb = sys.exc_info()
387 387 # This call adds attributes to the exception value
388 388 et,ev,tb = self.shell.formatTraceback(et,ev,tb,msg)
389 389 # Add another attribute
390 390 ev._ipython_engine_info = msg
391 391 f = failure.Failure(ev,et,None)
392 392 d.errback(f)
393 393 else:
394 394 d.callback(result)
395 395
396 396 return d
397 397
398 398 # The IEngine methods. See the interface for documentation.
399 399
400 400 def execute(self, lines):
401 401 msg = {'engineid':self.id,
402 402 'method':'execute',
403 403 'args':[lines]}
404 404 d = self.executeAndRaise(msg, self.shell.execute, lines)
405 405 d.addCallback(self.addIDToResult)
406 406 return d
407 407
408 408 def addIDToResult(self, result):
409 409 result['id'] = self.id
410 410 return result
411 411
412 412 def push(self, namespace):
413 413 msg = {'engineid':self.id,
414 414 'method':'push',
415 415 'args':[repr(namespace.keys())]}
416 416 d = self.executeAndRaise(msg, self.shell.push, namespace)
417 417 return d
418 418
419 419 def pull(self, keys):
420 420 msg = {'engineid':self.id,
421 421 'method':'pull',
422 422 'args':[repr(keys)]}
423 423 d = self.executeAndRaise(msg, self.shell.pull, keys)
424 424 return d
425 425
426 426 def push_function(self, namespace):
427 427 msg = {'engineid':self.id,
428 428 'method':'push_function',
429 429 'args':[repr(namespace.keys())]}
430 430 d = self.executeAndRaise(msg, self.shell.push_function, namespace)
431 431 return d
432 432
433 433 def pull_function(self, keys):
434 434 msg = {'engineid':self.id,
435 435 'method':'pull_function',
436 436 'args':[repr(keys)]}
437 437 d = self.executeAndRaise(msg, self.shell.pull_function, keys)
438 438 return d
439 439
440 440 def get_result(self, i=None):
441 441 msg = {'engineid':self.id,
442 442 'method':'get_result',
443 443 'args':[repr(i)]}
444 444 d = self.executeAndRaise(msg, self.shell.getCommand, i)
445 445 d.addCallback(self.addIDToResult)
446 446 return d
447 447
448 448 def reset(self):
449 449 msg = {'engineid':self.id,
450 450 'method':'reset',
451 451 'args':[]}
452 452 del self.shell
453 453 self.shell = self.shellClass()
454 454 self.properties.clear()
455 455 d = self.executeAndRaise(msg, self._seedNamespace)
456 456 return d
457 457
458 458 def kill(self):
459 459 drop_engine(self.id)
460 460 try:
461 461 reactor.stop()
462 462 except RuntimeError:
463 463 log.msg('The reactor was not running apparently.')
464 464 return defer.fail()
465 465 else:
466 466 return defer.succeed(None)
467 467
468 468 def keys(self):
469 469 """Return a list of variables names in the users top level namespace.
470 470
471 471 This used to return a dict of all the keys/repr(values) in the
472 472 user's namespace. This was too much info for the ControllerService
473 473 to handle so it is now just a list of keys.
474 474 """
475 475
476 476 remotes = []
477 477 for k in self.shell.user_ns.iterkeys():
478 478 if k not in ['__name__', '_ih', '_oh', '__builtins__',
479 479 'In', 'Out', '_', '__', '___', '__IP', 'input', 'raw_input']:
480 480 remotes.append(k)
481 481 return defer.succeed(remotes)
482 482
483 483 def set_properties(self, properties):
484 484 msg = {'engineid':self.id,
485 485 'method':'set_properties',
486 486 'args':[repr(properties.keys())]}
487 487 return self.executeAndRaise(msg, self.properties.update, properties)
488 488
489 489 def get_properties(self, keys=None):
490 490 msg = {'engineid':self.id,
491 491 'method':'get_properties',
492 492 'args':[repr(keys)]}
493 493 if keys is None:
494 494 keys = self.properties.keys()
495 495 return self.executeAndRaise(msg, self.properties.subDict, *keys)
496 496
497 497 def _doDel(self, keys):
498 498 for key in keys:
499 499 del self.properties[key]
500 500
501 501 def del_properties(self, keys):
502 502 msg = {'engineid':self.id,
503 503 'method':'del_properties',
504 504 'args':[repr(keys)]}
505 505 return self.executeAndRaise(msg, self._doDel, keys)
506 506
507 507 def _doHas(self, keys):
508 508 return [self.properties.has_key(key) for key in keys]
509 509
510 510 def has_properties(self, keys):
511 511 msg = {'engineid':self.id,
512 512 'method':'has_properties',
513 513 'args':[repr(keys)]}
514 514 return self.executeAndRaise(msg, self._doHas, keys)
515 515
516 516 def clear_properties(self):
517 517 msg = {'engineid':self.id,
518 518 'method':'clear_properties',
519 519 'args':[]}
520 520 return self.executeAndRaise(msg, self.properties.clear)
521 521
522 522 def push_serialized(self, sNamespace):
523 523 msg = {'engineid':self.id,
524 524 'method':'push_serialized',
525 525 'args':[repr(sNamespace.keys())]}
526 526 ns = {}
527 527 for k,v in sNamespace.iteritems():
528 528 try:
529 529 unserialized = newserialized.IUnSerialized(v)
530 530 ns[k] = unserialized.getObject()
531 531 except:
532 532 return defer.fail()
533 533 return self.executeAndRaise(msg, self.shell.push, ns)
534 534
535 535 def pull_serialized(self, keys):
536 536 msg = {'engineid':self.id,
537 537 'method':'pull_serialized',
538 538 'args':[repr(keys)]}
539 539 if isinstance(keys, str):
540 540 keys = [keys]
541 541 if len(keys)==1:
542 542 d = self.executeAndRaise(msg, self.shell.pull, keys)
543 543 d.addCallback(newserialized.serialize)
544 544 return d
545 545 elif len(keys)>1:
546 546 d = self.executeAndRaise(msg, self.shell.pull, keys)
547 547 @d.addCallback
548 548 def packThemUp(values):
549 549 serials = []
550 550 for v in values:
551 551 try:
552 552 serials.append(newserialized.serialize(v))
553 553 except:
554 554 return defer.fail(failure.Failure())
555 555 return serials
556 556 return packThemUp
557 557
558 558
559 559 def queue(methodToQueue):
560 560 def queuedMethod(this, *args, **kwargs):
561 561 name = methodToQueue.__name__
562 562 return this.submitCommand(Command(name, *args, **kwargs))
563 563 return queuedMethod
564 564
565 565 class QueuedEngine(object):
566 566 """Adapt an IEngineBase to an IEngineQueued by wrapping it.
567 567
568 568 The resulting object will implement IEngineQueued which extends
569 569 IEngineCore which extends (IEngineBase, IEngineSerialized).
570 570
571 571 This seems like the best way of handling it, but I am not sure. The
572 572 other option is to have the various base interfaces be used like
573 573 mix-in intefaces. The problem I have with this is adpatation is
574 574 more difficult and complicated because there can be can multiple
575 575 original and final Interfaces.
576 576 """
577 577
578 578 zi.implements(IEngineQueued)
579 579
580 580 def __init__(self, engine):
581 581 """Create a QueuedEngine object from an engine
582 582
583 583 engine: An implementor of IEngineCore and IEngineSerialized
584 584 keepUpToDate: whether to update the remote status when the
585 585 queue is empty. Defaults to False.
586 586 """
587 587
588 588 # This is the right way to do these tests rather than
589 589 # IEngineCore in list(zi.providedBy(engine)) which will only
590 590 # picks of the interfaces that are directly declared by engine.
591 591 assert IEngineBase.providedBy(engine), \
592 592 "engine passed to QueuedEngine doesn't provide IEngineBase"
593 593
594 594 self.engine = engine
595 595 self.id = engine.id
596 596 self.queued = []
597 597 self.history = {}
598 598 self.engineStatus = {}
599 599 self.currentCommand = None
600 600 self.failureObservers = []
601 601
602 602 def _get_properties(self):
603 603 return self.engine.properties
604 604
605 605 properties = property(_get_properties, lambda self, _: None)
606 606 # Queue management methods. You should not call these directly
607 607
608 608 def submitCommand(self, cmd):
609 609 """Submit command to queue."""
610 610
611 611 d = defer.Deferred()
612 612 cmd.setDeferred(d)
613 613 if self.currentCommand is not None:
614 614 if self.currentCommand.finished:
615 615 # log.msg("Running command immediately: %r" % cmd)
616 616 self.currentCommand = cmd
617 617 self.runCurrentCommand()
618 618 else: # command is still running
619 619 # log.msg("Command is running: %r" % self.currentCommand)
620 620 # log.msg("Queueing: %r" % cmd)
621 621 self.queued.append(cmd)
622 622 else:
623 623 # log.msg("No current commands, running: %r" % cmd)
624 624 self.currentCommand = cmd
625 625 self.runCurrentCommand()
626 626 return d
627 627
628 628 def runCurrentCommand(self):
629 629 """Run current command."""
630 630
631 631 cmd = self.currentCommand
632 632 f = getattr(self.engine, cmd.remoteMethod, None)
633 633 if f:
634 634 d = f(*cmd.args, **cmd.kwargs)
635 635 if cmd.remoteMethod is 'execute':
636 636 d.addCallback(self.saveResult)
637 637 d.addCallback(self.finishCommand)
638 638 d.addErrback(self.abortCommand)
639 639 else:
640 640 return defer.fail(AttributeError(cmd.remoteMethod))
641 641
642 642 def _flushQueue(self):
643 643 """Pop next command in queue and run it."""
644 644
645 645 if len(self.queued) > 0:
646 646 self.currentCommand = self.queued.pop(0)
647 647 self.runCurrentCommand()
648 648
649 649 def saveResult(self, result):
650 650 """Put the result in the history."""
651 651 self.history[result['number']] = result
652 652 return result
653 653
654 654 def finishCommand(self, result):
655 655 """Finish currrent command."""
656 656
657 657 # The order of these commands is absolutely critical.
658 658 self.currentCommand.handleResult(result)
659 659 self.currentCommand.finished = True
660 660 self._flushQueue()
661 661 return result
662 662
663 663 def abortCommand(self, reason):
664 664 """Abort current command.
665 665
666 666 This eats the Failure but first passes it onto the Deferred that the
667 667 user has.
668 668
669 669 It also clear out the queue so subsequence commands don't run.
670 670 """
671 671
672 672 # The order of these 3 commands is absolutely critical. The currentCommand
673 673 # must first be marked as finished BEFORE the queue is cleared and before
674 674 # the current command is sent the failure.
675 675 # Also, the queue must be cleared BEFORE the current command is sent the Failure
676 676 # otherwise the errback chain could trigger new commands to be added to the
677 677 # queue before we clear it. We should clear ONLY the commands that were in
678 678 # the queue when the error occured.
679 679 self.currentCommand.finished = True
680 680 s = "%r %r %r" % (self.currentCommand.remoteMethod, self.currentCommand.args, self.currentCommand.kwargs)
681 681 self.clear_queue(msg=s)
682 682 self.currentCommand.handleError(reason)
683 683
684 684 return None
685 685
686 686 #---------------------------------------------------------------------------
687 687 # IEngineCore methods
688 688 #---------------------------------------------------------------------------
689 689
690 690 @queue
691 691 def execute(self, lines):
692 692 pass
693 693
694 694 @queue
695 695 def push(self, namespace):
696 696 pass
697 697
698 698 @queue
699 699 def pull(self, keys):
700 700 pass
701 701
702 702 @queue
703 703 def push_function(self, namespace):
704 704 pass
705 705
706 706 @queue
707 707 def pull_function(self, keys):
708 708 pass
709 709
710 710 def get_result(self, i=None):
711 711 if i is None:
712 712 i = max(self.history.keys()+[None])
713 713
714 714 cmd = self.history.get(i, None)
715 715 # Uncomment this line to disable chaching of results
716 716 #cmd = None
717 717 if cmd is None:
718 718 return self.submitCommand(Command('get_result', i))
719 719 else:
720 720 return defer.succeed(cmd)
721 721
722 722 def reset(self):
723 723 self.clear_queue()
724 724 self.history = {} # reset the cache - I am not sure we should do this
725 725 return self.submitCommand(Command('reset'))
726 726
727 727 def kill(self):
728 728 self.clear_queue()
729 729 return self.submitCommand(Command('kill'))
730 730
731 731 @queue
732 732 def keys(self):
733 733 pass
734 734
735 735 #---------------------------------------------------------------------------
736 736 # IEngineSerialized methods
737 737 #---------------------------------------------------------------------------
738 738
739 739 @queue
740 740 def push_serialized(self, namespace):
741 741 pass
742 742
743 743 @queue
744 744 def pull_serialized(self, keys):
745 745 pass
746 746
747 747 #---------------------------------------------------------------------------
748 748 # IEngineProperties methods
749 749 #---------------------------------------------------------------------------
750 750
751 751 @queue
752 752 def set_properties(self, namespace):
753 753 pass
754 754
755 755 @queue
756 756 def get_properties(self, keys=None):
757 757 pass
758 758
759 759 @queue
760 760 def del_properties(self, keys):
761 761 pass
762 762
763 763 @queue
764 764 def has_properties(self, keys):
765 765 pass
766 766
767 767 @queue
768 768 def clear_properties(self):
769 769 pass
770 770
771 771 #---------------------------------------------------------------------------
772 772 # IQueuedEngine methods
773 773 #---------------------------------------------------------------------------
774 774
775 775 def clear_queue(self, msg=''):
776 776 """Clear the queue, but doesn't cancel the currently running commmand."""
777 777
778 778 for cmd in self.queued:
779 779 cmd.deferred.errback(failure.Failure(error.QueueCleared(msg)))
780 780 self.queued = []
781 781 return defer.succeed(None)
782 782
783 783 def queue_status(self):
784 784 if self.currentCommand is not None:
785 785 if self.currentCommand.finished:
786 786 pending = repr(None)
787 787 else:
788 788 pending = repr(self.currentCommand)
789 789 else:
790 790 pending = repr(None)
791 791 dikt = {'queue':map(repr,self.queued), 'pending':pending}
792 792 return defer.succeed(dikt)
793 793
794 794 def register_failure_observer(self, obs):
795 795 self.failureObservers.append(obs)
796 796
797 797 def unregister_failure_observer(self, obs):
798 798 self.failureObservers.remove(obs)
799 799
800 800
801 801 # Now register QueuedEngine as an adpater class that makes an IEngineBase into a
802 802 # IEngineQueued.
803 803 components.registerAdapter(QueuedEngine, IEngineBase, IEngineQueued)
804 804
805 805
806 806 class Command(object):
807 807 """A command object that encapslates queued commands.
808 808
809 809 This class basically keeps track of a command that has been queued
810 810 in a QueuedEngine. It manages the deferreds and hold the method to be called
811 811 and the arguments to that method.
812 812 """
813 813
814 814
815 815 def __init__(self, remoteMethod, *args, **kwargs):
816 816 """Build a new Command object."""
817 817
818 818 self.remoteMethod = remoteMethod
819 819 self.args = args
820 820 self.kwargs = kwargs
821 821 self.finished = False
822 822
823 823 def setDeferred(self, d):
824 824 """Sets the deferred attribute of the Command."""
825 825
826 826 self.deferred = d
827 827
828 828 def __repr__(self):
829 829 if not self.args:
830 830 args = ''
831 831 else:
832 832 args = str(self.args)[1:-2] #cut off (...,)
833 833 for k,v in self.kwargs.iteritems():
834 834 if args:
835 835 args += ', '
836 836 args += '%s=%r' %(k,v)
837 837 return "%s(%s)" %(self.remoteMethod, args)
838 838
839 839 def handleResult(self, result):
840 840 """When the result is ready, relay it to self.deferred."""
841 841
842 842 self.deferred.callback(result)
843 843
844 844 def handleError(self, reason):
845 845 """When an error has occured, relay it to self.deferred."""
846 846
847 847 self.deferred.errback(reason)
848 848
849 849 class ThreadedEngineService(EngineService):
850 """An EngineService subclass that defers execute commands to a separate thread.
850 """An EngineService subclass that defers execute commands to a separate
851 thread.
851 852
852 ThreadedEngineService uses twisted.internet.threads.deferToThread to defer execute
853 requests to a separate thread. GUI frontends may want to use ThreadedEngineService as
854 the engine in an IPython.frontend.frontendbase.FrontEndBase subclass to prevent
853 ThreadedEngineService uses twisted.internet.threads.deferToThread to
854 defer execute requests to a separate thread. GUI frontends may want to
855 use ThreadedEngineService as the engine in an
856 IPython.frontend.frontendbase.FrontEndBase subclass to prevent
855 857 block execution from blocking the GUI thread.
856 858 """
857 859
858 860 zi.implements(IEngineBase)
859 861
860 862 def __init__(self, shellClass=Interpreter, mpi=None):
861 863 EngineService.__init__(self, shellClass, mpi)
862 864
863 865
864 866 def execute(self, lines):
865 867 # Only import this if we are going to use this class
866 868 from twisted.internet import threads
867 869
868 870 msg = {'engineid':self.id,
869 871 'method':'execute',
870 872 'args':[lines]}
871 873
872 874 d = threads.deferToThread(self.shell.execute, lines)
873 875 d.addCallback(self.addIDToResult)
874 876 return d
1 NO CONTENT: file renamed from docs/ChangeLog to docs/attic/ChangeLog
@@ -1,161 +1,162 b''
1 1 .. _changes:
2 2
3 3 ==========
4 4 What's new
5 5 ==========
6 6
7 7 .. contents::
8 8
9 9 Release 0.9
10 10 ===========
11 11
12 12 New features
13 13 ------------
14 14
15 15 * All of the parallel computing capabilities from `ipython1-dev` have been merged into
16 16 IPython proper. This resulted in the following new subpackages:
17 17 :mod:`IPython.kernel`, :mod:`IPython.kernel.core`, :mod:`IPython.config`,
18 18 :mod:`IPython.tools` and :mod:`IPython.testing`.
19 19 * As part of merging in the `ipython1-dev` stuff, the `setup.py` script and friends
20 20 have been completely refactored. Now we are checking for dependencies using
21 21 the approach that matplotlib uses.
22 22 * The documentation has been completely reorganized to accept the documentation
23 23 from `ipython1-dev`.
24 24 * We have switched to using Foolscap for all of our network protocols in
25 25 :mod:`IPython.kernel`. This gives us secure connections that are both encrypted
26 26 and authenticated.
27 27 * We have a brand new `COPYING.txt` files that describes the IPython license
28 28 and copyright. The biggest change is that we are putting "The IPython
29 29 Development Team" as the copyright holder. We give more details about exactly
30 30 what this means in this file. All developer should read this and use the new
31 31 banner in all IPython source code files.
32 * sh profile: ./foo runs foo as system command, no need to do !./foo anymore
32 33
33 34 Bug fixes
34 35 ---------
35 36
36 37 * A few subpackages has missing `__init__.py` files.
37 38 * The documentation is only created is Sphinx is found. Previously, the `setup.py`
38 39 script would fail if it was missing.
39 40
40 41 Backwards incompatible changes
41 42 ------------------------------
42 43
43 44 * IPython has a larger set of dependencies if you want all of its capabilities.
44 45 See the `setup.py` script for details.
45 46 * The constructors for :class:`IPython.kernel.client.MultiEngineClient` and
46 47 :class:`IPython.kernel.client.TaskClient` no longer take the (ip,port) tuple.
47 48 Instead they take the filename of a file that contains the FURL for that
48 49 client. If the FURL file is in your IPYTHONDIR, it will be found automatically
49 50 and the constructor can be left empty.
50 51 * The asynchronous clients in :mod:`IPython.kernel.asyncclient` are now created
51 52 using the factory functions :func:`get_multiengine_client` and
52 53 :func:`get_task_client`. These return a `Deferred` to the actual client.
53 54 * The command line options to `ipcontroller` and `ipengine` have changed to
54 55 reflect the new Foolscap network protocol and the FURL files. Please see the
55 56 help for these scripts for details.
56 57 * The configuration files for the kernel have changed because of the Foolscap stuff.
57 58 If you were using custom config files before, you should delete them and regenerate
58 59 new ones.
59 60
60 61 Changes merged in from IPython1
61 62 -------------------------------
62 63
63 64 New features
64 65 ............
65 66
66 67 * Much improved ``setup.py`` and ``setupegg.py`` scripts. Because Twisted
67 68 and zope.interface are now easy installable, we can declare them as dependencies
68 69 in our setupegg.py script.
69 70 * IPython is now compatible with Twisted 2.5.0 and 8.x.
70 71 * Added a new example of how to use :mod:`ipython1.kernel.asynclient`.
71 72 * Initial draft of a process daemon in :mod:`ipython1.daemon`. This has not
72 73 been merged into IPython and is still in `ipython1-dev`.
73 74 * The ``TaskController`` now has methods for getting the queue status.
74 75 * The ``TaskResult`` objects not have information about how long the task
75 76 took to run.
76 77 * We are attaching additional attributes to exceptions ``(_ipython_*)`` that
77 78 we use to carry additional info around.
78 79 * New top-level module :mod:`asyncclient` that has asynchronous versions (that
79 80 return deferreds) of the client classes. This is designed to users who want
80 81 to run their own Twisted reactor
81 82 * All the clients in :mod:`client` are now based on Twisted. This is done by
82 83 running the Twisted reactor in a separate thread and using the
83 84 :func:`blockingCallFromThread` function that is in recent versions of Twisted.
84 85 * Functions can now be pushed/pulled to/from engines using
85 86 :meth:`MultiEngineClient.push_function` and :meth:`MultiEngineClient.pull_function`.
86 87 * Gather/scatter are now implemented in the client to reduce the work load
87 88 of the controller and improve performance.
88 89 * Complete rewrite of the IPython docuementation. All of the documentation
89 90 from the IPython website has been moved into docs/source as restructured
90 91 text documents. PDF and HTML documentation are being generated using
91 92 Sphinx.
92 93 * New developer oriented documentation: development guidelines and roadmap.
93 94 * Traditional ``ChangeLog`` has been changed to a more useful ``changes.txt`` file
94 95 that is organized by release and is meant to provide something more relevant
95 96 for users.
96 97
97 98 Bug fixes
98 99 .........
99 100
100 101 * Created a proper ``MANIFEST.in`` file to create source distributions.
101 102 * Fixed a bug in the ``MultiEngine`` interface. Previously, multi-engine
102 103 actions were being collected with a :class:`DeferredList` with
103 104 ``fireononeerrback=1``. This meant that methods were returning
104 105 before all engines had given their results. This was causing extremely odd
105 106 bugs in certain cases. To fix this problem, we have 1) set
106 107 ``fireononeerrback=0`` to make sure all results (or exceptions) are in
107 108 before returning and 2) introduced a :exc:`CompositeError` exception
108 109 that wraps all of the engine exceptions. This is a huge change as it means
109 110 that users will have to catch :exc:`CompositeError` rather than the actual
110 111 exception.
111 112
112 113 Backwards incompatible changes
113 114 ..............................
114 115
115 116 * All names have been renamed to conform to the lowercase_with_underscore
116 117 convention. This will require users to change references to all names like
117 118 ``queueStatus`` to ``queue_status``.
118 119 * Previously, methods like :meth:`MultiEngineClient.push` and
119 120 :meth:`MultiEngineClient.push` used ``*args`` and ``**kwargs``. This was
120 121 becoming a problem as we weren't able to introduce new keyword arguments into
121 122 the API. Now these methods simple take a dict or sequence. This has also allowed
122 123 us to get rid of the ``*All`` methods like :meth:`pushAll` and :meth:`pullAll`.
123 124 These things are now handled with the ``targets`` keyword argument that defaults
124 125 to ``'all'``.
125 126 * The :attr:`MultiEngineClient.magicTargets` has been renamed to
126 127 :attr:`MultiEngineClient.targets`.
127 128 * All methods in the MultiEngine interface now accept the optional keyword argument
128 129 ``block``.
129 130 * Renamed :class:`RemoteController` to :class:`MultiEngineClient` and
130 131 :class:`TaskController` to :class:`TaskClient`.
131 132 * Renamed the top-level module from :mod:`api` to :mod:`client`.
132 133 * Most methods in the multiengine interface now raise a :exc:`CompositeError` exception
133 134 that wraps the user's exceptions, rather than just raising the raw user's exception.
134 135 * Changed the ``setupNS`` and ``resultNames`` in the ``Task`` class to ``push``
135 136 and ``pull``.
136 137
137 138 Release 0.8.4
138 139 =============
139 140
140 141 Someone needs to describe what went into 0.8.4.
141 142
142 143 Release 0.8.2
143 144 =============
144 145
145 146 * %pushd/%popd behave differently; now "pushd /foo" pushes CURRENT directory
146 147 and jumps to /foo. The current behaviour is closer to the documented
147 148 behaviour, and should not trip anyone.
148 149
149 150 Release 0.8.3
150 151 =============
151 152
152 153 * pydb is now disabled by default (due to %run -d problems). You can enable
153 154 it by passing -pydb command line argument to IPython. Note that setting
154 155 it in config file won't work.
155 156
156 157 Older releases
157 158 ==============
158 159
159 160 Changes in earlier releases of IPython are described in the older file ``ChangeLog``.
160 161 Please refer to this document for details.
161 162
@@ -1,315 +1,360 b''
1 1 .. _development:
2 2
3 3 ==================================
4 4 IPython development guidelines
5 5 ==================================
6 6
7 7 .. contents::
8 ..
9 1 Overview
10 2 Project organization
11 2.1 Subpackages
12 2.2 Installation and dependencies
13 2.3 Specific subpackages
14 3 Version control
15 4 Documentation
16 4.1 Standalone documentation
17 4.2 Docstring format
18 5 Coding conventions
19 5.1 General
20 5.2 Naming conventions
21 6 Testing
22 7 Configuration
23 ..
24 8
25 9
26 10 Overview
27 11 ========
28 12
29 13 IPython is the next generation of IPython. It is named such for two reasons:
30 14
31 15 - Eventually, IPython will become IPython version 1.0.
32 16 - This new code base needs to be able to co-exist with the existing IPython until
33 17 it is a full replacement for it. Thus we needed a different name. We couldn't
34 18 use ``ipython`` (lowercase) as some files systems are case insensitive.
35 19
36 20 There are two, no three, main goals of the IPython effort:
37 21
38 22 1. Clean up the existing codebase and write lots of tests.
39 23 2. Separate the core functionality of IPython from the terminal to enable IPython
40 24 to be used from within a variety of GUI applications.
41 25 3. Implement a system for interactive parallel computing.
42 26
43 27 While the third goal may seem a bit unrelated to the main focus of IPython, it turns
44 28 out that the technologies required for this goal are nearly identical with those
45 29 required for goal two. This is the main reason the interactive parallel computing
46 30 capabilities are being put into IPython proper. Currently the third of these goals is
47 31 furthest along.
48 32
49 33 This document describes IPython from the perspective of developers.
50 34
51 35
52 36 Project organization
53 37 ====================
54 38
55 39 Subpackages
56 40 -----------
57 41
58 42 IPython is organized into semi self-contained subpackages. Each of the subpackages will have its own:
59 43
60 44 - **Dependencies**. One of the most important things to keep in mind in
61 45 partitioning code amongst subpackages, is that they should be used to cleanly
62 46 encapsulate dependencies.
63 47 - **Tests**. Each subpackage shoud have its own ``tests`` subdirectory that
64 48 contains all of the tests for that package. For information about writing tests
65 49 for IPython, see the `Testing System`_ section of this document.
66 50 - **Configuration**. Each subpackage should have its own ``config`` subdirectory
67 51 that contains the configuration information for the components of the
68 52 subpackage. For information about how the IPython configuration system
69 53 works, see the `Configuration System`_ section of this document.
70 54 - **Scripts**. Each subpackage should have its own ``scripts`` subdirectory that
71 55 contains all of the command line scripts associated with the subpackage.
72 56
73 57 Installation and dependencies
74 58 -----------------------------
75 59
76 60 IPython will not use `setuptools`_ for installation. Instead, we will use standard
77 61 ``setup.py`` scripts that use `distutils`_. While there are a number a extremely nice
78 62 features that `setuptools`_ has (like namespace packages), the current implementation
79 63 of `setuptools`_ has performance problems, particularly on shared file systems. In
80 64 particular, when Python packages are installed on NSF file systems, import times
81 65 become much too long (up towards 10 seconds).
82 66
83 67 Because IPython is being used extensively in the context of high performance
84 68 computing, where performance is critical but shared file systems are common, we feel
85 69 these performance hits are not acceptable. Thus, until the performance problems
86 70 associated with `setuptools`_ are addressed, we will stick with plain `distutils`_. We
87 71 are hopeful that these problems will be addressed and that we will eventually begin
88 72 using `setuptools`_. Because of this, we are trying to organize IPython in a way that
89 73 will make the eventual transition to `setuptools`_ as painless as possible.
90 74
91 75 Because we will be using `distutils`_, there will be no method for automatically installing dependencies. Instead, we are following the approach of `Matplotlib`_ which can be summarized as follows:
92 76
93 77 - Distinguish between required and optional dependencies. However, the required
94 78 dependencies for IPython should be only the Python standard library.
95 79 - Upon installation check to see which optional dependencies are present and tell
96 80 the user which parts of IPython need which optional dependencies.
97 81
98 82 It is absolutely critical that each subpackage of IPython has a clearly specified set
99 83 of dependencies and that dependencies are not carelessly inherited from other IPython
100 84 subpackages. Furthermore, tests that have certain dependencies should not fail if
101 85 those dependencies are not present. Instead they should be skipped and print a
102 86 message.
103 87
104 88 .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools
105 89 .. _distutils: http://docs.python.org/lib/module-distutils.html
106 90 .. _Matplotlib: http://matplotlib.sourceforge.net/
107 91
108 92 Specific subpackages
109 93 --------------------
110 94
111 95 ``core``
112 96 This is the core functionality of IPython that is independent of the
113 97 terminal, network and GUIs. Most of the code that is in the current
114 98 IPython trunk will be refactored, cleaned up and moved here.
115 99
116 100 ``kernel``
117 101 The enables the IPython core to be expose to a the network. This is
118 102 also where all of the parallel computing capabilities are to be found.
119 103
120 104 ``config``
121 105 The configuration package used by IPython.
122 106
123 107 ``frontends``
124 108 The various frontends for IPython. A frontend is the end-user application
125 109 that exposes the capabilities of IPython to the user. The most basic frontend
126 110 will simply be a terminal based application that looks just like today 's
127 111 IPython. Other frontends will likely be more powerful and based on GUI toolkits.
128 112
129 113 ``notebook``
130 114 An application that allows users to work with IPython notebooks.
131 115
132 116 ``tools``
133 117 This is where general utilities go.
134 118
135 119
136 120 Version control
137 121 ===============
138 122
139 In the past, IPython development has been done using `Subversion`__. We are currently trying out `Bazaar`__ and `Launchpad`__.
123 In the past, IPython development has been done using `Subversion`__. Recently, we made the transition to using `Bazaar`__ and `Launchpad`__. This makes it much easier for people
124 to contribute code to IPython. Here is a sketch of how to use Bazaar for IPython
125 development. First, you should install Bazaar. After you have done that, make
126 sure that it is working by getting the latest main branch of IPython::
127
128 $ bzr branch lp:ipython
129
130 Now you can create a new branch for you to do your work in::
131
132 $ bzr branch ipython ipython-mybranch
133
134 The typical work cycle in this branch will be to make changes in `ipython-mybranch`
135 and then commit those changes using the commit command::
136
137 $ ...do work in ipython-mybranch...
138 $ bzr ci -m "the commit message goes here"
139
140 Please note that since we now don't use an old-style linear ChangeLog
141 (that tends to cause problems with distributed version control
142 systems), you should ensure that your log messages are reasonably
143 detailed. Use a docstring-like approach in the commit messages
144 (including the second line being left *blank*)::
145
146 Single line summary of changes being committed.
147
148 - more details when warranted ...
149 - including crediting outside contributors if they sent the
150 code/bug/idea!
151
152 If we couple this with a policy of making single commits for each
153 reasonably atomic change, the bzr log should give an excellent view of
154 the project, and the `--short` log option becomes a nice summary.
155
156 While working with this branch, it is a good idea to merge in changes that have been
157 made upstream in the parent branch. This can be done by doing::
158
159 $ bzr pull
160
161 If this command shows that the branches have diverged, then you should do a merge
162 instead::
163
164 $ bzr merge lp:ipython
165
166 If you want others to be able to see your branch, you can create an account with
167 launchpad and push the branch to your own workspace::
168
169 $ bzr push bzr+ssh://<me>@bazaar.launchpad.net/~<me>/+junk/ipython-mybranch
170
171 Finally, once the work in your branch is done, you can merge your changes back into
172 the `ipython` branch by using merge::
173
174 $ cd ipython
175 $ merge ../ipython-mybranch
176 [resolve any conflicts]
177 $ bzr ci -m "Fixing that bug"
178 $ bzr push
179
180 But this will require you to have write permissions to the `ipython` branch. It you don't
181 you can tell one of the IPython devs about your branch and they can do the merge for you.
182
183 More information about Bazaar workflows can be found `here`__.
140 184
141 185 .. __: http://subversion.tigris.org/
142 186 .. __: http://bazaar-vcs.org/
143 187 .. __: http://www.launchpad.net/ipython
188 .. __: http://doc.bazaar-vcs.org/bzr.dev/en/user-guide/index.html
144 189
145 190 Documentation
146 191 =============
147 192
148 193 Standalone documentation
149 194 ------------------------
150 195
151 196 All standalone documentation should be written in plain text (``.txt``) files using
152 197 `reStructuredText`_ for markup and formatting. All such documentation should be placed
153 198 in the top level directory ``docs`` of the IPython source tree. Or, when appropriate,
154 199 a suitably named subdirectory should be used. The documentation in this location will
155 200 serve as the main source for IPython documentation and all existing documentation
156 201 should be converted to this format.
157 202
158 203 In the future, the text files in the ``docs`` directory will be used to generate all
159 204 forms of documentation for IPython. This include documentation on the IPython website
160 205 as well as *pdf* documentation.
161 206
162 207 .. _reStructuredText: http://docutils.sourceforge.net/rst.html
163 208
164 209 Docstring format
165 210 ----------------
166 211
167 212 Good docstrings are very important. All new code will use `Epydoc`_ for generating API
168 213 docs, so we will follow the `Epydoc`_ conventions. More specifically, we will use
169 214 `reStructuredText`_ for markup and formatting, since it is understood by a wide
170 215 variety of tools. This means that if in the future we have any reason to change from
171 216 `Epydoc`_ to something else, we'll have fewer transition pains.
172 217
173 218 Details about using `reStructuredText`_ for docstrings can be found `here
174 219 <http://epydoc.sourceforge.net/manual-othermarkup.html>`_.
175 220
176 221 .. _Epydoc: http://epydoc.sourceforge.net/
177 222
178 223 Additional PEPs of interest regarding documentation of code:
179 224
180 225 - `Docstring Conventions <http://www.python.org/peps/pep-0257.html>`_
181 226 - `Docstring Processing System Framework <http://www.python.org/peps/pep-0256.html>`_
182 227 - `Docutils Design Specification <http://www.python.org/peps/pep-0258.html>`_
183 228
184 229
185 230 Coding conventions
186 231 ==================
187 232
188 233 General
189 234 -------
190 235
191 236 In general, we'll try to follow the standard Python style conventions as described here:
192 237
193 238 - `Style Guide for Python Code <http://www.python.org/peps/pep-0008.html>`_
194 239
195 240
196 241 Other comments:
197 242
198 243 - In a large file, top level classes and functions should be
199 244 separated by 2-3 lines to make it easier to separate them visually.
200 245 - Use 4 spaces for indentation.
201 246 - Keep the ordering of methods the same in classes that have the same
202 247 methods. This is particularly true for classes that implement
203 248 similar interfaces and for interfaces that are similar.
204 249
205 250 Naming conventions
206 251 ------------------
207 252
208 253 In terms of naming conventions, we'll follow the guidelines from the `Style Guide for
209 254 Python Code`_.
210 255
211 256 For all new IPython code (and much existing code is being refactored), we'll use:
212 257
213 258 - All ``lowercase`` module names.
214 259
215 260 - ``CamelCase`` for class names.
216 261
217 262 - ``lowercase_with_underscores`` for methods, functions, variables and attributes.
218 263
219 264 This may be confusing as most of the existing IPython codebase uses a different convention (``lowerCamelCase`` for methods and attributes). Slowly, we will move IPython over to the new
220 265 convention, providing shadow names for backward compatibility in public interfaces.
221 266
222 267 There are, however, some important exceptions to these rules. In some cases, IPython
223 268 code will interface with packages (Twisted, Wx, Qt) that use other conventions. At some level this makes it impossible to adhere to our own standards at all times. In particular, when subclassing classes that use other naming conventions, you must follow their naming conventions. To deal with cases like this, we propose the following policy:
224 269
225 270 - If you are subclassing a class that uses different conventions, use its
226 271 naming conventions throughout your subclass. Thus, if you are creating a
227 272 Twisted Protocol class, used Twisted's ``namingSchemeForMethodsAndAttributes.``
228 273
229 274 - All IPython's official interfaces should use our conventions. In some cases
230 275 this will mean that you need to provide shadow names (first implement ``fooBar``
231 276 and then ``foo_bar = fooBar``). We want to avoid this at all costs, but it
232 277 will probably be necessary at times. But, please use this sparingly!
233 278
234 279 Implementation-specific *private* methods will use ``_single_underscore_prefix``.
235 280 Names with a leading double underscore will *only* be used in special cases, as they
236 281 makes subclassing difficult (such names are not easily seen by child classes).
237 282
238 283 Occasionally some run-in lowercase names are used, but mostly for very short names or
239 284 where we are implementing methods very similar to existing ones in a base class (like
240 285 ``runlines()`` where ``runsource()`` and ``runcode()`` had established precedent).
241 286
242 287 The old IPython codebase has a big mix of classes and modules prefixed with an
243 288 explicit ``IP``. In Python this is mostly unnecessary, redundant and frowned upon, as
244 289 namespaces offer cleaner prefixing. The only case where this approach is justified is
245 290 for classes which are expected to be imported into external namespaces and a very
246 291 generic name (like Shell) is too likely to clash with something else. We'll need to
247 292 revisit this issue as we clean up and refactor the code, but in general we should
248 293 remove as many unnecessary ``IP``/``ip`` prefixes as possible. However, if a prefix
249 294 seems absolutely necessary the more specific ``IPY`` or ``ipy`` are preferred.
250 295
251 296 .. _devel_testing:
252 297
253 298 Testing system
254 299 ==============
255 300
256 301 It is extremely important that all code contributed to IPython has tests. Tests should
257 302 be written as unittests, doctests or as entities that the `Nose`_ testing package will
258 303 find. Regardless of how the tests are written, we will use `Nose`_ for discovering and
259 304 running the tests. `Nose`_ will be required to run the IPython test suite, but will
260 305 not be required to simply use IPython.
261 306
262 307 .. _Nose: http://code.google.com/p/python-nose/
263 308
264 309 Tests of `Twisted`__ using code should be written by subclassing the ``TestCase`` class
265 310 that comes with ``twisted.trial.unittest``. When this is done, `Nose`_ will be able to
266 311 run the tests and the twisted reactor will be handled correctly.
267 312
268 313 .. __: http://www.twistedmatrix.com
269 314
270 315 Each subpackage in IPython should have its own ``tests`` directory that contains all
271 316 of the tests for that subpackage. This allows each subpackage to be self-contained. If
272 317 a subpackage has any dependencies beyond the Python standard library, the tests for
273 318 that subpackage should be skipped if the dependencies are not found. This is very
274 319 important so users don't get tests failing simply because they don't have dependencies.
275 320
276 321 We also need to look into use Noses ability to tag tests to allow a more modular
277 322 approach of running tests.
278 323
279 324 .. _devel_config:
280 325
281 326 Configuration system
282 327 ====================
283 328
284 329 IPython uses `.ini`_ files for configuration purposes. This represents a huge
285 330 improvement over the configuration system used in IPython. IPython works with these
286 331 files using the `ConfigObj`_ package, which IPython includes as
287 332 ``ipython1/external/configobj.py``.
288 333
289 334 Currently, we are using raw `ConfigObj`_ objects themselves. Each subpackage of IPython
290 335 should contain a ``config`` subdirectory that contains all of the configuration
291 336 information for the subpackage. To see how configuration information is defined (along
292 337 with defaults) see at the examples in ``ipython1/kernel/config`` and
293 338 ``ipython1/core/config``. Likewise, to see how the configuration information is used,
294 339 see examples in ``ipython1/kernel/scripts/ipengine.py``.
295 340
296 341 Eventually, we will add a new layer on top of the raw `ConfigObj`_ objects. We are
297 342 calling this new layer, ``tconfig``, as it will use a `Traits`_-like validation model.
298 343 We won't actually use `Traits`_, but will implement something similar in pure Python.
299 344 But, even in this new system, we will still use `ConfigObj`_ and `.ini`_ files
300 345 underneath the hood. Talk to Fernando if you are interested in working on this part of
301 346 IPython. The current prototype of ``tconfig`` is located in the IPython sandbox.
302 347
303 348 .. _.ini: http://docs.python.org/lib/module-ConfigParser.html
304 349 .. _ConfigObj: http://www.voidspace.org.uk/python/configobj.html
305 350 .. _Traits: http://code.enthought.com/traits/
306 351
307 352
308 353
309 354
310 355
311 356
312 357
313 358
314 359
315 360
@@ -1,226 +1,228 b''
1 1 # encoding: utf-8
2 2
3 3 """
4 4 This module defines the things that are used in setup.py for building IPython
5 5
6 6 This includes:
7 7
8 8 * The basic arguments to setup
9 9 * Functions for finding things like packages, package data, etc.
10 10 * A function for checking dependencies.
11 11 """
12 12
13 13 __docformat__ = "restructuredtext en"
14 14
15 15 #-------------------------------------------------------------------------------
16 16 # Copyright (C) 2008 The IPython Development Team
17 17 #
18 18 # Distributed under the terms of the BSD License. The full license is in
19 19 # the file COPYING, distributed as part of this software.
20 20 #-------------------------------------------------------------------------------
21 21
22 22 #-------------------------------------------------------------------------------
23 23 # Imports
24 24 #-------------------------------------------------------------------------------
25 25
26 26 import os, sys
27 27
28 28 from glob import glob
29 29
30 30 from setupext import install_data_ext
31 31
32 32 #-------------------------------------------------------------------------------
33 33 # Useful globals and utility functions
34 34 #-------------------------------------------------------------------------------
35 35
36 36 # A few handy globals
37 37 isfile = os.path.isfile
38 38 pjoin = os.path.join
39 39
40 40 def oscmd(s):
41 41 print ">", s
42 42 os.system(s)
43 43
44 44 # A little utility we'll need below, since glob() does NOT allow you to do
45 45 # exclusion on multiple endings!
46 46 def file_doesnt_endwith(test,endings):
47 47 """Return true if test is a file and its name does NOT end with any
48 48 of the strings listed in endings."""
49 49 if not isfile(test):
50 50 return False
51 51 for e in endings:
52 52 if test.endswith(e):
53 53 return False
54 54 return True
55 55
56 56 #---------------------------------------------------------------------------
57 57 # Basic project information
58 58 #---------------------------------------------------------------------------
59 59
60 60 # Release.py contains version, authors, license, url, keywords, etc.
61 61 execfile(pjoin('IPython','Release.py'))
62 62
63 63 # Create a dict with the basic information
64 64 # This dict is eventually passed to setup after additional keys are added.
65 65 setup_args = dict(
66 66 name = name,
67 67 version = version,
68 68 description = description,
69 69 long_description = long_description,
70 70 author = author,
71 71 author_email = author_email,
72 72 url = url,
73 73 download_url = download_url,
74 74 license = license,
75 75 platforms = platforms,
76 76 keywords = keywords,
77 77 cmdclass = {'install_data': install_data_ext},
78 78 )
79 79
80 80
81 81 #---------------------------------------------------------------------------
82 82 # Find packages
83 83 #---------------------------------------------------------------------------
84 84
85 85 def add_package(packages, pname, config=False, tests=False, scripts=False, others=None):
86 86 """
87 87 Add a package to the list of packages, including certain subpackages.
88 88 """
89 89 packages.append('.'.join(['IPython',pname]))
90 90 if config:
91 91 packages.append('.'.join(['IPython',pname,'config']))
92 92 if tests:
93 93 packages.append('.'.join(['IPython',pname,'tests']))
94 94 if scripts:
95 95 packages.append('.'.join(['IPython',pname,'scripts']))
96 96 if others is not None:
97 97 for o in others:
98 98 packages.append('.'.join(['IPython',pname,o]))
99 99
100 100 def find_packages():
101 101 """
102 102 Find all of IPython's packages.
103 103 """
104 104 packages = ['IPython']
105 105 add_package(packages, 'config', tests=True)
106 106 add_package(packages , 'Extensions')
107 107 add_package(packages, 'external')
108 108 add_package(packages, 'gui')
109 109 add_package(packages, 'gui.wx')
110 110 add_package(packages, 'kernel', config=True, tests=True, scripts=True)
111 111 add_package(packages, 'kernel.core', config=True, tests=True)
112 112 add_package(packages, 'testing', tests=True)
113 113 add_package(packages, 'tools', tests=True)
114 114 add_package(packages, 'UserConfig')
115 115 return packages
116 116
117 117 #---------------------------------------------------------------------------
118 118 # Find package data
119 119 #---------------------------------------------------------------------------
120 120
121 121 def find_package_data():
122 122 """
123 123 Find IPython's package_data.
124 124 """
125 125 # This is not enough for these things to appear in an sdist.
126 126 # We need to muck with the MANIFEST to get this to work
127 127 package_data = {'IPython.UserConfig' : ['*'] }
128 128 return package_data
129 129
130 130
131 131 #---------------------------------------------------------------------------
132 132 # Find data files
133 133 #---------------------------------------------------------------------------
134 134
135 135 def find_data_files():
136 136 """
137 137 Find IPython's data_files.
138 138 """
139 139
140 140 # I can't find how to make distutils create a nested dir. structure, so
141 141 # in the meantime do it manually. Butt ugly.
142 142 # Note that http://www.redbrick.dcu.ie/~noel/distutils.html, ex. 2/3, contain
143 143 # information on how to do this more cleanly once python 2.4 can be assumed.
144 144 # Thanks to Noel for the tip.
145 145 docdirbase = 'share/doc/ipython'
146 146 manpagebase = 'share/man/man1'
147 147
148 148 # We only need to exclude from this things NOT already excluded in the
149 149 # MANIFEST.in file.
150 150 exclude = ('.sh','.1.gz')
151 151 # We need to figure out how we want to package all of our rst docs?
152 152 # docfiles = filter(lambda f:file_doesnt_endwith(f,exclude),glob('docs/*'))
153 153 examfiles = filter(isfile, glob('docs/examples/core/*.py'))
154 154 examfiles.append(filter(isfile, glob('docs/examples/kernel/*.py')))
155 155 manpages = filter(isfile, glob('docs/man/*.1.gz'))
156 156 igridhelpfiles = filter(isfile, glob('IPython/Extensions/igrid_help.*'))
157 157
158 158 data_files = [#('data', docdirbase, docfiles),
159 159 ('data', pjoin(docdirbase, 'examples'),examfiles),
160 160 ('data', manpagebase, manpages),
161 161 ('data',pjoin(docdirbase, 'extensions'),igridhelpfiles),
162 162 ]
163 return data_files
163 # import pprint
164 # pprint.pprint(data_files)
165 return []
164 166
165 167 #---------------------------------------------------------------------------
166 168 # Find scripts
167 169 #---------------------------------------------------------------------------
168 170
169 171 def find_scripts():
170 172 """
171 173 Find IPython's scripts.
172 174 """
173 175 scripts = []
174 176 scripts.append('IPython/kernel/scripts/ipengine')
175 177 scripts.append('IPython/kernel/scripts/ipcontroller')
176 178 scripts.append('IPython/kernel/scripts/ipcluster')
177 179 scripts.append('scripts/ipython')
178 180 scripts.append('scripts/pycolor')
179 181 scripts.append('scripts/irunner')
180 182
181 183 # Script to be run by the windows binary installer after the default setup
182 184 # routine, to add shortcuts and similar windows-only things. Windows
183 185 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
184 186 # doesn't find them.
185 187 if 'bdist_wininst' in sys.argv:
186 188 if len(sys.argv) > 2 and ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
187 189 print >> sys.stderr,"ERROR: bdist_wininst must be run alone. Exiting."
188 190 sys.exit(1)
189 191 scripts.append('scripts/ipython_win_post_install.py')
190 192
191 193 return scripts
192 194
193 195 #---------------------------------------------------------------------------
194 196 # Find scripts
195 197 #---------------------------------------------------------------------------
196 198
197 199 def check_for_dependencies():
198 200 """Check for IPython's dependencies.
199 201
200 202 This function should NOT be called if running under setuptools!
201 203 """
202 204 from setupext.setupext import (
203 205 print_line, print_raw, print_status, print_message,
204 206 check_for_zopeinterface, check_for_twisted,
205 207 check_for_foolscap, check_for_pyopenssl,
206 208 check_for_sphinx, check_for_pygments,
207 209 check_for_nose, check_for_pexpect
208 210 )
209 211 print_line()
210 212 print_raw("BUILDING IPYTHON")
211 213 print_status('python', sys.version)
212 214 print_status('platform', sys.platform)
213 215 if sys.platform == 'win32':
214 216 print_status('Windows version', sys.getwindowsversion())
215 217
216 218 print_raw("")
217 219 print_raw("OPTIONAL DEPENDENCIES")
218 220
219 221 check_for_zopeinterface()
220 222 check_for_twisted()
221 223 check_for_foolscap()
222 224 check_for_pyopenssl()
223 225 check_for_sphinx()
224 226 check_for_pygments()
225 227 check_for_nose()
226 228 check_for_pexpect() No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now