##// END OF EJS Templates
Merge with upstream
Fernando Perez -
r1670:099aada6 merge
parent child Browse files
Show More
@@ -0,0 +1,26 b''
1 # encoding: utf-8
2
3 """This file contains unittests for the interpreter.py module."""
4
5 __docformat__ = "restructuredtext en"
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 #-----------------------------------------------------------------------------
17
18 from IPython.kernel.core.interpreter import Interpreter
19
20 def test_unicode():
21 """ Test unicode handling with the interpreter.
22 """
23 i = Interpreter()
24 i.execute_python(u'print "ù"')
25 i.execute_python('print "ù"')
26
1 NO CONTENT: new file 100644
@@ -0,0 +1,32 b''
1 # encoding: utf-8
2
3 """Tests for genutils.py"""
4
5 __docformat__ = "restructuredtext en"
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 #-----------------------------------------------------------------------------
17
18 from IPython import genutils
19
20
21 def test_get_home_dir():
22 """Make sure we can get the home directory."""
23 home_dir = genutils.get_home_dir()
24
25 def test_get_ipython_dir():
26 """Make sure we can get the ipython directory."""
27 ipdir = genutils.get_ipython_dir()
28
29 def test_get_security_dir():
30 """Make sure we can get the ipython/security directory."""
31 sdir = genutils.get_security_dir()
32 No newline at end of file
@@ -1,43 +1,45 b''
1 1 # -*- Mode: Shell-Script -*- Not really, but shows comments correctly
2 2 #***************************************************************************
3 3 #
4 4 # Configuration file for ipython -- ipythonrc format
5 5 #
6 6 # The format of this file is one of 'key value' lines.
7 7 # Lines containing only whitespace at the beginning and then a # are ignored
8 8 # as comments. But comments can NOT be put on lines with data.
9 9 #***************************************************************************
10 10
11 11 # If this file is found in the user's ~/.ipython directory as
12 12 # ipythonrc-physics, it can be loaded by calling passing the '-profile
13 13 # physics' (or '-p physics') option to IPython.
14 14
15 15 # This profile loads modules useful for doing interactive calculations with
16 16 # physical quantities (with units). It relies on modules from Konrad Hinsen's
17 17 # ScientificPython (http://dirac.cnrs-orleans.fr/ScientificPython/)
18 18
19 19 # First load basic user configuration
20 20 include ipythonrc
21 21
22 22 # import ...
23 23 # Module with alternate input syntax for PhysicalQuantity objects.
24 24 import_mod IPython.Extensions.PhysicalQInput
25 25
26 26 # from ... import *
27 27 # math CANNOT be imported after PhysicalQInteractive. It will override the
28 28 # functions defined there.
29 29 import_all math IPython.Extensions.PhysicalQInteractive
30 30
31 31 # from ... import ...
32 32 import_some
33 33
34 34 # code
35 35 execute q = PhysicalQuantityInteractive
36 36 execute g = PhysicalQuantityInteractive('9.8 m/s**2')
37 37 ececute rad = pi/180.
38 38 execute print '*** q is an alias for PhysicalQuantityInteractive'
39 39 execute print '*** g = 9.8 m/s^2 has been defined'
40 40 execute print '*** rad = pi/180 has been defined'
41 execute import ipy_constants as C
42 execute print '*** C is the physical constants module'
41 43
42 44 # Files to execute
43 45 execfile
@@ -1,104 +1,106 b''
1 1 # encoding: utf-8
2 2
3 3 """This is the official entry point to IPython's configuration system. """
4 4
5 5 __docformat__ = "restructuredtext en"
6 6
7 7 #-------------------------------------------------------------------------------
8 8 # Copyright (C) 2008 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-------------------------------------------------------------------------------
13 13
14 14 #-------------------------------------------------------------------------------
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 18 import os
19 from IPython.config.cutils import get_home_dir, get_ipython_dir
19 from os.path import join as pjoin
20
21 from IPython.genutils import get_home_dir, get_ipython_dir
20 22 from IPython.external.configobj import ConfigObj
21 23
22 24 # Traitlets config imports
23 25 from IPython.config import traitlets
24 26 from IPython.config.config import *
25 27 from traitlets import *
26 28
27 29 class ConfigObjManager(object):
28 30
29 31 def __init__(self, configObj, filename):
30 32 self.current = configObj
31 33 self.current.indent_type = ' '
32 34 self.filename = filename
33 35 # self.write_default_config_file()
34 36
35 37 def get_config_obj(self):
36 38 return self.current
37 39
38 40 def update_config_obj(self, newConfig):
39 41 self.current.merge(newConfig)
40 42
41 43 def update_config_obj_from_file(self, filename):
42 44 newConfig = ConfigObj(filename, file_error=False)
43 45 self.current.merge(newConfig)
44 46
45 47 def update_config_obj_from_default_file(self, ipythondir=None):
46 48 fname = self.resolve_file_path(self.filename, ipythondir)
47 49 self.update_config_obj_from_file(fname)
48 50
49 51 def write_config_obj_to_file(self, filename):
50 52 f = open(filename, 'w')
51 53 self.current.write(f)
52 54 f.close()
53 55
54 56 def write_default_config_file(self):
55 57 ipdir = get_ipython_dir()
56 fname = ipdir + '/' + self.filename
58 fname = pjoin(ipdir, self.filename)
57 59 if not os.path.isfile(fname):
58 60 print "Writing the configuration file to: " + fname
59 61 self.write_config_obj_to_file(fname)
60 62
61 63 def _import(self, key):
62 64 package = '.'.join(key.split('.')[0:-1])
63 65 obj = key.split('.')[-1]
64 66 execString = 'from %s import %s' % (package, obj)
65 67 exec execString
66 68 exec 'temp = %s' % obj
67 69 return temp
68 70
69 71 def resolve_file_path(self, filename, ipythondir = None):
70 72 """Resolve filenames into absolute paths.
71 73
72 74 This function looks in the following directories in order:
73 75
74 76 1. In the current working directory or by absolute path with ~ expanded
75 77 2. In ipythondir if that is set
76 78 3. In the IPYTHONDIR environment variable if it exists
77 79 4. In the ~/.ipython directory
78 80
79 81 Note: The IPYTHONDIR is also used by the trunk version of IPython so
80 82 changing it will also affect it was well.
81 83 """
82 84
83 85 # In cwd or by absolute path with ~ expanded
84 86 trythis = os.path.expanduser(filename)
85 87 if os.path.isfile(trythis):
86 88 return trythis
87 89
88 90 # In ipythondir if it is set
89 91 if ipythondir is not None:
90 trythis = ipythondir + '/' + filename
92 trythis = pjoin(ipythondir, filename)
91 93 if os.path.isfile(trythis):
92 94 return trythis
93 95
94 trythis = get_ipython_dir() + '/' + filename
96 trythis = pjoin(get_ipython_dir(), filename)
95 97 if os.path.isfile(trythis):
96 98 return trythis
97 99
98 100 return None
99 101
100 102
101 103
102 104
103 105
104 106
@@ -1,99 +1,34 b''
1 1 # encoding: utf-8
2 2
3 3 """Configuration-related utilities for all IPython."""
4 4
5 5 __docformat__ = "restructuredtext en"
6 6
7 7 #-------------------------------------------------------------------------------
8 8 # Copyright (C) 2008 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-------------------------------------------------------------------------------
13 13
14 14 #-------------------------------------------------------------------------------
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 18 import os
19 19 import sys
20 20
21 21 #---------------------------------------------------------------------------
22 22 # Normal code begins
23 23 #---------------------------------------------------------------------------
24 24
25 class HomeDirError(Exception):
26 pass
27
28 def get_home_dir():
29 """Return the closest possible equivalent to a 'home' directory.
30
31 We first try $HOME. Absent that, on NT it's $HOMEDRIVE\$HOMEPATH.
32
33 Currently only Posix and NT are implemented, a HomeDirError exception is
34 raised for all other OSes. """
35
36 isdir = os.path.isdir
37 env = os.environ
38 try:
39 homedir = env['HOME']
40 if not isdir(homedir):
41 # in case a user stuck some string which does NOT resolve to a
42 # valid path, it's as good as if we hadn't foud it
43 raise KeyError
44 return homedir
45 except KeyError:
46 if os.name == 'posix':
47 raise HomeDirError,'undefined $HOME, IPython can not proceed.'
48 elif os.name == 'nt':
49 # For some strange reason, win9x returns 'nt' for os.name.
50 try:
51 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
52 if not isdir(homedir):
53 homedir = os.path.join(env['USERPROFILE'])
54 if not isdir(homedir):
55 raise HomeDirError
56 return homedir
57 except:
58 try:
59 # Use the registry to get the 'My Documents' folder.
60 import _winreg as wreg
61 key = wreg.OpenKey(wreg.HKEY_CURRENT_USER,
62 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
63 homedir = wreg.QueryValueEx(key,'Personal')[0]
64 key.Close()
65 if not isdir(homedir):
66 e = ('Invalid "Personal" folder registry key '
67 'typically "My Documents".\n'
68 'Value: %s\n'
69 'This is not a valid directory on your system.' %
70 homedir)
71 raise HomeDirError(e)
72 return homedir
73 except HomeDirError:
74 raise
75 except:
76 return 'C:\\'
77 elif os.name == 'dos':
78 # Desperate, may do absurd things in classic MacOS. May work under DOS.
79 return 'C:\\'
80 else:
81 raise HomeDirError,'support for your operating system not implemented.'
82
83 def get_ipython_dir():
84 ipdir_def = '.ipython'
85 home_dir = get_home_dir()
86 ipdir = os.path.abspath(os.environ.get('IPYTHONDIR',
87 os.path.join(home_dir,ipdir_def)))
88 return ipdir
89
90 25 def import_item(key):
91 26 """
92 27 Import and return bar given the string foo.bar.
93 28 """
94 29 package = '.'.join(key.split('.')[0:-1])
95 30 obj = key.split('.')[-1]
96 31 execString = 'from %s import %s' % (package, obj)
97 32 exec execString
98 33 exec 'temp = %s' % obj
99 34 return temp
@@ -1,168 +1,179 b''
1 1 # Addapted from killableprocess.py.
2 2 #______________________________________________________________________________
3 3 #
4 4 # killableprocess - subprocesses which can be reliably killed
5 5 #
6 6 # Parts of this module are copied from the subprocess.py file contained
7 7 # in the Python distribution.
8 8 #
9 9 # Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se>
10 10 #
11 11 # Additions and modifications written by Benjamin Smedberg
12 12 # <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation
13 13 # <http://www.mozilla.org/>
14 14 #
15 15 # By obtaining, using, and/or copying this software and/or its
16 16 # associated documentation, you agree that you have read, understood,
17 17 # and will comply with the following terms and conditions:
18 18 #
19 19 # Permission to use, copy, modify, and distribute this software and
20 20 # its associated documentation for any purpose and without fee is
21 21 # hereby granted, provided that the above copyright notice appears in
22 22 # all copies, and that both that copyright notice and this permission
23 23 # notice appear in supporting documentation, and that the name of the
24 24 # author not be used in advertising or publicity pertaining to
25 25 # distribution of the software without specific, written prior
26 26 # permission.
27 27 #
28 28 # THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
29 29 # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
30 30 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
31 31 # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
32 32 # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
33 33 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
34 34 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
35 35
36 36 r"""killableprocess - Subprocesses which can be reliably killed
37 37
38 38 This module is a subclass of the builtin "subprocess" module. It allows
39 39 processes that launch subprocesses to be reliably killed on Windows (via the Popen.kill() method.
40 40
41 41 It also adds a timeout argument to Wait() for a limited period of time before
42 42 forcefully killing the process.
43 43
44 44 Note: On Windows, this module requires Windows 2000 or higher (no support for
45 45 Windows 95, 98, or NT 4.0). It also requires ctypes, which is bundled with
46 46 Python 2.5+ or available from http://python.net/crew/theller/ctypes/
47 47 """
48 48
49 49 import subprocess
50 50 from subprocess import PIPE
51 51 import sys
52 52 import os
53 import time
54 53 import types
55 54
56 55 try:
57 56 from subprocess import CalledProcessError
58 57 except ImportError:
59 58 # Python 2.4 doesn't implement CalledProcessError
60 59 class CalledProcessError(Exception):
61 60 """This exception is raised when a process run by check_call() returns
62 61 a non-zero exit status. The exit status will be stored in the
63 62 returncode attribute."""
64 63 def __init__(self, returncode, cmd):
65 64 self.returncode = returncode
66 65 self.cmd = cmd
67 66 def __str__(self):
68 67 return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
69 68
70 69 mswindows = (sys.platform == "win32")
71 70
71 skip = False
72
72 73 if mswindows:
74 import platform
75 if platform.uname()[3] == '' or platform.uname()[3] > '6.0.6000':
76 # Killable process does not work under vista when starting for
77 # something else than cmd.
78 skip = True
79 else:
73 80 import winprocess
74 81 else:
75 82 import signal
76 83
77 84 if not mswindows:
78 85 def DoNothing(*args):
79 86 pass
80 87
88
89 if skip:
90 Popen = subprocess.Popen
91 else:
81 92 class Popen(subprocess.Popen):
82 93 if not mswindows:
83 94 # Override __init__ to set a preexec_fn
84 95 def __init__(self, *args, **kwargs):
85 96 if len(args) >= 7:
86 97 raise Exception("Arguments preexec_fn and after must be passed by keyword.")
87 98
88 99 real_preexec_fn = kwargs.pop("preexec_fn", None)
89 100 def setpgid_preexec_fn():
90 101 os.setpgid(0, 0)
91 102 if real_preexec_fn:
92 103 apply(real_preexec_fn)
93 104
94 105 kwargs['preexec_fn'] = setpgid_preexec_fn
95 106
96 107 subprocess.Popen.__init__(self, *args, **kwargs)
97 108
98 109 if mswindows:
99 110 def _execute_child(self, args, executable, preexec_fn, close_fds,
100 111 cwd, env, universal_newlines, startupinfo,
101 112 creationflags, shell,
102 113 p2cread, p2cwrite,
103 114 c2pread, c2pwrite,
104 115 errread, errwrite):
105 116 if not isinstance(args, types.StringTypes):
106 117 args = subprocess.list2cmdline(args)
107 118
108 119 if startupinfo is None:
109 120 startupinfo = winprocess.STARTUPINFO()
110 121
111 122 if None not in (p2cread, c2pwrite, errwrite):
112 123 startupinfo.dwFlags |= winprocess.STARTF_USESTDHANDLES
113 124
114 125 startupinfo.hStdInput = int(p2cread)
115 126 startupinfo.hStdOutput = int(c2pwrite)
116 127 startupinfo.hStdError = int(errwrite)
117 128 if shell:
118 129 startupinfo.dwFlags |= winprocess.STARTF_USESHOWWINDOW
119 130 startupinfo.wShowWindow = winprocess.SW_HIDE
120 131 comspec = os.environ.get("COMSPEC", "cmd.exe")
121 132 args = comspec + " /c " + args
122 133
123 134 # We create a new job for this process, so that we can kill
124 135 # the process and any sub-processes
125 136 self._job = winprocess.CreateJobObject()
126 137
127 138 creationflags |= winprocess.CREATE_SUSPENDED
128 139 creationflags |= winprocess.CREATE_UNICODE_ENVIRONMENT
129 140
130 141 hp, ht, pid, tid = winprocess.CreateProcess(
131 142 executable, args,
132 143 None, None, # No special security
133 144 1, # Must inherit handles!
134 145 creationflags,
135 146 winprocess.EnvironmentBlock(env),
136 147 cwd, startupinfo)
137 148
138 149 self._child_created = True
139 150 self._handle = hp
140 151 self._thread = ht
141 152 self.pid = pid
142 153
143 154 winprocess.AssignProcessToJobObject(self._job, hp)
144 155 winprocess.ResumeThread(ht)
145 156
146 157 if p2cread is not None:
147 158 p2cread.Close()
148 159 if c2pwrite is not None:
149 160 c2pwrite.Close()
150 161 if errwrite is not None:
151 162 errwrite.Close()
152 163
153 164 def kill(self, group=True):
154 165 """Kill the process. If group=True, all sub-processes will also be killed."""
155 166 if mswindows:
156 167 if group:
157 168 winprocess.TerminateJobObject(self._job, 127)
158 169 else:
159 170 winprocess.TerminateProcess(self._handle, 127)
160 171 self.returncode = 127
161 172 else:
162 173 if group:
163 174 os.killpg(self.pid, signal.SIGKILL)
164 175 else:
165 176 os.kill(self.pid, signal.SIGKILL)
166 177 self.returncode = -9
167 178
168 179
@@ -1,74 +1,74 b''
1 1 # encoding: utf-8
2 2 """
3 3 Object for encapsulating process execution by using callbacks for stdout,
4 4 stderr and stdin.
5 5 """
6 6 __docformat__ = "restructuredtext en"
7 7
8 8 #-------------------------------------------------------------------------------
9 9 # Copyright (C) 2008 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-------------------------------------------------------------------------------
14 14
15 15 #-------------------------------------------------------------------------------
16 16 # Imports
17 17 #-------------------------------------------------------------------------------
18 18 from killableprocess import Popen, PIPE
19 19 from threading import Thread
20 20 from time import sleep
21 21 import os
22 22
23 23 class PipedProcess(Thread):
24 24 """ Class that encapsulates process execution by using callbacks for
25 25 stdout, stderr and stdin, and providing a reliable way of
26 26 killing it.
27 27 """
28 28
29 29 def __init__(self, command_string, out_callback,
30 30 end_callback=None,):
31 31 """ command_string: the command line executed to start the
32 32 process.
33 33
34 34 out_callback: the python callable called on stdout/stderr.
35 35
36 36 end_callback: an optional callable called when the process
37 37 finishes.
38 38
39 39 These callbacks are called from a different thread as the
40 40 thread from which is started.
41 41 """
42 42 self.command_string = command_string
43 43 self.out_callback = out_callback
44 44 self.end_callback = end_callback
45 45 Thread.__init__(self)
46 46
47 47
48 48 def run(self):
49 49 """ Start the process and hook up the callbacks.
50 50 """
51 51 env = os.environ
52 52 env['TERM'] = 'xterm'
53 process = Popen((self.command_string + ' 2>&1', ), shell=True,
53 process = Popen(self.command_string + ' 2>&1', shell=True,
54 54 env=env,
55 55 universal_newlines=True,
56 56 stdout=PIPE, stdin=PIPE, )
57 57 self.process = process
58 58 while True:
59 59 out_char = process.stdout.read(1)
60 60 if out_char == '':
61 61 if process.poll() is not None:
62 62 # The process has finished
63 63 break
64 64 else:
65 65 # The process is not giving any interesting
66 66 # output. No use polling it immediatly.
67 67 sleep(0.1)
68 68 else:
69 69 self.out_callback(out_char)
70 70
71 71 if self.end_callback is not None:
72 72 self.end_callback()
73 73
74 74
@@ -1,294 +1,320 b''
1 1 """
2 2 Base front end class for all line-oriented frontends, rather than
3 3 block-oriented.
4 4
5 5 Currently this focuses on synchronous frontends.
6 6 """
7 7 __docformat__ = "restructuredtext en"
8 8
9 9 #-------------------------------------------------------------------------------
10 10 # Copyright (C) 2008 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-------------------------------------------------------------------------------
15 15
16 16 #-------------------------------------------------------------------------------
17 17 # Imports
18 18 #-------------------------------------------------------------------------------
19 19 import re
20 20
21 21 import IPython
22 22 import sys
23 import codeop
24 import traceback
23 25
24 26 from frontendbase import FrontEndBase
25 27 from IPython.kernel.core.interpreter import Interpreter
26 28
27 29 def common_prefix(strings):
28 30 """ Given a list of strings, return the common prefix between all
29 31 these strings.
30 32 """
31 33 ref = strings[0]
32 34 prefix = ''
33 35 for size in range(len(ref)):
34 36 test_prefix = ref[:size+1]
35 37 for string in strings[1:]:
36 38 if not string.startswith(test_prefix):
37 39 return prefix
38 40 prefix = test_prefix
39 41
40 42 return prefix
41 43
42 44 #-------------------------------------------------------------------------------
43 45 # Base class for the line-oriented front ends
44 46 #-------------------------------------------------------------------------------
45 47 class LineFrontEndBase(FrontEndBase):
46 48 """ Concrete implementation of the FrontEndBase class. This is meant
47 49 to be the base class behind all the frontend that are line-oriented,
48 50 rather than block-oriented.
49 51 """
50 52
51 53 # We need to keep the prompt number, to be able to increment
52 54 # it when there is an exception.
53 55 prompt_number = 1
54 56
55 57 # We keep a reference to the last result: it helps testing and
56 58 # programatic control of the frontend.
57 59 last_result = dict(number=0)
58 60
59 61 # The input buffer being edited
60 62 input_buffer = ''
61 63
62 64 # Set to true for debug output
63 65 debug = False
64 66
65 67 # A banner to print at startup
66 68 banner = None
67 69
68 70 #--------------------------------------------------------------------------
69 71 # FrontEndBase interface
70 72 #--------------------------------------------------------------------------
71 73
72 74 def __init__(self, shell=None, history=None, banner=None, *args, **kwargs):
73 75 if shell is None:
74 76 shell = Interpreter()
75 77 FrontEndBase.__init__(self, shell=shell, history=history)
76 78
77 79 if banner is not None:
78 80 self.banner = banner
81
82 def start(self):
83 """ Put the frontend in a state where it is ready for user
84 interaction.
85 """
79 86 if self.banner is not None:
80 87 self.write(self.banner, refresh=False)
81 88
82 89 self.new_prompt(self.input_prompt_template.substitute(number=1))
83 90
84 91
85 92 def complete(self, line):
86 93 """Complete line in engine's user_ns
87 94
88 95 Parameters
89 96 ----------
90 97 line : string
91 98
92 99 Result
93 100 ------
94 101 The replacement for the line and the list of possible completions.
95 102 """
96 103 completions = self.shell.complete(line)
97 104 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
98 105 if completions:
99 106 prefix = common_prefix(completions)
100 107 residual = complete_sep.split(line)[:-1]
101 108 line = line[:-len(residual)] + prefix
102 109 return line, completions
103 110
104 111
105 112 def render_result(self, result):
106 113 """ Frontend-specific rendering of the result of a calculation
107 114 that has been sent to an engine.
108 115 """
109 116 if 'stdout' in result and result['stdout']:
110 117 self.write('\n' + result['stdout'])
111 118 if 'display' in result and result['display']:
112 119 self.write("%s%s\n" % (
113 120 self.output_prompt_template.substitute(
114 121 number=result['number']),
115 122 result['display']['pprint']
116 123 ) )
117 124
118 125
119 126 def render_error(self, failure):
120 127 """ Frontend-specific rendering of error.
121 128 """
122 129 self.write('\n\n'+str(failure)+'\n\n')
123 130 return failure
124 131
125 132
126 133 def is_complete(self, string):
127 134 """ Check if a string forms a complete, executable set of
128 135 commands.
129 136
130 137 For the line-oriented frontend, multi-line code is not executed
131 138 as soon as it is complete: the users has to enter two line
132 139 returns.
133 140 """
134 141 if string in ('', '\n'):
135 142 # Prefiltering, eg through ipython0, may return an empty
136 143 # string although some operations have been accomplished. We
137 144 # thus want to consider an empty string as a complete
138 145 # statement.
139 146 return True
140 147 elif ( len(self.input_buffer.split('\n'))>2
141 148 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
142 149 return False
143 150 else:
151 self.capture_output()
152 try:
144 153 # Add line returns here, to make sure that the statement is
145 154 # complete.
146 return FrontEndBase.is_complete(self, string.rstrip() + '\n\n')
155 is_complete = codeop.compile_command(string.rstrip() + '\n\n',
156 "<string>", "exec")
157 self.release_output()
158 except Exception, e:
159 # XXX: Hack: return True so that the
160 # code gets executed and the error captured.
161 is_complete = True
162 return is_complete
147 163
148 164
149 165 def write(self, string, refresh=True):
150 166 """ Write some characters to the display.
151 167
152 168 Subclass should overide this method.
153 169
154 170 The refresh keyword argument is used in frontends with an
155 171 event loop, to choose whether the write should trigget an UI
156 172 refresh, and thus be syncrhonous, or not.
157 173 """
158 174 print >>sys.__stderr__, string
159 175
160 176
161 177 def execute(self, python_string, raw_string=None):
162 178 """ Stores the raw_string in the history, and sends the
163 179 python string to the interpreter.
164 180 """
165 181 if raw_string is None:
166 182 raw_string = python_string
167 183 # Create a false result, in case there is an exception
168 184 self.last_result = dict(number=self.prompt_number)
169 185 try:
170 186 self.history.input_cache[-1] = raw_string.rstrip()
171 187 result = self.shell.execute(python_string)
172 188 self.last_result = result
173 189 self.render_result(result)
174 190 except:
175 191 self.show_traceback()
176 192 finally:
177 193 self.after_execute()
178 194
179 195 #--------------------------------------------------------------------------
180 196 # LineFrontEndBase interface
181 197 #--------------------------------------------------------------------------
182 198
183 199 def prefilter_input(self, string):
184 """ Priflter the input to turn it in valid python.
200 """ Prefilter the input to turn it in valid python.
185 201 """
186 202 string = string.replace('\r\n', '\n')
187 203 string = string.replace('\t', 4*' ')
188 204 # Clean the trailing whitespace
189 205 string = '\n'.join(l.rstrip() for l in string.split('\n'))
190 206 return string
191 207
192 208
193 209 def after_execute(self):
194 210 """ All the operations required after an execution to put the
195 211 terminal back in a shape where it is usable.
196 212 """
197 213 self.prompt_number += 1
198 214 self.new_prompt(self.input_prompt_template.substitute(
199 215 number=(self.last_result['number'] + 1)))
200 216 # Start a new empty history entry
201 217 self._add_history(None, '')
202 218 self.history_cursor = len(self.history.input_cache) - 1
203 219
204 220
205 221 def complete_current_input(self):
206 222 """ Do code completion on current line.
207 223 """
208 224 if self.debug:
209 225 print >>sys.__stdout__, "complete_current_input",
210 226 line = self.input_buffer
211 227 new_line, completions = self.complete(line)
212 228 if len(completions)>1:
213 self.write_completion(completions)
229 self.write_completion(completions, new_line=new_line)
230 elif not line == new_line:
214 231 self.input_buffer = new_line
215 232 if self.debug:
233 print >>sys.__stdout__, 'line', line
234 print >>sys.__stdout__, 'new_line', new_line
216 235 print >>sys.__stdout__, completions
217 236
218 237
219 238 def get_line_width(self):
220 239 """ Return the width of the line in characters.
221 240 """
222 241 return 80
223 242
224 243
225 def write_completion(self, possibilities):
244 def write_completion(self, possibilities, new_line=None):
226 245 """ Write the list of possible completions.
246
247 new_line is the completed input line that should be displayed
248 after the completion are writen. If None, the input_buffer
249 before the completion is used.
227 250 """
228 current_buffer = self.input_buffer
251 if new_line is None:
252 new_line = self.input_buffer
229 253
230 254 self.write('\n')
231 255 max_len = len(max(possibilities, key=len)) + 1
232 256
233 257 # Now we check how much symbol we can put on a line...
234 258 chars_per_line = self.get_line_width()
235 259 symbols_per_line = max(1, chars_per_line/max_len)
236 260
237 261 pos = 1
238 262 buf = []
239 263 for symbol in possibilities:
240 264 if pos < symbols_per_line:
241 265 buf.append(symbol.ljust(max_len))
242 266 pos += 1
243 267 else:
244 268 buf.append(symbol.rstrip() + '\n')
245 269 pos = 1
246 270 self.write(''.join(buf))
247 271 self.new_prompt(self.input_prompt_template.substitute(
248 272 number=self.last_result['number'] + 1))
249 self.input_buffer = current_buffer
273 self.input_buffer = new_line
250 274
251 275
252 276 def new_prompt(self, prompt):
253 277 """ Prints a prompt and starts a new editing buffer.
254 278
255 279 Subclasses should use this method to make sure that the
256 280 terminal is put in a state favorable for a new line
257 281 input.
258 282 """
259 283 self.input_buffer = ''
260 284 self.write(prompt)
261 285
262 286
263 287 #--------------------------------------------------------------------------
264 288 # Private API
265 289 #--------------------------------------------------------------------------
266 290
267 291 def _on_enter(self):
268 292 """ Called when the return key is pressed in a line editing
269 293 buffer.
270 294 """
271 295 current_buffer = self.input_buffer
272 296 cleaned_buffer = self.prefilter_input(current_buffer)
273 297 if self.is_complete(cleaned_buffer):
274 298 self.execute(cleaned_buffer, raw_string=current_buffer)
275 299 else:
276 300 self.input_buffer += self._get_indent_string(
277 301 current_buffer[:-1])
302 if len(current_buffer.split('\n')) == 2:
303 self.input_buffer += '\t\t'
278 304 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
279 305 self.input_buffer += '\t'
280 306
281 307
282 308 def _get_indent_string(self, string):
283 309 """ Return the string of whitespace that prefixes a line. Used to
284 310 add the right amount of indendation when creating a new line.
285 311 """
286 312 string = string.replace('\t', ' '*4)
287 313 string = string.split('\n')[-1]
288 314 indent_chars = len(string) - len(string.lstrip())
289 315 indent_string = '\t'*(indent_chars // 4) + \
290 316 ' '*(indent_chars % 4)
291 317
292 318 return indent_string
293 319
294 320
@@ -1,223 +1,230 b''
1 1 """
2 2 Frontend class that uses IPython0 to prefilter the inputs.
3 3
4 4 Using the IPython0 mechanism gives us access to the magics.
5 5
6 6 This is a transitory class, used here to do the transition between
7 7 ipython0 and ipython1. This class is meant to be short-lived as more
8 8 functionnality is abstracted out of ipython0 in reusable functions and
9 9 is added on the interpreter. This class can be a used to guide this
10 10 refactoring.
11 11 """
12 12 __docformat__ = "restructuredtext en"
13 13
14 14 #-------------------------------------------------------------------------------
15 15 # Copyright (C) 2008 The IPython Development Team
16 16 #
17 17 # Distributed under the terms of the BSD License. The full license is in
18 18 # the file COPYING, distributed as part of this software.
19 19 #-------------------------------------------------------------------------------
20 20
21 21 #-------------------------------------------------------------------------------
22 22 # Imports
23 23 #-------------------------------------------------------------------------------
24 24 import sys
25 25
26 26 from linefrontendbase import LineFrontEndBase, common_prefix
27 from frontendbase import FrontEndBase
27 28
28 29 from IPython.ipmaker import make_IPython
29 30 from IPython.ipapi import IPApi
30 31 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
31 32
32 33 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
33 34
34 35 from IPython.genutils import Term
35 36 import pydoc
36 37 import os
38 import sys
37 39
38 40
39 41 def mk_system_call(system_call_function, command):
40 42 """ given a os.system replacement, and a leading string command,
41 43 returns a function that will execute the command with the given
42 44 argument string.
43 45 """
44 46 def my_system_call(args):
45 47 system_call_function("%s %s" % (command, args))
46 48 return my_system_call
47 49
48 50 #-------------------------------------------------------------------------------
49 51 # Frontend class using ipython0 to do the prefiltering.
50 52 #-------------------------------------------------------------------------------
51 53 class PrefilterFrontEnd(LineFrontEndBase):
52 54 """ Class that uses ipython0 to do prefilter the input, do the
53 55 completion and the magics.
54 56
55 57 The core trick is to use an ipython0 instance to prefilter the
56 58 input, and share the namespace between the interpreter instance used
57 59 to execute the statements and the ipython0 used for code
58 60 completion...
59 61 """
60 62
63 debug = False
64
61 65 def __init__(self, ipython0=None, *args, **kwargs):
62 66 """ Parameters:
63 67 -----------
64 68
65 69 ipython0: an optional ipython0 instance to use for command
66 70 prefiltering and completion.
67 71 """
72 LineFrontEndBase.__init__(self, *args, **kwargs)
73 self.shell.output_trap = RedirectorOutputTrap(
74 out_callback=self.write,
75 err_callback=self.write,
76 )
77 self.shell.traceback_trap = SyncTracebackTrap(
78 formatters=self.shell.traceback_trap.formatters,
79 )
80
81 # Start the ipython0 instance:
68 82 self.save_output_hooks()
69 83 if ipython0 is None:
70 84 # Instanciate an IPython0 interpreter to be able to use the
71 85 # prefiltering.
72 86 # XXX: argv=[] is a bit bold.
73 ipython0 = make_IPython(argv=[])
87 ipython0 = make_IPython(argv=[],
88 user_ns=self.shell.user_ns,
89 user_global_ns=self.shell.user_global_ns)
74 90 self.ipython0 = ipython0
75 91 # Set the pager:
76 92 self.ipython0.set_hook('show_in_pager',
77 93 lambda s, string: self.write("\n" + string))
78 94 self.ipython0.write = self.write
79 95 self._ip = _ip = IPApi(self.ipython0)
80 96 # Make sure the raw system call doesn't get called, as we don't
81 97 # have a stdin accessible.
82 98 self._ip.system = self.system_call
83 99 # XXX: Muck around with magics so that they work better
84 100 # in our environment
85 101 self.ipython0.magic_ls = mk_system_call(self.system_call,
86 102 'ls -CF')
87 103 # And now clean up the mess created by ipython0
88 104 self.release_output()
105
106
89 107 if not 'banner' in kwargs and self.banner is None:
90 kwargs['banner'] = self.ipython0.BANNER + """
108 self.banner = self.ipython0.BANNER + """
91 109 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
92 110
93 LineFrontEndBase.__init__(self, *args, **kwargs)
94 # XXX: Hack: mix the two namespaces
95 self.shell.user_ns.update(self.ipython0.user_ns)
96 self.ipython0.user_ns = self.shell.user_ns
97 self.shell.user_global_ns.update(self.ipython0.user_global_ns)
98 self.ipython0.user_global_ns = self.shell.user_global_ns
99
100 self.shell.output_trap = RedirectorOutputTrap(
101 out_callback=self.write,
102 err_callback=self.write,
103 )
104 self.shell.traceback_trap = SyncTracebackTrap(
105 formatters=self.shell.traceback_trap.formatters,
106 )
111 self.start()
107 112
108 113 #--------------------------------------------------------------------------
109 114 # FrontEndBase interface
110 115 #--------------------------------------------------------------------------
111 116
112 117 def show_traceback(self):
113 118 """ Use ipython0 to capture the last traceback and display it.
114 119 """
115 120 self.capture_output()
116 self.ipython0.showtraceback()
121 self.ipython0.showtraceback(tb_offset=-1)
117 122 self.release_output()
118 123
119 124
120 125 def execute(self, python_string, raw_string=None):
121 126 if self.debug:
122 127 print 'Executing Python code:', repr(python_string)
123 128 self.capture_output()
124 129 LineFrontEndBase.execute(self, python_string,
125 130 raw_string=raw_string)
126 131 self.release_output()
127 132
128 133
129 134 def save_output_hooks(self):
130 135 """ Store all the output hooks we can think of, to be able to
131 136 restore them.
132 137
133 138 We need to do this early, as starting the ipython0 instance will
134 139 screw ouput hooks.
135 140 """
136 141 self.__old_cout_write = Term.cout.write
137 142 self.__old_cerr_write = Term.cerr.write
138 143 self.__old_stdout = sys.stdout
139 144 self.__old_stderr= sys.stderr
140 145 self.__old_help_output = pydoc.help.output
141 146 self.__old_display_hook = sys.displayhook
142 147
143 148
144 149 def capture_output(self):
145 150 """ Capture all the output mechanisms we can think of.
146 151 """
147 152 self.save_output_hooks()
148 153 Term.cout.write = self.write
149 154 Term.cerr.write = self.write
150 155 sys.stdout = Term.cout
151 156 sys.stderr = Term.cerr
152 157 pydoc.help.output = self.shell.output_trap.out
153 158
154 159
155 160 def release_output(self):
156 161 """ Release all the different captures we have made.
157 162 """
158 163 Term.cout.write = self.__old_cout_write
159 164 Term.cerr.write = self.__old_cerr_write
160 165 sys.stdout = self.__old_stdout
161 166 sys.stderr = self.__old_stderr
162 167 pydoc.help.output = self.__old_help_output
163 168 sys.displayhook = self.__old_display_hook
164 169
165 170
166 171 def complete(self, line):
172 # FIXME: This should be factored out in the linefrontendbase
173 # method.
167 174 word = line.split('\n')[-1].split(' ')[-1]
168 175 completions = self.ipython0.complete(word)
169 176 # FIXME: The proper sort should be done in the complete method.
170 177 key = lambda x: x.replace('_', '')
171 178 completions.sort(key=key)
172 179 if completions:
173 180 prefix = common_prefix(completions)
174 181 line = line[:-len(word)] + prefix
175 182 return line, completions
176 183
177 184
178 185 #--------------------------------------------------------------------------
179 186 # LineFrontEndBase interface
180 187 #--------------------------------------------------------------------------
181 188
182 189 def prefilter_input(self, input_string):
183 190 """ Using IPython0 to prefilter the commands to turn them
184 191 in executable statements that are valid Python strings.
185 192 """
186 193 input_string = LineFrontEndBase.prefilter_input(self, input_string)
187 194 filtered_lines = []
188 195 # The IPython0 prefilters sometime produce output. We need to
189 196 # capture it.
190 197 self.capture_output()
191 198 self.last_result = dict(number=self.prompt_number)
192 199 try:
193 200 for line in input_string.split('\n'):
194 201 filtered_lines.append(
195 202 self.ipython0.prefilter(line, False).rstrip())
196 203 except:
197 204 # XXX: probably not the right thing to do.
198 205 self.ipython0.showsyntaxerror()
199 206 self.after_execute()
200 207 finally:
201 208 self.release_output()
202 209
203 210 # Clean up the trailing whitespace, to avoid indentation errors
204 211 filtered_string = '\n'.join(filtered_lines)
205 212 return filtered_string
206 213
207 214
208 215 #--------------------------------------------------------------------------
209 216 # PrefilterFrontEnd interface
210 217 #--------------------------------------------------------------------------
211 218
212 219 def system_call(self, command_string):
213 220 """ Allows for frontend to define their own system call, to be
214 221 able capture output and redirect input.
215 222 """
216 223 return os.system(command_string)
217 224
218 225
219 226 def do_exit(self):
220 227 """ Exit the shell, cleanup and save the history.
221 228 """
222 229 self.ipython0.atexit_operations()
223 230
@@ -1,157 +1,155 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 7 #---------------------------------------------------------------------------
8 8 # Copyright (C) 2008 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #---------------------------------------------------------------------------
13 13
14 14 #---------------------------------------------------------------------------
15 15 # Imports
16 16 #---------------------------------------------------------------------------
17 17
18 18 import unittest
19 19
20 20 try:
21 21 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
22 22 from IPython.frontend import frontendbase
23 23 from IPython.kernel.engineservice import EngineService
24 24 except ImportError:
25 25 import nose
26 26 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
27 27
28 from IPython.testing.decorators import skip
29
28 30 class FrontEndCallbackChecker(AsyncFrontEndBase):
29 31 """FrontEndBase subclass for checking callbacks"""
30 32 def __init__(self, engine=None, history=None):
31 33 super(FrontEndCallbackChecker, self).__init__(engine=engine,
32 34 history=history)
33 35 self.updateCalled = False
34 36 self.renderResultCalled = False
35 37 self.renderErrorCalled = False
36 38
37 39 def update_cell_prompt(self, result, blockID=None):
38 40 self.updateCalled = True
39 41 return result
40 42
41 43 def render_result(self, result):
42 44 self.renderResultCalled = True
43 45 return result
44 46
45 47
46 48 def render_error(self, failure):
47 49 self.renderErrorCalled = True
48 50 return failure
49 51
50 52
51 53
52 54
53 55 class TestAsyncFrontendBase(unittest.TestCase):
54 56 def setUp(self):
55 57 """Setup the EngineService and FrontEndBase"""
56 58
57 59 self.fb = FrontEndCallbackChecker(engine=EngineService())
58 60
59
60 61 def test_implements_IFrontEnd(self):
61 62 assert(frontendbase.IFrontEnd.implementedBy(
62 63 AsyncFrontEndBase))
63 64
64
65 65 def test_is_complete_returns_False_for_incomplete_block(self):
66 66 """"""
67 67
68 68 block = """def test(a):"""
69 69
70 70 assert(self.fb.is_complete(block) == False)
71 71
72 72 def test_is_complete_returns_True_for_complete_block(self):
73 73 """"""
74 74
75 75 block = """def test(a): pass"""
76 76
77 77 assert(self.fb.is_complete(block))
78 78
79 79 block = """a=3"""
80 80
81 81 assert(self.fb.is_complete(block))
82 82
83
84 83 def test_blockID_added_to_result(self):
85 84 block = """3+3"""
86 85
87 86 d = self.fb.execute(block, blockID='TEST_ID')
88 87
89 88 d.addCallback(self.checkBlockID, expected='TEST_ID')
90 89
91 90 def test_blockID_added_to_failure(self):
92 91 block = "raise Exception()"
93 92
94 93 d = self.fb.execute(block,blockID='TEST_ID')
95 94 d.addErrback(self.checkFailureID, expected='TEST_ID')
96 95
97 96 def checkBlockID(self, result, expected=""):
98 97 assert(result['blockID'] == expected)
99 98
100 99
101 100 def checkFailureID(self, failure, expected=""):
102 101 assert(failure.blockID == expected)
103 102
104 103
105 104 def test_callbacks_added_to_execute(self):
106 105 """test that
107 106 update_cell_prompt
108 107 render_result
109 108
110 109 are added to execute request
111 110 """
112 111
113 112 d = self.fb.execute("10+10")
114 113 d.addCallback(self.checkCallbacks)
115 114
116
117 115 def checkCallbacks(self, result):
118 116 assert(self.fb.updateCalled)
119 117 assert(self.fb.renderResultCalled)
120 118
121
119 @skip("This test fails and lead to an unhandled error in a Deferred.")
122 120 def test_error_callback_added_to_execute(self):
123 121 """test that render_error called on execution error"""
124 122
125 123 d = self.fb.execute("raise Exception()")
126 124 d.addCallback(self.checkRenderError)
127 125
128 126 def checkRenderError(self, result):
129 127 assert(self.fb.renderErrorCalled)
130 128
131 129 def test_history_returns_expected_block(self):
132 130 """Make sure history browsing doesn't fail"""
133 131
134 132 blocks = ["a=1","a=2","a=3"]
135 133 for b in blocks:
136 134 d = self.fb.execute(b)
137 135
138 136 # d is now the deferred for the last executed block
139 137 d.addCallback(self.historyTests, blocks)
140 138
141 139
142 140 def historyTests(self, result, blocks):
143 141 """historyTests"""
144 142
145 143 assert(len(blocks) >= 3)
146 144 assert(self.fb.get_history_previous("") == blocks[-2])
147 145 assert(self.fb.get_history_previous("") == blocks[-3])
148 146 assert(self.fb.get_history_next() == blocks[-2])
149 147
150 148
151 149 def test_history_returns_none_at_startup(self):
152 150 """test_history_returns_none_at_startup"""
153 151
154 152 assert(self.fb.get_history_previous("")==None)
155 153 assert(self.fb.get_history_next()==None)
156 154
157 155
@@ -1,67 +1,67 b''
1 1 # encoding: utf-8
2 2 """
3 3 Test process execution and IO redirection.
4 4 """
5 5
6 6 __docformat__ = "restructuredtext en"
7 7
8 8 #-------------------------------------------------------------------------------
9 9 # Copyright (C) 2008 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is
12 12 # in the file COPYING, distributed as part of this software.
13 13 #-------------------------------------------------------------------------------
14 14
15 15 from cStringIO import StringIO
16 16 from time import sleep
17 17 import sys
18 18
19 19 from IPython.frontend._process import PipedProcess
20 20 from IPython.testing import decorators as testdec
21 21
22
22 23 def test_capture_out():
23 24 """ A simple test to see if we can execute a process and get the output.
24 25 """
25 26 s = StringIO()
26 27 p = PipedProcess('echo 1', out_callback=s.write, )
27 28 p.start()
28 29 p.join()
29 30 result = s.getvalue().rstrip()
30 31 assert result == '1'
31 32
32 # FIXME
33 @testdec.skip("This doesn't work under Windows")
33
34 34 def test_io():
35 35 """ Checks that we can send characters on stdin to the process.
36 36 """
37 37 s = StringIO()
38 38 p = PipedProcess(sys.executable + ' -c "a = raw_input(); print a"',
39 39 out_callback=s.write, )
40 40 p.start()
41 41 test_string = '12345\n'
42 42 while not hasattr(p, 'process'):
43 43 sleep(0.1)
44 44 p.process.stdin.write(test_string)
45 45 p.join()
46 46 result = s.getvalue()
47 47 assert result == test_string
48 48
49 49
50 50 def test_kill():
51 51 """ Check that we can kill a process, and its subprocess.
52 52 """
53 53 s = StringIO()
54 54 p = PipedProcess(sys.executable + ' -c "a = raw_input();"',
55 55 out_callback=s.write, )
56 56 p.start()
57 57 while not hasattr(p, 'process'):
58 58 sleep(0.1)
59 59 p.process.kill()
60 60 assert p.process.poll() is not None
61 61
62 62
63 63 if __name__ == '__main__':
64 64 test_capture_out()
65 65 test_io()
66 66 test_kill()
67 67
@@ -1,428 +1,436 b''
1 1 # encoding: utf-8
2 2 """
3 3 A Wx widget to act as a console and input commands.
4 4
5 5 This widget deals with prompts and provides an edit buffer
6 6 restricted to after the last prompt.
7 7 """
8 8
9 9 __docformat__ = "restructuredtext en"
10 10
11 11 #-------------------------------------------------------------------------------
12 12 # Copyright (C) 2008 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is
15 15 # in the file COPYING, distributed as part of this software.
16 16 #-------------------------------------------------------------------------------
17 17
18 18 #-------------------------------------------------------------------------------
19 19 # Imports
20 20 #-------------------------------------------------------------------------------
21 21
22 22 import wx
23 23 import wx.stc as stc
24 24
25 25 from wx.py import editwindow
26 import time
26 27 import sys
27 28 LINESEP = '\n'
28 29 if sys.platform == 'win32':
29 30 LINESEP = '\n\r'
30 31
31 32 import re
32 33
33 34 # FIXME: Need to provide an API for non user-generated display on the
34 35 # screen: this should not be editable by the user.
35 36
36 37 _DEFAULT_SIZE = 10
37 38 if sys.platform == 'darwin':
38 39 _DEFAULT_STYLE = 12
39 40
40 41 _DEFAULT_STYLE = {
41 42 'stdout' : 'fore:#0000FF',
42 43 'stderr' : 'fore:#007f00',
43 44 'trace' : 'fore:#FF0000',
44 45
45 46 'default' : 'size:%d' % _DEFAULT_SIZE,
46 47 'bracegood' : 'fore:#00AA00,back:#000000,bold',
47 48 'bracebad' : 'fore:#FF0000,back:#000000,bold',
48 49
49 50 # properties for the various Python lexer styles
50 51 'comment' : 'fore:#007F00',
51 52 'number' : 'fore:#007F7F',
52 53 'string' : 'fore:#7F007F,italic',
53 54 'char' : 'fore:#7F007F,italic',
54 55 'keyword' : 'fore:#00007F,bold',
55 56 'triple' : 'fore:#7F0000',
56 57 'tripledouble' : 'fore:#7F0000',
57 58 'class' : 'fore:#0000FF,bold,underline',
58 59 'def' : 'fore:#007F7F,bold',
59 60 'operator' : 'bold'
60 61 }
61 62
62 63 # new style numbers
63 64 _STDOUT_STYLE = 15
64 65 _STDERR_STYLE = 16
65 66 _TRACE_STYLE = 17
66 67
67 68
68 69 # system colors
69 70 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
70 71
71 72 #-------------------------------------------------------------------------------
72 73 # The console widget class
73 74 #-------------------------------------------------------------------------------
74 75 class ConsoleWidget(editwindow.EditWindow):
75 76 """ Specialized styled text control view for console-like workflow.
76 77
77 78 This widget is mainly interested in dealing with the prompt and
78 79 keeping the cursor inside the editing line.
79 80 """
80 81
81 82 # This is where the title captured from the ANSI escape sequences are
82 83 # stored.
83 84 title = 'Console'
84 85
85 86 # The buffer being edited.
86 87 def _set_input_buffer(self, string):
87 88 self.SetSelection(self.current_prompt_pos, self.GetLength())
88 89 self.ReplaceSelection(string)
89 90 self.GotoPos(self.GetLength())
90 91
91 92 def _get_input_buffer(self):
92 93 """ Returns the text in current edit buffer.
93 94 """
94 95 input_buffer = self.GetTextRange(self.current_prompt_pos,
95 96 self.GetLength())
96 97 input_buffer = input_buffer.replace(LINESEP, '\n')
97 98 return input_buffer
98 99
99 100 input_buffer = property(_get_input_buffer, _set_input_buffer)
100 101
101 102 style = _DEFAULT_STYLE.copy()
102 103
103 104 # Translation table from ANSI escape sequences to color. Override
104 105 # this to specify your colors.
105 106 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
106 107 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
107 108 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
108 109 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
109 110 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
110 111 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
111 112 '1;34': [12, 'LIGHT BLUE'], '1;35':
112 113 [13, 'MEDIUM VIOLET RED'],
113 114 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
114 115
115 116 # The color of the carret (call _apply_style() after setting)
116 117 carret_color = 'BLACK'
117 118
119 # Store the last time a refresh was done
120 _last_refresh_time = 0
121
118 122 #--------------------------------------------------------------------------
119 123 # Public API
120 124 #--------------------------------------------------------------------------
121 125
122 126 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
123 size=wx.DefaultSize, style=0, ):
127 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
124 128 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
125 129 self._configure_scintilla()
126 130
127 131 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
128 132 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
129 133
130 134
131 135 def write(self, text, refresh=True):
132 136 """ Write given text to buffer, while translating the ansi escape
133 137 sequences.
134 138 """
135 139 # XXX: do not put print statements to sys.stdout/sys.stderr in
136 140 # this method, the print statements will call this method, as
137 141 # you will end up with an infinit loop
138 142 title = self.title_pat.split(text)
139 143 if len(title)>1:
140 144 self.title = title[-2]
141 145
142 146 text = self.title_pat.sub('', text)
143 147 segments = self.color_pat.split(text)
144 148 segment = segments.pop(0)
145 149 self.GotoPos(self.GetLength())
146 150 self.StartStyling(self.GetLength(), 0xFF)
147 151 try:
148 152 self.AppendText(segment)
149 153 except UnicodeDecodeError:
150 154 # XXX: Do I really want to skip the exception?
151 155 pass
152 156
153 157 if segments:
154 158 for ansi_tag, text in zip(segments[::2], segments[1::2]):
155 159 self.StartStyling(self.GetLength(), 0xFF)
156 160 try:
157 161 self.AppendText(text)
158 162 except UnicodeDecodeError:
159 163 # XXX: Do I really want to skip the exception?
160 164 pass
161 165
162 166 if ansi_tag not in self.ANSI_STYLES:
163 167 style = 0
164 168 else:
165 169 style = self.ANSI_STYLES[ansi_tag][0]
166 170
167 171 self.SetStyling(len(text), style)
168 172
169 173 self.GotoPos(self.GetLength())
170 174 if refresh:
171 # Maybe this is faster than wx.Yield()
172 self.ProcessEvent(wx.PaintEvent())
173 #wx.Yield()
175 current_time = time.time()
176 if current_time - self._last_refresh_time > 0.03:
177 if sys.platform == 'win32':
178 wx.SafeYield()
179 else:
180 wx.Yield()
181 # self.ProcessEvent(wx.PaintEvent())
182 self._last_refresh_time = current_time
174 183
175 184
176 185 def new_prompt(self, prompt):
177 186 """ Prints a prompt at start of line, and move the start of the
178 187 current block there.
179 188
180 189 The prompt can be given with ascii escape sequences.
181 190 """
182 191 self.write(prompt, refresh=False)
183 192 # now we update our cursor giving end of prompt
184 193 self.current_prompt_pos = self.GetLength()
185 194 self.current_prompt_line = self.GetCurrentLine()
186 wx.Yield()
187 195 self.EnsureCaretVisible()
188 196
189 197
190 198 def scroll_to_bottom(self):
191 199 maxrange = self.GetScrollRange(wx.VERTICAL)
192 200 self.ScrollLines(maxrange)
193 201
194 202
195 203 def pop_completion(self, possibilities, offset=0):
196 204 """ Pops up an autocompletion menu. Offset is the offset
197 205 in characters of the position at which the menu should
198 206 appear, relativ to the cursor.
199 207 """
200 208 self.AutoCompSetIgnoreCase(False)
201 209 self.AutoCompSetAutoHide(False)
202 210 self.AutoCompSetMaxHeight(len(possibilities))
203 211 self.AutoCompShow(offset, " ".join(possibilities))
204 212
205 213
206 214 def get_line_width(self):
207 215 """ Return the width of the line in characters.
208 216 """
209 217 return self.GetSize()[0]/self.GetCharWidth()
210 218
211 219 #--------------------------------------------------------------------------
212 220 # EditWindow API
213 221 #--------------------------------------------------------------------------
214 222
215 223 def OnUpdateUI(self, event):
216 224 """ Override the OnUpdateUI of the EditWindow class, to prevent
217 225 syntax highlighting both for faster redraw, and for more
218 226 consistent look and feel.
219 227 """
220 228
221 229 #--------------------------------------------------------------------------
222 230 # Private API
223 231 #--------------------------------------------------------------------------
224 232
225 233 def _apply_style(self):
226 234 """ Applies the colors for the different text elements and the
227 235 carret.
228 236 """
229 237 self.SetCaretForeground(self.carret_color)
230 238
231 239 #self.StyleClearAll()
232 240 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
233 241 "fore:#FF0000,back:#0000FF,bold")
234 242 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
235 243 "fore:#000000,back:#FF0000,bold")
236 244
237 245 for style in self.ANSI_STYLES.values():
238 246 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
239 247
240 248
241 249 def _configure_scintilla(self):
242 250 self.SetEOLMode(stc.STC_EOL_LF)
243 251
244 252 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
245 253 # the widget
246 254 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
247 255 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
248 256 # Also allow Ctrl Shift "=" for poor non US keyboard users.
249 257 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
250 258 stc.STC_CMD_ZOOMIN)
251 259
252 260 # Keys: we need to clear some of the keys the that don't play
253 261 # well with a console.
254 262 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
255 263 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
256 264 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
257 265 self.CmdKeyClear(ord('A'), stc.STC_SCMOD_CTRL)
258 266
259 267 self.SetEOLMode(stc.STC_EOL_CRLF)
260 268 self.SetWrapMode(stc.STC_WRAP_CHAR)
261 269 self.SetWrapMode(stc.STC_WRAP_WORD)
262 270 self.SetBufferedDraw(True)
263 271 self.SetUseAntiAliasing(True)
264 272 self.SetLayoutCache(stc.STC_CACHE_PAGE)
265 273 self.SetUndoCollection(False)
266 274 self.SetUseTabs(True)
267 275 self.SetIndent(4)
268 276 self.SetTabWidth(4)
269 277
270 278 # we don't want scintilla's autocompletion to choose
271 279 # automaticaly out of a single choice list, as we pop it up
272 280 # automaticaly
273 281 self.AutoCompSetChooseSingle(False)
274 282 self.AutoCompSetMaxHeight(10)
275 283 # XXX: this doesn't seem to have an effect.
276 284 self.AutoCompSetFillUps('\n')
277 285
278 286 self.SetMargins(3, 3) #text is moved away from border with 3px
279 287 # Suppressing Scintilla margins
280 288 self.SetMarginWidth(0, 0)
281 289 self.SetMarginWidth(1, 0)
282 290 self.SetMarginWidth(2, 0)
283 291
284 292 self._apply_style()
285 293
286 294 # Xterm escape sequences
287 295 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
288 296 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
289 297
290 298 #self.SetEdgeMode(stc.STC_EDGE_LINE)
291 299 #self.SetEdgeColumn(80)
292 300
293 301 # styles
294 302 p = self.style
295 303 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
296 304 self.StyleClearAll()
297 305 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
298 306 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
299 307 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
300 308
301 309 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
302 310 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
303 311 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
304 312 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
305 313 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
306 314 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
307 315 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
308 316 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
309 317 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
310 318 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
311 319 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
312 320 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
313 321 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
314 322 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
315 323
316 324 def _on_key_down(self, event, skip=True):
317 325 """ Key press callback used for correcting behavior for
318 326 console-like interfaces: the cursor is constraint to be after
319 327 the last prompt.
320 328
321 329 Return True if event as been catched.
322 330 """
323 331 catched = True
324 332 # Intercept some specific keys.
325 333 if event.KeyCode == ord('L') and event.ControlDown() :
326 334 self.scroll_to_bottom()
327 335 elif event.KeyCode == ord('K') and event.ControlDown() :
328 336 self.input_buffer = ''
329 337 elif event.KeyCode == ord('A') and event.ControlDown() :
330 338 self.GotoPos(self.GetLength())
331 339 self.SetSelectionStart(self.current_prompt_pos)
332 340 self.SetSelectionEnd(self.GetCurrentPos())
333 341 catched = True
334 342 elif event.KeyCode == ord('E') and event.ControlDown() :
335 343 self.GotoPos(self.GetLength())
336 344 catched = True
337 345 elif event.KeyCode == wx.WXK_PAGEUP:
338 346 self.ScrollPages(-1)
339 347 elif event.KeyCode == wx.WXK_PAGEDOWN:
340 348 self.ScrollPages(1)
341 349 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
342 350 self.ScrollLines(-1)
343 351 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
344 352 self.ScrollLines(1)
345 353 else:
346 354 catched = False
347 355
348 356 if self.AutoCompActive():
349 357 event.Skip()
350 358 else:
351 359 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
352 360 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
353 361 catched = True
354 362 self.CallTipCancel()
355 363 self.write('\n', refresh=False)
356 364 # Under windows scintilla seems to be doing funny stuff to the
357 365 # line returns here, but the getter for input_buffer filters
358 366 # this out.
359 367 if sys.platform == 'win32':
360 368 self.input_buffer = self.input_buffer
361 369 self._on_enter()
362 370
363 371 elif event.KeyCode == wx.WXK_HOME:
364 372 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
365 373 self.GotoPos(self.current_prompt_pos)
366 374 catched = True
367 375
368 376 elif event.Modifiers == wx.MOD_SHIFT:
369 377 # FIXME: This behavior is not ideal: if the selection
370 378 # is already started, it will jump.
371 379 self.SetSelectionStart(self.current_prompt_pos)
372 380 self.SetSelectionEnd(self.GetCurrentPos())
373 381 catched = True
374 382
375 383 elif event.KeyCode == wx.WXK_UP:
376 384 if self.GetCurrentLine() > self.current_prompt_line:
377 385 if self.GetCurrentLine() == self.current_prompt_line + 1 \
378 386 and self.GetColumn(self.GetCurrentPos()) < \
379 387 self.GetColumn(self.current_prompt_pos):
380 388 self.GotoPos(self.current_prompt_pos)
381 389 else:
382 390 event.Skip()
383 391 catched = True
384 392
385 393 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
386 394 if self.GetCurrentPos() > self.current_prompt_pos:
387 395 event.Skip()
388 396 catched = True
389 397
390 398 if skip and not catched:
391 399 # Put the cursor back in the edit region
392 400 if self.GetCurrentPos() < self.current_prompt_pos:
393 401 self.GotoPos(self.current_prompt_pos)
394 402 else:
395 403 event.Skip()
396 404
397 405 return catched
398 406
399 407
400 408 def _on_key_up(self, event, skip=True):
401 409 """ If cursor is outside the editing region, put it back.
402 410 """
403 411 event.Skip()
404 412 if self.GetCurrentPos() < self.current_prompt_pos:
405 413 self.GotoPos(self.current_prompt_pos)
406 414
407 415
408 416
409 417 if __name__ == '__main__':
410 418 # Some simple code to test the console widget.
411 419 class MainWindow(wx.Frame):
412 420 def __init__(self, parent, id, title):
413 421 wx.Frame.__init__(self, parent, id, title, size=(300,250))
414 422 self._sizer = wx.BoxSizer(wx.VERTICAL)
415 423 self.console_widget = ConsoleWidget(self)
416 424 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
417 425 self.SetSizer(self._sizer)
418 426 self.SetAutoLayout(1)
419 427 self.Show(True)
420 428
421 429 app = wx.PySimpleApp()
422 430 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
423 431 w.SetSize((780, 460))
424 432 w.Show()
425 433
426 434 app.MainLoop()
427 435
428 436
@@ -1,510 +1,526 b''
1 1 # encoding: utf-8 -*- test-case-name:
2 2 # FIXME: Need to add tests.
3 3 # ipython1.frontend.wx.tests.test_wx_frontend -*-
4 4
5 5 """Classes to provide a Wx frontend to the
6 6 IPython.kernel.core.interpreter.
7 7
8 8 This class inherits from ConsoleWidget, that provides a console-like
9 9 widget to provide a text-rendering widget suitable for a terminal.
10 10 """
11 11
12 12 __docformat__ = "restructuredtext en"
13 13
14 14 #-------------------------------------------------------------------------------
15 15 # Copyright (C) 2008 The IPython Development Team
16 16 #
17 17 # Distributed under the terms of the BSD License. The full license is in
18 18 # the file COPYING, distributed as part of this software.
19 19 #-------------------------------------------------------------------------------
20 20
21 21 #-------------------------------------------------------------------------------
22 22 # Imports
23 23 #-------------------------------------------------------------------------------
24 24
25 25 # Major library imports
26 26 import re
27 27 import __builtin__
28 28 from time import sleep
29 29 import sys
30 30 from threading import Lock
31 31 import string
32 32
33 33 import wx
34 34 from wx import stc
35 35
36 36 # Ipython-specific imports.
37 37 from IPython.frontend._process import PipedProcess
38 38 from console_widget import ConsoleWidget
39 39 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
40 40
41 41 #-------------------------------------------------------------------------------
42 42 # Constants
43 43 #-------------------------------------------------------------------------------
44 44
45 45 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
46 46 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
47 47 _ERROR_BG = '#FFF1F1' # Nice red
48 48
49 49 _COMPLETE_BUFFER_MARKER = 31
50 50 _ERROR_MARKER = 30
51 51 _INPUT_MARKER = 29
52 52
53 53 prompt_in1 = \
54 54 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
55 55
56 56 prompt_out = \
57 57 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
58 58
59 59 #-------------------------------------------------------------------------------
60 60 # Classes to implement the Wx frontend
61 61 #-------------------------------------------------------------------------------
62 62 class WxController(ConsoleWidget, PrefilterFrontEnd):
63 63 """Classes to provide a Wx frontend to the
64 64 IPython.kernel.core.interpreter.
65 65
66 66 This class inherits from ConsoleWidget, that provides a console-like
67 67 widget to provide a text-rendering widget suitable for a terminal.
68 68 """
69 69
70 70 output_prompt_template = string.Template(prompt_out)
71 71
72 72 input_prompt_template = string.Template(prompt_in1)
73 73
74 74 # Print debug info on what is happening to the console.
75 75 debug = False
76 76
77 77 # The title of the terminal, as captured through the ANSI escape
78 78 # sequences.
79 79 def _set_title(self, title):
80 80 return self.Parent.SetTitle(title)
81 81
82 82 def _get_title(self):
83 83 return self.Parent.GetTitle()
84 84
85 85 title = property(_get_title, _set_title)
86 86
87 87
88 88 # The buffer being edited.
89 89 # We are duplicating the definition here because of multiple
90 90 # inheritence
91 91 def _set_input_buffer(self, string):
92 92 ConsoleWidget._set_input_buffer(self, string)
93 93 self._colorize_input_buffer()
94 94
95 95 def _get_input_buffer(self):
96 96 """ Returns the text in current edit buffer.
97 97 """
98 98 return ConsoleWidget._get_input_buffer(self)
99 99
100 100 input_buffer = property(_get_input_buffer, _set_input_buffer)
101 101
102 102
103 103 #--------------------------------------------------------------------------
104 104 # Private Attributes
105 105 #--------------------------------------------------------------------------
106 106
107 107 # A flag governing the behavior of the input. Can be:
108 108 #
109 109 # 'readline' for readline-like behavior with a prompt
110 110 # and an edit buffer.
111 111 # 'raw_input' similar to readline, but triggered by a raw-input
112 112 # call. Can be used by subclasses to act differently.
113 113 # 'subprocess' for sending the raw input directly to a
114 114 # subprocess.
115 115 # 'buffering' for buffering of the input, that will be used
116 116 # when the input state switches back to another state.
117 117 _input_state = 'readline'
118 118
119 119 # Attribute to store reference to the pipes of a subprocess, if we
120 120 # are running any.
121 121 _running_process = False
122 122
123 123 # A queue for writing fast streams to the screen without flooding the
124 124 # event loop
125 125 _out_buffer = []
126 126
127 127 # A lock to lock the _out_buffer to make sure we don't empty it
128 128 # while it is being swapped
129 129 _out_buffer_lock = Lock()
130 130
131 # The different line markers used to higlight the prompts.
131 132 _markers = dict()
132 133
133 134 #--------------------------------------------------------------------------
134 135 # Public API
135 136 #--------------------------------------------------------------------------
136 137
137 138 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
138 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
139 size=wx.DefaultSize,
140 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
139 141 *args, **kwds):
140 142 """ Create Shell instance.
141 143 """
142 144 ConsoleWidget.__init__(self, parent, id, pos, size, style)
143 145 PrefilterFrontEnd.__init__(self, **kwds)
144 146
147 # Stick in our own raw_input:
148 self.ipython0.raw_input = self.raw_input
149
145 150 # Marker for complete buffer.
146 151 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
147 152 background=_COMPLETE_BUFFER_BG)
148 153 # Marker for current input buffer.
149 154 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
150 155 background=_INPUT_BUFFER_BG)
151 156 # Marker for tracebacks.
152 157 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
153 158 background=_ERROR_BG)
154 159
155 160 # A time for flushing the write buffer
156 161 BUFFER_FLUSH_TIMER_ID = 100
157 162 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
158 163 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
159 164
160 165 if 'debug' in kwds:
161 166 self.debug = kwds['debug']
162 167 kwds.pop('debug')
163 168
164 169 # Inject self in namespace, for debug
165 170 if self.debug:
166 171 self.shell.user_ns['self'] = self
172 # Inject our own raw_input in namespace
173 self.shell.user_ns['raw_input'] = self.raw_input
167 174
168 175
169 def raw_input(self, prompt):
176 def raw_input(self, prompt=''):
170 177 """ A replacement from python's raw_input.
171 178 """
172 179 self.new_prompt(prompt)
173 180 self._input_state = 'raw_input'
174 181 if hasattr(self, '_cursor'):
175 182 del self._cursor
176 183 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
177 self.waiting = True
178 184 self.__old_on_enter = self._on_enter
185 event_loop = wx.EventLoop()
179 186 def my_on_enter():
180 self.waiting = False
187 event_loop.Exit()
181 188 self._on_enter = my_on_enter
182 # XXX: Busy waiting, ugly.
183 while self.waiting:
184 wx.Yield()
185 sleep(0.1)
189 # XXX: Running a separate event_loop. Ugly.
190 event_loop.Run()
186 191 self._on_enter = self.__old_on_enter
187 192 self._input_state = 'buffering'
188 193 self._cursor = wx.BusyCursor()
189 194 return self.input_buffer.rstrip('\n')
190 195
191 196
192 197 def system_call(self, command_string):
193 198 self._input_state = 'subprocess'
199 event_loop = wx.EventLoop()
200 def _end_system_call():
201 self._input_state = 'buffering'
202 self._running_process = False
203 event_loop.Exit()
204
194 205 self._running_process = PipedProcess(command_string,
195 206 out_callback=self.buffered_write,
196 end_callback = self._end_system_call)
207 end_callback = _end_system_call)
197 208 self._running_process.start()
198 # XXX: another one of these polling loops to have a blocking
199 # call
200 wx.Yield()
201 while self._running_process:
202 wx.Yield()
203 sleep(0.1)
209 # XXX: Running a separate event_loop. Ugly.
210 event_loop.Run()
204 211 # Be sure to flush the buffer.
205 212 self._buffer_flush(event=None)
206 213
207 214
208 215 def do_calltip(self):
209 216 """ Analyse current and displays useful calltip for it.
210 217 """
211 218 if self.debug:
212 219 print >>sys.__stdout__, "do_calltip"
213 220 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
214 221 symbol = self.input_buffer
215 222 symbol_string = separators.split(symbol)[-1]
216 223 base_symbol_string = symbol_string.split('.')[0]
217 224 if base_symbol_string in self.shell.user_ns:
218 225 symbol = self.shell.user_ns[base_symbol_string]
219 226 elif base_symbol_string in self.shell.user_global_ns:
220 227 symbol = self.shell.user_global_ns[base_symbol_string]
221 228 elif base_symbol_string in __builtin__.__dict__:
222 229 symbol = __builtin__.__dict__[base_symbol_string]
223 230 else:
224 231 return False
225 232 try:
226 233 for name in symbol_string.split('.')[1:] + ['__doc__']:
227 234 symbol = getattr(symbol, name)
228 235 self.AutoCompCancel()
229 wx.Yield()
230 self.CallTipShow(self.GetCurrentPos(), symbol)
236 # Check that the symbol can indeed be converted to a string:
237 symbol += ''
238 wx.CallAfter(self.CallTipShow, self.GetCurrentPos(), symbol)
231 239 except:
232 240 # The retrieve symbol couldn't be converted to a string
233 241 pass
234 242
235 243
236 244 def _popup_completion(self, create=False):
237 245 """ Updates the popup completion menu if it exists. If create is
238 246 true, open the menu.
239 247 """
240 248 if self.debug:
241 print >>sys.__stdout__, "_popup_completion",
249 print >>sys.__stdout__, "_popup_completion"
242 250 line = self.input_buffer
243 if (self.AutoCompActive() and not line[-1] == '.') \
251 if (self.AutoCompActive() and line and not line[-1] == '.') \
244 252 or create==True:
245 253 suggestion, completions = self.complete(line)
246 254 offset=0
247 255 if completions:
248 256 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
249 257 residual = complete_sep.split(line)[-1]
250 258 offset = len(residual)
251 259 self.pop_completion(completions, offset=offset)
252 260 if self.debug:
253 261 print >>sys.__stdout__, completions
254 262
255 263
256 264 def buffered_write(self, text):
257 265 """ A write method for streams, that caches the stream in order
258 266 to avoid flooding the event loop.
259 267
260 268 This can be called outside of the main loop, in separate
261 269 threads.
262 270 """
263 271 self._out_buffer_lock.acquire()
264 272 self._out_buffer.append(text)
265 273 self._out_buffer_lock.release()
266 274 if not self._buffer_flush_timer.IsRunning():
267 275 wx.CallAfter(self._buffer_flush_timer.Start,
268 276 milliseconds=100, oneShot=True)
269 277
270 278
271 279 #--------------------------------------------------------------------------
272 280 # LineFrontEnd interface
273 281 #--------------------------------------------------------------------------
274 282
275 283 def execute(self, python_string, raw_string=None):
276 284 self._input_state = 'buffering'
277 285 self.CallTipCancel()
278 286 self._cursor = wx.BusyCursor()
279 287 if raw_string is None:
280 288 raw_string = python_string
281 289 end_line = self.current_prompt_line \
282 290 + max(1, len(raw_string.split('\n'))-1)
283 291 for i in range(self.current_prompt_line, end_line):
284 292 if i in self._markers:
285 293 self.MarkerDeleteHandle(self._markers[i])
286 294 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
287 # Update the display:
288 wx.Yield()
295 # Use a callafter to update the display robustly under windows
296 def callback():
289 297 self.GotoPos(self.GetLength())
290 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
298 PrefilterFrontEnd.execute(self, python_string,
299 raw_string=raw_string)
300 wx.CallAfter(callback)
291 301
292 302 def save_output_hooks(self):
293 303 self.__old_raw_input = __builtin__.raw_input
294 304 PrefilterFrontEnd.save_output_hooks(self)
295 305
296 306 def capture_output(self):
297 __builtin__.raw_input = self.raw_input
298 307 self.SetLexer(stc.STC_LEX_NULL)
299 308 PrefilterFrontEnd.capture_output(self)
309 __builtin__.raw_input = self.raw_input
300 310
301 311
302 312 def release_output(self):
303 313 __builtin__.raw_input = self.__old_raw_input
304 314 PrefilterFrontEnd.release_output(self)
305 315 self.SetLexer(stc.STC_LEX_PYTHON)
306 316
307 317
308 318 def after_execute(self):
309 319 PrefilterFrontEnd.after_execute(self)
310 320 # Clear the wait cursor
311 321 if hasattr(self, '_cursor'):
312 322 del self._cursor
313 323 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
314 324
315 325
316 326 def show_traceback(self):
317 327 start_line = self.GetCurrentLine()
318 328 PrefilterFrontEnd.show_traceback(self)
319 wx.Yield()
329 self.ProcessEvent(wx.PaintEvent())
330 #wx.Yield()
331 for i in range(start_line, self.GetCurrentLine()):
332 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
333
334
335 #--------------------------------------------------------------------------
336 # FrontEndBase interface
337 #--------------------------------------------------------------------------
338
339 def render_error(self, e):
340 start_line = self.GetCurrentLine()
341 self.write('\n' + e + '\n')
320 342 for i in range(start_line, self.GetCurrentLine()):
321 343 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
322 344
323 345
324 346 #--------------------------------------------------------------------------
325 347 # ConsoleWidget interface
326 348 #--------------------------------------------------------------------------
327 349
328 350 def new_prompt(self, prompt):
329 351 """ Display a new prompt, and start a new input buffer.
330 352 """
331 353 self._input_state = 'readline'
332 354 ConsoleWidget.new_prompt(self, prompt)
333 355 i = self.current_prompt_line
334 356 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
335 357
336 358
337 359 def write(self, *args, **kwargs):
338 360 # Avoid multiple inheritence, be explicit about which
339 361 # parent method class gets called
340 362 ConsoleWidget.write(self, *args, **kwargs)
341 363
342 364
343 365 def _on_key_down(self, event, skip=True):
344 366 """ Capture the character events, let the parent
345 367 widget handle them, and put our logic afterward.
346 368 """
347 369 # FIXME: This method needs to be broken down in smaller ones.
348 370 current_line_number = self.GetCurrentLine()
349 371 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
350 372 # Capture Control-C
351 373 if self._input_state == 'subprocess':
352 374 if self.debug:
353 375 print >>sys.__stderr__, 'Killing running process'
376 if hasattr(self._running_process, 'process'):
354 377 self._running_process.process.kill()
355 378 elif self._input_state == 'buffering':
356 379 if self.debug:
357 380 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
358 381 raise KeyboardInterrupt
359 382 # XXX: We need to make really sure we
360 383 # get back to a prompt.
361 384 elif self._input_state == 'subprocess' and (
362 385 ( event.KeyCode<256 and
363 386 not event.ControlDown() )
364 387 or
365 388 ( event.KeyCode in (ord('d'), ord('D')) and
366 389 event.ControlDown())):
367 390 # We are running a process, we redirect keys.
368 391 ConsoleWidget._on_key_down(self, event, skip=skip)
369 392 char = chr(event.KeyCode)
370 393 # Deal with some inconsistency in wx keycodes:
371 394 if char == '\r':
372 395 char = '\n'
373 396 elif not event.ShiftDown():
374 397 char = char.lower()
375 398 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
376 399 char = '\04'
377 400 self._running_process.process.stdin.write(char)
378 401 self._running_process.process.stdin.flush()
379 elif event.KeyCode in (ord('('), 57):
402 elif event.KeyCode in (ord('('), 57, 53):
380 403 # Calltips
381 404 event.Skip()
382 405 self.do_calltip()
383 406 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
384 407 event.Skip()
385 408 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
386 409 wx.CallAfter(self._popup_completion, create=True)
387 410 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
388 411 wx.WXK_RIGHT, wx.WXK_ESCAPE):
389 412 wx.CallAfter(self._popup_completion)
390 413 else:
391 414 # Up history
392 415 if event.KeyCode == wx.WXK_UP and (
393 416 ( current_line_number == self.current_prompt_line and
394 417 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
395 418 or event.ControlDown() ):
396 419 new_buffer = self.get_history_previous(
397 420 self.input_buffer)
398 421 if new_buffer is not None:
399 422 self.input_buffer = new_buffer
400 423 if self.GetCurrentLine() > self.current_prompt_line:
401 424 # Go to first line, for seemless history up.
402 425 self.GotoPos(self.current_prompt_pos)
403 426 # Down history
404 427 elif event.KeyCode == wx.WXK_DOWN and (
405 428 ( current_line_number == self.LineCount -1 and
406 429 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
407 430 or event.ControlDown() ):
408 431 new_buffer = self.get_history_next()
409 432 if new_buffer is not None:
410 433 self.input_buffer = new_buffer
411 434 # Tab-completion
412 435 elif event.KeyCode == ord('\t'):
413 last_line = self.input_buffer.split('\n')[-1]
414 if not re.match(r'^\s*$', last_line):
436 current_line, current_line_number = self.CurLine
437 if not re.match(r'^\s*$', current_line):
415 438 self.complete_current_input()
416 439 if self.AutoCompActive():
417 440 wx.CallAfter(self._popup_completion, create=True)
418 441 else:
419 442 event.Skip()
420 443 else:
421 444 ConsoleWidget._on_key_down(self, event, skip=skip)
422 445
423 446
424 447 def _on_key_up(self, event, skip=True):
425 448 """ Called when any key is released.
426 449 """
427 450 if event.KeyCode in (59, ord('.')):
428 451 # Intercepting '.'
429 452 event.Skip()
430 self._popup_completion(create=True)
453 wx.CallAfter(self._popup_completion, create=True)
431 454 else:
432 455 ConsoleWidget._on_key_up(self, event, skip=skip)
433 456
434 457
435 458 def _on_enter(self):
436 459 """ Called on return key down, in readline input_state.
437 460 """
438 461 if self.debug:
439 462 print >>sys.__stdout__, repr(self.input_buffer)
440 463 PrefilterFrontEnd._on_enter(self)
441 464
442 465
443 466 #--------------------------------------------------------------------------
444 467 # EditWindow API
445 468 #--------------------------------------------------------------------------
446 469
447 470 def OnUpdateUI(self, event):
448 471 """ Override the OnUpdateUI of the EditWindow class, to prevent
449 472 syntax highlighting both for faster redraw, and for more
450 473 consistent look and feel.
451 474 """
452 475 if not self._input_state == 'readline':
453 476 ConsoleWidget.OnUpdateUI(self, event)
454 477
455 478 #--------------------------------------------------------------------------
456 479 # Private API
457 480 #--------------------------------------------------------------------------
458 481
459 def _end_system_call(self):
460 """ Called at the end of a system call.
461 """
462 self._input_state = 'buffering'
463 self._running_process = False
464
465
466 482 def _buffer_flush(self, event):
467 483 """ Called by the timer to flush the write buffer.
468 484
469 485 This is always called in the mainloop, by the wx timer.
470 486 """
471 487 self._out_buffer_lock.acquire()
472 488 _out_buffer = self._out_buffer
473 489 self._out_buffer = []
474 490 self._out_buffer_lock.release()
475 491 self.write(''.join(_out_buffer), refresh=False)
476 492
477 493
478 494 def _colorize_input_buffer(self):
479 495 """ Keep the input buffer lines at a bright color.
480 496 """
481 497 if not self._input_state in ('readline', 'raw_input'):
482 498 return
483 499 end_line = self.GetCurrentLine()
484 500 if not sys.platform == 'win32':
485 501 end_line += 1
486 502 for i in range(self.current_prompt_line, end_line):
487 503 if i in self._markers:
488 504 self.MarkerDeleteHandle(self._markers[i])
489 505 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
490 506
491 507
492 508 if __name__ == '__main__':
493 509 class MainWindow(wx.Frame):
494 510 def __init__(self, parent, id, title):
495 511 wx.Frame.__init__(self, parent, id, title, size=(300,250))
496 512 self._sizer = wx.BoxSizer(wx.VERTICAL)
497 513 self.shell = WxController(self)
498 514 self._sizer.Add(self.shell, 1, wx.EXPAND)
499 515 self.SetSizer(self._sizer)
500 516 self.SetAutoLayout(1)
501 517 self.Show(True)
502 518
503 519 app = wx.PySimpleApp()
504 520 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
505 521 frame.shell.SetFocus()
506 522 frame.SetSize((680, 460))
507 523 self = frame.shell
508 524
509 525 app.MainLoop()
510 526
@@ -1,2132 +1,2164 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 30 import platform
31 31 import re
32 32 import shlex
33 33 import shutil
34 34 import subprocess
35 35 import sys
36 36 import tempfile
37 37 import time
38 38 import types
39 39 import warnings
40 40
41 41 # Curses and termios are Unix-only modules
42 42 try:
43 43 import curses
44 44 # We need termios as well, so if its import happens to raise, we bail on
45 45 # using curses altogether.
46 46 import termios
47 47 except ImportError:
48 48 USE_CURSES = False
49 49 else:
50 50 # Curses on Solaris may not be complete, so we can't use it there
51 51 USE_CURSES = hasattr(curses,'initscr')
52 52
53 53 # Other IPython utilities
54 54 import IPython
55 55 from IPython.Itpl import Itpl,itpl,printpl
56 56 from IPython import DPyGetOpt, platutils
57 57 from IPython.generics import result_display
58 58 import IPython.ipapi
59 59 from IPython.external.path import path
60 60 if os.name == "nt":
61 61 from IPython.winconsole import get_console_size
62 62
63 63 try:
64 64 set
65 65 except:
66 66 from sets import Set as set
67 67
68 68
69 69 #****************************************************************************
70 70 # Exceptions
71 71 class Error(Exception):
72 72 """Base class for exceptions in this module."""
73 73 pass
74 74
75 75 #----------------------------------------------------------------------------
76 76 class IOStream:
77 77 def __init__(self,stream,fallback):
78 78 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
79 79 stream = fallback
80 80 self.stream = stream
81 81 self._swrite = stream.write
82 82 self.flush = stream.flush
83 83
84 84 def write(self,data):
85 85 try:
86 86 self._swrite(data)
87 87 except:
88 88 try:
89 89 # print handles some unicode issues which may trip a plain
90 90 # write() call. Attempt to emulate write() by using a
91 91 # trailing comma
92 92 print >> self.stream, data,
93 93 except:
94 94 # if we get here, something is seriously broken.
95 95 print >> sys.stderr, \
96 96 'ERROR - failed to write data to stream:', self.stream
97 97
98 98 def close(self):
99 99 pass
100 100
101 101
102 102 class IOTerm:
103 103 """ Term holds the file or file-like objects for handling I/O operations.
104 104
105 105 These are normally just sys.stdin, sys.stdout and sys.stderr but for
106 106 Windows they can can replaced to allow editing the strings before they are
107 107 displayed."""
108 108
109 109 # In the future, having IPython channel all its I/O operations through
110 110 # this class will make it easier to embed it into other environments which
111 111 # are not a normal terminal (such as a GUI-based shell)
112 112 def __init__(self,cin=None,cout=None,cerr=None):
113 113 self.cin = IOStream(cin,sys.stdin)
114 114 self.cout = IOStream(cout,sys.stdout)
115 115 self.cerr = IOStream(cerr,sys.stderr)
116 116
117 117 # Global variable to be used for all I/O
118 118 Term = IOTerm()
119 119
120 120 import IPython.rlineimpl as readline
121 121 # Remake Term to use the readline i/o facilities
122 122 if sys.platform == 'win32' and readline.have_readline:
123 123
124 124 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
125 125
126 126
127 127 #****************************************************************************
128 128 # Generic warning/error printer, used by everything else
129 129 def warn(msg,level=2,exit_val=1):
130 130 """Standard warning printer. Gives formatting consistency.
131 131
132 132 Output is sent to Term.cerr (sys.stderr by default).
133 133
134 134 Options:
135 135
136 136 -level(2): allows finer control:
137 137 0 -> Do nothing, dummy function.
138 138 1 -> Print message.
139 139 2 -> Print 'WARNING:' + message. (Default level).
140 140 3 -> Print 'ERROR:' + message.
141 141 4 -> Print 'FATAL ERROR:' + message and trigger a sys.exit(exit_val).
142 142
143 143 -exit_val (1): exit value returned by sys.exit() for a level 4
144 144 warning. Ignored for all other levels."""
145 145
146 146 if level>0:
147 147 header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
148 148 print >> Term.cerr, '%s%s' % (header[level],msg)
149 149 if level == 4:
150 150 print >> Term.cerr,'Exiting.\n'
151 151 sys.exit(exit_val)
152 152
153 153 def info(msg):
154 154 """Equivalent to warn(msg,level=1)."""
155 155
156 156 warn(msg,level=1)
157 157
158 158 def error(msg):
159 159 """Equivalent to warn(msg,level=3)."""
160 160
161 161 warn(msg,level=3)
162 162
163 163 def fatal(msg,exit_val=1):
164 164 """Equivalent to warn(msg,exit_val=exit_val,level=4)."""
165 165
166 166 warn(msg,exit_val=exit_val,level=4)
167 167
168 168 #---------------------------------------------------------------------------
169 169 # Debugging routines
170 170 #
171 171 def debugx(expr,pre_msg=''):
172 172 """Print the value of an expression from the caller's frame.
173 173
174 174 Takes an expression, evaluates it in the caller's frame and prints both
175 175 the given expression and the resulting value (as well as a debug mark
176 176 indicating the name of the calling function. The input must be of a form
177 177 suitable for eval().
178 178
179 179 An optional message can be passed, which will be prepended to the printed
180 180 expr->value pair."""
181 181
182 182 cf = sys._getframe(1)
183 183 print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
184 184 eval(expr,cf.f_globals,cf.f_locals))
185 185
186 186 # deactivate it by uncommenting the following line, which makes it a no-op
187 187 #def debugx(expr,pre_msg=''): pass
188 188
189 189 #----------------------------------------------------------------------------
190 190 StringTypes = types.StringTypes
191 191
192 192 # Basic timing functionality
193 193
194 194 # If possible (Unix), use the resource module instead of time.clock()
195 195 try:
196 196 import resource
197 197 def clocku():
198 198 """clocku() -> floating point number
199 199
200 200 Return the *USER* CPU time in seconds since the start of the process.
201 201 This is done via a call to resource.getrusage, so it avoids the
202 202 wraparound problems in time.clock()."""
203 203
204 204 return resource.getrusage(resource.RUSAGE_SELF)[0]
205 205
206 206 def clocks():
207 207 """clocks() -> floating point number
208 208
209 209 Return the *SYSTEM* CPU time in seconds since the start of the process.
210 210 This is done via a call to resource.getrusage, so it avoids the
211 211 wraparound problems in time.clock()."""
212 212
213 213 return resource.getrusage(resource.RUSAGE_SELF)[1]
214 214
215 215 def clock():
216 216 """clock() -> floating point number
217 217
218 218 Return the *TOTAL USER+SYSTEM* CPU time in seconds since the start of
219 219 the process. This is done via a call to resource.getrusage, so it
220 220 avoids the wraparound problems in time.clock()."""
221 221
222 222 u,s = resource.getrusage(resource.RUSAGE_SELF)[:2]
223 223 return u+s
224 224
225 225 def clock2():
226 226 """clock2() -> (t_user,t_system)
227 227
228 228 Similar to clock(), but return a tuple of user/system times."""
229 229 return resource.getrusage(resource.RUSAGE_SELF)[:2]
230 230
231 231 except ImportError:
232 232 # There is no distinction of user/system time under windows, so we just use
233 233 # time.clock() for everything...
234 234 clocku = clocks = clock = time.clock
235 235 def clock2():
236 236 """Under windows, system CPU time can't be measured.
237 237
238 238 This just returns clock() and zero."""
239 239 return time.clock(),0.0
240 240
241 241 def timings_out(reps,func,*args,**kw):
242 242 """timings_out(reps,func,*args,**kw) -> (t_total,t_per_call,output)
243 243
244 244 Execute a function reps times, return a tuple with the elapsed total
245 245 CPU time in seconds, the time per call and the function's output.
246 246
247 247 Under Unix, the return value is the sum of user+system time consumed by
248 248 the process, computed via the resource module. This prevents problems
249 249 related to the wraparound effect which the time.clock() function has.
250 250
251 251 Under Windows the return value is in wall clock seconds. See the
252 252 documentation for the time module for more details."""
253 253
254 254 reps = int(reps)
255 255 assert reps >=1, 'reps must be >= 1'
256 256 if reps==1:
257 257 start = clock()
258 258 out = func(*args,**kw)
259 259 tot_time = clock()-start
260 260 else:
261 261 rng = xrange(reps-1) # the last time is executed separately to store output
262 262 start = clock()
263 263 for dummy in rng: func(*args,**kw)
264 264 out = func(*args,**kw) # one last time
265 265 tot_time = clock()-start
266 266 av_time = tot_time / reps
267 267 return tot_time,av_time,out
268 268
269 269 def timings(reps,func,*args,**kw):
270 270 """timings(reps,func,*args,**kw) -> (t_total,t_per_call)
271 271
272 272 Execute a function reps times, return a tuple with the elapsed total CPU
273 273 time in seconds and the time per call. These are just the first two values
274 274 in timings_out()."""
275 275
276 276 return timings_out(reps,func,*args,**kw)[0:2]
277 277
278 278 def timing(func,*args,**kw):
279 279 """timing(func,*args,**kw) -> t_total
280 280
281 281 Execute a function once, return the elapsed total CPU time in
282 282 seconds. This is just the first value in timings_out()."""
283 283
284 284 return timings_out(1,func,*args,**kw)[0]
285 285
286 286 #****************************************************************************
287 287 # file and system
288 288
289 289 def arg_split(s,posix=False):
290 290 """Split a command line's arguments in a shell-like manner.
291 291
292 292 This is a modified version of the standard library's shlex.split()
293 293 function, but with a default of posix=False for splitting, so that quotes
294 294 in inputs are respected."""
295 295
296 296 # XXX - there may be unicode-related problems here!!! I'm not sure that
297 297 # shlex is truly unicode-safe, so it might be necessary to do
298 298 #
299 299 # s = s.encode(sys.stdin.encoding)
300 300 #
301 301 # first, to ensure that shlex gets a normal string. Input from anyone who
302 302 # knows more about unicode and shlex than I would be good to have here...
303 303 lex = shlex.shlex(s, posix=posix)
304 304 lex.whitespace_split = True
305 305 return list(lex)
306 306
307 307 def system(cmd,verbose=0,debug=0,header=''):
308 308 """Execute a system command, return its exit status.
309 309
310 310 Options:
311 311
312 312 - verbose (0): print the command to be executed.
313 313
314 314 - debug (0): only print, do not actually execute.
315 315
316 316 - header (''): Header to print on screen prior to the executed command (it
317 317 is only prepended to the command, no newlines are added).
318 318
319 319 Note: a stateful version of this function is available through the
320 320 SystemExec class."""
321 321
322 322 stat = 0
323 323 if verbose or debug: print header+cmd
324 324 sys.stdout.flush()
325 325 if not debug: stat = os.system(cmd)
326 326 return stat
327 327
328 328 def abbrev_cwd():
329 329 """ Return abbreviated version of cwd, e.g. d:mydir """
330 330 cwd = os.getcwd().replace('\\','/')
331 331 drivepart = ''
332 332 tail = cwd
333 333 if sys.platform == 'win32':
334 334 if len(cwd) < 4:
335 335 return cwd
336 336 drivepart,tail = os.path.splitdrive(cwd)
337 337
338 338
339 339 parts = tail.split('/')
340 340 if len(parts) > 2:
341 341 tail = '/'.join(parts[-2:])
342 342
343 343 return (drivepart + (
344 344 cwd == '/' and '/' or tail))
345 345
346 346
347 347 # This function is used by ipython in a lot of places to make system calls.
348 348 # We need it to be slightly different under win32, due to the vagaries of
349 349 # 'network shares'. A win32 override is below.
350 350
351 351 def shell(cmd,verbose=0,debug=0,header=''):
352 352 """Execute a command in the system shell, always return None.
353 353
354 354 Options:
355 355
356 356 - verbose (0): print the command to be executed.
357 357
358 358 - debug (0): only print, do not actually execute.
359 359
360 360 - header (''): Header to print on screen prior to the executed command (it
361 361 is only prepended to the command, no newlines are added).
362 362
363 363 Note: this is similar to genutils.system(), but it returns None so it can
364 364 be conveniently used in interactive loops without getting the return value
365 365 (typically 0) printed many times."""
366 366
367 367 stat = 0
368 368 if verbose or debug: print header+cmd
369 369 # flush stdout so we don't mangle python's buffering
370 370 sys.stdout.flush()
371 371
372 372 if not debug:
373 373 platutils.set_term_title("IPy " + cmd)
374 374 os.system(cmd)
375 375 platutils.set_term_title("IPy " + abbrev_cwd())
376 376
377 377 # override shell() for win32 to deal with network shares
378 378 if os.name in ('nt','dos'):
379 379
380 380 shell_ori = shell
381 381
382 382 def shell(cmd,verbose=0,debug=0,header=''):
383 383 if os.getcwd().startswith(r"\\"):
384 384 path = os.getcwd()
385 385 # change to c drive (cannot be on UNC-share when issuing os.system,
386 386 # as cmd.exe cannot handle UNC addresses)
387 387 os.chdir("c:")
388 388 # issue pushd to the UNC-share and then run the command
389 389 try:
390 390 shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header)
391 391 finally:
392 392 os.chdir(path)
393 393 else:
394 394 shell_ori(cmd,verbose,debug,header)
395 395
396 396 shell.__doc__ = shell_ori.__doc__
397 397
398 398 def getoutput(cmd,verbose=0,debug=0,header='',split=0):
399 399 """Dummy substitute for perl's backquotes.
400 400
401 401 Executes a command and returns the output.
402 402
403 403 Accepts the same arguments as system(), plus:
404 404
405 405 - split(0): if true, the output is returned as a list split on newlines.
406 406
407 407 Note: a stateful version of this function is available through the
408 408 SystemExec class.
409 409
410 410 This is pretty much deprecated and rarely used,
411 411 genutils.getoutputerror may be what you need.
412 412
413 413 """
414 414
415 415 if verbose or debug: print header+cmd
416 416 if not debug:
417 417 output = os.popen(cmd).read()
418 418 # stipping last \n is here for backwards compat.
419 419 if output.endswith('\n'):
420 420 output = output[:-1]
421 421 if split:
422 422 return output.split('\n')
423 423 else:
424 424 return output
425 425
426 426 def getoutputerror(cmd,verbose=0,debug=0,header='',split=0):
427 427 """Return (standard output,standard error) of executing cmd in a shell.
428 428
429 429 Accepts the same arguments as system(), plus:
430 430
431 431 - split(0): if true, each of stdout/err is returned as a list split on
432 432 newlines.
433 433
434 434 Note: a stateful version of this function is available through the
435 435 SystemExec class."""
436 436
437 437 if verbose or debug: print header+cmd
438 438 if not cmd:
439 439 if split:
440 440 return [],[]
441 441 else:
442 442 return '',''
443 443 if not debug:
444 444 pin,pout,perr = os.popen3(cmd)
445 445 tout = pout.read().rstrip()
446 446 terr = perr.read().rstrip()
447 447 pin.close()
448 448 pout.close()
449 449 perr.close()
450 450 if split:
451 451 return tout.split('\n'),terr.split('\n')
452 452 else:
453 453 return tout,terr
454 454
455 455 # for compatibility with older naming conventions
456 456 xsys = system
457 457 bq = getoutput
458 458
459 459 class SystemExec:
460 460 """Access the system and getoutput functions through a stateful interface.
461 461
462 462 Note: here we refer to the system and getoutput functions from this
463 463 library, not the ones from the standard python library.
464 464
465 465 This class offers the system and getoutput functions as methods, but the
466 466 verbose, debug and header parameters can be set for the instance (at
467 467 creation time or later) so that they don't need to be specified on each
468 468 call.
469 469
470 470 For efficiency reasons, there's no way to override the parameters on a
471 471 per-call basis other than by setting instance attributes. If you need
472 472 local overrides, it's best to directly call system() or getoutput().
473 473
474 474 The following names are provided as alternate options:
475 475 - xsys: alias to system
476 476 - bq: alias to getoutput
477 477
478 478 An instance can then be created as:
479 479 >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ')
480 480 """
481 481
482 482 def __init__(self,verbose=0,debug=0,header='',split=0):
483 483 """Specify the instance's values for verbose, debug and header."""
484 484 setattr_list(self,'verbose debug header split')
485 485
486 486 def system(self,cmd):
487 487 """Stateful interface to system(), with the same keyword parameters."""
488 488
489 489 system(cmd,self.verbose,self.debug,self.header)
490 490
491 491 def shell(self,cmd):
492 492 """Stateful interface to shell(), with the same keyword parameters."""
493 493
494 494 shell(cmd,self.verbose,self.debug,self.header)
495 495
496 496 xsys = system # alias
497 497
498 498 def getoutput(self,cmd):
499 499 """Stateful interface to getoutput()."""
500 500
501 501 return getoutput(cmd,self.verbose,self.debug,self.header,self.split)
502 502
503 503 def getoutputerror(self,cmd):
504 504 """Stateful interface to getoutputerror()."""
505 505
506 506 return getoutputerror(cmd,self.verbose,self.debug,self.header,self.split)
507 507
508 508 bq = getoutput # alias
509 509
510 510 #-----------------------------------------------------------------------------
511 511 def mutex_opts(dict,ex_op):
512 512 """Check for presence of mutually exclusive keys in a dict.
513 513
514 514 Call: mutex_opts(dict,[[op1a,op1b],[op2a,op2b]...]"""
515 515 for op1,op2 in ex_op:
516 516 if op1 in dict and op2 in dict:
517 517 raise ValueError,'\n*** ERROR in Arguments *** '\
518 518 'Options '+op1+' and '+op2+' are mutually exclusive.'
519 519
520 520 #-----------------------------------------------------------------------------
521 521 def get_py_filename(name):
522 522 """Return a valid python filename in the current directory.
523 523
524 524 If the given name is not a file, it adds '.py' and searches again.
525 525 Raises IOError with an informative message if the file isn't found."""
526 526
527 527 name = os.path.expanduser(name)
528 528 if not os.path.isfile(name) and not name.endswith('.py'):
529 529 name += '.py'
530 530 if os.path.isfile(name):
531 531 return name
532 532 else:
533 533 raise IOError,'File `%s` not found.' % name
534 534
535 535 #-----------------------------------------------------------------------------
536 536 def filefind(fname,alt_dirs = None):
537 537 """Return the given filename either in the current directory, if it
538 538 exists, or in a specified list of directories.
539 539
540 540 ~ expansion is done on all file and directory names.
541 541
542 542 Upon an unsuccessful search, raise an IOError exception."""
543 543
544 544 if alt_dirs is None:
545 545 try:
546 546 alt_dirs = get_home_dir()
547 547 except HomeDirError:
548 548 alt_dirs = os.getcwd()
549 549 search = [fname] + list_strings(alt_dirs)
550 550 search = map(os.path.expanduser,search)
551 551 #print 'search list for',fname,'list:',search # dbg
552 552 fname = search[0]
553 553 if os.path.isfile(fname):
554 554 return fname
555 555 for direc in search[1:]:
556 556 testname = os.path.join(direc,fname)
557 557 #print 'testname',testname # dbg
558 558 if os.path.isfile(testname):
559 559 return testname
560 560 raise IOError,'File' + `fname` + \
561 561 ' not found in current or supplied directories:' + `alt_dirs`
562 562
563 563 #----------------------------------------------------------------------------
564 564 def file_read(filename):
565 565 """Read a file and close it. Returns the file source."""
566 566 fobj = open(filename,'r');
567 567 source = fobj.read();
568 568 fobj.close()
569 569 return source
570 570
571 571 def file_readlines(filename):
572 572 """Read a file and close it. Returns the file source using readlines()."""
573 573 fobj = open(filename,'r');
574 574 lines = fobj.readlines();
575 575 fobj.close()
576 576 return lines
577 577
578 578 #----------------------------------------------------------------------------
579 579 def target_outdated(target,deps):
580 580 """Determine whether a target is out of date.
581 581
582 582 target_outdated(target,deps) -> 1/0
583 583
584 584 deps: list of filenames which MUST exist.
585 585 target: single filename which may or may not exist.
586 586
587 587 If target doesn't exist or is older than any file listed in deps, return
588 588 true, otherwise return false.
589 589 """
590 590 try:
591 591 target_time = os.path.getmtime(target)
592 592 except os.error:
593 593 return 1
594 594 for dep in deps:
595 595 dep_time = os.path.getmtime(dep)
596 596 if dep_time > target_time:
597 597 #print "For target",target,"Dep failed:",dep # dbg
598 598 #print "times (dep,tar):",dep_time,target_time # dbg
599 599 return 1
600 600 return 0
601 601
602 602 #-----------------------------------------------------------------------------
603 603 def target_update(target,deps,cmd):
604 604 """Update a target with a given command given a list of dependencies.
605 605
606 606 target_update(target,deps,cmd) -> runs cmd if target is outdated.
607 607
608 608 This is just a wrapper around target_outdated() which calls the given
609 609 command if target is outdated."""
610 610
611 611 if target_outdated(target,deps):
612 612 xsys(cmd)
613 613
614 614 #----------------------------------------------------------------------------
615 615 def unquote_ends(istr):
616 616 """Remove a single pair of quotes from the endpoints of a string."""
617 617
618 618 if not istr:
619 619 return istr
620 620 if (istr[0]=="'" and istr[-1]=="'") or \
621 621 (istr[0]=='"' and istr[-1]=='"'):
622 622 return istr[1:-1]
623 623 else:
624 624 return istr
625 625
626 626 #----------------------------------------------------------------------------
627 627 def process_cmdline(argv,names=[],defaults={},usage=''):
628 628 """ Process command-line options and arguments.
629 629
630 630 Arguments:
631 631
632 632 - argv: list of arguments, typically sys.argv.
633 633
634 634 - names: list of option names. See DPyGetOpt docs for details on options
635 635 syntax.
636 636
637 637 - defaults: dict of default values.
638 638
639 639 - usage: optional usage notice to print if a wrong argument is passed.
640 640
641 641 Return a dict of options and a list of free arguments."""
642 642
643 643 getopt = DPyGetOpt.DPyGetOpt()
644 644 getopt.setIgnoreCase(0)
645 645 getopt.parseConfiguration(names)
646 646
647 647 try:
648 648 getopt.processArguments(argv)
649 649 except DPyGetOpt.ArgumentError, exc:
650 650 print usage
651 651 warn('"%s"' % exc,level=4)
652 652
653 653 defaults.update(getopt.optionValues)
654 654 args = getopt.freeValues
655 655
656 656 return defaults,args
657 657
658 658 #----------------------------------------------------------------------------
659 659 def optstr2types(ostr):
660 660 """Convert a string of option names to a dict of type mappings.
661 661
662 662 optstr2types(str) -> {None:'string_opts',int:'int_opts',float:'float_opts'}
663 663
664 664 This is used to get the types of all the options in a string formatted
665 665 with the conventions of DPyGetOpt. The 'type' None is used for options
666 666 which are strings (they need no further conversion). This function's main
667 667 use is to get a typemap for use with read_dict().
668 668 """
669 669
670 670 typeconv = {None:'',int:'',float:''}
671 671 typemap = {'s':None,'i':int,'f':float}
672 672 opt_re = re.compile(r'([\w]*)([^:=]*:?=?)([sif]?)')
673 673
674 674 for w in ostr.split():
675 675 oname,alias,otype = opt_re.match(w).groups()
676 676 if otype == '' or alias == '!': # simple switches are integers too
677 677 otype = 'i'
678 678 typeconv[typemap[otype]] += oname + ' '
679 679 return typeconv
680 680
681 681 #----------------------------------------------------------------------------
682 682 def read_dict(filename,type_conv=None,**opt):
683 683 r"""Read a dictionary of key=value pairs from an input file, optionally
684 684 performing conversions on the resulting values.
685 685
686 686 read_dict(filename,type_conv,**opt) -> dict
687 687
688 688 Only one value per line is accepted, the format should be
689 689 # optional comments are ignored
690 690 key value\n
691 691
692 692 Args:
693 693
694 694 - type_conv: A dictionary specifying which keys need to be converted to
695 695 which types. By default all keys are read as strings. This dictionary
696 696 should have as its keys valid conversion functions for strings
697 697 (int,long,float,complex, or your own). The value for each key
698 698 (converter) should be a whitespace separated string containing the names
699 699 of all the entries in the file to be converted using that function. For
700 700 keys to be left alone, use None as the conversion function (only needed
701 701 with purge=1, see below).
702 702
703 703 - opt: dictionary with extra options as below (default in parens)
704 704
705 705 purge(0): if set to 1, all keys *not* listed in type_conv are purged out
706 706 of the dictionary to be returned. If purge is going to be used, the
707 707 set of keys to be left as strings also has to be explicitly specified
708 708 using the (non-existent) conversion function None.
709 709
710 710 fs(None): field separator. This is the key/value separator to be used
711 711 when parsing the file. The None default means any whitespace [behavior
712 712 of string.split()].
713 713
714 714 strip(0): if 1, strip string values of leading/trailinig whitespace.
715 715
716 716 warn(1): warning level if requested keys are not found in file.
717 717 - 0: silently ignore.
718 718 - 1: inform but proceed.
719 719 - 2: raise KeyError exception.
720 720
721 721 no_empty(0): if 1, remove keys with whitespace strings as a value.
722 722
723 723 unique([]): list of keys (or space separated string) which can't be
724 724 repeated. If one such key is found in the file, each new instance
725 725 overwrites the previous one. For keys not listed here, the behavior is
726 726 to make a list of all appearances.
727 727
728 728 Example:
729 729
730 730 If the input file test.ini contains (we put it in a string to keep the test
731 731 self-contained):
732 732
733 733 >>> test_ini = '''\
734 734 ... i 3
735 735 ... x 4.5
736 736 ... y 5.5
737 737 ... s hi ho'''
738 738
739 739 Then we can use it as follows:
740 740 >>> type_conv={int:'i',float:'x',None:'s'}
741 741
742 742 >>> d = read_dict(test_ini)
743 743
744 744 >>> sorted(d.items())
745 745 [('i', '3'), ('s', 'hi ho'), ('x', '4.5'), ('y', '5.5')]
746 746
747 747 >>> d = read_dict(test_ini,type_conv)
748 748
749 749 >>> sorted(d.items())
750 750 [('i', 3), ('s', 'hi ho'), ('x', 4.5), ('y', '5.5')]
751 751
752 752 >>> d = read_dict(test_ini,type_conv,purge=True)
753 753
754 754 >>> sorted(d.items())
755 755 [('i', 3), ('s', 'hi ho'), ('x', 4.5)]
756 756 """
757 757
758 758 # starting config
759 759 opt.setdefault('purge',0)
760 760 opt.setdefault('fs',None) # field sep defaults to any whitespace
761 761 opt.setdefault('strip',0)
762 762 opt.setdefault('warn',1)
763 763 opt.setdefault('no_empty',0)
764 764 opt.setdefault('unique','')
765 765 if type(opt['unique']) in StringTypes:
766 766 unique_keys = qw(opt['unique'])
767 767 elif type(opt['unique']) in (types.TupleType,types.ListType):
768 768 unique_keys = opt['unique']
769 769 else:
770 770 raise ValueError, 'Unique keys must be given as a string, List or Tuple'
771 771
772 772 dict = {}
773 773
774 774 # first read in table of values as strings
775 775 if '\n' in filename:
776 776 lines = filename.splitlines()
777 777 file = None
778 778 else:
779 779 file = open(filename,'r')
780 780 lines = file.readlines()
781 781 for line in lines:
782 782 line = line.strip()
783 783 if len(line) and line[0]=='#': continue
784 784 if len(line)>0:
785 785 lsplit = line.split(opt['fs'],1)
786 786 try:
787 787 key,val = lsplit
788 788 except ValueError:
789 789 key,val = lsplit[0],''
790 790 key = key.strip()
791 791 if opt['strip']: val = val.strip()
792 792 if val == "''" or val == '""': val = ''
793 793 if opt['no_empty'] and (val=='' or val.isspace()):
794 794 continue
795 795 # if a key is found more than once in the file, build a list
796 796 # unless it's in the 'unique' list. In that case, last found in file
797 797 # takes precedence. User beware.
798 798 try:
799 799 if dict[key] and key in unique_keys:
800 800 dict[key] = val
801 801 elif type(dict[key]) is types.ListType:
802 802 dict[key].append(val)
803 803 else:
804 804 dict[key] = [dict[key],val]
805 805 except KeyError:
806 806 dict[key] = val
807 807 # purge if requested
808 808 if opt['purge']:
809 809 accepted_keys = qwflat(type_conv.values())
810 810 for key in dict.keys():
811 811 if key in accepted_keys: continue
812 812 del(dict[key])
813 813 # now convert if requested
814 814 if type_conv==None: return dict
815 815 conversions = type_conv.keys()
816 816 try: conversions.remove(None)
817 817 except: pass
818 818 for convert in conversions:
819 819 for val in qw(type_conv[convert]):
820 820 try:
821 821 dict[val] = convert(dict[val])
822 822 except KeyError,e:
823 823 if opt['warn'] == 0:
824 824 pass
825 825 elif opt['warn'] == 1:
826 826 print >>sys.stderr, 'Warning: key',val,\
827 827 'not found in file',filename
828 828 elif opt['warn'] == 2:
829 829 raise KeyError,e
830 830 else:
831 831 raise ValueError,'Warning level must be 0,1 or 2'
832 832
833 833 return dict
834 834
835 835 #----------------------------------------------------------------------------
836 836 def flag_calls(func):
837 837 """Wrap a function to detect and flag when it gets called.
838 838
839 839 This is a decorator which takes a function and wraps it in a function with
840 840 a 'called' attribute. wrapper.called is initialized to False.
841 841
842 842 The wrapper.called attribute is set to False right before each call to the
843 843 wrapped function, so if the call fails it remains False. After the call
844 844 completes, wrapper.called is set to True and the output is returned.
845 845
846 846 Testing for truth in wrapper.called allows you to determine if a call to
847 847 func() was attempted and succeeded."""
848 848
849 849 def wrapper(*args,**kw):
850 850 wrapper.called = False
851 851 out = func(*args,**kw)
852 852 wrapper.called = True
853 853 return out
854 854
855 855 wrapper.called = False
856 856 wrapper.__doc__ = func.__doc__
857 857 return wrapper
858 858
859 859 #----------------------------------------------------------------------------
860 860 def dhook_wrap(func,*a,**k):
861 861 """Wrap a function call in a sys.displayhook controller.
862 862
863 863 Returns a wrapper around func which calls func, with all its arguments and
864 864 keywords unmodified, using the default sys.displayhook. Since IPython
865 865 modifies sys.displayhook, it breaks the behavior of certain systems that
866 866 rely on the default behavior, notably doctest.
867 867 """
868 868
869 869 def f(*a,**k):
870 870
871 871 dhook_s = sys.displayhook
872 872 sys.displayhook = sys.__displayhook__
873 873 try:
874 874 out = func(*a,**k)
875 875 finally:
876 876 sys.displayhook = dhook_s
877 877
878 878 return out
879 879
880 880 f.__doc__ = func.__doc__
881 881 return f
882 882
883 883 #----------------------------------------------------------------------------
884 884 def doctest_reload():
885 885 """Properly reload doctest to reuse it interactively.
886 886
887 887 This routine:
888 888
889 889 - reloads doctest
890 890
891 891 - resets its global 'master' attribute to None, so that multiple uses of
892 892 the module interactively don't produce cumulative reports.
893 893
894 894 - Monkeypatches its core test runner method to protect it from IPython's
895 895 modified displayhook. Doctest expects the default displayhook behavior
896 896 deep down, so our modification breaks it completely. For this reason, a
897 897 hard monkeypatch seems like a reasonable solution rather than asking
898 898 users to manually use a different doctest runner when under IPython."""
899 899
900 900 import doctest
901 901 reload(doctest)
902 902 doctest.master=None
903 903
904 904 try:
905 905 doctest.DocTestRunner
906 906 except AttributeError:
907 907 # This is only for python 2.3 compatibility, remove once we move to
908 908 # 2.4 only.
909 909 pass
910 910 else:
911 911 doctest.DocTestRunner.run = dhook_wrap(doctest.DocTestRunner.run)
912 912
913 913 #----------------------------------------------------------------------------
914 914 class HomeDirError(Error):
915 915 pass
916 916
917 917 def get_home_dir():
918 918 """Return the closest possible equivalent to a 'home' directory.
919 919
920 920 We first try $HOME. Absent that, on NT it's $HOMEDRIVE\$HOMEPATH.
921 921
922 922 Currently only Posix and NT are implemented, a HomeDirError exception is
923 923 raised for all other OSes. """
924 924
925 925 isdir = os.path.isdir
926 926 env = os.environ
927 927
928 928 # first, check py2exe distribution root directory for _ipython.
929 929 # This overrides all. Normally does not exist.
930 930
931 931 if '\\library.zip\\' in IPython.__file__.lower():
932 932 root, rest = IPython.__file__.lower().split('library.zip')
933 933 if isdir(root + '_ipython'):
934 934 os.environ["IPYKITROOT"] = root.rstrip('\\')
935 935 return root
936 936
937 937 try:
938 938 homedir = env['HOME']
939 939 if not isdir(homedir):
940 940 # in case a user stuck some string which does NOT resolve to a
941 941 # valid path, it's as good as if we hadn't foud it
942 942 raise KeyError
943 943 return homedir
944 944 except KeyError:
945 945 if os.name == 'posix':
946 946 raise HomeDirError,'undefined $HOME, IPython can not proceed.'
947 947 elif os.name == 'nt':
948 948 # For some strange reason, win9x returns 'nt' for os.name.
949 949 try:
950 950 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
951 951 if not isdir(homedir):
952 952 homedir = os.path.join(env['USERPROFILE'])
953 953 if not isdir(homedir):
954 954 raise HomeDirError
955 955 return homedir
956 956 except:
957 957 try:
958 958 # Use the registry to get the 'My Documents' folder.
959 959 import _winreg as wreg
960 960 key = wreg.OpenKey(wreg.HKEY_CURRENT_USER,
961 961 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
962 962 homedir = wreg.QueryValueEx(key,'Personal')[0]
963 963 key.Close()
964 964 if not isdir(homedir):
965 965 e = ('Invalid "Personal" folder registry key '
966 966 'typically "My Documents".\n'
967 967 'Value: %s\n'
968 968 'This is not a valid directory on your system.' %
969 969 homedir)
970 970 raise HomeDirError(e)
971 971 return homedir
972 972 except HomeDirError:
973 973 raise
974 974 except:
975 975 return 'C:\\'
976 976 elif os.name == 'dos':
977 977 # Desperate, may do absurd things in classic MacOS. May work under DOS.
978 978 return 'C:\\'
979 979 else:
980 980 raise HomeDirError,'support for your operating system not implemented.'
981 981
982
983 def get_ipython_dir():
984 """Get the IPython directory for this platform and user.
985
986 This uses the logic in `get_home_dir` to find the home directory
987 and the adds either .ipython or _ipython to the end of the path.
988 """
989 if os.name == 'posix':
990 ipdir_def = '.ipython'
991 else:
992 ipdir_def = '_ipython'
993 home_dir = get_home_dir()
994 ipdir = os.path.abspath(os.environ.get('IPYTHONDIR',
995 os.path.join(home_dir,ipdir_def)))
996 return ipdir
997
998 def get_security_dir():
999 """Get the IPython security directory.
1000
1001 This directory is the default location for all security related files,
1002 including SSL/TLS certificates and FURL files.
1003
1004 If the directory does not exist, it is created with 0700 permissions.
1005 If it exists, permissions are set to 0700.
1006 """
1007 security_dir = os.path.join(get_ipython_dir(), 'security')
1008 if not os.path.isdir(security_dir):
1009 os.mkdir(security_dir, 0700)
1010 else:
1011 os.chmod(security_dir, 0700)
1012 return security_dir
1013
982 1014 #****************************************************************************
983 1015 # strings and text
984 1016
985 1017 class LSString(str):
986 1018 """String derivative with a special access attributes.
987 1019
988 1020 These are normal strings, but with the special attributes:
989 1021
990 1022 .l (or .list) : value as list (split on newlines).
991 1023 .n (or .nlstr): original value (the string itself).
992 1024 .s (or .spstr): value as whitespace-separated string.
993 1025 .p (or .paths): list of path objects
994 1026
995 1027 Any values which require transformations are computed only once and
996 1028 cached.
997 1029
998 1030 Such strings are very useful to efficiently interact with the shell, which
999 1031 typically only understands whitespace-separated options for commands."""
1000 1032
1001 1033 def get_list(self):
1002 1034 try:
1003 1035 return self.__list
1004 1036 except AttributeError:
1005 1037 self.__list = self.split('\n')
1006 1038 return self.__list
1007 1039
1008 1040 l = list = property(get_list)
1009 1041
1010 1042 def get_spstr(self):
1011 1043 try:
1012 1044 return self.__spstr
1013 1045 except AttributeError:
1014 1046 self.__spstr = self.replace('\n',' ')
1015 1047 return self.__spstr
1016 1048
1017 1049 s = spstr = property(get_spstr)
1018 1050
1019 1051 def get_nlstr(self):
1020 1052 return self
1021 1053
1022 1054 n = nlstr = property(get_nlstr)
1023 1055
1024 1056 def get_paths(self):
1025 1057 try:
1026 1058 return self.__paths
1027 1059 except AttributeError:
1028 1060 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
1029 1061 return self.__paths
1030 1062
1031 1063 p = paths = property(get_paths)
1032 1064
1033 1065 def print_lsstring(arg):
1034 1066 """ Prettier (non-repr-like) and more informative printer for LSString """
1035 1067 print "LSString (.p, .n, .l, .s available). Value:"
1036 1068 print arg
1037 1069
1038 1070 print_lsstring = result_display.when_type(LSString)(print_lsstring)
1039 1071
1040 1072 #----------------------------------------------------------------------------
1041 1073 class SList(list):
1042 1074 """List derivative with a special access attributes.
1043 1075
1044 1076 These are normal lists, but with the special attributes:
1045 1077
1046 1078 .l (or .list) : value as list (the list itself).
1047 1079 .n (or .nlstr): value as a string, joined on newlines.
1048 1080 .s (or .spstr): value as a string, joined on spaces.
1049 1081 .p (or .paths): list of path objects
1050 1082
1051 1083 Any values which require transformations are computed only once and
1052 1084 cached."""
1053 1085
1054 1086 def get_list(self):
1055 1087 return self
1056 1088
1057 1089 l = list = property(get_list)
1058 1090
1059 1091 def get_spstr(self):
1060 1092 try:
1061 1093 return self.__spstr
1062 1094 except AttributeError:
1063 1095 self.__spstr = ' '.join(self)
1064 1096 return self.__spstr
1065 1097
1066 1098 s = spstr = property(get_spstr)
1067 1099
1068 1100 def get_nlstr(self):
1069 1101 try:
1070 1102 return self.__nlstr
1071 1103 except AttributeError:
1072 1104 self.__nlstr = '\n'.join(self)
1073 1105 return self.__nlstr
1074 1106
1075 1107 n = nlstr = property(get_nlstr)
1076 1108
1077 1109 def get_paths(self):
1078 1110 try:
1079 1111 return self.__paths
1080 1112 except AttributeError:
1081 1113 self.__paths = [path(p) for p in self if os.path.exists(p)]
1082 1114 return self.__paths
1083 1115
1084 1116 p = paths = property(get_paths)
1085 1117
1086 1118 def grep(self, pattern, prune = False, field = None):
1087 1119 """ Return all strings matching 'pattern' (a regex or callable)
1088 1120
1089 1121 This is case-insensitive. If prune is true, return all items
1090 1122 NOT matching the pattern.
1091 1123
1092 1124 If field is specified, the match must occur in the specified
1093 1125 whitespace-separated field.
1094 1126
1095 1127 Examples::
1096 1128
1097 1129 a.grep( lambda x: x.startswith('C') )
1098 1130 a.grep('Cha.*log', prune=1)
1099 1131 a.grep('chm', field=-1)
1100 1132 """
1101 1133
1102 1134 def match_target(s):
1103 1135 if field is None:
1104 1136 return s
1105 1137 parts = s.split()
1106 1138 try:
1107 1139 tgt = parts[field]
1108 1140 return tgt
1109 1141 except IndexError:
1110 1142 return ""
1111 1143
1112 1144 if isinstance(pattern, basestring):
1113 1145 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
1114 1146 else:
1115 1147 pred = pattern
1116 1148 if not prune:
1117 1149 return SList([el for el in self if pred(match_target(el))])
1118 1150 else:
1119 1151 return SList([el for el in self if not pred(match_target(el))])
1120 1152 def fields(self, *fields):
1121 1153 """ Collect whitespace-separated fields from string list
1122 1154
1123 1155 Allows quick awk-like usage of string lists.
1124 1156
1125 1157 Example data (in var a, created by 'a = !ls -l')::
1126 1158 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
1127 1159 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
1128 1160
1129 1161 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
1130 1162 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
1131 1163 (note the joining by space).
1132 1164 a.fields(-1) is ['ChangeLog', 'IPython']
1133 1165
1134 1166 IndexErrors are ignored.
1135 1167
1136 1168 Without args, fields() just split()'s the strings.
1137 1169 """
1138 1170 if len(fields) == 0:
1139 1171 return [el.split() for el in self]
1140 1172
1141 1173 res = SList()
1142 1174 for el in [f.split() for f in self]:
1143 1175 lineparts = []
1144 1176
1145 1177 for fd in fields:
1146 1178 try:
1147 1179 lineparts.append(el[fd])
1148 1180 except IndexError:
1149 1181 pass
1150 1182 if lineparts:
1151 1183 res.append(" ".join(lineparts))
1152 1184
1153 1185 return res
1154 1186 def sort(self,field= None, nums = False):
1155 1187 """ sort by specified fields (see fields())
1156 1188
1157 1189 Example::
1158 1190 a.sort(1, nums = True)
1159 1191
1160 1192 Sorts a by second field, in numerical order (so that 21 > 3)
1161 1193
1162 1194 """
1163 1195
1164 1196 #decorate, sort, undecorate
1165 1197 if field is not None:
1166 1198 dsu = [[SList([line]).fields(field), line] for line in self]
1167 1199 else:
1168 1200 dsu = [[line, line] for line in self]
1169 1201 if nums:
1170 1202 for i in range(len(dsu)):
1171 1203 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
1172 1204 try:
1173 1205 n = int(numstr)
1174 1206 except ValueError:
1175 1207 n = 0;
1176 1208 dsu[i][0] = n
1177 1209
1178 1210
1179 1211 dsu.sort()
1180 1212 return SList([t[1] for t in dsu])
1181 1213
1182 1214 def print_slist(arg):
1183 1215 """ Prettier (non-repr-like) and more informative printer for SList """
1184 1216 print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
1185 1217 if hasattr(arg, 'hideonce') and arg.hideonce:
1186 1218 arg.hideonce = False
1187 1219 return
1188 1220
1189 1221 nlprint(arg)
1190 1222
1191 1223 print_slist = result_display.when_type(SList)(print_slist)
1192 1224
1193 1225
1194 1226
1195 1227 #----------------------------------------------------------------------------
1196 1228 def esc_quotes(strng):
1197 1229 """Return the input string with single and double quotes escaped out"""
1198 1230
1199 1231 return strng.replace('"','\\"').replace("'","\\'")
1200 1232
1201 1233 #----------------------------------------------------------------------------
1202 1234 def make_quoted_expr(s):
1203 1235 """Return string s in appropriate quotes, using raw string if possible.
1204 1236
1205 1237 Effectively this turns string: cd \ao\ao\
1206 1238 to: r"cd \ao\ao\_"[:-1]
1207 1239
1208 1240 Note the use of raw string and padding at the end to allow trailing backslash.
1209 1241
1210 1242 """
1211 1243
1212 1244 tail = ''
1213 1245 tailpadding = ''
1214 1246 raw = ''
1215 1247 if "\\" in s:
1216 1248 raw = 'r'
1217 1249 if s.endswith('\\'):
1218 1250 tail = '[:-1]'
1219 1251 tailpadding = '_'
1220 1252 if '"' not in s:
1221 1253 quote = '"'
1222 1254 elif "'" not in s:
1223 1255 quote = "'"
1224 1256 elif '"""' not in s and not s.endswith('"'):
1225 1257 quote = '"""'
1226 1258 elif "'''" not in s and not s.endswith("'"):
1227 1259 quote = "'''"
1228 1260 else:
1229 1261 # give up, backslash-escaped string will do
1230 1262 return '"%s"' % esc_quotes(s)
1231 1263 res = raw + quote + s + tailpadding + quote + tail
1232 1264 return res
1233 1265
1234 1266
1235 1267 #----------------------------------------------------------------------------
1236 1268 def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):
1237 1269 """Take multiple lines of input.
1238 1270
1239 1271 A list with each line of input as a separate element is returned when a
1240 1272 termination string is entered (defaults to a single '.'). Input can also
1241 1273 terminate via EOF (^D in Unix, ^Z-RET in Windows).
1242 1274
1243 1275 Lines of input which end in \\ are joined into single entries (and a
1244 1276 secondary continuation prompt is issued as long as the user terminates
1245 1277 lines with \\). This allows entering very long strings which are still
1246 1278 meant to be treated as single entities.
1247 1279 """
1248 1280
1249 1281 try:
1250 1282 if header:
1251 1283 header += '\n'
1252 1284 lines = [raw_input(header + ps1)]
1253 1285 except EOFError:
1254 1286 return []
1255 1287 terminate = [terminate_str]
1256 1288 try:
1257 1289 while lines[-1:] != terminate:
1258 1290 new_line = raw_input(ps1)
1259 1291 while new_line.endswith('\\'):
1260 1292 new_line = new_line[:-1] + raw_input(ps2)
1261 1293 lines.append(new_line)
1262 1294
1263 1295 return lines[:-1] # don't return the termination command
1264 1296 except EOFError:
1265 1297 print
1266 1298 return lines
1267 1299
1268 1300 #----------------------------------------------------------------------------
1269 1301 def raw_input_ext(prompt='', ps2='... '):
1270 1302 """Similar to raw_input(), but accepts extended lines if input ends with \\."""
1271 1303
1272 1304 line = raw_input(prompt)
1273 1305 while line.endswith('\\'):
1274 1306 line = line[:-1] + raw_input(ps2)
1275 1307 return line
1276 1308
1277 1309 #----------------------------------------------------------------------------
1278 1310 def ask_yes_no(prompt,default=None):
1279 1311 """Asks a question and returns a boolean (y/n) answer.
1280 1312
1281 1313 If default is given (one of 'y','n'), it is used if the user input is
1282 1314 empty. Otherwise the question is repeated until an answer is given.
1283 1315
1284 1316 An EOF is treated as the default answer. If there is no default, an
1285 1317 exception is raised to prevent infinite loops.
1286 1318
1287 1319 Valid answers are: y/yes/n/no (match is not case sensitive)."""
1288 1320
1289 1321 answers = {'y':True,'n':False,'yes':True,'no':False}
1290 1322 ans = None
1291 1323 while ans not in answers.keys():
1292 1324 try:
1293 1325 ans = raw_input(prompt+' ').lower()
1294 1326 if not ans: # response was an empty string
1295 1327 ans = default
1296 1328 except KeyboardInterrupt:
1297 1329 pass
1298 1330 except EOFError:
1299 1331 if default in answers.keys():
1300 1332 ans = default
1301 1333 print
1302 1334 else:
1303 1335 raise
1304 1336
1305 1337 return answers[ans]
1306 1338
1307 1339 #----------------------------------------------------------------------------
1308 1340 def marquee(txt='',width=78,mark='*'):
1309 1341 """Return the input string centered in a 'marquee'."""
1310 1342 if not txt:
1311 1343 return (mark*width)[:width]
1312 1344 nmark = (width-len(txt)-2)/len(mark)/2
1313 1345 if nmark < 0: nmark =0
1314 1346 marks = mark*nmark
1315 1347 return '%s %s %s' % (marks,txt,marks)
1316 1348
1317 1349 #----------------------------------------------------------------------------
1318 1350 class EvalDict:
1319 1351 """
1320 1352 Emulate a dict which evaluates its contents in the caller's frame.
1321 1353
1322 1354 Usage:
1323 1355 >>> number = 19
1324 1356
1325 1357 >>> text = "python"
1326 1358
1327 1359 >>> print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
1328 1360 Python 2.1 rules!
1329 1361 """
1330 1362
1331 1363 # This version is due to sismex01@hebmex.com on c.l.py, and is basically a
1332 1364 # modified (shorter) version of:
1333 1365 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66018 by
1334 1366 # Skip Montanaro (skip@pobox.com).
1335 1367
1336 1368 def __getitem__(self, name):
1337 1369 frame = sys._getframe(1)
1338 1370 return eval(name, frame.f_globals, frame.f_locals)
1339 1371
1340 1372 EvalString = EvalDict # for backwards compatibility
1341 1373 #----------------------------------------------------------------------------
1342 1374 def qw(words,flat=0,sep=None,maxsplit=-1):
1343 1375 """Similar to Perl's qw() operator, but with some more options.
1344 1376
1345 1377 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
1346 1378
1347 1379 words can also be a list itself, and with flat=1, the output will be
1348 1380 recursively flattened.
1349 1381
1350 1382 Examples:
1351 1383
1352 1384 >>> qw('1 2')
1353 1385 ['1', '2']
1354 1386
1355 1387 >>> qw(['a b','1 2',['m n','p q']])
1356 1388 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
1357 1389
1358 1390 >>> qw(['a b','1 2',['m n','p q']],flat=1)
1359 1391 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
1360 1392 """
1361 1393
1362 1394 if type(words) in StringTypes:
1363 1395 return [word.strip() for word in words.split(sep,maxsplit)
1364 1396 if word and not word.isspace() ]
1365 1397 if flat:
1366 1398 return flatten(map(qw,words,[1]*len(words)))
1367 1399 return map(qw,words)
1368 1400
1369 1401 #----------------------------------------------------------------------------
1370 1402 def qwflat(words,sep=None,maxsplit=-1):
1371 1403 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
1372 1404 return qw(words,1,sep,maxsplit)
1373 1405
1374 1406 #----------------------------------------------------------------------------
1375 1407 def qw_lol(indata):
1376 1408 """qw_lol('a b') -> [['a','b']],
1377 1409 otherwise it's just a call to qw().
1378 1410
1379 1411 We need this to make sure the modules_some keys *always* end up as a
1380 1412 list of lists."""
1381 1413
1382 1414 if type(indata) in StringTypes:
1383 1415 return [qw(indata)]
1384 1416 else:
1385 1417 return qw(indata)
1386 1418
1387 1419 #-----------------------------------------------------------------------------
1388 1420 def list_strings(arg):
1389 1421 """Always return a list of strings, given a string or list of strings
1390 1422 as input."""
1391 1423
1392 1424 if type(arg) in StringTypes: return [arg]
1393 1425 else: return arg
1394 1426
1395 1427 #----------------------------------------------------------------------------
1396 1428 def grep(pat,list,case=1):
1397 1429 """Simple minded grep-like function.
1398 1430 grep(pat,list) returns occurrences of pat in list, None on failure.
1399 1431
1400 1432 It only does simple string matching, with no support for regexps. Use the
1401 1433 option case=0 for case-insensitive matching."""
1402 1434
1403 1435 # This is pretty crude. At least it should implement copying only references
1404 1436 # to the original data in case it's big. Now it copies the data for output.
1405 1437 out=[]
1406 1438 if case:
1407 1439 for term in list:
1408 1440 if term.find(pat)>-1: out.append(term)
1409 1441 else:
1410 1442 lpat=pat.lower()
1411 1443 for term in list:
1412 1444 if term.lower().find(lpat)>-1: out.append(term)
1413 1445
1414 1446 if len(out): return out
1415 1447 else: return None
1416 1448
1417 1449 #----------------------------------------------------------------------------
1418 1450 def dgrep(pat,*opts):
1419 1451 """Return grep() on dir()+dir(__builtins__).
1420 1452
1421 1453 A very common use of grep() when working interactively."""
1422 1454
1423 1455 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
1424 1456
1425 1457 #----------------------------------------------------------------------------
1426 1458 def idgrep(pat):
1427 1459 """Case-insensitive dgrep()"""
1428 1460
1429 1461 return dgrep(pat,0)
1430 1462
1431 1463 #----------------------------------------------------------------------------
1432 1464 def igrep(pat,list):
1433 1465 """Synonym for case-insensitive grep."""
1434 1466
1435 1467 return grep(pat,list,case=0)
1436 1468
1437 1469 #----------------------------------------------------------------------------
1438 1470 def indent(str,nspaces=4,ntabs=0):
1439 1471 """Indent a string a given number of spaces or tabstops.
1440 1472
1441 1473 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
1442 1474 """
1443 1475 if str is None:
1444 1476 return
1445 1477 ind = '\t'*ntabs+' '*nspaces
1446 1478 outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
1447 1479 if outstr.endswith(os.linesep+ind):
1448 1480 return outstr[:-len(ind)]
1449 1481 else:
1450 1482 return outstr
1451 1483
1452 1484 #-----------------------------------------------------------------------------
1453 1485 def native_line_ends(filename,backup=1):
1454 1486 """Convert (in-place) a file to line-ends native to the current OS.
1455 1487
1456 1488 If the optional backup argument is given as false, no backup of the
1457 1489 original file is left. """
1458 1490
1459 1491 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
1460 1492
1461 1493 bak_filename = filename + backup_suffixes[os.name]
1462 1494
1463 1495 original = open(filename).read()
1464 1496 shutil.copy2(filename,bak_filename)
1465 1497 try:
1466 1498 new = open(filename,'wb')
1467 1499 new.write(os.linesep.join(original.splitlines()))
1468 1500 new.write(os.linesep) # ALWAYS put an eol at the end of the file
1469 1501 new.close()
1470 1502 except:
1471 1503 os.rename(bak_filename,filename)
1472 1504 if not backup:
1473 1505 try:
1474 1506 os.remove(bak_filename)
1475 1507 except:
1476 1508 pass
1477 1509
1478 1510 #----------------------------------------------------------------------------
1479 1511 def get_pager_cmd(pager_cmd = None):
1480 1512 """Return a pager command.
1481 1513
1482 1514 Makes some attempts at finding an OS-correct one."""
1483 1515
1484 1516 if os.name == 'posix':
1485 1517 default_pager_cmd = 'less -r' # -r for color control sequences
1486 1518 elif os.name in ['nt','dos']:
1487 1519 default_pager_cmd = 'type'
1488 1520
1489 1521 if pager_cmd is None:
1490 1522 try:
1491 1523 pager_cmd = os.environ['PAGER']
1492 1524 except:
1493 1525 pager_cmd = default_pager_cmd
1494 1526 return pager_cmd
1495 1527
1496 1528 #-----------------------------------------------------------------------------
1497 1529 def get_pager_start(pager,start):
1498 1530 """Return the string for paging files with an offset.
1499 1531
1500 1532 This is the '+N' argument which less and more (under Unix) accept.
1501 1533 """
1502 1534
1503 1535 if pager in ['less','more']:
1504 1536 if start:
1505 1537 start_string = '+' + str(start)
1506 1538 else:
1507 1539 start_string = ''
1508 1540 else:
1509 1541 start_string = ''
1510 1542 return start_string
1511 1543
1512 1544 #----------------------------------------------------------------------------
1513 1545 # (X)emacs on W32 doesn't like to be bypassed with msvcrt.getch()
1514 1546 if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
1515 1547 import msvcrt
1516 1548 def page_more():
1517 1549 """ Smart pausing between pages
1518 1550
1519 1551 @return: True if need print more lines, False if quit
1520 1552 """
1521 1553 Term.cout.write('---Return to continue, q to quit--- ')
1522 1554 ans = msvcrt.getch()
1523 1555 if ans in ("q", "Q"):
1524 1556 result = False
1525 1557 else:
1526 1558 result = True
1527 1559 Term.cout.write("\b"*37 + " "*37 + "\b"*37)
1528 1560 return result
1529 1561 else:
1530 1562 def page_more():
1531 1563 ans = raw_input('---Return to continue, q to quit--- ')
1532 1564 if ans.lower().startswith('q'):
1533 1565 return False
1534 1566 else:
1535 1567 return True
1536 1568
1537 1569 esc_re = re.compile(r"(\x1b[^m]+m)")
1538 1570
1539 1571 def page_dumb(strng,start=0,screen_lines=25):
1540 1572 """Very dumb 'pager' in Python, for when nothing else works.
1541 1573
1542 1574 Only moves forward, same interface as page(), except for pager_cmd and
1543 1575 mode."""
1544 1576
1545 1577 out_ln = strng.splitlines()[start:]
1546 1578 screens = chop(out_ln,screen_lines-1)
1547 1579 if len(screens) == 1:
1548 1580 print >>Term.cout, os.linesep.join(screens[0])
1549 1581 else:
1550 1582 last_escape = ""
1551 1583 for scr in screens[0:-1]:
1552 1584 hunk = os.linesep.join(scr)
1553 1585 print >>Term.cout, last_escape + hunk
1554 1586 if not page_more():
1555 1587 return
1556 1588 esc_list = esc_re.findall(hunk)
1557 1589 if len(esc_list) > 0:
1558 1590 last_escape = esc_list[-1]
1559 1591 print >>Term.cout, last_escape + os.linesep.join(screens[-1])
1560 1592
1561 1593 #----------------------------------------------------------------------------
1562 1594 def page(strng,start=0,screen_lines=0,pager_cmd = None):
1563 1595 """Print a string, piping through a pager after a certain length.
1564 1596
1565 1597 The screen_lines parameter specifies the number of *usable* lines of your
1566 1598 terminal screen (total lines minus lines you need to reserve to show other
1567 1599 information).
1568 1600
1569 1601 If you set screen_lines to a number <=0, page() will try to auto-determine
1570 1602 your screen size and will only use up to (screen_size+screen_lines) for
1571 1603 printing, paging after that. That is, if you want auto-detection but need
1572 1604 to reserve the bottom 3 lines of the screen, use screen_lines = -3, and for
1573 1605 auto-detection without any lines reserved simply use screen_lines = 0.
1574 1606
1575 1607 If a string won't fit in the allowed lines, it is sent through the
1576 1608 specified pager command. If none given, look for PAGER in the environment,
1577 1609 and ultimately default to less.
1578 1610
1579 1611 If no system pager works, the string is sent through a 'dumb pager'
1580 1612 written in python, very simplistic.
1581 1613 """
1582 1614
1583 1615 # Some routines may auto-compute start offsets incorrectly and pass a
1584 1616 # negative value. Offset to 0 for robustness.
1585 1617 start = max(0,start)
1586 1618
1587 1619 # first, try the hook
1588 1620 ip = IPython.ipapi.get()
1589 1621 if ip:
1590 1622 try:
1591 1623 ip.IP.hooks.show_in_pager(strng)
1592 1624 return
1593 1625 except IPython.ipapi.TryNext:
1594 1626 pass
1595 1627
1596 1628 # Ugly kludge, but calling curses.initscr() flat out crashes in emacs
1597 1629 TERM = os.environ.get('TERM','dumb')
1598 1630 if TERM in ['dumb','emacs'] and os.name != 'nt':
1599 1631 print strng
1600 1632 return
1601 1633 # chop off the topmost part of the string we don't want to see
1602 1634 str_lines = strng.split(os.linesep)[start:]
1603 1635 str_toprint = os.linesep.join(str_lines)
1604 1636 num_newlines = len(str_lines)
1605 1637 len_str = len(str_toprint)
1606 1638
1607 1639 # Dumb heuristics to guesstimate number of on-screen lines the string
1608 1640 # takes. Very basic, but good enough for docstrings in reasonable
1609 1641 # terminals. If someone later feels like refining it, it's not hard.
1610 1642 numlines = max(num_newlines,int(len_str/80)+1)
1611 1643
1612 1644 if os.name == "nt":
1613 1645 screen_lines_def = get_console_size(defaulty=25)[1]
1614 1646 else:
1615 1647 screen_lines_def = 25 # default value if we can't auto-determine
1616 1648
1617 1649 # auto-determine screen size
1618 1650 if screen_lines <= 0:
1619 1651 if TERM=='xterm':
1620 1652 use_curses = USE_CURSES
1621 1653 else:
1622 1654 # curses causes problems on many terminals other than xterm.
1623 1655 use_curses = False
1624 1656 if use_curses:
1625 1657 # There is a bug in curses, where *sometimes* it fails to properly
1626 1658 # initialize, and then after the endwin() call is made, the
1627 1659 # terminal is left in an unusable state. Rather than trying to
1628 1660 # check everytime for this (by requesting and comparing termios
1629 1661 # flags each time), we just save the initial terminal state and
1630 1662 # unconditionally reset it every time. It's cheaper than making
1631 1663 # the checks.
1632 1664 term_flags = termios.tcgetattr(sys.stdout)
1633 1665 scr = curses.initscr()
1634 1666 screen_lines_real,screen_cols = scr.getmaxyx()
1635 1667 curses.endwin()
1636 1668 # Restore terminal state in case endwin() didn't.
1637 1669 termios.tcsetattr(sys.stdout,termios.TCSANOW,term_flags)
1638 1670 # Now we have what we needed: the screen size in rows/columns
1639 1671 screen_lines += screen_lines_real
1640 1672 #print '***Screen size:',screen_lines_real,'lines x',\
1641 1673 #screen_cols,'columns.' # dbg
1642 1674 else:
1643 1675 screen_lines += screen_lines_def
1644 1676
1645 1677 #print 'numlines',numlines,'screenlines',screen_lines # dbg
1646 1678 if numlines <= screen_lines :
1647 1679 #print '*** normal print' # dbg
1648 1680 print >>Term.cout, str_toprint
1649 1681 else:
1650 1682 # Try to open pager and default to internal one if that fails.
1651 1683 # All failure modes are tagged as 'retval=1', to match the return
1652 1684 # value of a failed system command. If any intermediate attempt
1653 1685 # sets retval to 1, at the end we resort to our own page_dumb() pager.
1654 1686 pager_cmd = get_pager_cmd(pager_cmd)
1655 1687 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
1656 1688 if os.name == 'nt':
1657 1689 if pager_cmd.startswith('type'):
1658 1690 # The default WinXP 'type' command is failing on complex strings.
1659 1691 retval = 1
1660 1692 else:
1661 1693 tmpname = tempfile.mktemp('.txt')
1662 1694 tmpfile = file(tmpname,'wt')
1663 1695 tmpfile.write(strng)
1664 1696 tmpfile.close()
1665 1697 cmd = "%s < %s" % (pager_cmd,tmpname)
1666 1698 if os.system(cmd):
1667 1699 retval = 1
1668 1700 else:
1669 1701 retval = None
1670 1702 os.remove(tmpname)
1671 1703 else:
1672 1704 try:
1673 1705 retval = None
1674 1706 # if I use popen4, things hang. No idea why.
1675 1707 #pager,shell_out = os.popen4(pager_cmd)
1676 1708 pager = os.popen(pager_cmd,'w')
1677 1709 pager.write(strng)
1678 1710 pager.close()
1679 1711 retval = pager.close() # success returns None
1680 1712 except IOError,msg: # broken pipe when user quits
1681 1713 if msg.args == (32,'Broken pipe'):
1682 1714 retval = None
1683 1715 else:
1684 1716 retval = 1
1685 1717 except OSError:
1686 1718 # Other strange problems, sometimes seen in Win2k/cygwin
1687 1719 retval = 1
1688 1720 if retval is not None:
1689 1721 page_dumb(strng,screen_lines=screen_lines)
1690 1722
1691 1723 #----------------------------------------------------------------------------
1692 1724 def page_file(fname,start = 0, pager_cmd = None):
1693 1725 """Page a file, using an optional pager command and starting line.
1694 1726 """
1695 1727
1696 1728 pager_cmd = get_pager_cmd(pager_cmd)
1697 1729 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
1698 1730
1699 1731 try:
1700 1732 if os.environ['TERM'] in ['emacs','dumb']:
1701 1733 raise EnvironmentError
1702 1734 xsys(pager_cmd + ' ' + fname)
1703 1735 except:
1704 1736 try:
1705 1737 if start > 0:
1706 1738 start -= 1
1707 1739 page(open(fname).read(),start)
1708 1740 except:
1709 1741 print 'Unable to show file',`fname`
1710 1742
1711 1743
1712 1744 #----------------------------------------------------------------------------
1713 1745 def snip_print(str,width = 75,print_full = 0,header = ''):
1714 1746 """Print a string snipping the midsection to fit in width.
1715 1747
1716 1748 print_full: mode control:
1717 1749 - 0: only snip long strings
1718 1750 - 1: send to page() directly.
1719 1751 - 2: snip long strings and ask for full length viewing with page()
1720 1752 Return 1 if snipping was necessary, 0 otherwise."""
1721 1753
1722 1754 if print_full == 1:
1723 1755 page(header+str)
1724 1756 return 0
1725 1757
1726 1758 print header,
1727 1759 if len(str) < width:
1728 1760 print str
1729 1761 snip = 0
1730 1762 else:
1731 1763 whalf = int((width -5)/2)
1732 1764 print str[:whalf] + ' <...> ' + str[-whalf:]
1733 1765 snip = 1
1734 1766 if snip and print_full == 2:
1735 1767 if raw_input(header+' Snipped. View (y/n)? [N]').lower() == 'y':
1736 1768 page(str)
1737 1769 return snip
1738 1770
1739 1771 #****************************************************************************
1740 1772 # lists, dicts and structures
1741 1773
1742 1774 def belong(candidates,checklist):
1743 1775 """Check whether a list of items appear in a given list of options.
1744 1776
1745 1777 Returns a list of 1 and 0, one for each candidate given."""
1746 1778
1747 1779 return [x in checklist for x in candidates]
1748 1780
1749 1781 #----------------------------------------------------------------------------
1750 1782 def uniq_stable(elems):
1751 1783 """uniq_stable(elems) -> list
1752 1784
1753 1785 Return from an iterable, a list of all the unique elements in the input,
1754 1786 but maintaining the order in which they first appear.
1755 1787
1756 1788 A naive solution to this problem which just makes a dictionary with the
1757 1789 elements as keys fails to respect the stability condition, since
1758 1790 dictionaries are unsorted by nature.
1759 1791
1760 1792 Note: All elements in the input must be valid dictionary keys for this
1761 1793 routine to work, as it internally uses a dictionary for efficiency
1762 1794 reasons."""
1763 1795
1764 1796 unique = []
1765 1797 unique_dict = {}
1766 1798 for nn in elems:
1767 1799 if nn not in unique_dict:
1768 1800 unique.append(nn)
1769 1801 unique_dict[nn] = None
1770 1802 return unique
1771 1803
1772 1804 #----------------------------------------------------------------------------
1773 1805 class NLprinter:
1774 1806 """Print an arbitrarily nested list, indicating index numbers.
1775 1807
1776 1808 An instance of this class called nlprint is available and callable as a
1777 1809 function.
1778 1810
1779 1811 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
1780 1812 and using 'sep' to separate the index from the value. """
1781 1813
1782 1814 def __init__(self):
1783 1815 self.depth = 0
1784 1816
1785 1817 def __call__(self,lst,pos='',**kw):
1786 1818 """Prints the nested list numbering levels."""
1787 1819 kw.setdefault('indent',' ')
1788 1820 kw.setdefault('sep',': ')
1789 1821 kw.setdefault('start',0)
1790 1822 kw.setdefault('stop',len(lst))
1791 1823 # we need to remove start and stop from kw so they don't propagate
1792 1824 # into a recursive call for a nested list.
1793 1825 start = kw['start']; del kw['start']
1794 1826 stop = kw['stop']; del kw['stop']
1795 1827 if self.depth == 0 and 'header' in kw.keys():
1796 1828 print kw['header']
1797 1829
1798 1830 for idx in range(start,stop):
1799 1831 elem = lst[idx]
1800 1832 if type(elem)==type([]):
1801 1833 self.depth += 1
1802 1834 self.__call__(elem,itpl('$pos$idx,'),**kw)
1803 1835 self.depth -= 1
1804 1836 else:
1805 1837 printpl(kw['indent']*self.depth+'$pos$idx$kw["sep"]$elem')
1806 1838
1807 1839 nlprint = NLprinter()
1808 1840 #----------------------------------------------------------------------------
1809 1841 def all_belong(candidates,checklist):
1810 1842 """Check whether a list of items ALL appear in a given list of options.
1811 1843
1812 1844 Returns a single 1 or 0 value."""
1813 1845
1814 1846 return 1-(0 in [x in checklist for x in candidates])
1815 1847
1816 1848 #----------------------------------------------------------------------------
1817 1849 def sort_compare(lst1,lst2,inplace = 1):
1818 1850 """Sort and compare two lists.
1819 1851
1820 1852 By default it does it in place, thus modifying the lists. Use inplace = 0
1821 1853 to avoid that (at the cost of temporary copy creation)."""
1822 1854 if not inplace:
1823 1855 lst1 = lst1[:]
1824 1856 lst2 = lst2[:]
1825 1857 lst1.sort(); lst2.sort()
1826 1858 return lst1 == lst2
1827 1859
1828 1860 #----------------------------------------------------------------------------
1829 1861 def list2dict(lst):
1830 1862 """Takes a list of (key,value) pairs and turns it into a dict."""
1831 1863
1832 1864 dic = {}
1833 1865 for k,v in lst: dic[k] = v
1834 1866 return dic
1835 1867
1836 1868 #----------------------------------------------------------------------------
1837 1869 def list2dict2(lst,default=''):
1838 1870 """Takes a list and turns it into a dict.
1839 1871 Much slower than list2dict, but more versatile. This version can take
1840 1872 lists with sublists of arbitrary length (including sclars)."""
1841 1873
1842 1874 dic = {}
1843 1875 for elem in lst:
1844 1876 if type(elem) in (types.ListType,types.TupleType):
1845 1877 size = len(elem)
1846 1878 if size == 0:
1847 1879 pass
1848 1880 elif size == 1:
1849 1881 dic[elem] = default
1850 1882 else:
1851 1883 k,v = elem[0], elem[1:]
1852 1884 if len(v) == 1: v = v[0]
1853 1885 dic[k] = v
1854 1886 else:
1855 1887 dic[elem] = default
1856 1888 return dic
1857 1889
1858 1890 #----------------------------------------------------------------------------
1859 1891 def flatten(seq):
1860 1892 """Flatten a list of lists (NOT recursive, only works for 2d lists)."""
1861 1893
1862 1894 return [x for subseq in seq for x in subseq]
1863 1895
1864 1896 #----------------------------------------------------------------------------
1865 1897 def get_slice(seq,start=0,stop=None,step=1):
1866 1898 """Get a slice of a sequence with variable step. Specify start,stop,step."""
1867 1899 if stop == None:
1868 1900 stop = len(seq)
1869 1901 item = lambda i: seq[i]
1870 1902 return map(item,xrange(start,stop,step))
1871 1903
1872 1904 #----------------------------------------------------------------------------
1873 1905 def chop(seq,size):
1874 1906 """Chop a sequence into chunks of the given size."""
1875 1907 chunk = lambda i: seq[i:i+size]
1876 1908 return map(chunk,xrange(0,len(seq),size))
1877 1909
1878 1910 #----------------------------------------------------------------------------
1879 1911 # with is a keyword as of python 2.5, so this function is renamed to withobj
1880 1912 # from its old 'with' name.
1881 1913 def with_obj(object, **args):
1882 1914 """Set multiple attributes for an object, similar to Pascal's with.
1883 1915
1884 1916 Example:
1885 1917 with_obj(jim,
1886 1918 born = 1960,
1887 1919 haircolour = 'Brown',
1888 1920 eyecolour = 'Green')
1889 1921
1890 1922 Credit: Greg Ewing, in
1891 1923 http://mail.python.org/pipermail/python-list/2001-May/040703.html.
1892 1924
1893 1925 NOTE: up until IPython 0.7.2, this was called simply 'with', but 'with'
1894 1926 has become a keyword for Python 2.5, so we had to rename it."""
1895 1927
1896 1928 object.__dict__.update(args)
1897 1929
1898 1930 #----------------------------------------------------------------------------
1899 1931 def setattr_list(obj,alist,nspace = None):
1900 1932 """Set a list of attributes for an object taken from a namespace.
1901 1933
1902 1934 setattr_list(obj,alist,nspace) -> sets in obj all the attributes listed in
1903 1935 alist with their values taken from nspace, which must be a dict (something
1904 1936 like locals() will often do) If nspace isn't given, locals() of the
1905 1937 *caller* is used, so in most cases you can omit it.
1906 1938
1907 1939 Note that alist can be given as a string, which will be automatically
1908 1940 split into a list on whitespace. If given as a list, it must be a list of
1909 1941 *strings* (the variable names themselves), not of variables."""
1910 1942
1911 1943 # this grabs the local variables from the *previous* call frame -- that is
1912 1944 # the locals from the function that called setattr_list().
1913 1945 # - snipped from weave.inline()
1914 1946 if nspace is None:
1915 1947 call_frame = sys._getframe().f_back
1916 1948 nspace = call_frame.f_locals
1917 1949
1918 1950 if type(alist) in StringTypes:
1919 1951 alist = alist.split()
1920 1952 for attr in alist:
1921 1953 val = eval(attr,nspace)
1922 1954 setattr(obj,attr,val)
1923 1955
1924 1956 #----------------------------------------------------------------------------
1925 1957 def getattr_list(obj,alist,*args):
1926 1958 """getattr_list(obj,alist[, default]) -> attribute list.
1927 1959
1928 1960 Get a list of named attributes for an object. When a default argument is
1929 1961 given, it is returned when the attribute doesn't exist; without it, an
1930 1962 exception is raised in that case.
1931 1963
1932 1964 Note that alist can be given as a string, which will be automatically
1933 1965 split into a list on whitespace. If given as a list, it must be a list of
1934 1966 *strings* (the variable names themselves), not of variables."""
1935 1967
1936 1968 if type(alist) in StringTypes:
1937 1969 alist = alist.split()
1938 1970 if args:
1939 1971 if len(args)==1:
1940 1972 default = args[0]
1941 1973 return map(lambda attr: getattr(obj,attr,default),alist)
1942 1974 else:
1943 1975 raise ValueError,'getattr_list() takes only one optional argument'
1944 1976 else:
1945 1977 return map(lambda attr: getattr(obj,attr),alist)
1946 1978
1947 1979 #----------------------------------------------------------------------------
1948 1980 def map_method(method,object_list,*argseq,**kw):
1949 1981 """map_method(method,object_list,*args,**kw) -> list
1950 1982
1951 1983 Return a list of the results of applying the methods to the items of the
1952 1984 argument sequence(s). If more than one sequence is given, the method is
1953 1985 called with an argument list consisting of the corresponding item of each
1954 1986 sequence. All sequences must be of the same length.
1955 1987
1956 1988 Keyword arguments are passed verbatim to all objects called.
1957 1989
1958 1990 This is Python code, so it's not nearly as fast as the builtin map()."""
1959 1991
1960 1992 out_list = []
1961 1993 idx = 0
1962 1994 for object in object_list:
1963 1995 try:
1964 1996 handler = getattr(object, method)
1965 1997 except AttributeError:
1966 1998 out_list.append(None)
1967 1999 else:
1968 2000 if argseq:
1969 2001 args = map(lambda lst:lst[idx],argseq)
1970 2002 #print 'ob',object,'hand',handler,'ar',args # dbg
1971 2003 out_list.append(handler(args,**kw))
1972 2004 else:
1973 2005 out_list.append(handler(**kw))
1974 2006 idx += 1
1975 2007 return out_list
1976 2008
1977 2009 #----------------------------------------------------------------------------
1978 2010 def get_class_members(cls):
1979 2011 ret = dir(cls)
1980 2012 if hasattr(cls,'__bases__'):
1981 2013 for base in cls.__bases__:
1982 2014 ret.extend(get_class_members(base))
1983 2015 return ret
1984 2016
1985 2017 #----------------------------------------------------------------------------
1986 2018 def dir2(obj):
1987 2019 """dir2(obj) -> list of strings
1988 2020
1989 2021 Extended version of the Python builtin dir(), which does a few extra
1990 2022 checks, and supports common objects with unusual internals that confuse
1991 2023 dir(), such as Traits and PyCrust.
1992 2024
1993 2025 This version is guaranteed to return only a list of true strings, whereas
1994 2026 dir() returns anything that objects inject into themselves, even if they
1995 2027 are later not really valid for attribute access (many extension libraries
1996 2028 have such bugs).
1997 2029 """
1998 2030
1999 2031 # Start building the attribute list via dir(), and then complete it
2000 2032 # with a few extra special-purpose calls.
2001 2033 words = dir(obj)
2002 2034
2003 2035 if hasattr(obj,'__class__'):
2004 2036 words.append('__class__')
2005 2037 words.extend(get_class_members(obj.__class__))
2006 2038 #if '__base__' in words: 1/0
2007 2039
2008 2040 # Some libraries (such as traits) may introduce duplicates, we want to
2009 2041 # track and clean this up if it happens
2010 2042 may_have_dupes = False
2011 2043
2012 2044 # this is the 'dir' function for objects with Enthought's traits
2013 2045 if hasattr(obj, 'trait_names'):
2014 2046 try:
2015 2047 words.extend(obj.trait_names())
2016 2048 may_have_dupes = True
2017 2049 except TypeError:
2018 2050 # This will happen if `obj` is a class and not an instance.
2019 2051 pass
2020 2052
2021 2053 # Support for PyCrust-style _getAttributeNames magic method.
2022 2054 if hasattr(obj, '_getAttributeNames'):
2023 2055 try:
2024 2056 words.extend(obj._getAttributeNames())
2025 2057 may_have_dupes = True
2026 2058 except TypeError:
2027 2059 # `obj` is a class and not an instance. Ignore
2028 2060 # this error.
2029 2061 pass
2030 2062
2031 2063 if may_have_dupes:
2032 2064 # eliminate possible duplicates, as some traits may also
2033 2065 # appear as normal attributes in the dir() call.
2034 2066 words = list(set(words))
2035 2067 words.sort()
2036 2068
2037 2069 # filter out non-string attributes which may be stuffed by dir() calls
2038 2070 # and poor coding in third-party modules
2039 2071 return [w for w in words if isinstance(w, basestring)]
2040 2072
2041 2073 #----------------------------------------------------------------------------
2042 2074 def import_fail_info(mod_name,fns=None):
2043 2075 """Inform load failure for a module."""
2044 2076
2045 2077 if fns == None:
2046 2078 warn("Loading of %s failed.\n" % (mod_name,))
2047 2079 else:
2048 2080 warn("Loading of %s from %s failed.\n" % (fns,mod_name))
2049 2081
2050 2082 #----------------------------------------------------------------------------
2051 2083 # Proposed popitem() extension, written as a method
2052 2084
2053 2085
2054 2086 class NotGiven: pass
2055 2087
2056 2088 def popkey(dct,key,default=NotGiven):
2057 2089 """Return dct[key] and delete dct[key].
2058 2090
2059 2091 If default is given, return it if dct[key] doesn't exist, otherwise raise
2060 2092 KeyError. """
2061 2093
2062 2094 try:
2063 2095 val = dct[key]
2064 2096 except KeyError:
2065 2097 if default is NotGiven:
2066 2098 raise
2067 2099 else:
2068 2100 return default
2069 2101 else:
2070 2102 del dct[key]
2071 2103 return val
2072 2104
2073 2105 def wrap_deprecated(func, suggest = '<nothing>'):
2074 2106 def newFunc(*args, **kwargs):
2075 2107 warnings.warn("Call to deprecated function %s, use %s instead" %
2076 2108 ( func.__name__, suggest),
2077 2109 category=DeprecationWarning,
2078 2110 stacklevel = 2)
2079 2111 return func(*args, **kwargs)
2080 2112 return newFunc
2081 2113
2082 2114
2083 2115 def _num_cpus_unix():
2084 2116 """Return the number of active CPUs on a Unix system."""
2085 2117 return os.sysconf("SC_NPROCESSORS_ONLN")
2086 2118
2087 2119
2088 2120 def _num_cpus_darwin():
2089 2121 """Return the number of active CPUs on a Darwin system."""
2090 2122 p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE)
2091 2123 return p.stdout.read()
2092 2124
2093 2125
2094 2126 def _num_cpus_windows():
2095 2127 """Return the number of active CPUs on a Windows system."""
2096 2128 return os.environ.get("NUMBER_OF_PROCESSORS")
2097 2129
2098 2130
2099 2131 def num_cpus():
2100 2132 """Return the effective number of CPUs in the system as an integer.
2101 2133
2102 2134 This cross-platform function makes an attempt at finding the total number of
2103 2135 available CPUs in the system, as returned by various underlying system and
2104 2136 python calls.
2105 2137
2106 2138 If it can't find a sensible answer, it returns 1 (though an error *may* make
2107 2139 it return a large positive number that's actually incorrect).
2108 2140 """
2109 2141
2110 2142 # Many thanks to the Parallel Python project (http://www.parallelpython.com)
2111 2143 # for the names of the keys we needed to look up for this function. This
2112 2144 # code was inspired by their equivalent function.
2113 2145
2114 2146 ncpufuncs = {'Linux':_num_cpus_unix,
2115 2147 'Darwin':_num_cpus_darwin,
2116 2148 'Windows':_num_cpus_windows,
2117 2149 # On Vista, python < 2.5.2 has a bug and returns 'Microsoft'
2118 2150 # See http://bugs.python.org/issue1082 for details.
2119 2151 'Microsoft':_num_cpus_windows,
2120 2152 }
2121 2153
2122 2154 ncpufunc = ncpufuncs.get(platform.system(),
2123 2155 # default to unix version (Solaris, AIX, etc)
2124 2156 _num_cpus_unix)
2125 2157
2126 2158 try:
2127 2159 ncpus = max(1,int(ncpufunc()))
2128 2160 except:
2129 2161 ncpus = 1
2130 2162 return ncpus
2131 2163
2132 2164 #*************************** end of file <genutils.py> **********************
@@ -1,125 +1,123 b''
1 1 # encoding: utf-8
2 2
3 3 """Default kernel configuration."""
4 4
5 5 __docformat__ = "restructuredtext en"
6 6
7 7 #-------------------------------------------------------------------------------
8 8 # Copyright (C) 2008 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-------------------------------------------------------------------------------
13 13
14 14 #-------------------------------------------------------------------------------
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 from os.path import join as pjoin
19
18 20 from IPython.external.configobj import ConfigObj
19 21 from IPython.config.api import ConfigObjManager
20 from IPython.config.cutils import get_ipython_dir
22 from IPython.genutils import get_ipython_dir, get_security_dir
21 23
22 24 default_kernel_config = ConfigObj()
23 25
24 try:
25 ipython_dir = get_ipython_dir() + '/'
26 except:
27 # This will defaults to the cwd
28 ipython_dir = ''
26 security_dir = get_security_dir()
29 27
30 28 #-------------------------------------------------------------------------------
31 29 # Engine Configuration
32 30 #-------------------------------------------------------------------------------
33 31
34 32 engine_config = dict(
35 33 logfile = '', # Empty means log to stdout
36 furl_file = ipython_dir + 'ipcontroller-engine.furl'
34 furl_file = pjoin(security_dir, 'ipcontroller-engine.furl')
37 35 )
38 36
39 37 #-------------------------------------------------------------------------------
40 38 # MPI Configuration
41 39 #-------------------------------------------------------------------------------
42 40
43 41 mpi_config = dict(
44 42 mpi4py = """from mpi4py import MPI as mpi
45 43 mpi.size = mpi.COMM_WORLD.Get_size()
46 44 mpi.rank = mpi.COMM_WORLD.Get_rank()
47 45 """,
48 46 pytrilinos = """from PyTrilinos import Epetra
49 47 class SimpleStruct:
50 48 pass
51 49 mpi = SimpleStruct()
52 50 mpi.rank = 0
53 51 mpi.size = 0
54 52 """,
55 53 default = ''
56 54 )
57 55
58 56 #-------------------------------------------------------------------------------
59 57 # Controller Configuration
60 58 #-------------------------------------------------------------------------------
61 59
62 60 controller_config = dict(
63 61
64 62 logfile = '', # Empty means log to stdout
65 63 import_statement = '',
66 64
67 65 engine_tub = dict(
68 66 ip = '', # Empty string means all interfaces
69 67 port = 0, # 0 means pick a port for me
70 68 location = '', # Empty string means try to set automatically
71 69 secure = True,
72 cert_file = ipython_dir + 'ipcontroller-engine.pem',
70 cert_file = pjoin(security_dir, 'ipcontroller-engine.pem'),
73 71 ),
74 72 engine_fc_interface = 'IPython.kernel.enginefc.IFCControllerBase',
75 engine_furl_file = ipython_dir + 'ipcontroller-engine.furl',
73 engine_furl_file = pjoin(security_dir, 'ipcontroller-engine.furl'),
76 74
77 75 controller_interfaces = dict(
78 76 # multiengine = dict(
79 77 # controller_interface = 'IPython.kernel.multiengine.IMultiEngine',
80 78 # fc_interface = 'IPython.kernel.multienginefc.IFCMultiEngine',
81 79 # furl_file = 'ipcontroller-mec.furl'
82 80 # ),
83 81 task = dict(
84 82 controller_interface = 'IPython.kernel.task.ITaskController',
85 83 fc_interface = 'IPython.kernel.taskfc.IFCTaskController',
86 furl_file = ipython_dir + 'ipcontroller-tc.furl'
84 furl_file = pjoin(security_dir, 'ipcontroller-tc.furl')
87 85 ),
88 86 multiengine = dict(
89 87 controller_interface = 'IPython.kernel.multiengine.IMultiEngine',
90 88 fc_interface = 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine',
91 furl_file = ipython_dir + 'ipcontroller-mec.furl'
89 furl_file = pjoin(security_dir, 'ipcontroller-mec.furl')
92 90 )
93 91 ),
94 92
95 93 client_tub = dict(
96 94 ip = '', # Empty string means all interfaces
97 95 port = 0, # 0 means pick a port for me
98 96 location = '', # Empty string means try to set automatically
99 97 secure = True,
100 cert_file = ipython_dir + 'ipcontroller-client.pem'
98 cert_file = pjoin(security_dir, 'ipcontroller-client.pem')
101 99 )
102 100 )
103 101
104 102 #-------------------------------------------------------------------------------
105 103 # Client Configuration
106 104 #-------------------------------------------------------------------------------
107 105
108 106 client_config = dict(
109 107 client_interfaces = dict(
110 108 task = dict(
111 furl_file = ipython_dir + 'ipcontroller-tc.furl'
109 furl_file = pjoin(security_dir, 'ipcontroller-tc.furl')
112 110 ),
113 111 multiengine = dict(
114 furl_file = ipython_dir + 'ipcontroller-mec.furl'
112 furl_file = pjoin(security_dir, 'ipcontroller-mec.furl')
115 113 )
116 114 )
117 115 )
118 116
119 117 default_kernel_config['engine'] = engine_config
120 118 default_kernel_config['mpi'] = mpi_config
121 119 default_kernel_config['controller'] = controller_config
122 120 default_kernel_config['client'] = client_config
123 121
124 122
125 123 config_manager = ConfigObjManager(default_kernel_config, 'IPython.kernel.ini') No newline at end of file
@@ -1,376 +1,376 b''
1 1 # encoding: utf-8
2 2 # -*- test-case-name: IPython.kernel.test.test_controllerservice -*-
3 3
4 4 """A Twisted Service for the IPython Controller.
5 5
6 6 The IPython Controller:
7 7
8 8 * Listens for Engines to connect and then manages access to those engines.
9 9 * Listens for clients and passes commands from client to the Engines.
10 10 * Exposes an asynchronous interfaces to the Engines which themselves can block.
11 11 * Acts as a gateway to the Engines.
12 12
13 13 The design of the controller is somewhat abstract to allow flexibility in how
14 14 the controller is presented to clients. This idea is that there is a basic
15 15 ControllerService class that allows engines to connect to it. But, this
16 16 basic class has no client interfaces. To expose client interfaces developers
17 17 provide an adapter that makes the ControllerService look like something. For
18 18 example, one client interface might support task farming and another might
19 19 support interactive usage. The important thing is that by using interfaces
20 20 and adapters, a single controller can be accessed from multiple interfaces.
21 21 Furthermore, by adapting various client interfaces to various network
22 22 protocols, each client interface can be exposed to multiple network protocols.
23 23 See multiengine.py for an example of how to adapt the ControllerService
24 24 to a client interface.
25 25 """
26 26
27 27 __docformat__ = "restructuredtext en"
28 28
29 29 #-------------------------------------------------------------------------------
30 30 # Copyright (C) 2008 The IPython Development Team
31 31 #
32 32 # Distributed under the terms of the BSD License. The full license is in
33 33 # the file COPYING, distributed as part of this software.
34 34 #-------------------------------------------------------------------------------
35 35
36 36 #-------------------------------------------------------------------------------
37 37 # Imports
38 38 #-------------------------------------------------------------------------------
39 39
40 40 import os, sys
41 41
42 42 from twisted.application import service
43 43 from twisted.internet import defer, reactor
44 44 from twisted.python import log, components
45 45 from zope.interface import Interface, implements, Attribute
46 46 import zope.interface as zi
47 47
48 48 from IPython.kernel.engineservice import \
49 49 IEngineCore, \
50 50 IEngineSerialized, \
51 51 IEngineQueued
52 52
53 from IPython.config import cutils
53 from IPython.genutils import get_ipython_dir
54 54 from IPython.kernel import codeutil
55 55
56 56 #-------------------------------------------------------------------------------
57 57 # Interfaces for the Controller
58 58 #-------------------------------------------------------------------------------
59 59
60 60 class IControllerCore(Interface):
61 61 """Basic methods any controller must have.
62 62
63 63 This is basically the aspect of the controller relevant to the
64 64 engines and does not assume anything about how the engines will
65 65 be presented to a client.
66 66 """
67 67
68 68 engines = Attribute("A dict of engine ids and engine instances.")
69 69
70 70 def register_engine(remoteEngine, id=None, ip=None, port=None,
71 71 pid=None):
72 72 """Register new remote engine.
73 73
74 74 The controller can use the ip, port, pid of the engine to do useful things
75 75 like kill the engines.
76 76
77 77 :Parameters:
78 78 remoteEngine
79 79 An implementer of IEngineCore, IEngineSerialized and IEngineQueued.
80 80 id : int
81 81 Requested id.
82 82 ip : str
83 83 IP address the engine is running on.
84 84 port : int
85 85 Port the engine is on.
86 86 pid : int
87 87 pid of the running engine.
88 88
89 89 :Returns: A dict of {'id':id} and possibly other key, value pairs.
90 90 """
91 91
92 92 def unregister_engine(id):
93 93 """Handle a disconnecting engine.
94 94
95 95 :Parameters:
96 96 id
97 97 The integer engine id of the engine to unregister.
98 98 """
99 99
100 100 def on_register_engine_do(f, includeID, *args, **kwargs):
101 101 """Call ``f(*args, **kwargs)`` when an engine is registered.
102 102
103 103 :Parameters:
104 104 includeID : int
105 105 If True the first argument to f will be the id of the engine.
106 106 """
107 107
108 108 def on_unregister_engine_do(f, includeID, *args, **kwargs):
109 109 """Call ``f(*args, **kwargs)`` when an engine is unregistered.
110 110
111 111 :Parameters:
112 112 includeID : int
113 113 If True the first argument to f will be the id of the engine.
114 114 """
115 115
116 116 def on_register_engine_do_not(f):
117 117 """Stop calling f on engine registration"""
118 118
119 119 def on_unregister_engine_do_not(f):
120 120 """Stop calling f on engine unregistration"""
121 121
122 122 def on_n_engines_registered_do(n, f, *arg, **kwargs):
123 123 """Call f(*args, **kwargs) the first time the nth engine registers."""
124 124
125 125 class IControllerBase(IControllerCore):
126 126 """The basic controller interface."""
127 127 pass
128 128
129 129
130 130 #-------------------------------------------------------------------------------
131 131 # Implementation of the ControllerService
132 132 #-------------------------------------------------------------------------------
133 133
134 134 class ControllerService(object, service.Service):
135 135 """A basic Controller represented as a Twisted Service.
136 136
137 137 This class doesn't implement any client notification mechanism. That
138 138 is up to adapted subclasses.
139 139 """
140 140
141 141 # I also pick up the IService interface by inheritance from service.Service
142 142 implements(IControllerBase)
143 143 name = 'ControllerService'
144 144
145 145 def __init__(self, maxEngines=511, saveIDs=False):
146 146 self.saveIDs = saveIDs
147 147 self.engines = {}
148 148 self.availableIDs = range(maxEngines,-1,-1) # [511,...,0]
149 149 self._onRegister = []
150 150 self._onUnregister = []
151 151 self._onNRegistered = []
152 152
153 153 #---------------------------------------------------------------------------
154 154 # Methods used to save the engine info to a log file
155 155 #---------------------------------------------------------------------------
156 156
157 157 def _buildEngineInfoString(self, id, ip, port, pid):
158 158 if id is None:
159 159 id = -99
160 160 if ip is None:
161 161 ip = "-99"
162 162 if port is None:
163 163 port = -99
164 164 if pid is None:
165 165 pid = -99
166 166 return "Engine Info: %d %s %d %d" % (id, ip , port, pid)
167 167
168 168 def _logEngineInfo(self, id, ip, port, pid):
169 169 log.msg(self._buildEngineInfoString(id,ip,port,pid))
170 170
171 171 def _getEngineInfoLogFile(self):
172 172 # Store all logs inside the ipython directory
173 ipdir = cutils.get_ipython_dir()
173 ipdir = get_ipython_dir()
174 174 pjoin = os.path.join
175 175 logdir_base = pjoin(ipdir,'log')
176 176 if not os.path.isdir(logdir_base):
177 177 os.makedirs(logdir_base)
178 178 logfile = os.path.join(logdir_base,'ipcontroller-%s-engine-info.log' % os.getpid())
179 179 return logfile
180 180
181 181 def _logEngineInfoToFile(self, id, ip, port, pid):
182 182 """Log info about an engine to a log file.
183 183
184 184 When an engine registers with a ControllerService, the ControllerService
185 185 saves information about the engine to a log file. That information
186 186 can be useful for various purposes, such as killing hung engines, etc.
187 187
188 188 This method takes the assigned id, ip/port and pid of the engine
189 189 and saves it to a file of the form:
190 190
191 191 ~/.ipython/log/ipcontroller-###-engine-info.log
192 192
193 193 where ### is the pid of the controller.
194 194
195 195 Each line of this file has the form:
196 196
197 197 Engine Info: ip ip port pid
198 198
199 199 If any of the entries are not known, they are replaced by -99.
200 200 """
201 201
202 202 fname = self._getEngineInfoLogFile()
203 203 f = open(fname, 'a')
204 204 s = self._buildEngineInfoString(id,ip,port,pid)
205 205 f.write(s + '\n')
206 206 f.close()
207 207
208 208 #---------------------------------------------------------------------------
209 209 # IControllerCore methods
210 210 #---------------------------------------------------------------------------
211 211
212 212 def register_engine(self, remoteEngine, id=None,
213 213 ip=None, port=None, pid=None):
214 214 """Register new engine connection"""
215 215
216 216 # What happens if these assertions fail?
217 217 assert IEngineCore.providedBy(remoteEngine), \
218 218 "engine passed to register_engine doesn't provide IEngineCore"
219 219 assert IEngineSerialized.providedBy(remoteEngine), \
220 220 "engine passed to register_engine doesn't provide IEngineSerialized"
221 221 assert IEngineQueued.providedBy(remoteEngine), \
222 222 "engine passed to register_engine doesn't provide IEngineQueued"
223 223 assert isinstance(id, int) or id is None, \
224 224 "id to register_engine must be an integer or None"
225 225 assert isinstance(ip, str) or ip is None, \
226 226 "ip to register_engine must be a string or None"
227 227 assert isinstance(port, int) or port is None, \
228 228 "port to register_engine must be an integer or None"
229 229 assert isinstance(pid, int) or pid is None, \
230 230 "pid to register_engine must be an integer or None"
231 231
232 232 desiredID = id
233 233 if desiredID in self.engines.keys():
234 234 desiredID = None
235 235
236 236 if desiredID in self.availableIDs:
237 237 getID = desiredID
238 238 self.availableIDs.remove(desiredID)
239 239 else:
240 240 getID = self.availableIDs.pop()
241 241 remoteEngine.id = getID
242 242 remoteEngine.service = self
243 243 self.engines[getID] = remoteEngine
244 244
245 245 # Log the Engine Information for monitoring purposes
246 246 self._logEngineInfoToFile(getID, ip, port, pid)
247 247
248 248 msg = "registered engine with id: %i" %getID
249 249 log.msg(msg)
250 250
251 251 for i in range(len(self._onRegister)):
252 252 (f,args,kwargs,ifid) = self._onRegister[i]
253 253 try:
254 254 if ifid:
255 255 f(getID, *args, **kwargs)
256 256 else:
257 257 f(*args, **kwargs)
258 258 except:
259 259 self._onRegister.pop(i)
260 260
261 261 # Call functions when the nth engine is registered and them remove them
262 262 for i, (n, f, args, kwargs) in enumerate(self._onNRegistered):
263 263 if len(self.engines.keys()) == n:
264 264 try:
265 265 try:
266 266 f(*args, **kwargs)
267 267 except:
268 268 log.msg("Function %r failed when the %ith engine registered" % (f, n))
269 269 finally:
270 270 self._onNRegistered.pop(i)
271 271
272 272 return {'id':getID}
273 273
274 274 def unregister_engine(self, id):
275 275 """Unregister engine by id."""
276 276
277 277 assert isinstance(id, int) or id is None, \
278 278 "id to unregister_engine must be an integer or None"
279 279
280 280 msg = "unregistered engine with id: %i" %id
281 281 log.msg(msg)
282 282 try:
283 283 del self.engines[id]
284 284 except KeyError:
285 285 log.msg("engine with id %i was not registered" % id)
286 286 else:
287 287 if not self.saveIDs:
288 288 self.availableIDs.append(id)
289 289 # Sort to assign lower ids first
290 290 self.availableIDs.sort(reverse=True)
291 291 else:
292 292 log.msg("preserving id %i" %id)
293 293
294 294 for i in range(len(self._onUnregister)):
295 295 (f,args,kwargs,ifid) = self._onUnregister[i]
296 296 try:
297 297 if ifid:
298 298 f(id, *args, **kwargs)
299 299 else:
300 300 f(*args, **kwargs)
301 301 except:
302 302 self._onUnregister.pop(i)
303 303
304 304 def on_register_engine_do(self, f, includeID, *args, **kwargs):
305 305 assert callable(f), "f must be callable"
306 306 self._onRegister.append((f,args,kwargs,includeID))
307 307
308 308 def on_unregister_engine_do(self, f, includeID, *args, **kwargs):
309 309 assert callable(f), "f must be callable"
310 310 self._onUnregister.append((f,args,kwargs,includeID))
311 311
312 312 def on_register_engine_do_not(self, f):
313 313 for i in range(len(self._onRegister)):
314 314 g = self._onRegister[i][0]
315 315 if f == g:
316 316 self._onRegister.pop(i)
317 317 return
318 318
319 319 def on_unregister_engine_do_not(self, f):
320 320 for i in range(len(self._onUnregister)):
321 321 g = self._onUnregister[i][0]
322 322 if f == g:
323 323 self._onUnregister.pop(i)
324 324 return
325 325
326 326 def on_n_engines_registered_do(self, n, f, *args, **kwargs):
327 327 if len(self.engines.keys()) >= n:
328 328 f(*args, **kwargs)
329 329 else:
330 330 self._onNRegistered.append((n,f,args,kwargs))
331 331
332 332
333 333 #-------------------------------------------------------------------------------
334 334 # Base class for adapting controller to different client APIs
335 335 #-------------------------------------------------------------------------------
336 336
337 337 class ControllerAdapterBase(object):
338 338 """All Controller adapters should inherit from this class.
339 339
340 340 This class provides a wrapped version of the IControllerBase interface that
341 341 can be used to easily create new custom controllers. Subclasses of this
342 342 will provide a full implementation of IControllerBase.
343 343
344 344 This class doesn't implement any client notification mechanism. That
345 345 is up to subclasses.
346 346 """
347 347
348 348 implements(IControllerBase)
349 349
350 350 def __init__(self, controller):
351 351 self.controller = controller
352 352 # Needed for IControllerCore
353 353 self.engines = self.controller.engines
354 354
355 355 def register_engine(self, remoteEngine, id=None,
356 356 ip=None, port=None, pid=None):
357 357 return self.controller.register_engine(remoteEngine,
358 358 id, ip, port, pid)
359 359
360 360 def unregister_engine(self, id):
361 361 return self.controller.unregister_engine(id)
362 362
363 363 def on_register_engine_do(self, f, includeID, *args, **kwargs):
364 364 return self.controller.on_register_engine_do(f, includeID, *args, **kwargs)
365 365
366 366 def on_unregister_engine_do(self, f, includeID, *args, **kwargs):
367 367 return self.controller.on_unregister_engine_do(f, includeID, *args, **kwargs)
368 368
369 369 def on_register_engine_do_not(self, f):
370 370 return self.controller.on_register_engine_do_not(f)
371 371
372 372 def on_unregister_engine_do_not(self, f):
373 373 return self.controller.on_unregister_engine_do_not(f)
374 374
375 375 def on_n_engines_registered_do(self, n, f, *args, **kwargs):
376 376 return self.controller.on_n_engines_registered_do(n, f, *args, **kwargs)
@@ -1,747 +1,754 b''
1 1 # encoding: utf-8
2 2
3 3 """Central interpreter object for an IPython engine.
4 4
5 5 The interpreter is the object whose job is to process lines of user input and
6 6 actually execute them in the user's namespace.
7 7 """
8 8
9 9 __docformat__ = "restructuredtext en"
10 10
11 11 #-------------------------------------------------------------------------------
12 12 # Copyright (C) 2008 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 16 #-------------------------------------------------------------------------------
17 17
18 18 #-------------------------------------------------------------------------------
19 19 # Imports
20 20 #-------------------------------------------------------------------------------
21 21
22 22 # Standard library imports.
23 23 from types import FunctionType
24 24
25 25 import __builtin__
26 26 import codeop
27 27 import compiler
28 28 import sys
29 29 import traceback
30 30
31 31 # Local imports.
32 32 from IPython.kernel.core import ultraTB
33 33 from IPython.kernel.core.display_trap import DisplayTrap
34 34 from IPython.kernel.core.macro import Macro
35 35 from IPython.kernel.core.prompts import CachedOutput
36 36 from IPython.kernel.core.traceback_trap import TracebackTrap
37 37 from IPython.kernel.core.util import Bunch, system_shell
38 38 from IPython.external.Itpl import ItplNS
39 39
40 40 # Global constants
41 41 COMPILER_ERROR = 'error'
42 42 INCOMPLETE_INPUT = 'incomplete'
43 43 COMPLETE_INPUT = 'complete'
44 44
45 45 ##############################################################################
46 46 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
47 47 # not
48 48
49 49 rc = Bunch()
50 50 rc.cache_size = 100
51 51 rc.pprint = True
52 52 rc.separate_in = '\n'
53 53 rc.separate_out = '\n'
54 54 rc.separate_out2 = ''
55 55 rc.prompt_in1 = r'In [\#]: '
56 56 rc.prompt_in2 = r' .\\D.: '
57 57 rc.prompt_out = ''
58 58 rc.prompts_pad_left = False
59 59
60 60 ##############################################################################
61 61
62 62 # Top-level utilities
63 63 def default_display_formatters():
64 64 """ Return a list of default display formatters.
65 65 """
66 66
67 67 from display_formatter import PPrintDisplayFormatter, ReprDisplayFormatter
68 68 return [PPrintDisplayFormatter(), ReprDisplayFormatter()]
69 69
70 70 def default_traceback_formatters():
71 71 """ Return a list of default traceback formatters.
72 72 """
73 73
74 74 from traceback_formatter import PlainTracebackFormatter
75 75 return [PlainTracebackFormatter()]
76 76
77 77 # Top-level classes
78 78 class NotDefined(object): pass
79 79
80 80 class Interpreter(object):
81 81 """ An interpreter object.
82 82
83 83 fixme: needs to negotiate available formatters with frontends.
84 84
85 85 Important: the interpeter should be built so that it exposes a method
86 86 for each attribute/method of its sub-object. This way it can be
87 87 replaced by a network adapter.
88 88 """
89 89
90 90 def __init__(self, user_ns=None, global_ns=None,translator=None,
91 91 magic=None, display_formatters=None,
92 92 traceback_formatters=None, output_trap=None, history=None,
93 93 message_cache=None, filename='<string>', config=None):
94 94
95 95 # The local/global namespaces for code execution
96 96 local_ns = user_ns # compatibility name
97 97 if local_ns is None:
98 98 local_ns = {}
99 99 self.user_ns = local_ns
100 100 # The local namespace
101 101 if global_ns is None:
102 102 global_ns = {}
103 103 self.user_global_ns = global_ns
104 104
105 105 # An object that will translate commands into executable Python.
106 106 # The current translator does not work properly so for now we are going
107 107 # without!
108 108 # if translator is None:
109 109 # from IPython.kernel.core.translator import IPythonTranslator
110 110 # translator = IPythonTranslator()
111 111 self.translator = translator
112 112
113 113 # An object that maintains magic commands.
114 114 if magic is None:
115 115 from IPython.kernel.core.magic import Magic
116 116 magic = Magic(self)
117 117 self.magic = magic
118 118
119 119 # A list of formatters for the displayhook.
120 120 if display_formatters is None:
121 121 display_formatters = default_display_formatters()
122 122 self.display_formatters = display_formatters
123 123
124 124 # A list of formatters for tracebacks.
125 125 if traceback_formatters is None:
126 126 traceback_formatters = default_traceback_formatters()
127 127 self.traceback_formatters = traceback_formatters
128 128
129 129 # The object trapping stdout/stderr.
130 130 if output_trap is None:
131 131 from IPython.kernel.core.output_trap import OutputTrap
132 132 output_trap = OutputTrap()
133 133 self.output_trap = output_trap
134 134
135 135 # An object that manages the history.
136 136 if history is None:
137 137 from IPython.kernel.core.history import InterpreterHistory
138 138 history = InterpreterHistory()
139 139 self.history = history
140 140 self.get_history_item = history.get_history_item
141 141 self.get_history_input_cache = history.get_input_cache
142 142 self.get_history_input_after = history.get_input_after
143 143
144 144 # An object that caches all of the return messages.
145 145 if message_cache is None:
146 146 from IPython.kernel.core.message_cache import SimpleMessageCache
147 147 message_cache = SimpleMessageCache()
148 148 self.message_cache = message_cache
149 149
150 150 # The "filename" of the code that is executed in this interpreter.
151 151 self.filename = filename
152 152
153 153 # An object that contains much configuration information.
154 154 if config is None:
155 155 # fixme: Move this constant elsewhere!
156 156 config = Bunch(ESC_MAGIC='%')
157 157 self.config = config
158 158
159 159 # Hook managers.
160 160 # fixme: make the display callbacks configurable. In the meantime,
161 161 # enable macros.
162 162 self.display_trap = DisplayTrap(
163 163 formatters=self.display_formatters,
164 164 callbacks=[self._possible_macro],
165 165 )
166 166 self.traceback_trap = TracebackTrap(
167 167 formatters=self.traceback_formatters)
168 168
169 169 # This is used temporarily for reformating exceptions in certain
170 170 # cases. It will go away once the ultraTB stuff is ported
171 171 # to ipython1
172 172 self.tbHandler = ultraTB.FormattedTB(color_scheme='NoColor',
173 173 mode='Context',
174 174 tb_offset=2)
175 175
176 176 # An object that can compile commands and remember __future__
177 177 # statements.
178 178 self.command_compiler = codeop.CommandCompiler()
179 179
180 180 # A replacement for the raw_input() and input() builtins. Change these
181 181 # attributes later to configure them.
182 182 self.raw_input_builtin = raw_input
183 183 self.input_builtin = input
184 184
185 185 # The number of the current cell.
186 186 self.current_cell_number = 1
187 187
188 188 # Initialize cache, set in/out prompts and printing system
189 189 self.outputcache = CachedOutput(self,
190 190 rc.cache_size,
191 191 rc.pprint,
192 192 input_sep = rc.separate_in,
193 193 output_sep = rc.separate_out,
194 194 output_sep2 = rc.separate_out2,
195 195 ps1 = rc.prompt_in1,
196 196 ps2 = rc.prompt_in2,
197 197 ps_out = rc.prompt_out,
198 198 pad_left = rc.prompts_pad_left)
199 199
200 200 # Need to decide later if this is the right approach, but clients
201 201 # commonly use sys.ps1/2, so it may be best to just set them here
202 202 sys.ps1 = self.outputcache.prompt1.p_str
203 203 sys.ps2 = self.outputcache.prompt2.p_str
204 204
205 205 # This is the message dictionary assigned temporarily when running the
206 206 # code.
207 207 self.message = None
208 208
209 209 self.setup_namespace()
210 210
211 211
212 212 #### Public 'Interpreter' interface ########################################
213 213
214 214 def formatTraceback(self, et, ev, tb, message=''):
215 215 """Put a formatted version of the traceback into value and reraise.
216 216
217 217 When exceptions have to be sent over the network, the traceback
218 218 needs to be put into the value of the exception in a nicely
219 219 formatted way. The method takes the type, value and tb of an
220 220 exception and puts a string representation of the tb into the
221 221 value of the exception and reraises it.
222 222
223 223 Currently this method uses the ultraTb formatter from IPython trunk.
224 224 Eventually it should simply use the traceback formatters in core
225 225 that are loaded into self.tracback_trap.formatters.
226 226 """
227 227 tbinfo = self.tbHandler.text(et,ev,tb)
228 228 ev._ipython_traceback_text = tbinfo
229 229 return et, ev, tb
230 230
231 231 def execute(self, commands, raiseException=True):
232 232 """ Execute some IPython commands.
233 233
234 234 1. Translate them into Python.
235 235 2. Run them.
236 236 3. Trap stdout/stderr.
237 237 4. Trap sys.displayhook().
238 238 5. Trap exceptions.
239 239 6. Return a message object.
240 240
241 241 Parameters
242 242 ----------
243 243 commands : str
244 244 The raw commands that the user typed into the prompt.
245 245
246 246 Returns
247 247 -------
248 248 message : dict
249 249 The dictionary of responses. See the README.txt in this directory
250 250 for an explanation of the format.
251 251 """
252 252
253 253 # Create a message dictionary with all of the information we will be
254 254 # returning to the frontend and other listeners.
255 255 message = self.setup_message()
256 256
257 257 # Massage the input and store the raw and translated commands into
258 258 # a dict.
259 259 user_input = dict(raw=commands)
260 260 if self.translator is not None:
261 261 python = self.translator(commands, message)
262 262 if python is None:
263 263 # Something went wrong with the translation. The translator
264 264 # should have added an appropriate entry to the message object.
265 265 return message
266 266 else:
267 267 python = commands
268 268 user_input['translated'] = python
269 269 message['input'] = user_input
270 270
271 271 # Set the message object so that any magics executed in the code have
272 272 # access.
273 273 self.message = message
274 274
275 275 # Set all of the output/exception traps.
276 276 self.set_traps()
277 277
278 278 # Actually execute the Python code.
279 279 status = self.execute_python(python)
280 280
281 281 # Unset all of the traps.
282 282 self.unset_traps()
283 283
284 284 # Unset the message object.
285 285 self.message = None
286 286
287 287 # Update the history variables in the namespace.
288 288 # E.g. In, Out, _, __, ___
289 289 if self.history is not None:
290 290 self.history.update_history(self, python)
291 291
292 292 # Let all of the traps contribute to the message and then clear their
293 293 # stored information.
294 294 self.output_trap.add_to_message(message)
295 295 self.output_trap.clear()
296 296 self.display_trap.add_to_message(message)
297 297 self.display_trap.clear()
298 298 self.traceback_trap.add_to_message(message)
299 299 # Pull out the type, value and tb of the current exception
300 300 # before clearing it.
301 301 einfo = self.traceback_trap.args
302 302 self.traceback_trap.clear()
303 303
304 304 # Cache the message.
305 305 self.message_cache.add_message(self.current_cell_number, message)
306 306
307 307 # Bump the number.
308 308 self.current_cell_number += 1
309 309
310 310 # This conditional lets the execute method either raise any
311 311 # exception that has occured in user code OR return the message
312 312 # dict containing the traceback and other useful info.
313 313 if raiseException and einfo:
314 314 raise einfo[0],einfo[1],einfo[2]
315 315 else:
316 316 return message
317 317
318 318 def generate_prompt(self, is_continuation):
319 319 """Calculate and return a string with the prompt to display.
320 320
321 321 :Parameters:
322 322 is_continuation : bool
323 323 Whether the input line is continuing multiline input or not, so
324 324 that a proper continuation prompt can be computed."""
325 325
326 326 if is_continuation:
327 327 return str(self.outputcache.prompt2)
328 328 else:
329 329 return str(self.outputcache.prompt1)
330 330
331 331 def execute_python(self, python):
332 332 """ Actually run the Python code in the namespace.
333 333
334 334 :Parameters:
335 335
336 336 python : str
337 337 Pure, exec'able Python code. Special IPython commands should have
338 338 already been translated into pure Python.
339 339 """
340 340
341 341 # We use a CommandCompiler instance to compile the code so as to keep
342 342 # track of __future__ imports.
343 343 try:
344 344 commands = self.split_commands(python)
345 345 except (SyntaxError, IndentationError), e:
346 346 # Save the exc_info so compilation related exceptions can be
347 347 # reraised
348 348 self.traceback_trap.args = sys.exc_info()
349 349 self.pack_exception(self.message,e)
350 350 return None
351 351
352 352 for cmd in commands:
353 353 try:
354 354 code = self.command_compiler(cmd, self.filename, 'single')
355 355 except (SyntaxError, OverflowError, ValueError), e:
356 356 self.traceback_trap.args = sys.exc_info()
357 357 self.pack_exception(self.message,e)
358 358 # No point in continuing if one block raised
359 359 return None
360 360 else:
361 361 self.execute_block(code)
362 362
363 363 def execute_block(self,code):
364 364 """Execute a single block of code in the user namespace.
365 365
366 366 Return value: a flag indicating whether the code to be run completed
367 367 successfully:
368 368
369 369 - 0: successful execution.
370 370 - 1: an error occurred.
371 371 """
372 372
373 373 outflag = 1 # start by assuming error, success will reset it
374 374 try:
375 375 exec code in self.user_ns
376 376 outflag = 0
377 377 except SystemExit:
378 378 self.resetbuffer()
379 379 self.traceback_trap.args = sys.exc_info()
380 380 except:
381 381 self.traceback_trap.args = sys.exc_info()
382 382
383 383 return outflag
384 384
385 385 def execute_macro(self, macro):
386 386 """ Execute the value of a macro.
387 387
388 388 Parameters
389 389 ----------
390 390 macro : Macro
391 391 """
392 392
393 393 python = macro.value
394 394 if self.translator is not None:
395 395 python = self.translator(python)
396 396 self.execute_python(python)
397 397
398 398 def getCommand(self, i=None):
399 399 """Gets the ith message in the message_cache.
400 400
401 401 This is implemented here for compatibility with the old ipython1 shell
402 402 I am not sure we need this though. I even seem to remember that we
403 403 were going to get rid of it.
404 404 """
405 405 return self.message_cache.get_message(i)
406 406
407 407 def reset(self):
408 408 """Reset the interpreter.
409 409
410 410 Currently this only resets the users variables in the namespace.
411 411 In the future we might want to also reset the other stateful
412 412 things like that the Interpreter has, like In, Out, etc.
413 413 """
414 414 self.user_ns.clear()
415 415 self.setup_namespace()
416 416
417 417 def complete(self,line,text=None, pos=None):
418 418 """Complete the given text.
419 419
420 420 :Parameters:
421 421
422 422 text : str
423 423 Text fragment to be completed on. Typically this is
424 424 """
425 425 # fixme: implement
426 426 raise NotImplementedError
427 427
428 428 def push(self, ns):
429 429 """ Put value into the namespace with name key.
430 430
431 431 Parameters
432 432 ----------
433 433 **kwds
434 434 """
435 435
436 436 self.user_ns.update(ns)
437 437
438 438 def push_function(self, ns):
439 439 # First set the func_globals for all functions to self.user_ns
440 440 new_kwds = {}
441 441 for k, v in ns.iteritems():
442 442 if not isinstance(v, FunctionType):
443 443 raise TypeError("function object expected")
444 444 new_kwds[k] = FunctionType(v.func_code, self.user_ns)
445 445 self.user_ns.update(new_kwds)
446 446
447 447 def pack_exception(self,message,exc):
448 448 message['exception'] = exc.__class__
449 449 message['exception_value'] = \
450 450 traceback.format_exception_only(exc.__class__, exc)
451 451
452 452 def feed_block(self, source, filename='<input>', symbol='single'):
453 453 """Compile some source in the interpreter.
454 454
455 455 One several things can happen:
456 456
457 457 1) The input is incorrect; compile_command() raised an
458 458 exception (SyntaxError or OverflowError).
459 459
460 460 2) The input is incomplete, and more input is required;
461 461 compile_command() returned None. Nothing happens.
462 462
463 463 3) The input is complete; compile_command() returned a code
464 464 object. The code is executed by calling self.runcode() (which
465 465 also handles run-time exceptions, except for SystemExit).
466 466
467 467 The return value is:
468 468
469 469 - True in case 2
470 470
471 471 - False in the other cases, unless an exception is raised, where
472 472 None is returned instead. This can be used by external callers to
473 473 know whether to continue feeding input or not.
474 474
475 475 The return value can be used to decide whether to use sys.ps1 or
476 476 sys.ps2 to prompt the next line."""
477 477
478 478 self.message = self.setup_message()
479 479
480 480 try:
481 481 code = self.command_compiler(source,filename,symbol)
482 482 except (OverflowError, SyntaxError, IndentationError, ValueError ), e:
483 483 # Case 1
484 484 self.traceback_trap.args = sys.exc_info()
485 485 self.pack_exception(self.message,e)
486 486 return COMPILER_ERROR,False
487 487
488 488 if code is None:
489 489 # Case 2: incomplete input. This means that the input can span
490 490 # multiple lines. But we still need to decide when to actually
491 491 # stop taking user input. Later we'll add auto-indentation support
492 492 # somehow. In the meantime, we'll just stop if there are two lines
493 493 # of pure whitespace at the end.
494 494 last_two = source.rsplit('\n',2)[-2:]
495 495 print 'last two:',last_two # dbg
496 496 if len(last_two)==2 and all(s.isspace() for s in last_two):
497 497 return COMPLETE_INPUT,False
498 498 else:
499 499 return INCOMPLETE_INPUT, True
500 500 else:
501 501 # Case 3
502 502 return COMPLETE_INPUT, False
503 503
504 504 def pull(self, keys):
505 505 """ Get an item out of the namespace by key.
506 506
507 507 Parameters
508 508 ----------
509 509 key : str
510 510
511 511 Returns
512 512 -------
513 513 value : object
514 514
515 515 Raises
516 516 ------
517 517 TypeError if the key is not a string.
518 518 NameError if the object doesn't exist.
519 519 """
520 520
521 521 if isinstance(keys, str):
522 522 result = self.user_ns.get(keys, NotDefined())
523 523 if isinstance(result, NotDefined):
524 524 raise NameError('name %s is not defined' % keys)
525 525 elif isinstance(keys, (list, tuple)):
526 526 result = []
527 527 for key in keys:
528 528 if not isinstance(key, str):
529 529 raise TypeError("objects must be keyed by strings.")
530 530 else:
531 531 r = self.user_ns.get(key, NotDefined())
532 532 if isinstance(r, NotDefined):
533 533 raise NameError('name %s is not defined' % key)
534 534 else:
535 535 result.append(r)
536 536 if len(keys)==1:
537 537 result = result[0]
538 538 else:
539 539 raise TypeError("keys must be a strong or a list/tuple of strings")
540 540 return result
541 541
542 542 def pull_function(self, keys):
543 543 return self.pull(keys)
544 544
545 545 #### Interactive user API ##################################################
546 546
547 547 def ipsystem(self, command):
548 548 """ Execute a command in a system shell while expanding variables in the
549 549 current namespace.
550 550
551 551 Parameters
552 552 ----------
553 553 command : str
554 554 """
555 555
556 556 # Expand $variables.
557 557 command = self.var_expand(command)
558 558
559 559 system_shell(command,
560 560 header='IPython system call: ',
561 561 verbose=self.rc.system_verbose,
562 562 )
563 563
564 564 def ipmagic(self, arg_string):
565 565 """ Call a magic function by name.
566 566
567 567 ipmagic('name -opt foo bar') is equivalent to typing at the ipython
568 568 prompt:
569 569
570 570 In[1]: %name -opt foo bar
571 571
572 572 To call a magic without arguments, simply use ipmagic('name').
573 573
574 574 This provides a proper Python function to call IPython's magics in any
575 575 valid Python code you can type at the interpreter, including loops and
576 576 compound statements. It is added by IPython to the Python builtin
577 577 namespace upon initialization.
578 578
579 579 Parameters
580 580 ----------
581 581 arg_string : str
582 582 A string containing the name of the magic function to call and any
583 583 additional arguments to be passed to the magic.
584 584
585 585 Returns
586 586 -------
587 587 something : object
588 588 The return value of the actual object.
589 589 """
590 590
591 591 # Taken from IPython.
592 592 raise NotImplementedError('Not ported yet')
593 593
594 594 args = arg_string.split(' ', 1)
595 595 magic_name = args[0]
596 596 magic_name = magic_name.lstrip(self.config.ESC_MAGIC)
597 597
598 598 try:
599 599 magic_args = args[1]
600 600 except IndexError:
601 601 magic_args = ''
602 602 fn = getattr(self.magic, 'magic_'+magic_name, None)
603 603 if fn is None:
604 604 self.error("Magic function `%s` not found." % magic_name)
605 605 else:
606 606 magic_args = self.var_expand(magic_args)
607 607 return fn(magic_args)
608 608
609 609
610 610 #### Private 'Interpreter' interface #######################################
611 611
612 612 def setup_message(self):
613 613 """Return a message object.
614 614
615 615 This method prepares and returns a message dictionary. This dict
616 616 contains the various fields that are used to transfer information about
617 617 execution, results, tracebacks, etc, to clients (either in or out of
618 618 process ones). Because of the need to work with possibly out of
619 619 process clients, this dict MUST contain strictly pickle-safe values.
620 620 """
621 621
622 622 return dict(number=self.current_cell_number)
623 623
624 624 def setup_namespace(self):
625 625 """ Add things to the namespace.
626 626 """
627 627
628 628 self.user_ns.setdefault('__name__', '__main__')
629 629 self.user_ns.setdefault('__builtins__', __builtin__)
630 630 self.user_ns['__IP'] = self
631 631 if self.raw_input_builtin is not None:
632 632 self.user_ns['raw_input'] = self.raw_input_builtin
633 633 if self.input_builtin is not None:
634 634 self.user_ns['input'] = self.input_builtin
635 635
636 636 builtin_additions = dict(
637 637 ipmagic=self.ipmagic,
638 638 )
639 639 __builtin__.__dict__.update(builtin_additions)
640 640
641 641 if self.history is not None:
642 642 self.history.setup_namespace(self.user_ns)
643 643
644 644 def set_traps(self):
645 645 """ Set all of the output, display, and traceback traps.
646 646 """
647 647
648 648 self.output_trap.set()
649 649 self.display_trap.set()
650 650 self.traceback_trap.set()
651 651
652 652 def unset_traps(self):
653 653 """ Unset all of the output, display, and traceback traps.
654 654 """
655 655
656 656 self.output_trap.unset()
657 657 self.display_trap.unset()
658 658 self.traceback_trap.unset()
659 659
660 660 def split_commands(self, python):
661 661 """ Split multiple lines of code into discrete commands that can be
662 662 executed singly.
663 663
664 664 Parameters
665 665 ----------
666 666 python : str
667 667 Pure, exec'able Python code.
668 668
669 669 Returns
670 670 -------
671 671 commands : list of str
672 672 Separate commands that can be exec'ed independently.
673 673 """
674 674
675 675 # compiler.parse treats trailing spaces after a newline as a
676 676 # SyntaxError. This is different than codeop.CommandCompiler, which
677 677 # will compile the trailng spaces just fine. We simply strip any
678 678 # trailing whitespace off. Passing a string with trailing whitespace
679 679 # to exec will fail however. There seems to be some inconsistency in
680 680 # how trailing whitespace is handled, but this seems to work.
681 681 python = python.strip()
682 682
683 # The compiler module does not like unicode. We need to convert
684 # it encode it:
685 if isinstance(python, unicode):
686 # Use the utf-8-sig BOM so the compiler detects this a UTF-8
687 # encode string.
688 python = '\xef\xbb\xbf' + python.encode('utf-8')
689
683 690 # The compiler module will parse the code into an abstract syntax tree.
684 691 ast = compiler.parse(python)
685 692
686 693 # Uncomment to help debug the ast tree
687 694 # for n in ast.node:
688 695 # print n.lineno,'->',n
689 696
690 697 # Each separate command is available by iterating over ast.node. The
691 698 # lineno attribute is the line number (1-indexed) beginning the commands
692 699 # suite.
693 700 # lines ending with ";" yield a Discard Node that doesn't have a lineno
694 701 # attribute. These nodes can and should be discarded. But there are
695 702 # other situations that cause Discard nodes that shouldn't be discarded.
696 703 # We might eventually discover other cases where lineno is None and have
697 704 # to put in a more sophisticated test.
698 705 linenos = [x.lineno-1 for x in ast.node if x.lineno is not None]
699 706
700 707 # When we finally get the slices, we will need to slice all the way to
701 708 # the end even though we don't have a line number for it. Fortunately,
702 709 # None does the job nicely.
703 710 linenos.append(None)
704 711 lines = python.splitlines()
705 712
706 713 # Create a list of atomic commands.
707 714 cmds = []
708 715 for i, j in zip(linenos[:-1], linenos[1:]):
709 716 cmd = lines[i:j]
710 717 if cmd:
711 718 cmds.append('\n'.join(cmd)+'\n')
712 719
713 720 return cmds
714 721
715 722 def error(self, text):
716 723 """ Pass an error message back to the shell.
717 724
718 725 Preconditions
719 726 -------------
720 727 This should only be called when self.message is set. In other words,
721 728 when code is being executed.
722 729
723 730 Parameters
724 731 ----------
725 732 text : str
726 733 """
727 734
728 735 errors = self.message.get('IPYTHON_ERROR', [])
729 736 errors.append(text)
730 737
731 738 def var_expand(self, template):
732 739 """ Expand $variables in the current namespace using Itpl.
733 740
734 741 Parameters
735 742 ----------
736 743 template : str
737 744 """
738 745
739 746 return str(ItplNS(template, self.user_ns))
740 747
741 748 def _possible_macro(self, obj):
742 749 """ If the object is a macro, execute it.
743 750 """
744 751
745 752 if isinstance(obj, Macro):
746 753 self.execute_macro(obj)
747 754
@@ -1,324 +1,347 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3
4 4 """Start an IPython cluster conveniently, either locally or remotely.
5 5
6 6 Basic usage
7 7 -----------
8 8
9 9 For local operation, the simplest mode of usage is:
10 10
11 11 %prog -n N
12 12
13 13 where N is the number of engines you want started.
14 14
15 15 For remote operation, you must call it with a cluster description file:
16 16
17 17 %prog -f clusterfile.py
18 18
19 19 The cluster file is a normal Python script which gets run via execfile(). You
20 20 can have arbitrary logic in it, but all that matters is that at the end of the
21 21 execution, it declares the variables 'controller', 'engines', and optionally
22 22 'sshx'. See the accompanying examples for details on what these variables must
23 23 contain.
24 24
25 25
26 26 Notes
27 27 -----
28 28
29 29 WARNING: this code is still UNFINISHED and EXPERIMENTAL! It is incomplete,
30 30 some listed options are not really implemented, and all of its interfaces are
31 31 subject to change.
32 32
33 33 When operating over SSH for a remote cluster, this program relies on the
34 34 existence of a particular script called 'sshx'. This script must live in the
35 35 target systems where you'll be running your controller and engines, and is
36 36 needed to configure your PATH and PYTHONPATH variables for further execution of
37 37 python code at the other end of an SSH connection. The script can be as simple
38 38 as:
39 39
40 40 #!/bin/sh
41 41 . $HOME/.bashrc
42 42 "$@"
43 43
44 44 which is the default one provided by IPython. You can modify this or provide
45 45 your own. Since it's quite likely that for different clusters you may need
46 46 this script to configure things differently or that it may live in different
47 47 locations, its full path can be set in the same file where you define the
48 48 cluster setup. IPython's order of evaluation for this variable is the
49 49 following:
50 50
51 51 a) Internal default: 'sshx'. This only works if it is in the default system
52 52 path which SSH sets up in non-interactive mode.
53 53
54 54 b) Environment variable: if $IPYTHON_SSHX is defined, this overrides the
55 55 internal default.
56 56
57 57 c) Variable 'sshx' in the cluster configuration file: finally, this will
58 58 override the previous two values.
59 59
60 60 This code is Unix-only, with precious little hope of any of this ever working
61 61 under Windows, since we need SSH from the ground up, we background processes,
62 62 etc. Ports of this functionality to Windows are welcome.
63 63
64 64
65 65 Call summary
66 66 ------------
67 67
68 68 %prog [options]
69 69 """
70 70
71 71 __docformat__ = "restructuredtext en"
72 72
73 73 #-------------------------------------------------------------------------------
74 74 # Copyright (C) 2008 The IPython Development Team
75 75 #
76 76 # Distributed under the terms of the BSD License. The full license is in
77 77 # the file COPYING, distributed as part of this software.
78 78 #-------------------------------------------------------------------------------
79 79
80 80 #-------------------------------------------------------------------------------
81 81 # Stdlib imports
82 82 #-------------------------------------------------------------------------------
83 83
84 84 import os
85 85 import signal
86 86 import sys
87 87 import time
88 88
89 89 from optparse import OptionParser
90 90 from subprocess import Popen,call
91 91
92 92 #---------------------------------------------------------------------------
93 93 # IPython imports
94 94 #---------------------------------------------------------------------------
95 95 from IPython.tools import utils
96 from IPython.config import cutils
96 from IPython.genutils import get_ipython_dir
97 97
98 98 #---------------------------------------------------------------------------
99 99 # Normal code begins
100 100 #---------------------------------------------------------------------------
101 101
102 102 def parse_args():
103 103 """Parse command line and return opts,args."""
104 104
105 105 parser = OptionParser(usage=__doc__)
106 106 newopt = parser.add_option # shorthand
107 107
108 108 newopt("--controller-port", type="int", dest="controllerport",
109 109 help="the TCP port the controller is listening on")
110 110
111 111 newopt("--controller-ip", type="string", dest="controllerip",
112 112 help="the TCP ip address of the controller")
113 113
114 114 newopt("-n", "--num", type="int", dest="n",default=2,
115 115 help="the number of engines to start")
116 116
117 117 newopt("--engine-port", type="int", dest="engineport",
118 118 help="the TCP port the controller will listen on for engine "
119 119 "connections")
120 120
121 121 newopt("--engine-ip", type="string", dest="engineip",
122 122 help="the TCP ip address the controller will listen on "
123 123 "for engine connections")
124 124
125 125 newopt("--mpi", type="string", dest="mpi",
126 126 help="use mpi with package: for instance --mpi=mpi4py")
127 127
128 128 newopt("-l", "--logfile", type="string", dest="logfile",
129 129 help="log file name")
130 130
131 131 newopt('-f','--cluster-file',dest='clusterfile',
132 132 help='file describing a remote cluster')
133 133
134 134 return parser.parse_args()
135 135
136 136 def numAlive(controller,engines):
137 137 """Return the number of processes still alive."""
138 138 retcodes = [controller.poll()] + \
139 139 [e.poll() for e in engines]
140 140 return retcodes.count(None)
141 141
142 142 stop = lambda pid: os.kill(pid,signal.SIGINT)
143 143 kill = lambda pid: os.kill(pid,signal.SIGTERM)
144 144
145 145 def cleanup(clean,controller,engines):
146 146 """Stop the controller and engines with the given cleanup method."""
147 147
148 148 for e in engines:
149 149 if e.poll() is None:
150 150 print 'Stopping engine, pid',e.pid
151 151 clean(e.pid)
152 152 if controller.poll() is None:
153 153 print 'Stopping controller, pid',controller.pid
154 154 clean(controller.pid)
155 155
156 156
157 157 def ensureDir(path):
158 158 """Ensure a directory exists or raise an exception."""
159 159 if not os.path.isdir(path):
160 160 os.makedirs(path)
161 161
162 162
163 163 def startMsg(control_host,control_port=10105):
164 164 """Print a startup message"""
165 165 print
166 166 print 'Your cluster is up and running.'
167 167 print
168 168 print 'For interactive use, you can make a MultiEngineClient with:'
169 169 print
170 170 print 'from IPython.kernel import client'
171 171 print "mec = client.MultiEngineClient()"
172 172 print
173 173 print 'You can then cleanly stop the cluster from IPython using:'
174 174 print
175 175 print 'mec.kill(controller=True)'
176 176 print
177 177
178 178
179 179 def clusterLocal(opt,arg):
180 180 """Start a cluster on the local machine."""
181 181
182 182 # Store all logs inside the ipython directory
183 ipdir = cutils.get_ipython_dir()
183 ipdir = get_ipython_dir()
184 184 pjoin = os.path.join
185 185
186 186 logfile = opt.logfile
187 187 if logfile is None:
188 188 logdir_base = pjoin(ipdir,'log')
189 189 ensureDir(logdir_base)
190 190 logfile = pjoin(logdir_base,'ipcluster-')
191 191
192 192 print 'Starting controller:',
193 193 controller = Popen(['ipcontroller','--logfile',logfile,'-x','-y'])
194 194 print 'Controller PID:',controller.pid
195 195
196 196 print 'Starting engines: ',
197 197 time.sleep(5)
198 198
199 199 englogfile = '%s%s-' % (logfile,controller.pid)
200 200 mpi = opt.mpi
201 201 if mpi: # start with mpi - killing the engines with sigterm will not work if you do this
202 202 engines = [Popen(['mpirun', '-np', str(opt.n), 'ipengine', '--mpi',
203 203 mpi, '--logfile',englogfile])]
204 204 # engines = [Popen(['mpirun', '-np', str(opt.n), 'ipengine', '--mpi', mpi])]
205 205 else: # do what we would normally do
206 206 engines = [ Popen(['ipengine','--logfile',englogfile])
207 207 for i in range(opt.n) ]
208 208 eids = [e.pid for e in engines]
209 209 print 'Engines PIDs: ',eids
210 210 print 'Log files: %s*' % englogfile
211 211
212 212 proc_ids = eids + [controller.pid]
213 213 procs = engines + [controller]
214 214
215 215 grpid = os.getpgrp()
216 216 try:
217 217 startMsg('127.0.0.1')
218 218 print 'You can also hit Ctrl-C to stop it, or use from the cmd line:'
219 219 print
220 220 print 'kill -INT',grpid
221 221 print
222 222 try:
223 223 while True:
224 224 time.sleep(5)
225 225 except:
226 226 pass
227 227 finally:
228 228 print 'Stopping cluster. Cleaning up...'
229 229 cleanup(stop,controller,engines)
230 230 for i in range(4):
231 231 time.sleep(i+2)
232 232 nZombies = numAlive(controller,engines)
233 233 if nZombies== 0:
234 234 print 'OK: All processes cleaned up.'
235 235 break
236 236 print 'Trying again, %d processes did not stop...' % nZombies
237 237 cleanup(kill,controller,engines)
238 238 if numAlive(controller,engines) == 0:
239 239 print 'OK: All processes cleaned up.'
240 240 break
241 241 else:
242 242 print '*'*75
243 243 print 'ERROR: could not kill some processes, try to do it',
244 244 print 'manually.'
245 245 zombies = []
246 246 if controller.returncode is None:
247 247 print 'Controller is alive: pid =',controller.pid
248 248 zombies.append(controller.pid)
249 249 liveEngines = [ e for e in engines if e.returncode is None ]
250 250 for e in liveEngines:
251 251 print 'Engine is alive: pid =',e.pid
252 252 zombies.append(e.pid)
253 253 print
254 254 print 'Zombie summary:',' '.join(map(str,zombies))
255 255
256 256 def clusterRemote(opt,arg):
257 257 """Start a remote cluster over SSH"""
258 258
259 # B. Granger, 9/3/08
260 # The launching of a remote cluster using SSH and a clusterfile
261 # is broken. Because it won't be fixed before the 0.9 release,
262 # we are removing it. For now, we just print a message to the
263 # user and abort.
264
265 print """The launching of a remote IPython cluster using SSL
266 and a clusterfile has been removed in this release.
267 It has been broken for a while and we are in the process
268 of building a new process management system that will be
269 used to provide a more robust way of starting an IPython
270 cluster.
271
272 For now remote clusters have to be launched using ipcontroller
273 and ipengine separately.
274 """
275 sys.exit(1)
276
259 277 # Load the remote cluster configuration
260 278 clConfig = {}
261 279 execfile(opt.clusterfile,clConfig)
262 280 contConfig = clConfig['controller']
263 281 engConfig = clConfig['engines']
264 282 # Determine where to find sshx:
265 283 sshx = clConfig.get('sshx',os.environ.get('IPYTHON_SSHX','sshx'))
266 284
267 285 # Store all logs inside the ipython directory
268 ipdir = cutils.get_ipython_dir()
286 ipdir = get_ipython_dir()
269 287 pjoin = os.path.join
270 288
271 289 logfile = opt.logfile
272 290 if logfile is None:
273 291 logdir_base = pjoin(ipdir,'log')
274 292 ensureDir(logdir_base)
275 293 logfile = pjoin(logdir_base,'ipcluster')
276 294
277 295 # Append this script's PID to the logfile name always
278 296 logfile = '%s-%s' % (logfile,os.getpid())
279 297
280 298 print 'Starting controller:'
281 299 # Controller data:
282 300 xsys = os.system
283 301
284 302 contHost = contConfig['host']
285 303 contLog = '%s-con-%s-' % (logfile,contHost)
286 304 cmd = "ssh %s '%s' 'ipcontroller --logfile %s' &" % \
287 305 (contHost,sshx,contLog)
288 306 #print 'cmd:<%s>' % cmd # dbg
289 307 xsys(cmd)
290 308 time.sleep(2)
291 309
292 310 print 'Starting engines: '
293 311 for engineHost,engineData in engConfig.iteritems():
294 312 if isinstance(engineData,int):
295 313 numEngines = engineData
296 314 else:
297 315 raise NotImplementedError('port configuration not finished for engines')
298 316
299 317 print 'Sarting %d engines on %s' % (numEngines,engineHost)
300 318 engLog = '%s-eng-%s-' % (logfile,engineHost)
301 319 for i in range(numEngines):
302 320 cmd = "ssh %s '%s' 'ipengine --controller-ip %s --logfile %s' &" % \
303 321 (engineHost,sshx,contHost,engLog)
304 322 #print 'cmd:<%s>' % cmd # dbg
305 323 xsys(cmd)
306 324 # Wait after each host a little bit
307 325 time.sleep(1)
308 326
309 327 startMsg(contConfig['host'])
310 328
311 329 def main():
312 330 """Main driver for the two big options: local or remote cluster."""
313 331
332 if sys.platform=='win32':
333 print """ipcluster does not work on Microsoft Windows. Please start
334 your IPython cluster using the ipcontroller and ipengine scripts."""
335 sys.exit(1)
336
314 337 opt,arg = parse_args()
315 338
316 339 clusterfile = opt.clusterfile
317 340 if clusterfile:
318 341 clusterRemote(opt,arg)
319 342 else:
320 343 clusterLocal(opt,arg)
321 344
322 345
323 346 if __name__=='__main__':
324 347 main()
@@ -1,171 +1,172 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3
4 4 """Start the IPython Engine."""
5 5
6 6 __docformat__ = "restructuredtext en"
7 7
8 8 #-------------------------------------------------------------------------------
9 9 # Copyright (C) 2008 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-------------------------------------------------------------------------------
14 14
15 15 #-------------------------------------------------------------------------------
16 16 # Imports
17 17 #-------------------------------------------------------------------------------
18 18
19 19 # Python looks for an empty string at the beginning of sys.path to enable
20 20 # importing from the cwd.
21 21 import sys
22 22 sys.path.insert(0, '')
23 23
24 24 import sys, os
25 25 from optparse import OptionParser
26 26
27 27 from twisted.application import service
28 28 from twisted.internet import reactor
29 29 from twisted.python import log
30 30
31 31 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
32 32
33 33 from IPython.kernel.core.config import config_manager as core_config_manager
34 34 from IPython.config.cutils import import_item
35 35 from IPython.kernel.engineservice import EngineService
36 36 from IPython.kernel.config import config_manager as kernel_config_manager
37 37 from IPython.kernel.engineconnector import EngineConnector
38 38
39 39
40 40 #-------------------------------------------------------------------------------
41 41 # Code
42 42 #-------------------------------------------------------------------------------
43 43
44 44 def start_engine():
45 45 """
46 46 Start the engine, by creating it and starting the Twisted reactor.
47 47
48 48 This method does:
49 49
50 50 * If it exists, runs the `mpi_import_statement` to call `MPI_Init`
51 51 * Starts the engine logging
52 52 * Creates an IPython shell and wraps it in an `EngineService`
53 53 * Creates a `foolscap.Tub` to use in connecting to a controller.
54 54 * Uses the tub and the `EngineService` along with a Foolscap URL
55 55 (or FURL) to connect to the controller and register the engine
56 56 with the controller
57 57 """
58 58 kernel_config = kernel_config_manager.get_config_obj()
59 59 core_config = core_config_manager.get_config_obj()
60 60
61 61
62 62 # Execute the mpi import statement that needs to call MPI_Init
63 63 global mpi
64 64 mpikey = kernel_config['mpi']['default']
65 65 mpi_import_statement = kernel_config['mpi'].get(mpikey, None)
66 66 if mpi_import_statement is not None:
67 67 try:
68 68 exec mpi_import_statement in globals()
69 69 except:
70 70 mpi = None
71 71 else:
72 72 mpi = None
73 73
74 74 # Start logging
75 75 logfile = kernel_config['engine']['logfile']
76 76 if logfile:
77 77 logfile = logfile + str(os.getpid()) + '.log'
78 78 try:
79 79 openLogFile = open(logfile, 'w')
80 80 except:
81 81 openLogFile = sys.stdout
82 82 else:
83 83 openLogFile = sys.stdout
84 84 log.startLogging(openLogFile)
85 85
86 86 # Create the underlying shell class and EngineService
87 87 shell_class = import_item(core_config['shell']['shell_class'])
88 88 engine_service = EngineService(shell_class, mpi=mpi)
89 89 shell_import_statement = core_config['shell']['import_statement']
90 90 if shell_import_statement:
91 91 try:
92 92 engine_service.execute(shell_import_statement)
93 93 except:
94 94 log.msg("Error running import_statement: %s" % sis)
95 95
96 96 # Create the service hierarchy
97 97 main_service = service.MultiService()
98 98 engine_service.setServiceParent(main_service)
99 99 tub_service = Tub()
100 100 tub_service.setServiceParent(main_service)
101 101 # This needs to be called before the connection is initiated
102 102 main_service.startService()
103 103
104 104 # This initiates the connection to the controller and calls
105 105 # register_engine to tell the controller we are ready to do work
106 106 engine_connector = EngineConnector(tub_service)
107 107 furl_file = kernel_config['engine']['furl_file']
108 log.msg("Using furl file: %s" % furl_file)
108 109 d = engine_connector.connect_to_controller(engine_service, furl_file)
109 110 d.addErrback(lambda _: reactor.stop())
110 111
111 112 reactor.run()
112 113
113 114
114 115 def init_config():
115 116 """
116 117 Initialize the configuration using default and command line options.
117 118 """
118 119
119 120 parser = OptionParser()
120 121
121 122 parser.add_option(
122 123 "--furl-file",
123 124 type="string",
124 125 dest="furl_file",
125 126 help="The filename containing the FURL of the controller"
126 127 )
127 128 parser.add_option(
128 129 "--mpi",
129 130 type="string",
130 131 dest="mpi",
131 132 help="How to enable MPI (mpi4py, pytrilinos, or empty string to disable)"
132 133 )
133 134 parser.add_option(
134 135 "-l",
135 136 "--logfile",
136 137 type="string",
137 138 dest="logfile",
138 139 help="log file name (default is stdout)"
139 140 )
140 141 parser.add_option(
141 142 "--ipythondir",
142 143 type="string",
143 144 dest="ipythondir",
144 145 help="look for config files and profiles in this directory"
145 146 )
146 147
147 148 (options, args) = parser.parse_args()
148 149
149 150 kernel_config_manager.update_config_obj_from_default_file(options.ipythondir)
150 151 core_config_manager.update_config_obj_from_default_file(options.ipythondir)
151 152
152 153 kernel_config = kernel_config_manager.get_config_obj()
153 154 # Now override with command line options
154 155 if options.furl_file is not None:
155 156 kernel_config['engine']['furl_file'] = options.furl_file
156 157 if options.logfile is not None:
157 158 kernel_config['engine']['logfile'] = options.logfile
158 159 if options.mpi is not None:
159 160 kernel_config['mpi']['default'] = options.mpi
160 161
161 162
162 163 def main():
163 164 """
164 165 After creating the configuration information, start the engine.
165 166 """
166 167 init_config()
167 168 start_engine()
168 169
169 170
170 171 if __name__ == "__main__":
171 172 main() No newline at end of file
@@ -1,373 +1,373 b''
1 1 # encoding: utf-8
2 2
3 3 """Test template for complete engine object"""
4 4
5 5 __docformat__ = "restructuredtext en"
6 6
7 7 #-------------------------------------------------------------------------------
8 8 # Copyright (C) 2008 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-------------------------------------------------------------------------------
13 13
14 14 #-------------------------------------------------------------------------------
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 18 import cPickle as pickle
19 19
20 20 from twisted.internet import defer, reactor
21 21 from twisted.python import failure
22 22 from twisted.application import service
23 23 import zope.interface as zi
24 24
25 25 from IPython.kernel import newserialized
26 26 from IPython.kernel import error
27 27 from IPython.kernel.pickleutil import can, uncan
28 28 import IPython.kernel.engineservice as es
29 29 from IPython.kernel.core.interpreter import Interpreter
30 30 from IPython.testing.parametric import Parametric, parametric
31 31
32 32 #-------------------------------------------------------------------------------
33 33 # Tests
34 34 #-------------------------------------------------------------------------------
35 35
36 36
37 37 # A sequence of valid commands run through execute
38 38 validCommands = ['a=5',
39 39 'b=10',
40 40 'a=5; b=10; c=a+b',
41 41 'import math; 2.0*math.pi',
42 42 """def f():
43 43 result = 0.0
44 44 for i in range(10):
45 45 result += i
46 46 """,
47 47 'if 1<2: a=5',
48 48 """import time
49 49 time.sleep(0.1)""",
50 50 """from math import cos;
51 51 x = 1.0*cos(0.5)""", # Semicolons lead to Discard ast nodes that should be discarded
52 52 """from sets import Set
53 53 s = Set()
54 54 """, # Trailing whitespace should be allowed.
55 55 """import math
56 56 math.cos(1.0)""", # Test a method call with a discarded return value
57 57 """x=1.0234
58 58 a=5; b=10""", # Test an embedded semicolon
59 59 """x=1.0234
60 60 a=5; b=10;""" # Test both an embedded and trailing semicolon
61 61 ]
62 62
63 63 # A sequence of commands that raise various exceptions
64 64 invalidCommands = [('a=1/0',ZeroDivisionError),
65 65 ('print v',NameError),
66 66 ('l=[];l[0]',IndexError),
67 67 ("d={};d['a']",KeyError),
68 68 ("assert 1==0",AssertionError),
69 69 ("import abababsdbfsbaljasdlja",ImportError),
70 70 ("raise Exception()",Exception)]
71 71
72 72 def testf(x):
73 73 return 2.0*x
74 74
75 75 globala = 99
76 76
77 77 def testg(x):
78 78 return globala*x
79 79
80 80 class IEngineCoreTestCase(object):
81 81 """Test an IEngineCore implementer."""
82 82
83 83 def createShell(self):
84 84 return Interpreter()
85 85
86 86 def catchQueueCleared(self, f):
87 87 try:
88 88 f.raiseException()
89 89 except error.QueueCleared:
90 90 pass
91 91
92 92 def testIEngineCoreInterface(self):
93 93 """Does self.engine claim to implement IEngineCore?"""
94 94 self.assert_(es.IEngineCore.providedBy(self.engine))
95 95
96 96 def testIEngineCoreInterfaceMethods(self):
97 97 """Does self.engine have the methods and attributes in IEngineCore."""
98 98 for m in list(es.IEngineCore):
99 99 self.assert_(hasattr(self.engine, m))
100 100
101 101 def testIEngineCoreDeferreds(self):
102 102 d = self.engine.execute('a=5')
103 103 d.addCallback(lambda _: self.engine.pull('a'))
104 104 d.addCallback(lambda _: self.engine.get_result())
105 105 d.addCallback(lambda _: self.engine.keys())
106 106 d.addCallback(lambda _: self.engine.push(dict(a=10)))
107 107 return d
108 108
109 109 def runTestExecute(self, cmd):
110 110 self.shell = Interpreter()
111 111 actual = self.shell.execute(cmd)
112 112 def compare(computed):
113 113 actual['id'] = computed['id']
114 114 self.assertEquals(actual, computed)
115 115 d = self.engine.execute(cmd)
116 116 d.addCallback(compare)
117 117 return d
118 118
119 119 @parametric
120 120 def testExecute(cls):
121 121 return [(cls.runTestExecute, cmd) for cmd in validCommands]
122 122
123 123 def runTestExecuteFailures(self, cmd, exc):
124 124 def compare(f):
125 125 self.assertRaises(exc, f.raiseException)
126 126 d = self.engine.execute(cmd)
127 127 d.addErrback(compare)
128 128 return d
129 129
130 130 @parametric
131 131 def testExecuteFailuresEngineService(cls):
132 132 return [(cls.runTestExecuteFailures, cmd, exc)
133 133 for cmd, exc in invalidCommands]
134 134
135 135 def runTestPushPull(self, o):
136 136 d = self.engine.push(dict(a=o))
137 137 d.addCallback(lambda r: self.engine.pull('a'))
138 138 d.addCallback(lambda r: self.assertEquals(o,r))
139 139 return d
140 140
141 141 @parametric
142 142 def testPushPull(cls):
143 143 objs = [10,"hi there",1.2342354,{"p":(1,2)},None]
144 144 return [(cls.runTestPushPull, o) for o in objs]
145 145
146 146 def testPullNameError(self):
147 147 d = self.engine.push(dict(a=5))
148 148 d.addCallback(lambda _:self.engine.reset())
149 149 d.addCallback(lambda _: self.engine.pull("a"))
150 150 d.addErrback(lambda f: self.assertRaises(NameError, f.raiseException))
151 151 return d
152 152
153 153 def testPushPullFailures(self):
154 154 d = self.engine.pull('a')
155 155 d.addErrback(lambda f: self.assertRaises(NameError, f.raiseException))
156 156 d.addCallback(lambda _: self.engine.execute('l = lambda x: x'))
157 157 d.addCallback(lambda _: self.engine.pull('l'))
158 158 d.addErrback(lambda f: self.assertRaises(pickle.PicklingError, f.raiseException))
159 159 d.addCallback(lambda _: self.engine.push(dict(l=lambda x: x)))
160 160 d.addErrback(lambda f: self.assertRaises(pickle.PicklingError, f.raiseException))
161 161 return d
162 162
163 163 def testPushPullArray(self):
164 164 try:
165 165 import numpy
166 166 except:
167 167 return
168 168 a = numpy.random.random(1000)
169 169 d = self.engine.push(dict(a=a))
170 170 d.addCallback(lambda _: self.engine.pull('a'))
171 171 d.addCallback(lambda b: b==a)
172 172 d.addCallback(lambda c: c.all())
173 173 return self.assertDeferredEquals(d, True)
174 174
175 175 def testPushFunction(self):
176 176
177 177 d = self.engine.push_function(dict(f=testf))
178 178 d.addCallback(lambda _: self.engine.execute('result = f(10)'))
179 179 d.addCallback(lambda _: self.engine.pull('result'))
180 180 d.addCallback(lambda r: self.assertEquals(r, testf(10)))
181 181 return d
182 182
183 183 def testPullFunction(self):
184 184 d = self.engine.push_function(dict(f=testf, g=testg))
185 185 d.addCallback(lambda _: self.engine.pull_function(('f','g')))
186 186 d.addCallback(lambda r: self.assertEquals(r[0](10), testf(10)))
187 187 return d
188 188
189 189 def testPushFunctionGlobal(self):
190 190 """Make sure that pushed functions pick up the user's namespace for globals."""
191 191 d = self.engine.push(dict(globala=globala))
192 192 d.addCallback(lambda _: self.engine.push_function(dict(g=testg)))
193 193 d.addCallback(lambda _: self.engine.execute('result = g(10)'))
194 194 d.addCallback(lambda _: self.engine.pull('result'))
195 195 d.addCallback(lambda r: self.assertEquals(r, testg(10)))
196 196 return d
197 197
198 198 def testGetResultFailure(self):
199 199 d = self.engine.get_result(None)
200 200 d.addErrback(lambda f: self.assertRaises(IndexError, f.raiseException))
201 201 d.addCallback(lambda _: self.engine.get_result(10))
202 202 d.addErrback(lambda f: self.assertRaises(IndexError, f.raiseException))
203 203 return d
204 204
205 205 def runTestGetResult(self, cmd):
206 206 self.shell = Interpreter()
207 207 actual = self.shell.execute(cmd)
208 208 def compare(computed):
209 209 actual['id'] = computed['id']
210 210 self.assertEquals(actual, computed)
211 211 d = self.engine.execute(cmd)
212 212 d.addCallback(lambda r: self.engine.get_result(r['number']))
213 213 d.addCallback(compare)
214 214 return d
215 215
216 216 @parametric
217 217 def testGetResult(cls):
218 218 return [(cls.runTestGetResult, cmd) for cmd in validCommands]
219 219
220 220 def testGetResultDefault(self):
221 221 cmd = 'a=5'
222 222 shell = self.createShell()
223 223 shellResult = shell.execute(cmd)
224 224 def popit(dikt, key):
225 225 dikt.pop(key)
226 226 return dikt
227 227 d = self.engine.execute(cmd)
228 228 d.addCallback(lambda _: self.engine.get_result())
229 229 d.addCallback(lambda r: self.assertEquals(shellResult, popit(r,'id')))
230 230 return d
231 231
232 232 def testKeys(self):
233 233 d = self.engine.keys()
234 234 d.addCallback(lambda s: isinstance(s, list))
235 235 d.addCallback(lambda r: self.assertEquals(r, True))
236 236 return d
237 237
238 238 Parametric(IEngineCoreTestCase)
239 239
240 240 class IEngineSerializedTestCase(object):
241 241 """Test an IEngineCore implementer."""
242 242
243 243 def testIEngineSerializedInterface(self):
244 244 """Does self.engine claim to implement IEngineCore?"""
245 245 self.assert_(es.IEngineSerialized.providedBy(self.engine))
246 246
247 247 def testIEngineSerializedInterfaceMethods(self):
248 """Does self.engine have the methods and attributes in IEngireCore."""
248 """Does self.engine have the methods and attributes in IEngineCore."""
249 249 for m in list(es.IEngineSerialized):
250 250 self.assert_(hasattr(self.engine, m))
251 251
252 252 def testIEngineSerializedDeferreds(self):
253 253 dList = []
254 254 d = self.engine.push_serialized(dict(key=newserialized.serialize(12345)))
255 255 self.assert_(isinstance(d, defer.Deferred))
256 256 dList.append(d)
257 257 d = self.engine.pull_serialized('key')
258 258 self.assert_(isinstance(d, defer.Deferred))
259 259 dList.append(d)
260 260 D = defer.DeferredList(dList)
261 261 return D
262 262
263 263 def testPushPullSerialized(self):
264 264 objs = [10,"hi there",1.2342354,{"p":(1,2)}]
265 265 d = defer.succeed(None)
266 266 for o in objs:
267 267 self.engine.push_serialized(dict(key=newserialized.serialize(o)))
268 268 value = self.engine.pull_serialized('key')
269 269 value.addCallback(lambda serial: newserialized.IUnSerialized(serial).getObject())
270 270 d = self.assertDeferredEquals(value,o,d)
271 271 return d
272 272
273 273 def testPullSerializedFailures(self):
274 274 d = self.engine.pull_serialized('a')
275 275 d.addErrback(lambda f: self.assertRaises(NameError, f.raiseException))
276 276 d.addCallback(lambda _: self.engine.execute('l = lambda x: x'))
277 277 d.addCallback(lambda _: self.engine.pull_serialized('l'))
278 278 d.addErrback(lambda f: self.assertRaises(pickle.PicklingError, f.raiseException))
279 279 return d
280 280
281 281 Parametric(IEngineSerializedTestCase)
282 282
283 283 class IEngineQueuedTestCase(object):
284 284 """Test an IEngineQueued implementer."""
285 285
286 286 def testIEngineQueuedInterface(self):
287 287 """Does self.engine claim to implement IEngineQueued?"""
288 288 self.assert_(es.IEngineQueued.providedBy(self.engine))
289 289
290 290 def testIEngineQueuedInterfaceMethods(self):
291 """Does self.engine have the methods and attributes in IEngireQueued."""
291 """Does self.engine have the methods and attributes in IEngineQueued."""
292 292 for m in list(es.IEngineQueued):
293 293 self.assert_(hasattr(self.engine, m))
294 294
295 295 def testIEngineQueuedDeferreds(self):
296 296 dList = []
297 297 d = self.engine.clear_queue()
298 298 self.assert_(isinstance(d, defer.Deferred))
299 299 dList.append(d)
300 300 d = self.engine.queue_status()
301 301 self.assert_(isinstance(d, defer.Deferred))
302 302 dList.append(d)
303 303 D = defer.DeferredList(dList)
304 304 return D
305 305
306 306 def testClearQueue(self):
307 307 result = self.engine.clear_queue()
308 308 d1 = self.assertDeferredEquals(result, None)
309 309 d1.addCallback(lambda _: self.engine.queue_status())
310 310 d2 = self.assertDeferredEquals(d1, {'queue':[], 'pending':'None'})
311 311 return d2
312 312
313 313 def testQueueStatus(self):
314 314 result = self.engine.queue_status()
315 315 result.addCallback(lambda r: 'queue' in r and 'pending' in r)
316 316 d = self.assertDeferredEquals(result, True)
317 317 return d
318 318
319 319 Parametric(IEngineQueuedTestCase)
320 320
321 321 class IEnginePropertiesTestCase(object):
322 322 """Test an IEngineProperties implementor."""
323 323
324 324 def testIEnginePropertiesInterface(self):
325 325 """Does self.engine claim to implement IEngineProperties?"""
326 326 self.assert_(es.IEngineProperties.providedBy(self.engine))
327 327
328 328 def testIEnginePropertiesInterfaceMethods(self):
329 """Does self.engine have the methods and attributes in IEngireProperties."""
329 """Does self.engine have the methods and attributes in IEngineProperties."""
330 330 for m in list(es.IEngineProperties):
331 331 self.assert_(hasattr(self.engine, m))
332 332
333 333 def testGetSetProperties(self):
334 334 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
335 335 d = self.engine.set_properties(dikt)
336 336 d.addCallback(lambda r: self.engine.get_properties())
337 337 d = self.assertDeferredEquals(d, dikt)
338 338 d.addCallback(lambda r: self.engine.get_properties(('c',)))
339 339 d = self.assertDeferredEquals(d, {'c': dikt['c']})
340 340 d.addCallback(lambda r: self.engine.set_properties(dict(c=False)))
341 341 d.addCallback(lambda r: self.engine.get_properties(('c', 'd')))
342 342 d = self.assertDeferredEquals(d, dict(c=False, d=None))
343 343 return d
344 344
345 345 def testClearProperties(self):
346 346 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
347 347 d = self.engine.set_properties(dikt)
348 348 d.addCallback(lambda r: self.engine.clear_properties())
349 349 d.addCallback(lambda r: self.engine.get_properties())
350 350 d = self.assertDeferredEquals(d, {})
351 351 return d
352 352
353 353 def testDelHasProperties(self):
354 354 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
355 355 d = self.engine.set_properties(dikt)
356 356 d.addCallback(lambda r: self.engine.del_properties(('b','e')))
357 357 d.addCallback(lambda r: self.engine.has_properties(('a','b','c','d','e')))
358 358 d = self.assertDeferredEquals(d, [True, False, True, True, False])
359 359 return d
360 360
361 361 def testStrictDict(self):
362 362 s = """from IPython.kernel.engineservice import get_engine
363 363 p = get_engine(%s).properties"""%self.engine.id
364 364 d = self.engine.execute(s)
365 365 d.addCallback(lambda r: self.engine.execute("p['a'] = lambda _:None"))
366 366 d = self.assertDeferredRaises(d, error.InvalidProperty)
367 367 d.addCallback(lambda r: self.engine.execute("p['a'] = range(5)"))
368 368 d.addCallback(lambda r: self.engine.execute("p['a'].append(5)"))
369 369 d.addCallback(lambda r: self.engine.get_properties('a'))
370 370 d = self.assertDeferredEquals(d, dict(a=range(5)))
371 371 return d
372 372
373 373 Parametric(IEnginePropertiesTestCase)
@@ -1,75 +1,74 b''
1 1 # Set this prefix to where you want to install the plugin
2 PREFIX=~/usr/local
3 PREFIX=~/tmp/local
2 PREFIX=/usr/local
4 3
5 4 NOSE0=nosetests -vs --with-doctest --doctest-tests --detailed-errors
6 5 NOSE=nosetests -vvs --with-ipdoctest --doctest-tests --doctest-extension=txt \
7 6 --detailed-errors
8 7
9 8 SRC=ipdoctest.py setup.py ../decorators.py
10 9
11 10 # Default target for clean 'make'
12 11 default: iplib
13 12
14 13 # The actual plugin installation
15 14 plugin: IPython_doctest_plugin.egg-info
16 15
17 16 # Simple targets that test one thing
18 17 simple: plugin simple.py
19 18 $(NOSE) simple.py
20 19
21 20 dtest: plugin dtexample.py
22 21 $(NOSE) dtexample.py
23 22
24 23 rtest: plugin test_refs.py
25 24 $(NOSE) test_refs.py
26 25
27 26 test: plugin dtexample.py
28 27 $(NOSE) dtexample.py test*.py test*.txt
29 28
30 29 deb: plugin dtexample.py
31 30 $(NOSE) test_combo.txt
32 31
33 32 # IPython tests
34 33 deco:
35 34 $(NOSE0) IPython.testing.decorators
36 35
37 36 magic: plugin
38 37 $(NOSE) IPython.Magic
39 38
40 39 ipipe: plugin
41 40 $(NOSE) IPython.Extensions.ipipe
42 41
43 42 iplib: plugin
44 43 $(NOSE) IPython.iplib
45 44
46 45 strd: plugin
47 46 $(NOSE) IPython.strdispatch
48 47
49 48 engine: plugin
50 49 $(NOSE) IPython.kernel
51 50
52 51 tf: plugin
53 52 $(NOSE) IPython.config.traitlets
54 53
55 54 # All of ipython itself
56 55 ipython: plugin
57 56 $(NOSE) IPython
58 57
59 58
60 59 # Combined targets
61 60 sr: rtest strd
62 61
63 62 base: dtest rtest test strd deco
64 63
65 64 quick: base iplib ipipe
66 65
67 66 all: base ipython
68 67
69 68 # Main plugin and cleanup
70 69 IPython_doctest_plugin.egg-info: $(SRC)
71 70 python setup.py install --prefix=$(PREFIX)
72 71 touch $@
73 72
74 73 clean:
75 74 rm -rf IPython_doctest_plugin.egg-info *~ *pyc build/ dist/
@@ -1,42 +1,43 b''
1 1 import numpy as N
2 2 from math import *
3 3
4 4 class MCOptionPricer(object):
5 5 def __init__(self, S=100.0, K=100.0, sigma=0.25, r=0.05, days=260, paths=10000):
6 6 self.S = S
7 7 self.K = K
8 8 self.sigma = sigma
9 9 self.r = r
10 10 self.days = days
11 11 self.paths = paths
12 12 self.h = 1.0/self.days
13 13 self.const1 = exp((self.r-0.5*self.sigma**2)*self.h)
14 14 self.const2 = self.sigma*sqrt(self.h)
15 15
16 16 def run(self):
17 17 stock_price = self.S*N.ones(self.paths, dtype='float64')
18 18 stock_price_sum = N.zeros(self.paths, dtype='float64')
19 19 for j in range(self.days):
20 20 growth_factor = self.const1*N.exp(self.const2*N.random.standard_normal(self.paths))
21 21 stock_price = stock_price*growth_factor
22 22 stock_price_sum = stock_price_sum + stock_price
23 23 stock_price_avg = stock_price_sum/self.days
24 24 zeros = N.zeros(self.paths, dtype='float64')
25 25 r_factor = exp(-self.r*self.h*self.days)
26 26 self.vanilla_put = r_factor*N.mean(N.maximum(zeros,self.K-stock_price))
27 27 self.asian_put = r_factor*N.mean(N.maximum(zeros,self.K-stock_price_avg))
28 28 self.vanilla_call = r_factor*N.mean(N.maximum(zeros,stock_price-self.K))
29 29 self.asian_call = r_factor*N.mean(N.maximum(zeros,stock_price_avg-self.K))
30 30
31 31
32 32 def main():
33 33 op = MCOptionPricer()
34 34 op.run()
35 35 print "Vanilla Put Price = ", op.vanilla_put
36 36 print "Asian Put Price = ", op.asian_put
37 37 print "Vanilla Call Price = ", op.vanilla_call
38 38 print "Asian Call Price = ", op.asian_call
39 39
40 40
41 41 if __name__ == '__main__':
42 42 main()
43
@@ -1,57 +1,57 b''
1 1 #-------------------------------------------------------------------------------
2 2 # Driver code that the client runs.
3 3 #-------------------------------------------------------------------------------
4 4 # To run this code start a controller and engines using:
5 5 # ipcluster -n 2
6 6 # Then run the scripts by doing irunner rmt.ipy or by starting ipython and
7 7 # doing run rmt.ipy.
8 8
9 9 from rmtkernel import *
10 10 from IPython.kernel import client
11 11
12 12
13 13 def wignerDistribution(s):
14 14 """Returns (s, rho(s)) for the Wigner GOE distribution."""
15 15 return (numpy.pi*s/2.0) * numpy.exp(-numpy.pi*s**2/4.)
16 16
17 17
18 18 def generateWignerData():
19 19 s = numpy.linspace(0.0,4.0,400)
20 20 rhos = wignerDistribution(s)
21 21 return s, rhos
22 22
23 23
24 24 def serialDiffs(num, N):
25 25 diffs = ensembleDiffs(num, N)
26 26 normalizedDiffs = normalizeDiffs(diffs)
27 27 return normalizedDiffs
28 28
29 29
30 30 def parallelDiffs(rc, num, N):
31 31 nengines = len(rc.get_ids())
32 32 num_per_engine = num/nengines
33 33 print "Running with", num_per_engine, "per engine."
34 34 rc.push(dict(num_per_engine=num_per_engine, N=N))
35 35 rc.execute('diffs = ensembleDiffs(num_per_engine, N)')
36 36 # gather blocks always for now
37 37 pr = rc.gather('diffs')
38 38 return pr.r
39 39
40 40
41 41 # Main code
42 42 if __name__ == '__main__':
43 43 rc = client.MultiEngineClient()
44 44 print "Distributing code to engines..."
45 45 r = rc.run('rmtkernel.py')
46 46 rc.block = False
47 47
48 48 # Simulation parameters
49 49 nmats = 100
50 50 matsize = 30
51 51
52 52 %timeit -n1 -r1 serialDiffs(nmats,matsize)
53 53 %timeit -n1 -r1 parallelDiffs(rc, nmats, matsize)
54 54
55 55 # Uncomment these to plot the histogram
56 import pylab
57 pylab.hist(parallelDiffs(rc,matsize,matsize))
56 # import pylab
57 # pylab.hist(parallelDiffs(rc,matsize,matsize))
@@ -1,89 +1,106 b''
1 1 #!python
2 2 """Windows-specific part of the installation"""
3 3
4 4 import os, sys, shutil
5 pjoin = os.path.join
5 6
6 7 def mkshortcut(target,description,link_file,*args,**kw):
7 8 """make a shortcut if it doesn't exist, and register its creation"""
8 9
9 10 create_shortcut(target, description, link_file,*args,**kw)
10 11 file_created(link_file)
11 12
12 13 def install():
13 14 """Routine to be run by the win32 installer with the -install switch."""
14 15
15 16 from IPython.Release import version
16 17
17 18 # Get some system constants
18 19 prefix = sys.prefix
19 python = prefix + r'\python.exe'
20 python = pjoin(prefix, 'python.exe')
21
20 22 # Lookup path to common startmenu ...
21 ip_dir = get_special_folder_path('CSIDL_COMMON_PROGRAMS') + r'\IPython'
22
23 # Some usability warnings at installation time. I don't want them at the
24 # top-level, so they don't appear if the user is uninstalling.
25 try:
26 import ctypes
27 except ImportError:
28 print ('To take full advantage of IPython, you need ctypes from:\n'
29 'http://sourceforge.net/projects/ctypes')
30
31 try:
32 import win32con
33 except ImportError:
34 print ('To take full advantage of IPython, you need pywin32 from:\n'
35 'http://starship.python.net/crew/mhammond/win32/Downloads.html')
36
37 try:
38 import readline
39 except ImportError:
40 print ('To take full advantage of IPython, you need readline from:\n'
41 'https://launchpad.net/pyreadline')
42
43 ipybase = '"' + prefix + r'\scripts\ipython"'
23 ip_start_menu = pjoin(get_special_folder_path('CSIDL_COMMON_PROGRAMS'), 'IPython')
44 24 # Create IPython entry ...
45 if not os.path.isdir(ip_dir):
46 os.mkdir(ip_dir)
47 directory_created(ip_dir)
48
49 # Create program shortcuts ...
50 f = ip_dir + r'\IPython.lnk'
51 a = ipybase
52 mkshortcut(python,'IPython',f,a)
53
54 f = ip_dir + r'\pysh.lnk'
55 a = ipybase+' -p sh'
56 mkshortcut(python,'IPython (command prompt mode)',f,a)
57
58 f = ip_dir + r'\pylab.lnk'
59 a = ipybase+' -pylab'
60 mkshortcut(python,'IPython (PyLab mode)',f,a)
61
62 f = ip_dir + r'\scipy.lnk'
63 a = ipybase+' -pylab -p scipy'
64 mkshortcut(python,'IPython (scipy profile)',f,a)
25 if not os.path.isdir(ip_start_menu):
26 os.mkdir(ip_start_menu)
27 directory_created(ip_start_menu)
28
29 # Create .py and .bat files to make things available from
30 # the Windows command line. Thanks to the Twisted project
31 # for this logic!
32 programs = [
33 'ipython',
34 'iptest',
35 'ipcontroller',
36 'ipengine',
37 'ipcluster',
38 'ipythonx',
39 'ipython-wx',
40 'irunner'
41 ]
42 scripts = pjoin(prefix,'scripts')
43 for program in programs:
44 raw = pjoin(scripts, program)
45 bat = raw + '.bat'
46 py = raw + '.py'
47 # Create .py versions of the scripts
48 shutil.copy(raw, py)
49 # Create .bat files for each of the scripts
50 bat_file = file(bat,'w')
51 bat_file.write("@%s %s %%*" % (python, py))
52 bat_file.close()
53
54 # Now move onto setting the Start Menu up
55 ipybase = pjoin(scripts, 'ipython')
56
57 link = pjoin(ip_start_menu, 'IPython.lnk')
58 cmd = '"%s"' % ipybase
59 mkshortcut(python,'IPython',link,cmd)
60
61 link = pjoin(ip_start_menu, 'pysh.lnk')
62 cmd = '"%s" -p sh' % ipybase
63 mkshortcut(python,'IPython (command prompt mode)',link,cmd)
64
65 link = pjoin(ip_start_menu, 'pylab.lnk')
66 cmd = '"%s" -pylab' % ipybase
67 mkshortcut(python,'IPython (PyLab mode)',link,cmd)
68
69 link = pjoin(ip_start_menu, 'scipy.lnk')
70 cmd = '"%s" -pylab -p scipy' % ipybase
71 mkshortcut(python,'IPython (scipy profile)',link,cmd)
72
73 link = pjoin(ip_start_menu, 'IPython test suite.lnk')
74 cmd = '"%s" -vv' % pjoin(scripts, 'iptest')
75 mkshortcut(python,'Run the IPython test suite',link,cmd)
76
77 link = pjoin(ip_start_menu, 'ipcontroller.lnk')
78 cmd = '"%s" -xy' % pjoin(scripts, 'ipcontroller')
79 mkshortcut(python,'IPython controller',link,cmd)
80
81 link = pjoin(ip_start_menu, 'ipengine.lnk')
82 cmd = '"%s"' % pjoin(scripts, 'ipengine')
83 mkshortcut(python,'IPython engine',link,cmd)
65 84
66 85 # Create documentation shortcuts ...
67 86 t = prefix + r'\share\doc\ipython\manual\ipython.pdf'
68 f = ip_dir + r'\Manual in PDF.lnk'
87 f = ip_start_menu + r'\Manual in PDF.lnk'
69 88 mkshortcut(t,r'IPython Manual - PDF-Format',f)
70 89
71 90 t = prefix + r'\share\doc\ipython\manual\html\index.html'
72 f = ip_dir + r'\Manual in HTML.lnk'
91 f = ip_start_menu + r'\Manual in HTML.lnk'
73 92 mkshortcut(t,'IPython Manual - HTML-Format',f)
74 93
75 # make ipython.py
76 shutil.copy(prefix + r'\scripts\ipython', prefix + r'\scripts\ipython.py')
77 94
78 95 def remove():
79 96 """Routine to be run by the win32 installer with the -remove switch."""
80 97 pass
81 98
82 99 # main()
83 100 if len(sys.argv) > 1:
84 101 if sys.argv[1] == '-install':
85 102 install()
86 103 elif sys.argv[1] == '-remove':
87 104 remove()
88 105 else:
89 106 print "Script was called with option %s" % sys.argv[1]
@@ -1,278 +1,279 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,
86 86 others=None):
87 87 """
88 88 Add a package to the list of packages, including certain subpackages.
89 89 """
90 90 packages.append('.'.join(['IPython',pname]))
91 91 if config:
92 92 packages.append('.'.join(['IPython',pname,'config']))
93 93 if tests:
94 94 packages.append('.'.join(['IPython',pname,'tests']))
95 95 if scripts:
96 96 packages.append('.'.join(['IPython',pname,'scripts']))
97 97 if others is not None:
98 98 for o in others:
99 99 packages.append('.'.join(['IPython',pname,o]))
100 100
101 101 def find_packages():
102 102 """
103 103 Find all of IPython's packages.
104 104 """
105 105 packages = ['IPython']
106 106 add_package(packages, 'config', tests=True)
107 107 add_package(packages , 'Extensions')
108 108 add_package(packages, 'external')
109 109 add_package(packages, 'gui')
110 110 add_package(packages, 'gui.wx')
111 111 add_package(packages, 'frontend', tests=True)
112 112 add_package(packages, 'frontend._process')
113 113 add_package(packages, 'frontend.wx')
114 114 add_package(packages, 'frontend.cocoa', tests=True)
115 115 add_package(packages, 'kernel', config=True, tests=True, scripts=True)
116 116 add_package(packages, 'kernel.core', config=True, tests=True)
117 117 add_package(packages, 'testing', tests=True)
118 add_package(packages, 'tests')
118 119 add_package(packages, 'testing.plugin', tests=False)
119 120 add_package(packages, 'tools', tests=True)
120 121 add_package(packages, 'UserConfig')
121 122 return packages
122 123
123 124 #---------------------------------------------------------------------------
124 125 # Find package data
125 126 #---------------------------------------------------------------------------
126 127
127 128 def find_package_data():
128 129 """
129 130 Find IPython's package_data.
130 131 """
131 132 # This is not enough for these things to appear in an sdist.
132 133 # We need to muck with the MANIFEST to get this to work
133 134 package_data = {
134 135 'IPython.UserConfig' : ['*'],
135 136 'IPython.tools.tests' : ['*.txt'],
136 137 'IPython.testing' : ['*.txt']
137 138 }
138 139 return package_data
139 140
140 141
141 142 #---------------------------------------------------------------------------
142 143 # Find data files
143 144 #---------------------------------------------------------------------------
144 145
145 146 def make_dir_struct(tag,base,out_base):
146 147 """Make the directory structure of all files below a starting dir.
147 148
148 149 This is just a convenience routine to help build a nested directory
149 150 hierarchy because distutils is too stupid to do this by itself.
150 151
151 152 XXX - this needs a proper docstring!
152 153 """
153 154
154 155 # we'll use these a lot below
155 156 lbase = len(base)
156 157 pathsep = os.path.sep
157 158 lpathsep = len(pathsep)
158 159
159 160 out = []
160 161 for (dirpath,dirnames,filenames) in os.walk(base):
161 162 # we need to strip out the dirpath from the base to map it to the
162 163 # output (installation) path. This requires possibly stripping the
163 164 # path separator, because otherwise pjoin will not work correctly
164 165 # (pjoin('foo/','/bar') returns '/bar').
165 166
166 167 dp_eff = dirpath[lbase:]
167 168 if dp_eff.startswith(pathsep):
168 169 dp_eff = dp_eff[lpathsep:]
169 170 # The output path must be anchored at the out_base marker
170 171 out_path = pjoin(out_base,dp_eff)
171 172 # Now we can generate the final filenames. Since os.walk only produces
172 173 # filenames, we must join back with the dirpath to get full valid file
173 174 # paths:
174 175 pfiles = [pjoin(dirpath,f) for f in filenames]
175 176 # Finally, generate the entry we need, which is a triple of (tag,output
176 177 # path, files) for use as a data_files parameter in install_data.
177 178 out.append((tag,out_path,pfiles))
178 179
179 180 return out
180 181
181 182
182 183 def find_data_files():
183 184 """
184 185 Find IPython's data_files.
185 186
186 187 Most of these are docs.
187 188 """
188 189
189 190 docdirbase = 'share/doc/ipython'
190 191 manpagebase = 'share/man/man1'
191 192
192 193 # Simple file lists can be made by hand
193 194 manpages = filter(isfile, glob('docs/man/*.1.gz'))
194 195 igridhelpfiles = filter(isfile, glob('IPython/Extensions/igrid_help.*'))
195 196
196 197 # For nested structures, use the utility above
197 198 example_files = make_dir_struct('data','docs/examples',
198 199 pjoin(docdirbase,'examples'))
199 200 manual_files = make_dir_struct('data','docs/dist',pjoin(docdirbase,'manual'))
200 201
201 202 # And assemble the entire output list
202 203 data_files = [ ('data',manpagebase, manpages),
203 204 ('data',pjoin(docdirbase,'extensions'),igridhelpfiles),
204 205 ] + manual_files + example_files
205 206
206 207 ## import pprint # dbg
207 208 ## print '*'*80
208 209 ## print 'data files'
209 210 ## pprint.pprint(data_files)
210 211 ## print '*'*80
211 212
212 213 return data_files
213 214
214 215 #---------------------------------------------------------------------------
215 216 # Find scripts
216 217 #---------------------------------------------------------------------------
217 218
218 219 def find_scripts():
219 220 """
220 221 Find IPython's scripts.
221 222 """
222 223 scripts = ['IPython/kernel/scripts/ipengine',
223 224 'IPython/kernel/scripts/ipcontroller',
224 225 'IPython/kernel/scripts/ipcluster',
225 226 'scripts/ipython',
226 227 'scripts/ipythonx',
227 228 'scripts/ipython-wx',
228 229 'scripts/pycolor',
229 230 'scripts/irunner',
230 231 'scripts/iptest',
231 232 ]
232 233
233 234 # Script to be run by the windows binary installer after the default setup
234 235 # routine, to add shortcuts and similar windows-only things. Windows
235 236 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
236 237 # doesn't find them.
237 238 if 'bdist_wininst' in sys.argv:
238 239 if len(sys.argv) > 2 and ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
239 240 print >> sys.stderr,"ERROR: bdist_wininst must be run alone. Exiting."
240 241 sys.exit(1)
241 242 scripts.append('scripts/ipython_win_post_install.py')
242 243
243 244 return scripts
244 245
245 246 #---------------------------------------------------------------------------
246 247 # Verify all dependencies
247 248 #---------------------------------------------------------------------------
248 249
249 250 def check_for_dependencies():
250 251 """Check for IPython's dependencies.
251 252
252 253 This function should NOT be called if running under setuptools!
253 254 """
254 255 from setupext.setupext import (
255 256 print_line, print_raw, print_status, print_message,
256 257 check_for_zopeinterface, check_for_twisted,
257 258 check_for_foolscap, check_for_pyopenssl,
258 259 check_for_sphinx, check_for_pygments,
259 260 check_for_nose, check_for_pexpect
260 261 )
261 262 print_line()
262 263 print_raw("BUILDING IPYTHON")
263 264 print_status('python', sys.version)
264 265 print_status('platform', sys.platform)
265 266 if sys.platform == 'win32':
266 267 print_status('Windows version', sys.getwindowsversion())
267 268
268 269 print_raw("")
269 270 print_raw("OPTIONAL DEPENDENCIES")
270 271
271 272 check_for_zopeinterface()
272 273 check_for_twisted()
273 274 check_for_foolscap()
274 275 check_for_pyopenssl()
275 276 check_for_sphinx()
276 277 check_for_pygments()
277 278 check_for_nose()
278 279 check_for_pexpect()
@@ -1,20 +1,14 b''
1 1 #!/usr/bin/env python
2 2 """Wrapper to run setup.py using setuptools."""
3 3
4 import os
5 4 import sys
6 5
7 # Add my local path to sys.path
8 home = os.environ['HOME']
9 sys.path.insert(0,'%s/usr/local/lib/python%s/site-packages' %
10 (home,sys.version[:3]))
11
12 6 # now, import setuptools and call the actual setup
13 7 import setuptools
14 8 # print sys.argv
15 9 #sys.argv=['','bdist_egg']
16 10 execfile('setup.py')
17 11
18 12 # clean up the junk left around by setuptools
19 13 if "develop" not in sys.argv:
20 14 os.system('rm -rf ipython.egg-info build')
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now