##// END OF EJS Templates
Merging with upstream
Fernando Perez -
r2532:227ec978 merge
parent child Browse files
Show More
@@ -0,0 +1,132 b''
1 # encoding: utf-8
2 """
3 Testing related decorators for use with twisted.trial.
4
5 The decorators in this files are designed to follow the same API as those
6 in the decorators module (in this same directory). But they can be used
7 with twisted.trial
8 """
9
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2008-2009 The IPython Development Team
12 #
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
15 #-----------------------------------------------------------------------------
16
17 #-----------------------------------------------------------------------------
18 # Imports
19 #-----------------------------------------------------------------------------
20
21 import os
22 import sys
23
24 from IPython.testing.decorators import make_label_dec
25
26 #-----------------------------------------------------------------------------
27 # Testing decorators
28 #-----------------------------------------------------------------------------
29
30
31 def skipif(skip_condition, msg=None):
32 """Create a decorator that marks a test function for skipping.
33
34 The is a decorator factory that returns a decorator that will
35 conditionally skip a test based on the value of skip_condition. The
36 skip_condition argument can either be a boolean or a callable that returns
37 a boolean.
38
39 Parameters
40 ----------
41 skip_condition : boolean or callable
42 If this evaluates to True, the test is skipped.
43 msg : str
44 The message to print if the test is skipped.
45
46 Returns
47 -------
48 decorator : function
49 The decorator function that can be applied to the test function.
50 """
51
52 def skip_decorator(f):
53
54 # Allow for both boolean or callable skip conditions.
55 if callable(skip_condition):
56 skip_val = lambda : skip_condition()
57 else:
58 skip_val = lambda : skip_condition
59
60 if msg is None:
61 out = 'Test skipped due to test condition.'
62 else:
63 out = msg
64 final_msg = "Skipping test: %s. %s" % (f.__name__,out)
65
66 if skip_val():
67 f.skip = final_msg
68
69 return f
70 return skip_decorator
71
72
73 def skip(msg=None):
74 """Create a decorator that marks a test function for skipping.
75
76 This is a decorator factory that returns a decorator that will cause
77 tests to be skipped.
78
79 Parameters
80 ----------
81 msg : str
82 Optional message to be added.
83
84 Returns
85 -------
86 decorator : function
87 Decorator, which, when applied to a function, sets the skip
88 attribute of the function causing `twisted.trial` to skip it.
89 """
90
91 return skipif(True,msg)
92
93
94 def numpy_not_available():
95 """Can numpy be imported? Returns true if numpy does NOT import.
96
97 This is used to make a decorator to skip tests that require numpy to be
98 available, but delay the 'import numpy' to test execution time.
99 """
100 try:
101 import numpy
102 np_not_avail = False
103 except ImportError:
104 np_not_avail = True
105
106 return np_not_avail
107
108 #-----------------------------------------------------------------------------
109 # Decorators for public use
110 #-----------------------------------------------------------------------------
111
112 # Decorators to skip certain tests on specific platforms.
113 skip_win32 = skipif(sys.platform == 'win32',
114 "This test does not run under Windows")
115 skip_linux = skipif(sys.platform == 'linux2',
116 "This test does not run under Linux")
117 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
118
119 # Decorators to skip tests if not on specific platforms.
120 skip_if_not_win32 = skipif(sys.platform != 'win32',
121 "This test only runs under Windows")
122 skip_if_not_linux = skipif(sys.platform != 'linux2',
123 "This test only runs under Linux")
124 skip_if_not_osx = skipif(sys.platform != 'darwin',
125 "This test only runs under OSX")
126
127 # Other skip decorators
128 skipif_not_numpy = skipif(numpy_not_available,"This test requires numpy")
129
130 skipknownfailure = skip('This test is known to fail')
131
132
@@ -0,0 +1,167 b''
1 # encoding: utf-8
2 """
3 Older utilities that are not being used.
4
5 WARNING: IF YOU NEED TO USE ONE OF THESE FUNCTIONS, PLEASE FIRST MOVE IT
6 TO ANOTHER APPROPRIATE MODULE IN IPython.utils.
7 """
8
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2008-2009 The IPython Development Team
11 #
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
15
16 #-----------------------------------------------------------------------------
17 # Imports
18 #-----------------------------------------------------------------------------
19
20 import sys
21 import warnings
22
23 from IPython.utils.warn import warn
24
25 #-----------------------------------------------------------------------------
26 # Code
27 #-----------------------------------------------------------------------------
28
29
30 def mutex_opts(dict,ex_op):
31 """Check for presence of mutually exclusive keys in a dict.
32
33 Call: mutex_opts(dict,[[op1a,op1b],[op2a,op2b]...]"""
34 for op1,op2 in ex_op:
35 if op1 in dict and op2 in dict:
36 raise ValueError,'\n*** ERROR in Arguments *** '\
37 'Options '+op1+' and '+op2+' are mutually exclusive.'
38
39
40 class EvalDict:
41 """
42 Emulate a dict which evaluates its contents in the caller's frame.
43
44 Usage:
45 >>> number = 19
46
47 >>> text = "python"
48
49 >>> print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
50 Python 2.1 rules!
51 """
52
53 # This version is due to sismex01@hebmex.com on c.l.py, and is basically a
54 # modified (shorter) version of:
55 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66018 by
56 # Skip Montanaro (skip@pobox.com).
57
58 def __getitem__(self, name):
59 frame = sys._getframe(1)
60 return eval(name, frame.f_globals, frame.f_locals)
61
62 EvalString = EvalDict # for backwards compatibility
63
64
65 def all_belong(candidates,checklist):
66 """Check whether a list of items ALL appear in a given list of options.
67
68 Returns a single 1 or 0 value."""
69
70 return 1-(0 in [x in checklist for x in candidates])
71
72
73 def belong(candidates,checklist):
74 """Check whether a list of items appear in a given list of options.
75
76 Returns a list of 1 and 0, one for each candidate given."""
77
78 return [x in checklist for x in candidates]
79
80
81 def with_obj(object, **args):
82 """Set multiple attributes for an object, similar to Pascal's with.
83
84 Example:
85 with_obj(jim,
86 born = 1960,
87 haircolour = 'Brown',
88 eyecolour = 'Green')
89
90 Credit: Greg Ewing, in
91 http://mail.python.org/pipermail/python-list/2001-May/040703.html.
92
93 NOTE: up until IPython 0.7.2, this was called simply 'with', but 'with'
94 has become a keyword for Python 2.5, so we had to rename it."""
95
96 object.__dict__.update(args)
97
98
99 def map_method(method,object_list,*argseq,**kw):
100 """map_method(method,object_list,*args,**kw) -> list
101
102 Return a list of the results of applying the methods to the items of the
103 argument sequence(s). If more than one sequence is given, the method is
104 called with an argument list consisting of the corresponding item of each
105 sequence. All sequences must be of the same length.
106
107 Keyword arguments are passed verbatim to all objects called.
108
109 This is Python code, so it's not nearly as fast as the builtin map()."""
110
111 out_list = []
112 idx = 0
113 for object in object_list:
114 try:
115 handler = getattr(object, method)
116 except AttributeError:
117 out_list.append(None)
118 else:
119 if argseq:
120 args = map(lambda lst:lst[idx],argseq)
121 #print 'ob',object,'hand',handler,'ar',args # dbg
122 out_list.append(handler(args,**kw))
123 else:
124 out_list.append(handler(**kw))
125 idx += 1
126 return out_list
127
128
129 def import_fail_info(mod_name,fns=None):
130 """Inform load failure for a module."""
131
132 if fns == None:
133 warn("Loading of %s failed.\n" % (mod_name,))
134 else:
135 warn("Loading of %s from %s failed.\n" % (fns,mod_name))
136
137
138 class NotGiven: pass
139
140 def popkey(dct,key,default=NotGiven):
141 """Return dct[key] and delete dct[key].
142
143 If default is given, return it if dct[key] doesn't exist, otherwise raise
144 KeyError. """
145
146 try:
147 val = dct[key]
148 except KeyError:
149 if default is NotGiven:
150 raise
151 else:
152 return default
153 else:
154 del dct[key]
155 return val
156
157
158 def wrap_deprecated(func, suggest = '<nothing>'):
159 def newFunc(*args, **kwargs):
160 warnings.warn("Call to deprecated function %s, use %s instead" %
161 ( func.__name__, suggest),
162 category=DeprecationWarning,
163 stacklevel = 2)
164 return func(*args, **kwargs)
165 return newFunc
166
167
@@ -0,0 +1,31 b''
1 # encoding: utf-8
2 """
3 See if we have curses.
4 """
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 #-----------------------------------------------------------------------------
18 # Code
19 #-----------------------------------------------------------------------------
20
21 # Curses and termios are Unix-only modules
22 try:
23 import curses
24 # We need termios as well, so if its import happens to raise, we bail on
25 # using curses altogether.
26 import termios
27 except ImportError:
28 use_curses = False
29 else:
30 # Curses on Solaris may not be complete, so we can't use it there
31 use_curses = hasattr(curses,'initscr') No newline at end of file
@@ -0,0 +1,106 b''
1 # encoding: utf-8
2 """Utilities for working with data structures like lists, dicts and tuples.
3 """
4
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2008-2009 The IPython Development Team
7 #
8 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING, distributed as part of this software.
10 #-----------------------------------------------------------------------------
11
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
15
16 import types
17
18 #-----------------------------------------------------------------------------
19 # Code
20 #-----------------------------------------------------------------------------
21
22 def uniq_stable(elems):
23 """uniq_stable(elems) -> list
24
25 Return from an iterable, a list of all the unique elements in the input,
26 but maintaining the order in which they first appear.
27
28 A naive solution to this problem which just makes a dictionary with the
29 elements as keys fails to respect the stability condition, since
30 dictionaries are unsorted by nature.
31
32 Note: All elements in the input must be valid dictionary keys for this
33 routine to work, as it internally uses a dictionary for efficiency
34 reasons."""
35
36 unique = []
37 unique_dict = {}
38 for nn in elems:
39 if nn not in unique_dict:
40 unique.append(nn)
41 unique_dict[nn] = None
42 return unique
43
44
45 def sort_compare(lst1, lst2, inplace=1):
46 """Sort and compare two lists.
47
48 By default it does it in place, thus modifying the lists. Use inplace = 0
49 to avoid that (at the cost of temporary copy creation)."""
50 if not inplace:
51 lst1 = lst1[:]
52 lst2 = lst2[:]
53 lst1.sort(); lst2.sort()
54 return lst1 == lst2
55
56
57 def list2dict(lst):
58 """Takes a list of (key,value) pairs and turns it into a dict."""
59
60 dic = {}
61 for k,v in lst: dic[k] = v
62 return dic
63
64
65 def list2dict2(lst, default=''):
66 """Takes a list and turns it into a dict.
67 Much slower than list2dict, but more versatile. This version can take
68 lists with sublists of arbitrary length (including sclars)."""
69
70 dic = {}
71 for elem in lst:
72 if type(elem) in (types.ListType,types.TupleType):
73 size = len(elem)
74 if size == 0:
75 pass
76 elif size == 1:
77 dic[elem] = default
78 else:
79 k,v = elem[0], elem[1:]
80 if len(v) == 1: v = v[0]
81 dic[k] = v
82 else:
83 dic[elem] = default
84 return dic
85
86
87 def flatten(seq):
88 """Flatten a list of lists (NOT recursive, only works for 2d lists)."""
89
90 return [x for subseq in seq for x in subseq]
91
92
93 def get_slice(seq, start=0, stop=None, step=1):
94 """Get a slice of a sequence with variable step. Specify start,stop,step."""
95 if stop == None:
96 stop = len(seq)
97 item = lambda i: seq[i]
98 return map(item,xrange(start,stop,step))
99
100
101 def chop(seq, size):
102 """Chop a sequence into chunks of the given size."""
103 chunk = lambda i: seq[i:i+size]
104 return map(chunk,xrange(0,len(seq),size))
105
106
@@ -0,0 +1,46 b''
1 # encoding: utf-8
2 """Decorators that don't go anywhere else.
3
4 This module contains misc. decorators that don't really go with another module
5 in :mod:`IPython.utils`. Beore putting something here please see if it should
6 go into another topical module in :mod:`IPython.utils`.
7 """
8
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2008-2009 The IPython Development Team
11 #
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
15
16 #-----------------------------------------------------------------------------
17 # Imports
18 #-----------------------------------------------------------------------------
19
20 #-----------------------------------------------------------------------------
21 # Code
22 #-----------------------------------------------------------------------------
23
24 def flag_calls(func):
25 """Wrap a function to detect and flag when it gets called.
26
27 This is a decorator which takes a function and wraps it in a function with
28 a 'called' attribute. wrapper.called is initialized to False.
29
30 The wrapper.called attribute is set to False right before each call to the
31 wrapped function, so if the call fails it remains False. After the call
32 completes, wrapper.called is set to True and the output is returned.
33
34 Testing for truth in wrapper.called allows you to determine if a call to
35 func() was attempted and succeeded."""
36
37 def wrapper(*args,**kw):
38 wrapper.called = False
39 out = func(*args,**kw)
40 wrapper.called = True
41 return out
42
43 wrapper.called = False
44 wrapper.__doc__ = func.__doc__
45 return wrapper
46
@@ -0,0 +1,82 b''
1 # encoding: utf-8
2 """A fancy version of Python's builtin :func:`dir` function.
3 """
4
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2008-2009 The IPython Development Team
7 #
8 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING, distributed as part of this software.
10 #-----------------------------------------------------------------------------
11
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
15
16 #-----------------------------------------------------------------------------
17 # Code
18 #-----------------------------------------------------------------------------
19
20 def get_class_members(cls):
21 ret = dir(cls)
22 if hasattr(cls,'__bases__'):
23 for base in cls.__bases__:
24 ret.extend(get_class_members(base))
25 return ret
26
27
28 def dir2(obj):
29 """dir2(obj) -> list of strings
30
31 Extended version of the Python builtin dir(), which does a few extra
32 checks, and supports common objects with unusual internals that confuse
33 dir(), such as Traits and PyCrust.
34
35 This version is guaranteed to return only a list of true strings, whereas
36 dir() returns anything that objects inject into themselves, even if they
37 are later not really valid for attribute access (many extension libraries
38 have such bugs).
39 """
40
41 # Start building the attribute list via dir(), and then complete it
42 # with a few extra special-purpose calls.
43 words = dir(obj)
44
45 if hasattr(obj,'__class__'):
46 words.append('__class__')
47 words.extend(get_class_members(obj.__class__))
48 #if '__base__' in words: 1/0
49
50 # Some libraries (such as traits) may introduce duplicates, we want to
51 # track and clean this up if it happens
52 may_have_dupes = False
53
54 # this is the 'dir' function for objects with Enthought's traits
55 if hasattr(obj, 'trait_names'):
56 try:
57 words.extend(obj.trait_names())
58 may_have_dupes = True
59 except TypeError:
60 # This will happen if `obj` is a class and not an instance.
61 pass
62
63 # Support for PyCrust-style _getAttributeNames magic method.
64 if hasattr(obj, '_getAttributeNames'):
65 try:
66 words.extend(obj._getAttributeNames())
67 may_have_dupes = True
68 except TypeError:
69 # `obj` is a class and not an instance. Ignore
70 # this error.
71 pass
72
73 if may_have_dupes:
74 # eliminate possible duplicates, as some traits may also
75 # appear as normal attributes in the dir() call.
76 words = list(set(words))
77 words.sort()
78
79 # filter out non-string attributes which may be stuffed by dir() calls
80 # and poor coding in third-party modules
81 return [w for w in words if isinstance(w, basestring)]
82
@@ -0,0 +1,75 b''
1 # encoding: utf-8
2 """
3 A utility for handling the reloading of doctest.
4 """
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 import sys
18
19 #-----------------------------------------------------------------------------
20 # Code
21 #-----------------------------------------------------------------------------
22
23 def dhook_wrap(func,*a,**k):
24 """Wrap a function call in a sys.displayhook controller.
25
26 Returns a wrapper around func which calls func, with all its arguments and
27 keywords unmodified, using the default sys.displayhook. Since IPython
28 modifies sys.displayhook, it breaks the behavior of certain systems that
29 rely on the default behavior, notably doctest.
30 """
31
32 def f(*a,**k):
33
34 dhook_s = sys.displayhook
35 sys.displayhook = sys.__displayhook__
36 try:
37 out = func(*a,**k)
38 finally:
39 sys.displayhook = dhook_s
40
41 return out
42
43 f.__doc__ = func.__doc__
44 return f
45
46
47 def doctest_reload():
48 """Properly reload doctest to reuse it interactively.
49
50 This routine:
51
52 - imports doctest but does NOT reload it (see below).
53
54 - resets its global 'master' attribute to None, so that multiple uses of
55 the module interactively don't produce cumulative reports.
56
57 - Monkeypatches its core test runner method to protect it from IPython's
58 modified displayhook. Doctest expects the default displayhook behavior
59 deep down, so our modification breaks it completely. For this reason, a
60 hard monkeypatch seems like a reasonable solution rather than asking
61 users to manually use a different doctest runner when under IPython.
62
63 Notes
64 -----
65
66 This function *used to* reload doctest, but this has been disabled because
67 reloading doctest unconditionally can cause massive breakage of other
68 doctest-dependent modules already in memory, such as those for IPython's
69 own testing system. The name wasn't changed to avoid breaking people's
70 code, but the reload call isn't actually made anymore."""
71
72 import doctest
73 doctest.master = None
74 doctest.DocTestRunner.run = dhook_wrap(doctest.DocTestRunner.run)
75
@@ -0,0 +1,85 b''
1 # encoding: utf-8
2 """
3 Utilities for working with stack frames.
4 """
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 import sys
18
19 #-----------------------------------------------------------------------------
20 # Code
21 #-----------------------------------------------------------------------------
22
23 def extract_vars(*names,**kw):
24 """Extract a set of variables by name from another frame.
25
26 :Parameters:
27 - `*names`: strings
28 One or more variable names which will be extracted from the caller's
29 frame.
30
31 :Keywords:
32 - `depth`: integer (0)
33 How many frames in the stack to walk when looking for your variables.
34
35
36 Examples:
37
38 In [2]: def func(x):
39 ...: y = 1
40 ...: print extract_vars('x','y')
41 ...:
42
43 In [3]: func('hello')
44 {'y': 1, 'x': 'hello'}
45 """
46
47 depth = kw.get('depth',0)
48
49 callerNS = sys._getframe(depth+1).f_locals
50 return dict((k,callerNS[k]) for k in names)
51
52
53 def extract_vars_above(*names):
54 """Extract a set of variables by name from another frame.
55
56 Similar to extractVars(), but with a specified depth of 1, so that names
57 are exctracted exactly from above the caller.
58
59 This is simply a convenience function so that the very common case (for us)
60 of skipping exactly 1 frame doesn't have to construct a special dict for
61 keyword passing."""
62
63 callerNS = sys._getframe(2).f_locals
64 return dict((k,callerNS[k]) for k in names)
65
66
67 def debugx(expr,pre_msg=''):
68 """Print the value of an expression from the caller's frame.
69
70 Takes an expression, evaluates it in the caller's frame and prints both
71 the given expression and the resulting value (as well as a debug mark
72 indicating the name of the calling function. The input must be of a form
73 suitable for eval().
74
75 An optional message can be passed, which will be prepended to the printed
76 expr->value pair."""
77
78 cf = sys._getframe(1)
79 print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
80 eval(expr,cf.f_globals,cf.f_locals))
81
82
83 # deactivate it by uncommenting the following line, which makes it a no-op
84 #def debugx(expr,pre_msg=''): pass
85
@@ -0,0 +1,292 b''
1 # encoding: utf-8
2 """
3 IO related utilities.
4 """
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 import sys
18 import tempfile
19
20 from IPython.external.Itpl import itpl, printpl
21
22 #-----------------------------------------------------------------------------
23 # Code
24 #-----------------------------------------------------------------------------
25
26
27 class IOStream:
28
29 def __init__(self,stream,fallback):
30 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
31 stream = fallback
32 self.stream = stream
33 self._swrite = stream.write
34 self.flush = stream.flush
35
36 def write(self,data):
37 try:
38 self._swrite(data)
39 except:
40 try:
41 # print handles some unicode issues which may trip a plain
42 # write() call. Attempt to emulate write() by using a
43 # trailing comma
44 print >> self.stream, data,
45 except:
46 # if we get here, something is seriously broken.
47 print >> sys.stderr, \
48 'ERROR - failed to write data to stream:', self.stream
49
50 # This class used to have a writeln method, but regular files and streams
51 # in Python don't have this method. We need to keep this completely
52 # compatible so we removed it.
53
54 def close(self):
55 pass
56
57
58 class IOTerm:
59 """ Term holds the file or file-like objects for handling I/O operations.
60
61 These are normally just sys.stdin, sys.stdout and sys.stderr but for
62 Windows they can can replaced to allow editing the strings before they are
63 displayed."""
64
65 # In the future, having IPython channel all its I/O operations through
66 # this class will make it easier to embed it into other environments which
67 # are not a normal terminal (such as a GUI-based shell)
68 def __init__(self,cin=None,cout=None,cerr=None):
69 self.cin = IOStream(cin,sys.stdin)
70 self.cout = IOStream(cout,sys.stdout)
71 self.cerr = IOStream(cerr,sys.stderr)
72
73
74 # Global variable to be used for all I/O
75 Term = IOTerm()
76
77
78 import IPython.utils.rlineimpl as readline
79 # Remake Term to use the readline i/o facilities
80 if sys.platform == 'win32' and readline.have_readline:
81
82 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
83
84
85 class Tee(object):
86 """A class to duplicate an output stream to stdout/err.
87
88 This works in a manner very similar to the Unix 'tee' command.
89
90 When the object is closed or deleted, it closes the original file given to
91 it for duplication.
92 """
93 # Inspired by:
94 # http://mail.python.org/pipermail/python-list/2007-May/442737.html
95
96 def __init__(self, file_or_name, mode=None, channel='stdout'):
97 """Construct a new Tee object.
98
99 Parameters
100 ----------
101 file_or_name : filename or open filehandle (writable)
102 File that will be duplicated
103
104 mode : optional, valid mode for open().
105 If a filename was give, open with this mode.
106
107 channel : str, one of ['stdout', 'stderr']
108 """
109 if channel not in ['stdout', 'stderr']:
110 raise ValueError('Invalid channel spec %s' % channel)
111
112 if hasattr(file, 'write') and hasattr(file, 'seek'):
113 self.file = file_or_name
114 else:
115 self.file = open(file_or_name, mode)
116 self.channel = channel
117 self.ostream = getattr(sys, channel)
118 setattr(sys, channel, self)
119 self._closed = False
120
121 def close(self):
122 """Close the file and restore the channel."""
123 self.flush()
124 setattr(sys, self.channel, self.ostream)
125 self.file.close()
126 self._closed = True
127
128 def write(self, data):
129 """Write data to both channels."""
130 self.file.write(data)
131 self.ostream.write(data)
132 self.ostream.flush()
133
134 def flush(self):
135 """Flush both channels."""
136 self.file.flush()
137 self.ostream.flush()
138
139 def __del__(self):
140 if not self._closed:
141 self.close()
142
143
144 def file_read(filename):
145 """Read a file and close it. Returns the file source."""
146 fobj = open(filename,'r');
147 source = fobj.read();
148 fobj.close()
149 return source
150
151
152 def file_readlines(filename):
153 """Read a file and close it. Returns the file source using readlines()."""
154 fobj = open(filename,'r');
155 lines = fobj.readlines();
156 fobj.close()
157 return lines
158
159
160 def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):
161 """Take multiple lines of input.
162
163 A list with each line of input as a separate element is returned when a
164 termination string is entered (defaults to a single '.'). Input can also
165 terminate via EOF (^D in Unix, ^Z-RET in Windows).
166
167 Lines of input which end in \\ are joined into single entries (and a
168 secondary continuation prompt is issued as long as the user terminates
169 lines with \\). This allows entering very long strings which are still
170 meant to be treated as single entities.
171 """
172
173 try:
174 if header:
175 header += '\n'
176 lines = [raw_input(header + ps1)]
177 except EOFError:
178 return []
179 terminate = [terminate_str]
180 try:
181 while lines[-1:] != terminate:
182 new_line = raw_input(ps1)
183 while new_line.endswith('\\'):
184 new_line = new_line[:-1] + raw_input(ps2)
185 lines.append(new_line)
186
187 return lines[:-1] # don't return the termination command
188 except EOFError:
189 print
190 return lines
191
192
193 def raw_input_ext(prompt='', ps2='... '):
194 """Similar to raw_input(), but accepts extended lines if input ends with \\."""
195
196 line = raw_input(prompt)
197 while line.endswith('\\'):
198 line = line[:-1] + raw_input(ps2)
199 return line
200
201
202 def ask_yes_no(prompt,default=None):
203 """Asks a question and returns a boolean (y/n) answer.
204
205 If default is given (one of 'y','n'), it is used if the user input is
206 empty. Otherwise the question is repeated until an answer is given.
207
208 An EOF is treated as the default answer. If there is no default, an
209 exception is raised to prevent infinite loops.
210
211 Valid answers are: y/yes/n/no (match is not case sensitive)."""
212
213 answers = {'y':True,'n':False,'yes':True,'no':False}
214 ans = None
215 while ans not in answers.keys():
216 try:
217 ans = raw_input(prompt+' ').lower()
218 if not ans: # response was an empty string
219 ans = default
220 except KeyboardInterrupt:
221 pass
222 except EOFError:
223 if default in answers.keys():
224 ans = default
225 print
226 else:
227 raise
228
229 return answers[ans]
230
231
232 class NLprinter:
233 """Print an arbitrarily nested list, indicating index numbers.
234
235 An instance of this class called nlprint is available and callable as a
236 function.
237
238 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
239 and using 'sep' to separate the index from the value. """
240
241 def __init__(self):
242 self.depth = 0
243
244 def __call__(self,lst,pos='',**kw):
245 """Prints the nested list numbering levels."""
246 kw.setdefault('indent',' ')
247 kw.setdefault('sep',': ')
248 kw.setdefault('start',0)
249 kw.setdefault('stop',len(lst))
250 # we need to remove start and stop from kw so they don't propagate
251 # into a recursive call for a nested list.
252 start = kw['start']; del kw['start']
253 stop = kw['stop']; del kw['stop']
254 if self.depth == 0 and 'header' in kw.keys():
255 print kw['header']
256
257 for idx in range(start,stop):
258 elem = lst[idx]
259 if type(elem)==type([]):
260 self.depth += 1
261 self.__call__(elem,itpl('$pos$idx,'),**kw)
262 self.depth -= 1
263 else:
264 printpl(kw['indent']*self.depth+'$pos$idx$kw["sep"]$elem')
265
266 nlprint = NLprinter()
267
268
269 def temp_pyfile(src, ext='.py'):
270 """Make a temporary python file, return filename and filehandle.
271
272 Parameters
273 ----------
274 src : string or list of strings (no need for ending newlines if list)
275 Source code to be written to the file.
276
277 ext : optional, string
278 Extension for the generated file.
279
280 Returns
281 -------
282 (filename, open filehandle)
283 It is the caller's responsibility to close the open file and unlink it.
284 """
285 fname = tempfile.mkstemp(ext)[1]
286 f = open(fname,'w')
287 f.write(src)
288 f.flush()
289 return fname, f
290
291
292
@@ -0,0 +1,345 b''
1 # encoding: utf-8
2 """
3 Utilities for path handling.
4 """
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 import os
18 import sys
19
20 import IPython
21 from IPython.utils.process import xsys
22 from IPython.utils.importstring import import_item
23
24 #-----------------------------------------------------------------------------
25 # Code
26 #-----------------------------------------------------------------------------
27
28
29 def _get_long_path_name(path):
30 """Dummy no-op."""
31 return path
32
33
34 if sys.platform == 'win32':
35 def _get_long_path_name(path):
36 """Get a long path name (expand ~) on Windows using ctypes.
37
38 Examples
39 --------
40
41 >>> get_long_path_name('c:\\docume~1')
42 u'c:\\\\Documents and Settings'
43
44 """
45 try:
46 import ctypes
47 except ImportError:
48 raise ImportError('you need to have ctypes installed for this to work')
49 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
50 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
51 ctypes.c_uint ]
52
53 buf = ctypes.create_unicode_buffer(260)
54 rv = _GetLongPathName(path, buf, 260)
55 if rv == 0 or rv > 260:
56 return path
57 else:
58 return buf.value
59
60
61 def get_long_path_name(path):
62 """Expand a path into its long form.
63
64 On Windows this expands any ~ in the paths. On other platforms, it is
65 a null operation.
66 """
67 return _get_long_path_name(path)
68
69
70 def get_py_filename(name):
71 """Return a valid python filename in the current directory.
72
73 If the given name is not a file, it adds '.py' and searches again.
74 Raises IOError with an informative message if the file isn't found."""
75
76 name = os.path.expanduser(name)
77 if not os.path.isfile(name) and not name.endswith('.py'):
78 name += '.py'
79 if os.path.isfile(name):
80 return name
81 else:
82 raise IOError,'File `%s` not found.' % name
83
84
85 def filefind(filename, path_dirs=None):
86 """Find a file by looking through a sequence of paths.
87
88 This iterates through a sequence of paths looking for a file and returns
89 the full, absolute path of the first occurence of the file. If no set of
90 path dirs is given, the filename is tested as is, after running through
91 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
92
93 filefind('myfile.txt')
94
95 will find the file in the current working dir, but::
96
97 filefind('~/myfile.txt')
98
99 Will find the file in the users home directory. This function does not
100 automatically try any paths, such as the cwd or the user's home directory.
101
102 Parameters
103 ----------
104 filename : str
105 The filename to look for.
106 path_dirs : str, None or sequence of str
107 The sequence of paths to look for the file in. If None, the filename
108 need to be absolute or be in the cwd. If a string, the string is
109 put into a sequence and the searched. If a sequence, walk through
110 each element and join with ``filename``, calling :func:`expandvars`
111 and :func:`expanduser` before testing for existence.
112
113 Returns
114 -------
115 Raises :exc:`IOError` or returns absolute path to file.
116 """
117
118 # If paths are quoted, abspath gets confused, strip them...
119 filename = filename.strip('"').strip("'")
120 # If the input is an absolute path, just check it exists
121 if os.path.isabs(filename) and os.path.isfile(filename):
122 return filename
123
124 if path_dirs is None:
125 path_dirs = ("",)
126 elif isinstance(path_dirs, basestring):
127 path_dirs = (path_dirs,)
128
129 for path in path_dirs:
130 if path == '.': path = os.getcwd()
131 testname = expand_path(os.path.join(path, filename))
132 if os.path.isfile(testname):
133 return os.path.abspath(testname)
134
135 raise IOError("File %r does not exist in any of the search paths: %r" %
136 (filename, path_dirs) )
137
138
139 class HomeDirError(Exception):
140 pass
141
142
143 def get_home_dir():
144 """Return the closest possible equivalent to a 'home' directory.
145
146 * On POSIX, we try $HOME.
147 * On Windows we try:
148 - %HOMESHARE%
149 - %HOMEDRIVE\%HOMEPATH%
150 - %USERPROFILE%
151 - Registry hack for My Documents
152 - %HOME%: rare, but some people with unix-like setups may have defined it
153 * On Dos C:\
154
155 Currently only Posix and NT are implemented, a HomeDirError exception is
156 raised for all other OSes.
157 """
158
159 isdir = os.path.isdir
160 env = os.environ
161
162 # first, check py2exe distribution root directory for _ipython.
163 # This overrides all. Normally does not exist.
164
165 if hasattr(sys, "frozen"): #Is frozen by py2exe
166 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
167 root, rest = IPython.__file__.lower().split('library.zip')
168 else:
169 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
170 root=os.path.abspath(root).rstrip('\\')
171 if isdir(os.path.join(root, '_ipython')):
172 os.environ["IPYKITROOT"] = root
173 return root.decode(sys.getfilesystemencoding())
174
175 if os.name == 'posix':
176 # Linux, Unix, AIX, OS X
177 try:
178 homedir = env['HOME']
179 except KeyError:
180 raise HomeDirError('Undefined $HOME, IPython cannot proceed.')
181 else:
182 return homedir.decode(sys.getfilesystemencoding())
183 elif os.name == 'nt':
184 # Now for win9x, XP, Vista, 7?
185 # For some strange reason all of these return 'nt' for os.name.
186 # First look for a network home directory. This will return the UNC
187 # path (\\server\\Users\%username%) not the mapped path (Z:\). This
188 # is needed when running IPython on cluster where all paths have to
189 # be UNC.
190 try:
191 homedir = env['HOMESHARE']
192 except KeyError:
193 pass
194 else:
195 if isdir(homedir):
196 return homedir.decode(sys.getfilesystemencoding())
197
198 # Now look for a local home directory
199 try:
200 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
201 except KeyError:
202 pass
203 else:
204 if isdir(homedir):
205 return homedir.decode(sys.getfilesystemencoding())
206
207 # Now the users profile directory
208 try:
209 homedir = os.path.join(env['USERPROFILE'])
210 except KeyError:
211 pass
212 else:
213 if isdir(homedir):
214 return homedir.decode(sys.getfilesystemencoding())
215
216 # Use the registry to get the 'My Documents' folder.
217 try:
218 import _winreg as wreg
219 key = wreg.OpenKey(
220 wreg.HKEY_CURRENT_USER,
221 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
222 )
223 homedir = wreg.QueryValueEx(key,'Personal')[0]
224 key.Close()
225 except:
226 pass
227 else:
228 if isdir(homedir):
229 return homedir.decode(sys.getfilesystemencoding())
230
231 # A user with a lot of unix tools in win32 may have defined $HOME.
232 # Try this as a last ditch option.
233 try:
234 homedir = env['HOME']
235 except KeyError:
236 pass
237 else:
238 if isdir(homedir):
239 return homedir.decode(sys.getfilesystemencoding())
240
241 # If all else fails, raise HomeDirError
242 raise HomeDirError('No valid home directory could be found')
243 elif os.name == 'dos':
244 # Desperate, may do absurd things in classic MacOS. May work under DOS.
245 return 'C:\\'.decode(sys.getfilesystemencoding())
246 else:
247 raise HomeDirError('No valid home directory could be found for your OS')
248
249
250 def get_ipython_dir():
251 """Get the IPython directory for this platform and user.
252
253 This uses the logic in `get_home_dir` to find the home directory
254 and the adds .ipython to the end of the path.
255 """
256 ipdir_def = '.ipython'
257 home_dir = get_home_dir()
258 # import pdb; pdb.set_trace() # dbg
259 ipdir = os.environ.get(
260 'IPYTHON_DIR', os.environ.get(
261 'IPYTHONDIR', os.path.join(home_dir, ipdir_def)
262 )
263 )
264 return ipdir.decode(sys.getfilesystemencoding())
265
266
267 def get_ipython_package_dir():
268 """Get the base directory where IPython itself is installed."""
269 ipdir = os.path.dirname(IPython.__file__)
270 return ipdir.decode(sys.getfilesystemencoding())
271
272
273 def get_ipython_module_path(module_str):
274 """Find the path to an IPython module in this version of IPython.
275
276 This will always find the version of the module that is in this importable
277 IPython package. This will always return the path to the ``.py``
278 version of the module.
279 """
280 if module_str == 'IPython':
281 return os.path.join(get_ipython_package_dir(), '__init__.py')
282 mod = import_item(module_str)
283 the_path = mod.__file__.replace('.pyc', '.py')
284 the_path = the_path.replace('.pyo', '.py')
285 return the_path.decode(sys.getfilesystemencoding())
286
287
288 def expand_path(s):
289 """Expand $VARS and ~names in a string, like a shell
290
291 :Examples:
292
293 In [2]: os.environ['FOO']='test'
294
295 In [3]: expand_path('variable FOO is $FOO')
296 Out[3]: 'variable FOO is test'
297 """
298 # This is a pretty subtle hack. When expand user is given a UNC path
299 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
300 # the $ to get (\\server\share\%username%). I think it considered $
301 # alone an empty var. But, we need the $ to remains there (it indicates
302 # a hidden share).
303 if os.name=='nt':
304 s = s.replace('$\\', 'IPYTHON_TEMP')
305 s = os.path.expandvars(os.path.expanduser(s))
306 if os.name=='nt':
307 s = s.replace('IPYTHON_TEMP', '$\\')
308 return s
309
310
311 def target_outdated(target,deps):
312 """Determine whether a target is out of date.
313
314 target_outdated(target,deps) -> 1/0
315
316 deps: list of filenames which MUST exist.
317 target: single filename which may or may not exist.
318
319 If target doesn't exist or is older than any file listed in deps, return
320 true, otherwise return false.
321 """
322 try:
323 target_time = os.path.getmtime(target)
324 except os.error:
325 return 1
326 for dep in deps:
327 dep_time = os.path.getmtime(dep)
328 if dep_time > target_time:
329 #print "For target",target,"Dep failed:",dep # dbg
330 #print "times (dep,tar):",dep_time,target_time # dbg
331 return 1
332 return 0
333
334
335 def target_update(target,deps,cmd):
336 """Update a target with a given command given a list of dependencies.
337
338 target_update(target,deps,cmd) -> runs cmd if target is outdated.
339
340 This is just a wrapper around target_outdated() which calls the given
341 command if target is outdated."""
342
343 if target_outdated(target,deps):
344 xsys(cmd)
345
@@ -0,0 +1,368 b''
1 # encoding: utf-8
2 """
3 Utilities for working with external processes.
4 """
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 import os
18 import sys
19 import shlex
20 import subprocess
21
22 from IPython.utils.terminal import set_term_title
23
24 #-----------------------------------------------------------------------------
25 # Code
26 #-----------------------------------------------------------------------------
27
28
29 class FindCmdError(Exception):
30 pass
31
32
33 def _find_cmd(cmd):
34 """Find the full path to a command using which."""
35 return os.popen('which %s' % cmd).read().strip()
36
37
38 if os.name == 'posix':
39 def _find_cmd(cmd):
40 """Find the full path to a command using which."""
41 return getoutputerror('/usr/bin/env which %s' % cmd)[0]
42
43
44 if sys.platform == 'win32':
45 def _find_cmd(cmd):
46 """Find the full path to a .bat or .exe using the win32api module."""
47 try:
48 from win32api import SearchPath
49 except ImportError:
50 raise ImportError('you need to have pywin32 installed for this to work')
51 else:
52 PATH = os.environ['PATH']
53 extensions = ['.exe', '.com', '.bat', '.py']
54 path = None
55 for ext in extensions:
56 try:
57 path = SearchPath(PATH,cmd + ext)[0]
58 except:
59 pass
60 if path is None:
61 raise OSError("command %r not found" % cmd)
62 else:
63 return path
64
65
66 def find_cmd(cmd):
67 """Find absolute path to executable cmd in a cross platform manner.
68
69 This function tries to determine the full path to a command line program
70 using `which` on Unix/Linux/OS X and `win32api` on Windows. Most of the
71 time it will use the version that is first on the users `PATH`. If
72 cmd is `python` return `sys.executable`.
73
74 Warning, don't use this to find IPython command line programs as there
75 is a risk you will find the wrong one. Instead find those using the
76 following code and looking for the application itself::
77
78 from IPython.utils.path import get_ipython_module_path
79 from IPython.utils.process import pycmd2argv
80 argv = pycmd2argv(get_ipython_module_path('IPython.core.ipapp'))
81
82 Parameters
83 ----------
84 cmd : str
85 The command line program to look for.
86 """
87 if cmd == 'python':
88 return os.path.abspath(sys.executable)
89 try:
90 path = _find_cmd(cmd)
91 except OSError:
92 raise FindCmdError('command could not be found: %s' % cmd)
93 # which returns empty if not found
94 if path == '':
95 raise FindCmdError('command could not be found: %s' % cmd)
96 return os.path.abspath(path)
97
98
99 def pycmd2argv(cmd):
100 r"""Take the path of a python command and return a list (argv-style).
101
102 This only works on Python based command line programs and will find the
103 location of the ``python`` executable using ``sys.executable`` to make
104 sure the right version is used.
105
106 For a given path ``cmd``, this returns [cmd] if cmd's extension is .exe,
107 .com or .bat, and [, cmd] otherwise.
108
109 Parameters
110 ----------
111 cmd : string
112 The path of the command.
113
114 Returns
115 -------
116 argv-style list.
117 """
118 ext = os.path.splitext(cmd)[1]
119 if ext in ['.exe', '.com', '.bat']:
120 return [cmd]
121 else:
122 if sys.platform == 'win32':
123 # The -u option here turns on unbuffered output, which is required
124 # on Win32 to prevent wierd conflict and problems with Twisted.
125 # Also, use sys.executable to make sure we are picking up the
126 # right python exe.
127 return [sys.executable, '-u', cmd]
128 else:
129 return [sys.executable, cmd]
130
131
132 def arg_split(s, posix=False):
133 """Split a command line's arguments in a shell-like manner.
134
135 This is a modified version of the standard library's shlex.split()
136 function, but with a default of posix=False for splitting, so that quotes
137 in inputs are respected."""
138
139 # XXX - there may be unicode-related problems here!!! I'm not sure that
140 # shlex is truly unicode-safe, so it might be necessary to do
141 #
142 # s = s.encode(sys.stdin.encoding)
143 #
144 # first, to ensure that shlex gets a normal string. Input from anyone who
145 # knows more about unicode and shlex than I would be good to have here...
146 lex = shlex.shlex(s, posix=posix)
147 lex.whitespace_split = True
148 return list(lex)
149
150
151 def system(cmd, verbose=0, debug=0, header=''):
152 """Execute a system command, return its exit status.
153
154 Options:
155
156 - verbose (0): print the command to be executed.
157
158 - debug (0): only print, do not actually execute.
159
160 - header (''): Header to print on screen prior to the executed command (it
161 is only prepended to the command, no newlines are added).
162
163 Note: a stateful version of this function is available through the
164 SystemExec class."""
165
166 stat = 0
167 if verbose or debug: print header+cmd
168 sys.stdout.flush()
169 if not debug: stat = os.system(cmd)
170 return stat
171
172
173 def abbrev_cwd():
174 """ Return abbreviated version of cwd, e.g. d:mydir """
175 cwd = os.getcwd().replace('\\','/')
176 drivepart = ''
177 tail = cwd
178 if sys.platform == 'win32':
179 if len(cwd) < 4:
180 return cwd
181 drivepart,tail = os.path.splitdrive(cwd)
182
183
184 parts = tail.split('/')
185 if len(parts) > 2:
186 tail = '/'.join(parts[-2:])
187
188 return (drivepart + (
189 cwd == '/' and '/' or tail))
190
191
192 # This function is used by ipython in a lot of places to make system calls.
193 # We need it to be slightly different under win32, due to the vagaries of
194 # 'network shares'. A win32 override is below.
195
196 def shell(cmd, verbose=0, debug=0, header=''):
197 """Execute a command in the system shell, always return None.
198
199 Options:
200
201 - verbose (0): print the command to be executed.
202
203 - debug (0): only print, do not actually execute.
204
205 - header (''): Header to print on screen prior to the executed command (it
206 is only prepended to the command, no newlines are added).
207
208 Note: this is similar to system(), but it returns None so it can
209 be conveniently used in interactive loops without getting the return value
210 (typically 0) printed many times."""
211
212 stat = 0
213 if verbose or debug: print header+cmd
214 # flush stdout so we don't mangle python's buffering
215 sys.stdout.flush()
216
217 if not debug:
218 set_term_title("IPy " + cmd)
219 os.system(cmd)
220 set_term_title("IPy " + abbrev_cwd())
221
222 # override shell() for win32 to deal with network shares
223 if os.name in ('nt','dos'):
224
225 shell_ori = shell
226
227 def shell(cmd, verbose=0, debug=0, header=''):
228 if os.getcwd().startswith(r"\\"):
229 path = os.getcwd()
230 # change to c drive (cannot be on UNC-share when issuing os.system,
231 # as cmd.exe cannot handle UNC addresses)
232 os.chdir("c:")
233 # issue pushd to the UNC-share and then run the command
234 try:
235 shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header)
236 finally:
237 os.chdir(path)
238 else:
239 shell_ori(cmd,verbose,debug,header)
240
241 shell.__doc__ = shell_ori.__doc__
242
243
244 def getoutput(cmd, verbose=0, debug=0, header='', split=0):
245 """Dummy substitute for perl's backquotes.
246
247 Executes a command and returns the output.
248
249 Accepts the same arguments as system(), plus:
250
251 - split(0): if true, the output is returned as a list split on newlines.
252
253 Note: a stateful version of this function is available through the
254 SystemExec class.
255
256 This is pretty much deprecated and rarely used, getoutputerror may be
257 what you need.
258
259 """
260
261 if verbose or debug: print header+cmd
262 if not debug:
263 pipe = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout
264 output = pipe.read()
265 # stipping last \n is here for backwards compat.
266 if output.endswith('\n'):
267 output = output[:-1]
268 if split:
269 return output.split('\n')
270 else:
271 return output
272
273
274 # for compatibility with older naming conventions
275 xsys = system
276
277
278 def getoutputerror(cmd, verbose=0, debug=0, header='', split=0):
279 """Return (standard output,standard error) of executing cmd in a shell.
280
281 Accepts the same arguments as system(), plus:
282
283 - split(0): if true, each of stdout/err is returned as a list split on
284 newlines.
285
286 Note: a stateful version of this function is available through the
287 SystemExec class."""
288
289 if verbose or debug: print header+cmd
290 if not cmd:
291 if split:
292 return [],[]
293 else:
294 return '',''
295 if not debug:
296 p = subprocess.Popen(cmd, shell=True,
297 stdin=subprocess.PIPE,
298 stdout=subprocess.PIPE,
299 stderr=subprocess.PIPE,
300 close_fds=True)
301 pin, pout, perr = (p.stdin, p.stdout, p.stderr)
302
303 tout = pout.read().rstrip()
304 terr = perr.read().rstrip()
305 pin.close()
306 pout.close()
307 perr.close()
308 if split:
309 return tout.split('\n'),terr.split('\n')
310 else:
311 return tout,terr
312
313
314 class SystemExec:
315 """Access the system and getoutput functions through a stateful interface.
316
317 Note: here we refer to the system and getoutput functions from this
318 library, not the ones from the standard python library.
319
320 This class offers the system and getoutput functions as methods, but the
321 verbose, debug and header parameters can be set for the instance (at
322 creation time or later) so that they don't need to be specified on each
323 call.
324
325 For efficiency reasons, there's no way to override the parameters on a
326 per-call basis other than by setting instance attributes. If you need
327 local overrides, it's best to directly call system() or getoutput().
328
329 The following names are provided as alternate options:
330 - xsys: alias to system
331 - bq: alias to getoutput
332
333 An instance can then be created as:
334 >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ')
335 """
336
337 def __init__(self, verbose=0, debug=0, header='', split=0):
338 """Specify the instance's values for verbose, debug and header."""
339 self.verbose = verbose
340 self.debug = debug
341 self.header = header
342 self.split = split
343
344 def system(self, cmd):
345 """Stateful interface to system(), with the same keyword parameters."""
346
347 system(cmd, self.verbose, self.debug, self.header)
348
349 def shell(self, cmd):
350 """Stateful interface to shell(), with the same keyword parameters."""
351
352 shell(cmd, self.verbose, self.debug, self.header)
353
354 xsys = system # alias
355
356 def getoutput(self, cmd):
357 """Stateful interface to getoutput()."""
358
359 return getoutput(cmd, self.verbose, self.debug, self.header, self.split)
360
361 def getoutputerror(self, cmd):
362 """Stateful interface to getoutputerror()."""
363
364 return getoutputerror(cmd, self.verbose, self.debug, self.header, self.split)
365
366 bq = getoutput # alias
367
368
@@ -0,0 +1,100 b''
1 # encoding: utf-8
2 """
3 Utilities for getting information about a system.
4 """
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 import os
18 import platform
19 import sys
20 import subprocess
21
22 from IPython.core import release
23
24 #-----------------------------------------------------------------------------
25 # Code
26 #-----------------------------------------------------------------------------
27
28 def sys_info():
29 """Return useful information about IPython and the system, as a string.
30
31 Examples
32 --------
33 In [1]: print(sys_info())
34 IPython version: 0.11.bzr.r1340 # random
35 BZR revision : 1340
36 Platform info : os.name -> posix, sys.platform -> linux2
37 : Linux-2.6.31-17-generic-i686-with-Ubuntu-9.10-karmic
38 Python info : 2.6.4 (r264:75706, Dec 7 2009, 18:45:15)
39 [GCC 4.4.1]
40 """
41 out = []
42 out.append('IPython version: %s' % release.version)
43 out.append('BZR revision : %s' % release.revision)
44 out.append('Platform info : os.name -> %s, sys.platform -> %s' %
45 (os.name,sys.platform) )
46 out.append(' : %s' % platform.platform())
47 out.append('Python info : %s' % sys.version)
48 out.append('') # ensure closing newline
49 return '\n'.join(out)
50
51
52 def _num_cpus_unix():
53 """Return the number of active CPUs on a Unix system."""
54 return os.sysconf("SC_NPROCESSORS_ONLN")
55
56
57 def _num_cpus_darwin():
58 """Return the number of active CPUs on a Darwin system."""
59 p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE)
60 return p.stdout.read()
61
62
63 def _num_cpus_windows():
64 """Return the number of active CPUs on a Windows system."""
65 return os.environ.get("NUMBER_OF_PROCESSORS")
66
67
68 def num_cpus():
69 """Return the effective number of CPUs in the system as an integer.
70
71 This cross-platform function makes an attempt at finding the total number of
72 available CPUs in the system, as returned by various underlying system and
73 python calls.
74
75 If it can't find a sensible answer, it returns 1 (though an error *may* make
76 it return a large positive number that's actually incorrect).
77 """
78
79 # Many thanks to the Parallel Python project (http://www.parallelpython.com)
80 # for the names of the keys we needed to look up for this function. This
81 # code was inspired by their equivalent function.
82
83 ncpufuncs = {'Linux':_num_cpus_unix,
84 'Darwin':_num_cpus_darwin,
85 'Windows':_num_cpus_windows,
86 # On Vista, python < 2.5.2 has a bug and returns 'Microsoft'
87 # See http://bugs.python.org/issue1082 for details.
88 'Microsoft':_num_cpus_windows,
89 }
90
91 ncpufunc = ncpufuncs.get(platform.system(),
92 # default to unix version (Solaris, AIX, etc)
93 _num_cpus_unix)
94
95 try:
96 ncpus = max(1,int(ncpufunc()))
97 except:
98 ncpus = 1
99 return ncpus
100
@@ -0,0 +1,162 b''
1 # encoding: utf-8
2 """
3 Utilities for working with terminals.
4
5 Authors:
6
7 * Brian E. Granger
8 * Fernando Perez
9 * Alexander Belchenko (e-mail: bialix AT ukr.net)
10 """
11
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2008-2009 The IPython Development Team
14 #
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
18
19 #-----------------------------------------------------------------------------
20 # Imports
21 #-----------------------------------------------------------------------------
22
23 import os
24 import struct
25 import sys
26 import warnings
27
28 #-----------------------------------------------------------------------------
29 # Code
30 #-----------------------------------------------------------------------------
31
32 # This variable is part of the expected API of the module:
33 ignore_termtitle = True
34
35
36 def _term_clear():
37 pass
38
39
40 if os.name == 'posix':
41 def _term_clear():
42 os.system('clear')
43
44
45 if sys.platform == 'win32':
46 def _term_clear():
47 os.system('cls')
48
49
50 def term_clear():
51 _term_clear()
52
53
54 def toggle_set_term_title(val):
55 """Control whether set_term_title is active or not.
56
57 set_term_title() allows writing to the console titlebar. In embedded
58 widgets this can cause problems, so this call can be used to toggle it on
59 or off as needed.
60
61 The default state of the module is for the function to be disabled.
62
63 Parameters
64 ----------
65 val : bool
66 If True, set_term_title() actually writes to the terminal (using the
67 appropriate platform-specific module). If False, it is a no-op.
68 """
69 global ignore_termtitle
70 ignore_termtitle = not(val)
71
72
73 def _set_term_title(*args,**kw):
74 """Dummy no-op."""
75 pass
76
77
78 def _set_term_title_xterm(title):
79 """ Change virtual terminal title in xterm-workalikes """
80 sys.stdout.write('\033]0;%s\007' % title)
81
82 if os.name == 'posix':
83 TERM = os.environ.get('TERM','')
84 if (TERM == 'xterm') or (TERM == 'xterm-color'):
85 _set_term_title = _set_term_title_xterm
86
87
88 if sys.platform == 'win32':
89 try:
90 import ctypes
91
92 SetConsoleTitleW = ctypes.windll.kernel32.SetConsoleTitleW
93 SetConsoleTitleW.argtypes = [ctypes.c_wchar_p]
94
95 def _set_term_title(title):
96 """Set terminal title using ctypes to access the Win32 APIs."""
97 SetConsoleTitleW(title)
98 except ImportError:
99 def _set_term_title(title):
100 """Set terminal title using the 'title' command."""
101 global ignore_termtitle
102
103 try:
104 # Cannot be on network share when issuing system commands
105 curr = os.getcwd()
106 os.chdir("C:")
107 ret = os.system("title " + title)
108 finally:
109 os.chdir(curr)
110 if ret:
111 # non-zero return code signals error, don't try again
112 ignore_termtitle = True
113
114
115 def set_term_title(title):
116 """Set terminal title using the necessary platform-dependent calls."""
117 if ignore_termtitle:
118 return
119 _set_term_title(title)
120
121
122 def freeze_term_title():
123 warnings.warn("This function is deprecated, use toggle_set_term_title()")
124 global ignore_termtitle
125 ignore_termtitle = True
126
127
128 def get_terminal_size(defaultx=80, defaulty=25):
129 return defaultx, defaulty
130
131
132 if sys.platform == 'win32':
133 def get_terminal_size(defaultx=80, defaulty=25):
134 """Return size of current terminal console.
135
136 This function try to determine actual size of current working
137 console window and return tuple (sizex, sizey) if success,
138 or default size (defaultx, defaulty) otherwise.
139
140 Dependencies: ctypes should be installed.
141
142 Author: Alexander Belchenko (e-mail: bialix AT ukr.net)
143 """
144 try:
145 import ctypes
146 except ImportError:
147 return defaultx, defaulty
148
149 h = ctypes.windll.kernel32.GetStdHandle(-11)
150 csbi = ctypes.create_string_buffer(22)
151 res = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
152
153 if res:
154 (bufx, bufy, curx, cury, wattr,
155 left, top, right, bottom, maxx, maxy) = struct.unpack(
156 "hhhhHhhhhhh", csbi.raw)
157 sizex = right - left + 1
158 sizey = bottom - top + 1
159 return (sizex, sizey)
160 else:
161 return (defaultx, defaulty)
162
@@ -0,0 +1,61 b''
1 # encoding: utf-8
2 """Tests for io.py"""
3
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2008 The IPython Development Team
6 #
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
10
11 #-----------------------------------------------------------------------------
12 # Imports
13 #-----------------------------------------------------------------------------
14
15 import sys
16
17 from cStringIO import StringIO
18
19 import nose.tools as nt
20
21 from IPython.testing import decorators as dec
22 from IPython.utils.io import Tee
23
24 #-----------------------------------------------------------------------------
25 # Tests
26 #-----------------------------------------------------------------------------
27
28
29 def test_tee_simple():
30 "Very simple check with stdout only"
31 chan = StringIO()
32 text = 'Hello'
33 tee = Tee(chan, channel='stdout')
34 print >> chan, text,
35 nt.assert_equal(chan.getvalue(), text)
36
37
38 class TeeTestCase(dec.ParametricTestCase):
39
40 def tchan(self, channel, check='close'):
41 trap = StringIO()
42 chan = StringIO()
43 text = 'Hello'
44
45 std_ori = getattr(sys, channel)
46 setattr(sys, channel, trap)
47
48 tee = Tee(chan, channel=channel)
49 print >> chan, text,
50 setattr(sys, channel, std_ori)
51 trap_val = trap.getvalue()
52 nt.assert_equals(chan.getvalue(), text)
53 if check=='close':
54 tee.close()
55 else:
56 del tee
57
58 def test(self):
59 for chan in ['stdout', 'stderr']:
60 for check in ['close', 'del']:
61 yield self.tchan(chan, check)
@@ -0,0 +1,473 b''
1 # encoding: utf-8
2 """
3 Utilities for working with strings and text.
4 """
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 import __main__
18
19 import os
20 import re
21 import shutil
22 import types
23
24 from IPython.external.path import path
25
26 from IPython.utils.generics import result_display
27 from IPython.utils.io import nlprint
28 from IPython.utils.data import flatten
29
30 #-----------------------------------------------------------------------------
31 # Code
32 #-----------------------------------------------------------------------------
33
34 StringTypes = types.StringTypes
35
36
37 def unquote_ends(istr):
38 """Remove a single pair of quotes from the endpoints of a string."""
39
40 if not istr:
41 return istr
42 if (istr[0]=="'" and istr[-1]=="'") or \
43 (istr[0]=='"' and istr[-1]=='"'):
44 return istr[1:-1]
45 else:
46 return istr
47
48
49 class LSString(str):
50 """String derivative with a special access attributes.
51
52 These are normal strings, but with the special attributes:
53
54 .l (or .list) : value as list (split on newlines).
55 .n (or .nlstr): original value (the string itself).
56 .s (or .spstr): value as whitespace-separated string.
57 .p (or .paths): list of path objects
58
59 Any values which require transformations are computed only once and
60 cached.
61
62 Such strings are very useful to efficiently interact with the shell, which
63 typically only understands whitespace-separated options for commands."""
64
65 def get_list(self):
66 try:
67 return self.__list
68 except AttributeError:
69 self.__list = self.split('\n')
70 return self.__list
71
72 l = list = property(get_list)
73
74 def get_spstr(self):
75 try:
76 return self.__spstr
77 except AttributeError:
78 self.__spstr = self.replace('\n',' ')
79 return self.__spstr
80
81 s = spstr = property(get_spstr)
82
83 def get_nlstr(self):
84 return self
85
86 n = nlstr = property(get_nlstr)
87
88 def get_paths(self):
89 try:
90 return self.__paths
91 except AttributeError:
92 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
93 return self.__paths
94
95 p = paths = property(get_paths)
96
97
98 def print_lsstring(arg):
99 """ Prettier (non-repr-like) and more informative printer for LSString """
100 print "LSString (.p, .n, .l, .s available). Value:"
101 print arg
102
103
104 print_lsstring = result_display.when_type(LSString)(print_lsstring)
105
106
107 class SList(list):
108 """List derivative with a special access attributes.
109
110 These are normal lists, but with the special attributes:
111
112 .l (or .list) : value as list (the list itself).
113 .n (or .nlstr): value as a string, joined on newlines.
114 .s (or .spstr): value as a string, joined on spaces.
115 .p (or .paths): list of path objects
116
117 Any values which require transformations are computed only once and
118 cached."""
119
120 def get_list(self):
121 return self
122
123 l = list = property(get_list)
124
125 def get_spstr(self):
126 try:
127 return self.__spstr
128 except AttributeError:
129 self.__spstr = ' '.join(self)
130 return self.__spstr
131
132 s = spstr = property(get_spstr)
133
134 def get_nlstr(self):
135 try:
136 return self.__nlstr
137 except AttributeError:
138 self.__nlstr = '\n'.join(self)
139 return self.__nlstr
140
141 n = nlstr = property(get_nlstr)
142
143 def get_paths(self):
144 try:
145 return self.__paths
146 except AttributeError:
147 self.__paths = [path(p) for p in self if os.path.exists(p)]
148 return self.__paths
149
150 p = paths = property(get_paths)
151
152 def grep(self, pattern, prune = False, field = None):
153 """ Return all strings matching 'pattern' (a regex or callable)
154
155 This is case-insensitive. If prune is true, return all items
156 NOT matching the pattern.
157
158 If field is specified, the match must occur in the specified
159 whitespace-separated field.
160
161 Examples::
162
163 a.grep( lambda x: x.startswith('C') )
164 a.grep('Cha.*log', prune=1)
165 a.grep('chm', field=-1)
166 """
167
168 def match_target(s):
169 if field is None:
170 return s
171 parts = s.split()
172 try:
173 tgt = parts[field]
174 return tgt
175 except IndexError:
176 return ""
177
178 if isinstance(pattern, basestring):
179 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
180 else:
181 pred = pattern
182 if not prune:
183 return SList([el for el in self if pred(match_target(el))])
184 else:
185 return SList([el for el in self if not pred(match_target(el))])
186
187 def fields(self, *fields):
188 """ Collect whitespace-separated fields from string list
189
190 Allows quick awk-like usage of string lists.
191
192 Example data (in var a, created by 'a = !ls -l')::
193 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
194 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
195
196 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
197 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
198 (note the joining by space).
199 a.fields(-1) is ['ChangeLog', 'IPython']
200
201 IndexErrors are ignored.
202
203 Without args, fields() just split()'s the strings.
204 """
205 if len(fields) == 0:
206 return [el.split() for el in self]
207
208 res = SList()
209 for el in [f.split() for f in self]:
210 lineparts = []
211
212 for fd in fields:
213 try:
214 lineparts.append(el[fd])
215 except IndexError:
216 pass
217 if lineparts:
218 res.append(" ".join(lineparts))
219
220 return res
221
222 def sort(self,field= None, nums = False):
223 """ sort by specified fields (see fields())
224
225 Example::
226 a.sort(1, nums = True)
227
228 Sorts a by second field, in numerical order (so that 21 > 3)
229
230 """
231
232 #decorate, sort, undecorate
233 if field is not None:
234 dsu = [[SList([line]).fields(field), line] for line in self]
235 else:
236 dsu = [[line, line] for line in self]
237 if nums:
238 for i in range(len(dsu)):
239 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
240 try:
241 n = int(numstr)
242 except ValueError:
243 n = 0;
244 dsu[i][0] = n
245
246
247 dsu.sort()
248 return SList([t[1] for t in dsu])
249
250
251 def print_slist(arg):
252 """ Prettier (non-repr-like) and more informative printer for SList """
253 print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
254 if hasattr(arg, 'hideonce') and arg.hideonce:
255 arg.hideonce = False
256 return
257
258 nlprint(arg)
259
260
261 print_slist = result_display.when_type(SList)(print_slist)
262
263
264 def esc_quotes(strng):
265 """Return the input string with single and double quotes escaped out"""
266
267 return strng.replace('"','\\"').replace("'","\\'")
268
269
270 def make_quoted_expr(s):
271 """Return string s in appropriate quotes, using raw string if possible.
272
273 XXX - example removed because it caused encoding errors in documentation
274 generation. We need a new example that doesn't contain invalid chars.
275
276 Note the use of raw string and padding at the end to allow trailing
277 backslash.
278 """
279
280 tail = ''
281 tailpadding = ''
282 raw = ''
283 if "\\" in s:
284 raw = 'r'
285 if s.endswith('\\'):
286 tail = '[:-1]'
287 tailpadding = '_'
288 if '"' not in s:
289 quote = '"'
290 elif "'" not in s:
291 quote = "'"
292 elif '"""' not in s and not s.endswith('"'):
293 quote = '"""'
294 elif "'''" not in s and not s.endswith("'"):
295 quote = "'''"
296 else:
297 # give up, backslash-escaped string will do
298 return '"%s"' % esc_quotes(s)
299 res = raw + quote + s + tailpadding + quote + tail
300 return res
301
302
303 def qw(words,flat=0,sep=None,maxsplit=-1):
304 """Similar to Perl's qw() operator, but with some more options.
305
306 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
307
308 words can also be a list itself, and with flat=1, the output will be
309 recursively flattened.
310
311 Examples:
312
313 >>> qw('1 2')
314 ['1', '2']
315
316 >>> qw(['a b','1 2',['m n','p q']])
317 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
318
319 >>> qw(['a b','1 2',['m n','p q']],flat=1)
320 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
321 """
322
323 if type(words) in StringTypes:
324 return [word.strip() for word in words.split(sep,maxsplit)
325 if word and not word.isspace() ]
326 if flat:
327 return flatten(map(qw,words,[1]*len(words)))
328 return map(qw,words)
329
330
331 def qwflat(words,sep=None,maxsplit=-1):
332 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
333 return qw(words,1,sep,maxsplit)
334
335
336 def qw_lol(indata):
337 """qw_lol('a b') -> [['a','b']],
338 otherwise it's just a call to qw().
339
340 We need this to make sure the modules_some keys *always* end up as a
341 list of lists."""
342
343 if type(indata) in StringTypes:
344 return [qw(indata)]
345 else:
346 return qw(indata)
347
348
349 def grep(pat,list,case=1):
350 """Simple minded grep-like function.
351 grep(pat,list) returns occurrences of pat in list, None on failure.
352
353 It only does simple string matching, with no support for regexps. Use the
354 option case=0 for case-insensitive matching."""
355
356 # This is pretty crude. At least it should implement copying only references
357 # to the original data in case it's big. Now it copies the data for output.
358 out=[]
359 if case:
360 for term in list:
361 if term.find(pat)>-1: out.append(term)
362 else:
363 lpat=pat.lower()
364 for term in list:
365 if term.lower().find(lpat)>-1: out.append(term)
366
367 if len(out): return out
368 else: return None
369
370
371 def dgrep(pat,*opts):
372 """Return grep() on dir()+dir(__builtins__).
373
374 A very common use of grep() when working interactively."""
375
376 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
377
378
379 def idgrep(pat):
380 """Case-insensitive dgrep()"""
381
382 return dgrep(pat,0)
383
384
385 def igrep(pat,list):
386 """Synonym for case-insensitive grep."""
387
388 return grep(pat,list,case=0)
389
390
391 def indent(str,nspaces=4,ntabs=0):
392 """Indent a string a given number of spaces or tabstops.
393
394 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
395 """
396 if str is None:
397 return
398 ind = '\t'*ntabs+' '*nspaces
399 outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
400 if outstr.endswith(os.linesep+ind):
401 return outstr[:-len(ind)]
402 else:
403 return outstr
404
405 def native_line_ends(filename,backup=1):
406 """Convert (in-place) a file to line-ends native to the current OS.
407
408 If the optional backup argument is given as false, no backup of the
409 original file is left. """
410
411 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
412
413 bak_filename = filename + backup_suffixes[os.name]
414
415 original = open(filename).read()
416 shutil.copy2(filename,bak_filename)
417 try:
418 new = open(filename,'wb')
419 new.write(os.linesep.join(original.splitlines()))
420 new.write(os.linesep) # ALWAYS put an eol at the end of the file
421 new.close()
422 except:
423 os.rename(bak_filename,filename)
424 if not backup:
425 try:
426 os.remove(bak_filename)
427 except:
428 pass
429
430
431 def list_strings(arg):
432 """Always return a list of strings, given a string or list of strings
433 as input.
434
435 :Examples:
436
437 In [7]: list_strings('A single string')
438 Out[7]: ['A single string']
439
440 In [8]: list_strings(['A single string in a list'])
441 Out[8]: ['A single string in a list']
442
443 In [9]: list_strings(['A','list','of','strings'])
444 Out[9]: ['A', 'list', 'of', 'strings']
445 """
446
447 if isinstance(arg,basestring): return [arg]
448 else: return arg
449
450
451 def marquee(txt='',width=78,mark='*'):
452 """Return the input string centered in a 'marquee'.
453
454 :Examples:
455
456 In [16]: marquee('A test',40)
457 Out[16]: '**************** A test ****************'
458
459 In [17]: marquee('A test',40,'-')
460 Out[17]: '---------------- A test ----------------'
461
462 In [18]: marquee('A test',40,' ')
463 Out[18]: ' A test '
464
465 """
466 if not txt:
467 return (mark*width)[:width]
468 nmark = (width-len(txt)-2)/len(mark)/2
469 if nmark < 0: nmark =0
470 marks = mark*nmark
471 return '%s %s %s' % (marks,txt,marks)
472
473
@@ -0,0 +1,116 b''
1 # encoding: utf-8
2 """
3 Utilities for timing code execution.
4 """
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 import time
18
19 #-----------------------------------------------------------------------------
20 # Code
21 #-----------------------------------------------------------------------------
22
23 # If possible (Unix), use the resource module instead of time.clock()
24 try:
25 import resource
26 def clocku():
27 """clocku() -> floating point number
28
29 Return the *USER* CPU time in seconds since the start of the process.
30 This is done via a call to resource.getrusage, so it avoids the
31 wraparound problems in time.clock()."""
32
33 return resource.getrusage(resource.RUSAGE_SELF)[0]
34
35 def clocks():
36 """clocks() -> floating point number
37
38 Return the *SYSTEM* CPU time in seconds since the start of the process.
39 This is done via a call to resource.getrusage, so it avoids the
40 wraparound problems in time.clock()."""
41
42 return resource.getrusage(resource.RUSAGE_SELF)[1]
43
44 def clock():
45 """clock() -> floating point number
46
47 Return the *TOTAL USER+SYSTEM* CPU time in seconds since the start of
48 the process. This is done via a call to resource.getrusage, so it
49 avoids the wraparound problems in time.clock()."""
50
51 u,s = resource.getrusage(resource.RUSAGE_SELF)[:2]
52 return u+s
53
54 def clock2():
55 """clock2() -> (t_user,t_system)
56
57 Similar to clock(), but return a tuple of user/system times."""
58 return resource.getrusage(resource.RUSAGE_SELF)[:2]
59 except ImportError:
60 # There is no distinction of user/system time under windows, so we just use
61 # time.clock() for everything...
62 clocku = clocks = clock = time.clock
63 def clock2():
64 """Under windows, system CPU time can't be measured.
65
66 This just returns clock() and zero."""
67 return time.clock(),0.0
68
69
70 def timings_out(reps,func,*args,**kw):
71 """timings_out(reps,func,*args,**kw) -> (t_total,t_per_call,output)
72
73 Execute a function reps times, return a tuple with the elapsed total
74 CPU time in seconds, the time per call and the function's output.
75
76 Under Unix, the return value is the sum of user+system time consumed by
77 the process, computed via the resource module. This prevents problems
78 related to the wraparound effect which the time.clock() function has.
79
80 Under Windows the return value is in wall clock seconds. See the
81 documentation for the time module for more details."""
82
83 reps = int(reps)
84 assert reps >=1, 'reps must be >= 1'
85 if reps==1:
86 start = clock()
87 out = func(*args,**kw)
88 tot_time = clock()-start
89 else:
90 rng = xrange(reps-1) # the last time is executed separately to store output
91 start = clock()
92 for dummy in rng: func(*args,**kw)
93 out = func(*args,**kw) # one last time
94 tot_time = clock()-start
95 av_time = tot_time / reps
96 return tot_time,av_time,out
97
98
99 def timings(reps,func,*args,**kw):
100 """timings(reps,func,*args,**kw) -> (t_total,t_per_call)
101
102 Execute a function reps times, return a tuple with the elapsed total CPU
103 time in seconds and the time per call. These are just the first two values
104 in timings_out()."""
105
106 return timings_out(reps,func,*args,**kw)[0:2]
107
108
109 def timing(func,*args,**kw):
110 """timing(func,*args,**kw) -> t_total
111
112 Execute a function once, return the elapsed total CPU time in
113 seconds. This is just the first value in timings_out()."""
114
115 return timings_out(1,func,*args,**kw)[0]
116
@@ -0,0 +1,66 b''
1 # encoding: utf-8
2 """
3 Utilities for warnings. Shoudn't we just use the built in warnings module.
4 """
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 import sys
18
19 from IPython.utils.io import Term
20
21 #-----------------------------------------------------------------------------
22 # Code
23 #-----------------------------------------------------------------------------
24
25 def warn(msg,level=2,exit_val=1):
26 """Standard warning printer. Gives formatting consistency.
27
28 Output is sent to Term.cerr (sys.stderr by default).
29
30 Options:
31
32 -level(2): allows finer control:
33 0 -> Do nothing, dummy function.
34 1 -> Print message.
35 2 -> Print 'WARNING:' + message. (Default level).
36 3 -> Print 'ERROR:' + message.
37 4 -> Print 'FATAL ERROR:' + message and trigger a sys.exit(exit_val).
38
39 -exit_val (1): exit value returned by sys.exit() for a level 4
40 warning. Ignored for all other levels."""
41
42 if level>0:
43 header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
44 print >> Term.cerr, '%s%s' % (header[level],msg)
45 if level == 4:
46 print >> Term.cerr,'Exiting.\n'
47 sys.exit(exit_val)
48
49
50 def info(msg):
51 """Equivalent to warn(msg,level=1)."""
52
53 warn(msg,level=1)
54
55
56 def error(msg):
57 """Equivalent to warn(msg,level=3)."""
58
59 warn(msg,level=3)
60
61
62 def fatal(msg,exit_val=1):
63 """Equivalent to warn(msg,exit_val=exit_val,level=4)."""
64
65 warn(msg,exit_val=exit_val,level=4)
66
@@ -0,0 +1,286 b''
1 ========================================
2 Design proposal for mod:`IPython.core`
3 ========================================
4
5 Currently mod:`IPython.core` is not well suited for use in GUI applications.
6 The purpose of this document is to describe a design that will resolve this
7 limitation.
8
9 Process and thread model
10 ========================
11
12 The design described here is based on a two process model. These two processes
13 are:
14
15 1. The IPython engine/kernel. This process contains the user's namespace and
16 is responsible for executing user code. If user code uses
17 :mod:`enthought.traits` or uses a GUI toolkit to perform plotting, the GUI
18 event loop will run in this process.
19
20 2. The GUI application. The user facing GUI application will run in a second
21 process that communicates directly with the IPython engine using
22 asynchronous messaging. The GUI application will not execute any user code.
23 The canonical example of a GUI application that talks to the IPython
24 engine, would be a GUI based IPython terminal. However, the GUI application
25 could provide a more sophisticated interface such as a notebook.
26
27 We now describe the threading model of the IPython engine. Two threads will be
28 used to implement the IPython engine: a main thread that executes user code
29 and a networking thread that communicates with the outside world. This
30 specific design is required by a number of different factors.
31
32 First, The IPython engine must run the GUI event loop if the user wants to
33 perform interactive plotting. Because of the design of most GUIs, this means
34 that the user code (which will make GUI calls) must live in the main thread.
35
36 Second, networking code in the engine (Twisted or otherwise) must be able to
37 communicate with the outside world while user code runs. An example would be
38 if user code does the following::
39
40 import time
41 for i in range(10):
42 print i
43 time.sleep(2)
44
45 We would like to result of each ``print i`` to be seen by the GUI application
46 before the entire code block completes. We call this asynchronous printing.
47 For this to be possible, the networking code has to be able to be able to
48 communicate the current value of ``sys.stdout`` to the GUI application while
49 user code is run. Another example is using :mod:`IPython.kernel.client` in
50 user code to perform a parallel computation by talking to an IPython
51 controller and a set of engines (these engines are separate from the one we
52 are discussing here). This module requires the Twisted event loop to be run in
53 a different thread than user code.
54
55 For the GUI application, threads are optional. However, the GUI application
56 does need to be able to perform network communications asynchronously (without
57 blocking the GUI itself). With this in mind, there are two options:
58
59 * Use Twisted (or another non-blocking socket library) in the same thread as
60 the GUI event loop.
61
62 * Don't use Twisted, but instead run networking code in the GUI application
63 using blocking sockets in threads. This would require the usage of polling
64 and queues to manage the networking in the GUI application.
65
66 Thus, for the GUI application, there is a choice between non-blocking sockets
67 (Twisted) or threads.
68
69 Asynchronous messaging
70 ======================
71
72 The GUI application will use asynchronous message queues to communicate with
73 the networking thread of the engine. Because this communication will typically
74 happen over localhost, a simple, one way, network protocol like XML-RPC or
75 JSON-RPC can be used to implement this messaging. These options will also make
76 it easy to implement the required networking in the GUI application using the
77 standard library. In applications where secure communications are required,
78 Twisted and Foolscap will probably be the best way to go for now, but HTTP is
79 also an option.
80
81 There is some flexibility as to where the message queues are located. One
82 option is that we could create a third process (like the IPython controller)
83 that only manages the message queues. This is attractive, but does require
84 an additional process.
85
86 Using this communication channel, the GUI application and kernel/engine will
87 be able to send messages back and forth. For the most part, these messages
88 will have a request/reply form, but it will be possible for the kernel/engine
89 to send multiple replies for a single request.
90
91 The GUI application will use these messages to control the engine/kernel.
92 Examples of the types of things that will be possible are:
93
94 * Pass code (as a string) to be executed by the engine in the user's namespace
95 as a string.
96
97 * Get the current value of stdout and stderr.
98
99 * Get the ``repr`` of an object returned (Out []:).
100
101 * Pass a string to the engine to be completed when the GUI application
102 receives a tab completion event.
103
104 * Get a list of all variable names in the user's namespace.
105
106 The in memory format of a message should be a Python dictionary, as this
107 will be easy to serialize using virtually any network protocol. The
108 message dict should only contain basic types, such as strings, floats,
109 ints, lists, tuples and other dicts.
110
111 Each message will have a unique id and will probably be determined by the
112 messaging system and returned when something is queued in the message
113 system. This unique id will be used to pair replies with requests.
114
115 Each message should have a header of key value pairs that can be introspected
116 by the message system and a body, or payload, that is opaque. The queues
117 themselves will be purpose agnostic, so the purpose of the message will have
118 to be encoded in the message itself. While we are getting started, we
119 probably don't need to distinguish between the header and body.
120
121 Here are some examples::
122
123 m1 = dict(
124 method='execute',
125 id=24, # added by the message system
126 parent=None # not a reply,
127 source_code='a=my_func()'
128 )
129
130 This single message could generate a number of reply messages::
131
132 m2 = dict(
133 method='stdout'
134 id=25, # my id, added by the message system
135 parent_id=24, # The message id of the request
136 value='This was printed by my_func()'
137 )
138
139 m3 = dict(
140 method='stdout'
141 id=26, # my id, added by the message system
142 parent_id=24, # The message id of the request
143 value='This too was printed by my_func() at a later time.'
144 )
145
146 m4 = dict(
147 method='execute_finished',
148 id=27,
149 parent_id=24
150 # not sure what else should come back with this message,
151 # but we will need a way for the GUI app to tell that an execute
152 # is done.
153 )
154
155 We should probably use flags for the method and other purposes:
156
157 EXECUTE='0'
158 EXECUTE_REPLY='1'
159
160 This will keep out network traffic down and enable us to easily change the
161 actual value that is sent.
162
163 Engine details
164 ==============
165
166 As discussed above, the engine will consist of two threads: a main thread and
167 a networking thread. These two threads will communicate using a pair of
168 queues: one for data and requests passing to the main thread (the main
169 thread's "input queue") and another for data and requests passing out of the
170 main thread (the main thread's "output queue"). Both threads will have an
171 event loop that will enqueue elements on one queue and dequeue elements on the
172 other queue.
173
174 The event loop of the main thread will be of a different nature depending on
175 if the user wants to perform interactive plotting. If they do want to perform
176 interactive plotting, the main threads event loop will simply be the GUI event
177 loop. In that case, GUI timers will be used to monitor the main threads input
178 queue. When elements appear on that queue, the main thread will respond
179 appropriately. For example, if the queue contains an element that consists of
180 user code to execute, the main thread will call the appropriate method of its
181 IPython instance. If the user does not want to perform interactive plotting,
182 the main thread will have a simpler event loop that will simply block on the
183 input queue. When something appears on that queue, the main thread will awake
184 and handle the request.
185
186 The event loop of the networking thread will typically be the Twisted event
187 loop. While it is possible to implement the engine's networking without using
188 Twisted, at this point, Twisted provides the best solution. Note that the GUI
189 application does not need to use Twisted in this case. The Twisted event loop
190 will contain an XML-RPC or JSON-RPC server that takes requests over the
191 network and handles those requests by enqueing elements on the main thread's
192 input queue or dequeing elements on the main thread's output queue.
193
194 Because of the asynchronous nature of the network communication, a single
195 input and output queue will be used to handle the interaction with the main
196 thread. It is also possible to use multiple queues to isolate the different
197 types of requests, but our feeling is that this is more complicated than it
198 needs to be.
199
200 One of the main issues is how stdout/stderr will be handled. Our idea is to
201 replace sys.stdout/sys.stderr by custom classes that will immediately write
202 data to the main thread's output queue when user code writes to these streams
203 (by doing print). Once on the main thread's output queue, the networking
204 thread will make the data available to the GUI application over the network.
205
206 One unavoidable limitation in this design is that if user code does a print
207 and then enters non-GIL-releasing extension code, the networking thread will
208 go silent until the GIL is again released. During this time, the networking
209 thread will not be able to process the GUI application's requests of the
210 engine. Thus, the values of stdout/stderr will be unavailable during this
211 time. This goes beyond stdout/stderr, however. Anytime the main thread is
212 holding the GIL, the networking thread will go silent and be unable to handle
213 requests.
214
215 GUI Application details
216 =======================
217
218 The GUI application will also have two threads. While this is not a strict
219 requirement, it probably makes sense and is a good place to start. The main
220 thread will be the GUI tread. The other thread will be a networking thread and
221 will handle the messages that are sent to and from the engine process.
222
223 Like the engine, we will use two queues to control the flow of messages
224 between the main thread and networking thread. One of these queues will be
225 used for messages sent from the GUI application to the engine. When the GUI
226 application needs to send a message to the engine, it will simply enque the
227 appropriate message on this queue. The networking thread will watch this queue
228 and forward messages to the engine using an appropriate network protocol.
229
230 The other queue will be used for incoming messages from the engine. The
231 networking thread will poll for incoming messages from the engine. When it
232 receives any message, it will simply put that message on this other queue. The
233 GUI application will periodically see if there are any messages on this queue
234 and if there are it will handle them.
235
236 The GUI application must be prepared to handle any incoming message at any
237 time. Due to a variety of reasons, the one or more reply messages associated
238 with a request, may appear at any time in the future and possible in different
239 orders. It is also possible that a reply might not appear. An example of this
240 would be a request for a tab completion event. If the engine is busy, it won't
241 be possible to fulfill the request for a while. While the tab completion
242 request will eventually be handled, the GUI application has to be prepared to
243 abandon waiting for the reply if the user moves on or a certain timeout
244 expires.
245
246 Prototype details
247 =================
248
249 With this design, it should be possible to develop a relatively complete GUI
250 application, while using a mock engine. This prototype should use the two
251 process design described above, but instead of making actual network calls,
252 the network thread of the GUI application should have an object that fakes the
253 network traffic. This mock object will consume messages off of one queue,
254 pause for a short while (to model network and other latencies) and then place
255 reply messages on the other queue.
256
257 This simple design will allow us to determine exactly what the message types
258 and formats should be as well as how the GUI application should interact with
259 the two message queues. Note, it is not required that the mock object actually
260 be able to execute Python code or actually complete strings in the users
261 namespace. All of these things can simply be faked. This will also help us to
262 understand what the interface needs to look like that handles the network
263 traffic. This will also help us to understand the design of the engine better.
264
265 The GUI application should be developed using IPython's component, application
266 and configuration system. It may take some work to see what the best way of
267 integrating these things with PyQt are.
268
269 After this stage is done, we can move onto creating a real IPython engine for
270 the GUI application to communicate with. This will likely be more work that
271 the GUI application itself, but having a working GUI application will make it
272 *much* easier to design and implement the engine.
273
274 We also might want to introduce a third process into the mix. Basically, this
275 would be a central messaging hub that both the engine and GUI application
276 would use to send and retrieve messages. This is not required, but it might be
277 a really good idea.
278
279 Also, I have some ideas on the best way to handle notebook saving and
280 persistence.
281
282 Refactoring of IPython.core
283 ===========================
284
285 We need to go through IPython.core and describe what specifically needs to be
286 done.
@@ -0,0 +1,55 b''
1 =========================
2 IPython GUI Support Notes
3 =========================
4
5 IPython allows GUI event loops to be run in an interactive IPython session.
6 This is done using Python's PyOS_InputHook hook which Python calls
7 when the :func:`raw_input` function is called and waiting for user input.
8 IPython has versions of this hook for wx, pyqt4 and pygtk.
9
10 When a GUI program is used interactively within IPython, the event loop of
11 the GUI should *not* be started. This is because, the PyOS_Inputhook itself
12 is responsible for iterating the GUI event loop.
13
14 IPython has facilities for installing the needed input hook for each GUI
15 toolkit and for creating the needed main GUI application object. Usually,
16 these main application objects should be created only once and for some
17 GUI toolkits, special options have to be passed to the application object
18 to enable it to function properly in IPython.
19
20 We need to answer the following questions:
21
22 * Who is responsible for creating the main GUI application object, IPython
23 or third parties (matplotlib, enthought.traits, etc.)?
24
25 * What is the proper way for third party code to detect if a GUI application
26 object has already been created? If one has been created, how should
27 the existing instance be retrieved?
28
29 * In a GUI application object has been created, how should third party code
30 detect if the GUI event loop is running. It is not sufficient to call the
31 relevant function methods in the GUI toolkits (like ``IsMainLoopRunning``)
32 because those don't know if the GUI event loop is running through the
33 input hook.
34
35 * We might need a way for third party code to determine if it is running
36 in IPython or not. Currently, the only way of running GUI code in IPython
37 is by using the input hook, but eventually, GUI based versions of IPython
38 will allow the GUI event loop in the more traditional manner. We will need
39 a way for third party code to distinguish between these two cases.
40
41 Here is some sample code I have been using to debug this issue::
42
43 from matplotlib import pyplot as plt
44
45 from enthought.traits import api as traits
46
47 class Foo(traits.HasTraits):
48 a = traits.Float()
49
50 f = Foo()
51 f.configure_traits()
52
53 plt.plot(range(10))
54
55
@@ -0,0 +1,111 b''
1 ===============================
2 IPython session storage notes
3 ===============================
4
5 This document serves as a sample/template for ideas on how to store session
6 data on disk. This stems from discussions we had on various mailing lists, and
7 should be considered a pure work in progress. We haven't settled these ideas
8 completely yet, and there's a lot to discuss; this document should just serve
9 as a reference of the distilled points from various conversations on multiple
10 mailing lists, and will congeal over time on a specific design we implement.
11
12 The frontend would store, for now, 5 types of data:
13
14 #. Input: this is python/ipython code to be executed.
15
16 #. Output (python): result of executing Inputs.
17
18 #. Standard output: from subprocesses.
19
20 #. Standard error: from subprocesses.
21
22 #. Text: arbitrary text. For now, we'll just store plain text and will defer
23 to the user on how to format it, though it should be valid reST if it is
24 later to be converted into html/pdf.
25
26 The non-text cells would be stored on-disk as follows::
27
28 .. input-cell::
29 :id: 1
30
31 3+3
32
33 .. output-cell::
34 :id: 1
35
36 6
37
38 .. input-cell::
39 :id: 2
40
41 ls
42
43 .. stdout-cell::
44 :id: 2
45
46 a.py b.py
47
48 .. input-cell::
49 :id: 3
50
51 !askdfj
52
53 .. stderr-cell::
54 :id: 3
55
56 sh: askdfj: command not found
57
58 Brian made some interesting points on the mailing list inspired by the
59 Mathematica format, reproduced here for reference:
60
61 The Mathematica notebook format is a plain text file that itself is *valid
62 Mathematica code*. This id documented here:
63
64 http://reference.wolfram.com/mathematica/guide/LowLevelNotebookProgramming.html
65
66 For examples a simple notebook with one text cell is just::
67
68 Notebook[{Cell['Here is my text', 'Text']}]
69
70 Everything - input cells, output cells, static images and all are represented
71 in this way and embedded in the plain text notebook file. The Python
72 generalization of this would be the following:
73
74 * A Python notebook is plain text, importable Python code.
75
76 * That code is simply a tree of objects that declare the relevant parts of the
77 notebook.
78
79 This has a number of advantages:
80
81 * A notebook can be imported, manipulated and run by anyone who has the support
82 code (the notebook module that defines the relevant classes).
83
84 * A notebook doesn't need to be parsed. It is valid Python and can be imported
85 or exec'd. Once that is done, you have the full notebook in memory. You can
86 immediately do anything you want with it.
87
88 * The various Notebook, Cell, Image, etc. classes can know about how to output
89 to various formats, latex, html, reST, XML, etc::
90
91 import mynotebook
92 mynotebook.notebook.export('rest')
93
94 * Each individual format (HTML, reST, latex) has weaknesses. If you pick any
95 one to be *the* notebook format, you are building those weaknesses into your
96 design. A pure python based notebook format won't suffer from that syndrome.
97
98 * It is a clean separation of the model (Notebook, Cell, Image, etc.) and the
99 view (HTML, reST, etc.). Picking HTML or reST for the notebook format
100 confuses (at some level) the model and view...
101
102 * Third party code can define new Notebook elements that specify how they can
103 be rendered in different contexts. For example, matplotlib could ship a
104 Figure element that knows how to render itself as a native PyQt GUI, a static
105 image, a web page, etc.
106
107 * A notebook remains a single plain text file that anyone can edit - even if it
108 has embedded images. Neither HTML nor reST have the ability to inline
109 graphics in plain text files. While I love reST, it is a pain that I need an
110 entire directory of files to render a single Sphinx doc.
111 No newline at end of file
@@ -30,14 +30,14 b" if sys.version[0:3] < '2.5':"
30
30
31
31
32 # Make it easy to import extensions - they are always directly on pythonpath.
32 # Make it easy to import extensions - they are always directly on pythonpath.
33 # Therefore, non-IPython modules can be added to extensions directory
33 # Therefore, non-IPython modules can be added to extensions directory.
34 # This should probably be in ipapp.py.
34 sys.path.append(os.path.join(os.path.dirname(__file__), "extensions"))
35 sys.path.append(os.path.join(os.path.dirname(__file__), "extensions"))
35
36
36 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
37 # Setup the top level names
38 # Setup the top level names
38 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
39
40
40 # In some cases, these are causing circular imports.
41 from .config.loader import Config
41 from .config.loader import Config
42 from .core import release
42 from .core import release
43 from .core.application import Application
43 from .core.application import Application
@@ -23,7 +23,7 b' import os'
23 import sys
23 import sys
24
24
25 from IPython.external import argparse
25 from IPython.external import argparse
26 from IPython.utils.genutils import filefind
26 from IPython.utils.path import filefind
27
27
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 # Exceptions
29 # Exceptions
@@ -40,6 +40,7 b' class ConfigLoaderError(ConfigError):'
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Argparse fix
41 # Argparse fix
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43 # Unfortunately argparse by default prints help messages to stderr instead of
44 # Unfortunately argparse by default prints help messages to stderr instead of
44 # stdout. This makes it annoying to capture long help screens at the command
45 # stdout. This makes it annoying to capture long help screens at the command
45 # line, since one must know how to pipe stderr, which many users don't know how
46 # line, since one must know how to pipe stderr, which many users don't know how
@@ -200,10 +201,13 b' class ConfigLoader(object):'
200 self.config = Config()
201 self.config = Config()
201
202
202 def load_config(self):
203 def load_config(self):
203 """Load a config from somewhere, return a Struct.
204 """Load a config from somewhere, return a :class:`Config` instance.
204
205
205 Usually, this will cause self.config to be set and then returned.
206 Usually, this will cause self.config to be set and then returned.
207 However, in most cases, :meth:`ConfigLoader.clear` should be called
208 to erase any previous state.
206 """
209 """
210 self.clear()
207 return self.config
211 return self.config
208
212
209
213
@@ -242,6 +246,7 b' class PyFileConfigLoader(FileConfigLoader):'
242
246
243 def load_config(self):
247 def load_config(self):
244 """Load the config from a file and return it as a Struct."""
248 """Load the config from a file and return it as a Struct."""
249 self.clear()
245 self._find_file()
250 self._find_file()
246 self._read_file_as_dict()
251 self._read_file_as_dict()
247 self._convert_to_config()
252 self._convert_to_config()
@@ -292,20 +297,10 b' class CommandLineConfigLoader(ConfigLoader):'
292 """
297 """
293
298
294
299
295 class __NoConfigDefault(object): pass
296 NoConfigDefault = __NoConfigDefault()
297
298
299 class ArgParseConfigLoader(CommandLineConfigLoader):
300 class ArgParseConfigLoader(CommandLineConfigLoader):
300 #: Global default for arguments (see argparse docs for details)
301 argument_default = NoConfigDefault
302
303 def __init__(self, argv=None, arguments=(), *args, **kw):
304 """Create a config loader for use with argparse.
305
301
306 With the exception of ``argv`` and ``arguments``, other args and kwargs
302 def __init__(self, argv=None, *parser_args, **parser_kw):
307 arguments here are passed onto the constructor of
303 """Create a config loader for use with argparse.
308 :class:`argparse.ArgumentParser`.
309
304
310 Parameters
305 Parameters
311 ----------
306 ----------
@@ -314,19 +309,22 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
314 If given, used to read command-line arguments from, otherwise
309 If given, used to read command-line arguments from, otherwise
315 sys.argv[1:] is used.
310 sys.argv[1:] is used.
316
311
317 arguments : optional, tuple
312 parser_args : tuple
318 Description of valid command-line arguments, to be called in sequence
313 A tuple of positional arguments that will be passed to the
319 with parser.add_argument() to configure the parser.
314 constructor of :class:`argparse.ArgumentParser`.
315
316 parser_kw : dict
317 A tuple of keyword arguments that will be passed to the
318 constructor of :class:`argparse.ArgumentParser`.
320 """
319 """
321 super(CommandLineConfigLoader, self).__init__()
320 super(CommandLineConfigLoader, self).__init__()
322 if argv == None:
321 if argv == None:
323 argv = sys.argv[1:]
322 argv = sys.argv[1:]
324 self.argv = argv
323 self.argv = argv
325 self.arguments = arguments
324 self.parser_args = parser_args
326 self.args = args
325 kwargs = dict(argument_default=argparse.SUPPRESS)
327 kwargs = dict(argument_default=self.argument_default)
326 kwargs.update(parser_kw)
328 kwargs.update(kw)
327 self.parser_kw = kwargs
329 self.kw = kwargs
330
328
331 def load_config(self, args=None):
329 def load_config(self, args=None):
332 """Parse command line arguments and return as a Struct.
330 """Parse command line arguments and return as a Struct.
@@ -335,10 +333,10 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
335 ----------
333 ----------
336
334
337 args : optional, list
335 args : optional, list
338 If given, a list with the structure of sys.argv[1:] to parse arguments
336 If given, a list with the structure of sys.argv[1:] to parse
339 from. If not given, the instance's self.argv attribute (given at
337 arguments from. If not given, the instance's self.argv attribute
340 construction time) is used."""
338 (given at construction time) is used."""
341
339 self.clear()
342 if args is None:
340 if args is None:
343 args = self.argv
341 args = self.argv
344 self._create_parser()
342 self._create_parser()
@@ -353,17 +351,11 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
353 return []
351 return []
354
352
355 def _create_parser(self):
353 def _create_parser(self):
356 self.parser = ArgumentParser(*self.args, **self.kw)
354 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
357 self._add_arguments()
355 self._add_arguments()
358 self._add_other_arguments()
359
356
360 def _add_arguments(self):
357 def _add_arguments(self):
361 for argument in self.arguments:
358 raise NotImplementedError("subclasses must implement _add_arguments")
362 self.parser.add_argument(*argument[0],**argument[1])
363
364 def _add_other_arguments(self):
365 """Meant for subclasses to add their own arguments."""
366 pass
367
359
368 def _parse_args(self, args):
360 def _parse_args(self, args):
369 """self.parser->self.parsed_data"""
361 """self.parser->self.parsed_data"""
@@ -372,6 +364,7 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
372 def _convert_to_config(self):
364 def _convert_to_config(self):
373 """self.parsed_data->self.config"""
365 """self.parsed_data->self.config"""
374 for k, v in vars(self.parsed_data).items():
366 for k, v in vars(self.parsed_data).items():
375 if v is not NoConfigDefault:
367 exec_str = 'self.config.' + k + '= v'
376 exec_str = 'self.config.' + k + '= v'
368 exec exec_str in locals(), globals()
377 exec exec_str in locals(), globals()
369
370
@@ -61,35 +61,38 b' class TestPyFileCL(TestCase):'
61 self.assertEquals(config.Foo.Bam.value, range(10))
61 self.assertEquals(config.Foo.Bam.value, range(10))
62 self.assertEquals(config.D.C.value, 'hi there')
62 self.assertEquals(config.D.C.value, 'hi there')
63
63
64 class MyLoader1(ArgParseConfigLoader):
65 def _add_arguments(self):
66 p = self.parser
67 p.add_argument('-f', '--foo', dest='Global.foo', type=str)
68 p.add_argument('-b', dest='MyClass.bar', type=int)
69 p.add_argument('-n', dest='n', action='store_true')
70 p.add_argument('Global.bam', type=str)
71
72 class MyLoader2(ArgParseConfigLoader):
73 def _add_arguments(self):
74 subparsers = self.parser.add_subparsers(dest='subparser_name')
75 subparser1 = subparsers.add_parser('1')
76 subparser1.add_argument('-x',dest='Global.x')
77 subparser2 = subparsers.add_parser('2')
78 subparser2.add_argument('y')
64
79
65 class TestArgParseCL(TestCase):
80 class TestArgParseCL(TestCase):
66
81
67 def test_basic(self):
82 def test_basic(self):
68
83 cl = MyLoader1()
69 arguments = (
70 (('-f','--foo'), dict(dest='Global.foo', type=str)),
71 (('-b',), dict(dest='MyClass.bar', type=int)),
72 (('-n',), dict(dest='n', action='store_true')),
73 (('Global.bam',), dict(type=str))
74 )
75 cl = ArgParseConfigLoader(arguments=arguments)
76 config = cl.load_config('-f hi -b 10 -n wow'.split())
84 config = cl.load_config('-f hi -b 10 -n wow'.split())
77 self.assertEquals(config.Global.foo, 'hi')
85 self.assertEquals(config.Global.foo, 'hi')
78 self.assertEquals(config.MyClass.bar, 10)
86 self.assertEquals(config.MyClass.bar, 10)
79 self.assertEquals(config.n, True)
87 self.assertEquals(config.n, True)
80 self.assertEquals(config.Global.bam, 'wow')
88 self.assertEquals(config.Global.bam, 'wow')
89 config = cl.load_config(['wow'])
90 self.assertEquals(config.keys(), ['Global'])
91 self.assertEquals(config.Global.keys(), ['bam'])
92 self.assertEquals(config.Global.bam, 'wow')
81
93
82 def test_add_arguments(self):
94 def test_add_arguments(self):
83
95 cl = MyLoader2()
84 class MyLoader(ArgParseConfigLoader):
85 def _add_arguments(self):
86 subparsers = self.parser.add_subparsers(dest='subparser_name')
87 subparser1 = subparsers.add_parser('1')
88 subparser1.add_argument('-x',dest='Global.x')
89 subparser2 = subparsers.add_parser('2')
90 subparser2.add_argument('y')
91
92 cl = MyLoader()
93 config = cl.load_config('2 frobble'.split())
96 config = cl.load_config('2 frobble'.split())
94 self.assertEquals(config.subparser_name, '2')
97 self.assertEquals(config.subparser_name, '2')
95 self.assertEquals(config.y, 'frobble')
98 self.assertEquals(config.y, 'frobble')
@@ -97,6 +100,15 b' class TestArgParseCL(TestCase):'
97 self.assertEquals(config.subparser_name, '1')
100 self.assertEquals(config.subparser_name, '1')
98 self.assertEquals(config.Global.x, 'frobble')
101 self.assertEquals(config.Global.x, 'frobble')
99
102
103 def test_argv(self):
104 cl = MyLoader1(argv='-f hi -b 10 -n wow'.split())
105 config = cl.load_config()
106 self.assertEquals(config.Global.foo, 'hi')
107 self.assertEquals(config.MyClass.bar, 10)
108 self.assertEquals(config.n, True)
109 self.assertEquals(config.Global.bam, 'wow')
110
111
100 class TestConfig(TestCase):
112 class TestConfig(TestCase):
101
113
102 def test_setget(self):
114 def test_setget(self):
@@ -28,9 +28,9 b' import sys'
28 from IPython.core.component import Component
28 from IPython.core.component import Component
29 from IPython.core.splitinput import split_user_input
29 from IPython.core.splitinput import split_user_input
30
30
31 from IPython.utils.traitlets import CBool, List, Instance
31 from IPython.utils.traitlets import List
32 from IPython.utils.genutils import error
33 from IPython.utils.autoattr import auto_attr
32 from IPython.utils.autoattr import auto_attr
33 from IPython.utils.warn import warn, error
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Utilities
36 # Utilities
@@ -33,7 +33,7 b' import os'
33 import sys
33 import sys
34
34
35 from IPython.core import release, crashhandler
35 from IPython.core import release, crashhandler
36 from IPython.utils.genutils import get_ipython_dir, get_ipython_package_dir
36 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
37 from IPython.config.loader import (
37 from IPython.config.loader import (
38 PyFileConfigLoader,
38 PyFileConfigLoader,
39 ArgParseConfigLoader,
39 ArgParseConfigLoader,
@@ -48,108 +48,86 b' class ApplicationError(Exception):'
48 pass
48 pass
49
49
50
50
51 app_cl_args = (
51 class BaseAppConfigLoader(ArgParseConfigLoader):
52 (('--ipython-dir', ), dict(
52 """Default command line options for IPython based applications."""
53 dest='Global.ipython_dir',type=unicode,
53
54 help=
54 def _add_ipython_dir(self, parser):
55 """Set to override default location of the IPython directory
55 """Add the --ipython-dir option to the parser."""
56 IPYTHON_DIR, stored as Global.ipython_dir. This can also be specified
56 paa = parser.add_argument
57 through the environment variable IPYTHON_DIR.""",
57 paa('--ipython-dir',
58 metavar='Global.ipython_dir') ),
58 dest='Global.ipython_dir',type=unicode,
59 (('-p', '--profile',), dict(
59 help=
60 dest='Global.profile',type=unicode,
60 """Set to override default location of the IPython directory
61 help=
61 IPYTHON_DIR, stored as Global.ipython_dir. This can also be
62 """The string name of the ipython profile to be used. Assume that your
62 specified through the environment variable IPYTHON_DIR.""",
63 config file is ipython_config-<name>.py (looks in current dir first,
63 metavar='Global.ipython_dir')
64 then in IPYTHON_DIR). This is a quick way to keep and load multiple
64
65 config files for different tasks, especially if include your basic one
65 def _add_log_level(self, parser):
66 in your more specialized ones. You can keep a basic
66 """Add the --log-level option to the parser."""
67 IPYTHON_DIR/ipython_config.py file and then have other 'profiles' which
67 paa = parser.add_argument
68 include this one and load extra things for particular tasks.""",
68 paa('--log-level',
69 metavar='Global.profile') ),
69 dest="Global.log_level",type=int,
70 (('--log-level',), dict(
70 help='Set the log level (0,10,20,30,40,50). Default is 30.',
71 dest="Global.log_level",type=int,
71 metavar='Global.log_level')
72 help='Set the log level (0,10,20,30,40,50). Default is 30.',
72
73 metavar='Global.log_level')),
73 def _add_arguments(self):
74 (('--config-file',), dict(
74 self._add_ipython_dir(self.parser)
75 dest='Global.config_file',type=unicode,
75 self._add_log_level(self.parser)
76 help=
76
77 """Set the config file name to override default. Normally IPython
78 loads ipython_config.py (from current directory) or
79 IPYTHON_DIR/ipython_config.py. If the loading of your config file
80 fails, IPython starts with a bare bones configuration (no modules
81 loaded at all).""",
82 metavar='Global.config_file')),
83 )
84
77
85 class Application(object):
78 class Application(object):
86 """Load a config, construct components and set them running.
79 """Load a config, construct components and set them running.
87
80
88 The configuration of an application can be done via four different Config
81 The configuration of an application can be done via three different Config
89 objects, which are loaded and ultimately merged into a single one used from
82 objects, which are loaded and ultimately merged into a single one used
90 that point on by the app. These are:
83 from that point on by the app. These are:
91
84
92 1. default_config: internal defaults, implemented in code.
85 1. default_config: internal defaults, implemented in code.
93 2. file_config: read from the filesystem.
86 2. file_config: read from the filesystem.
94 3. command_line_config: read from the system's command line flags.
87 3. command_line_config: read from the system's command line flags.
95 4. constructor_config: passed parametrically to the constructor.
96
88
97 During initialization, 3 is actually read before 2, since at the
89 During initialization, 3 is actually read before 2, since at the
98 command-line one may override the location of the file to be read. But the
90 command-line one may override the location of the file to be read. But the
99 above is the order in which the merge is made.
91 above is the order in which the merge is made.
100
101 There is a final config object can be created and passed to the
102 constructor: override_config. If it exists, this completely overrides the
103 configs 2-4 above (the default is still used to ensure that all needed
104 fields at least are created). This makes it easier to create
105 parametrically (e.g. in testing or sphinx plugins) objects with a known
106 configuration, that are unaffected by whatever arguments may be present in
107 sys.argv or files in the user's various directories.
108 """
92 """
109
93
110 name = u'ipython'
94 name = u'ipython'
111 description = 'IPython: an enhanced interactive Python shell.'
95 description = 'IPython: an enhanced interactive Python shell.'
112 #: usage message printed by argparse. If None, auto-generate
96 #: Usage message printed by argparse. If None, auto-generate
113 usage = None
97 usage = None
114 config_file_name = u'ipython_config.py'
98 #: The command line config loader. Subclass of ArgParseConfigLoader.
115 #: Track the default and actual separately because some messages are
99 command_line_loader = BaseAppConfigLoader
116 #: only printed if we aren't using the default.
100 #: The name of the config file to load, determined at runtime
117 default_config_file_name = config_file_name
101 config_file_name = None
102 #: The name of the default config file. Track separately from the actual
103 #: name because some logic happens only if we aren't using the default.
104 default_config_file_name = u'ipython_config.py'
118 default_log_level = logging.WARN
105 default_log_level = logging.WARN
119 #: Set by --profile option
106 #: Set by --profile option
120 profile_name = None
107 profile_name = None
121 #: User's ipython directory, typically ~/.ipython/
108 #: User's ipython directory, typically ~/.ipython/
122 ipython_dir = None
109 ipython_dir = None
123 #: internal defaults, implemented in code.
110 #: Internal defaults, implemented in code.
124 default_config = None
111 default_config = None
125 #: read from the filesystem
112 #: Read from the filesystem.
126 file_config = None
113 file_config = None
127 #: read from the system's command line flags
114 #: Read from the system's command line flags.
128 command_line_config = None
115 command_line_config = None
129 #: passed parametrically to the constructor.
116 #: The final config that will be passed to the component.
130 constructor_config = None
117 master_config = None
131 #: final override, if given supercedes file/command/constructor configs
132 override_config = None
133 #: A reference to the argv to be used (typically ends up being sys.argv[1:])
118 #: A reference to the argv to be used (typically ends up being sys.argv[1:])
134 argv = None
119 argv = None
135 #: Default command line arguments. Subclasses should create a new tuple
136 #: that *includes* these.
137 cl_arguments = app_cl_args
138
139 #: extra arguments computed by the command-line loader
120 #: extra arguments computed by the command-line loader
140 extra_args = None
121 extra_args = None
122 #: The class to use as the crash handler.
123 crash_handler_class = crashhandler.CrashHandler
141
124
142 # Private attributes
125 # Private attributes
143 _exiting = False
126 _exiting = False
144 _initialized = False
127 _initialized = False
145
128
146 # Class choices for things that will be instantiated at runtime.
129 def __init__(self, argv=None):
147 _CrashHandler = crashhandler.CrashHandler
148
149 def __init__(self, argv=None, constructor_config=None, override_config=None):
150 self.argv = sys.argv[1:] if argv is None else argv
130 self.argv = sys.argv[1:] if argv is None else argv
151 self.constructor_config = constructor_config
152 self.override_config = override_config
153 self.init_logger()
131 self.init_logger()
154
132
155 def init_logger(self):
133 def init_logger(self):
@@ -194,13 +172,12 b' class Application(object):'
194 self.log_default_config()
172 self.log_default_config()
195 self.set_default_config_log_level()
173 self.set_default_config_log_level()
196
174
197 if self.override_config is None:
175 # Command-line config
198 # Command-line config
176 self.pre_load_command_line_config()
199 self.pre_load_command_line_config()
177 self.load_command_line_config()
200 self.load_command_line_config()
178 self.set_command_line_config_log_level()
201 self.set_command_line_config_log_level()
179 self.post_load_command_line_config()
202 self.post_load_command_line_config()
180 self.log_command_line_config()
203 self.log_command_line_config()
204
181
205 # Find resources needed for filesystem access, using information from
182 # Find resources needed for filesystem access, using information from
206 # the above two
183 # the above two
@@ -209,13 +186,12 b' class Application(object):'
209 self.find_config_file_name()
186 self.find_config_file_name()
210 self.find_config_file_paths()
187 self.find_config_file_paths()
211
188
212 if self.override_config is None:
189 # File-based config
213 # File-based config
190 self.pre_load_file_config()
214 self.pre_load_file_config()
191 self.load_file_config()
215 self.load_file_config()
192 self.set_file_config_log_level()
216 self.set_file_config_log_level()
193 self.post_load_file_config()
217 self.post_load_file_config()
194 self.log_file_config()
218 self.log_file_config()
219
195
220 # Merge all config objects into a single one the app can then use
196 # Merge all config objects into a single one the app can then use
221 self.merge_configs()
197 self.merge_configs()
@@ -240,7 +216,7 b' class Application(object):'
240
216
241 def create_crash_handler(self):
217 def create_crash_handler(self):
242 """Create a crash handler, typically setting sys.excepthook to it."""
218 """Create a crash handler, typically setting sys.excepthook to it."""
243 self.crash_handler = self._CrashHandler(self, self.name)
219 self.crash_handler = self.crash_handler_class(self)
244 sys.excepthook = self.crash_handler
220 sys.excepthook = self.crash_handler
245
221
246 def create_default_config(self):
222 def create_default_config(self):
@@ -270,11 +246,12 b' class Application(object):'
270
246
271 def create_command_line_config(self):
247 def create_command_line_config(self):
272 """Create and return a command line config loader."""
248 """Create and return a command line config loader."""
273 return ArgParseConfigLoader(self.argv, self.cl_arguments,
249 return self.command_line_loader(
274 description=self.description,
250 self.argv,
275 version=release.version,
251 description=self.description,
276 usage=self.usage,
252 version=release.version,
277 )
253 usage=self.usage
254 )
278
255
279 def pre_load_command_line_config(self):
256 def pre_load_command_line_config(self):
280 """Do actions just before loading the command line config."""
257 """Do actions just before loading the command line config."""
@@ -338,18 +315,22 b' class Application(object):'
338
315
339 If a profile has been set at the command line, this will resolve it.
316 If a profile has been set at the command line, this will resolve it.
340 """
317 """
341
342 try:
318 try:
343 self.config_file_name = self.command_line_config.Global.config_file
319 self.config_file_name = self.command_line_config.Global.config_file
344 except AttributeError:
320 except AttributeError:
345 pass
321 pass
322 else:
323 return
346
324
347 try:
325 try:
348 self.profile_name = self.command_line_config.Global.profile
326 self.profile_name = self.command_line_config.Global.profile
349 except AttributeError:
327 except AttributeError:
350 pass
328 # Just use the default as there is no profile
329 self.config_file_name = self.default_config_file_name
351 else:
330 else:
352 name_parts = self.config_file_name.split('.')
331 # Use the default config file name and profile name if set
332 # to determine the used config file name.
333 name_parts = self.default_config_file_name.split('.')
353 name_parts.insert(1, u'_' + self.profile_name + u'.')
334 name_parts.insert(1, u'_' + self.profile_name + u'.')
354 self.config_file_name = ''.join(name_parts)
335 self.config_file_name = ''.join(name_parts)
355
336
@@ -418,13 +399,9 b' class Application(object):'
418 """Merge the default, command line and file config objects."""
399 """Merge the default, command line and file config objects."""
419 config = Config()
400 config = Config()
420 config._merge(self.default_config)
401 config._merge(self.default_config)
421 if self.override_config is None:
402 config._merge(self.file_config)
422 config._merge(self.file_config)
403 config._merge(self.command_line_config)
423 config._merge(self.command_line_config)
404
424 if self.constructor_config is not None:
425 config._merge(self.constructor_config)
426 else:
427 config._merge(self.override_config)
428 # XXX fperez - propose to Brian we rename master_config to simply
405 # XXX fperez - propose to Brian we rename master_config to simply
429 # config, I think this is going to be heavily used in examples and
406 # config, I think this is going to be heavily used in examples and
430 # application code and the name is shorter/easier to find/remember.
407 # application code and the name is shorter/easier to find/remember.
@@ -456,15 +433,6 b' class Application(object):'
456 # Utility methods
433 # Utility methods
457 #-------------------------------------------------------------------------
434 #-------------------------------------------------------------------------
458
435
459 def abort(self):
460 """Abort the starting of the application."""
461 if self._exiting:
462 pass
463 else:
464 self.log.critical("Aborting application: %s" % self.name, exc_info=True)
465 self._exiting = True
466 sys.exit(1)
467
468 def exit(self, exit_status=0):
436 def exit(self, exit_status=0):
469 if self._exiting:
437 if self._exiting:
470 pass
438 pass
@@ -473,17 +441,13 b' class Application(object):'
473 self._exiting = True
441 self._exiting = True
474 sys.exit(exit_status)
442 sys.exit(exit_status)
475
443
476 def attempt(self, func, action='abort'):
444 def attempt(self, func):
477 try:
445 try:
478 func()
446 func()
479 except SystemExit:
447 except SystemExit:
480 raise
448 raise
481 except:
449 except:
482 if action == 'abort':
450 self.log.critical("Aborting application: %s" % self.name,
483 self.log.critical("Aborting application: %s" % self.name,
451 exc_info=True)
484 exc_info=True)
452 self.exit(0)
485 self.abort()
486 raise
487 elif action == 'exit':
488 self.exit(0)
489
453
@@ -69,19 +69,20 b' used, and this module (and the readline module) are silently inactive.'
69 import __builtin__
69 import __builtin__
70 import __main__
70 import __main__
71 import glob
71 import glob
72 import inspect
72 import itertools
73 import itertools
73 import keyword
74 import keyword
74 import os
75 import os
75 import re
76 import re
76 import shlex
77 import shlex
77 import sys
78 import sys
78 import types
79
79
80 import IPython.utils.rlineimpl as readline
81 from IPython.core.error import TryNext
80 from IPython.core.error import TryNext
82 from IPython.core.prefilter import ESC_MAGIC
81 from IPython.core.prefilter import ESC_MAGIC
83 from IPython.utils import generics
82 from IPython.utils import generics
84 from IPython.utils.genutils import debugx, dir2
83 from IPython.utils.frame import debugx
84 from IPython.utils.dir2 import dir2
85 import IPython.utils.rlineimpl as readline
85
86
86 #-----------------------------------------------------------------------------
87 #-----------------------------------------------------------------------------
87 # Globals
88 # Globals
@@ -216,7 +217,6 b' class Completer:'
216 with a __getattr__ hook is evaluated.
217 with a __getattr__ hook is evaluated.
217
218
218 """
219 """
219 import re
220
220
221 #print 'Completer->attr_matches, txt=%r' % text # dbg
221 #print 'Completer->attr_matches, txt=%r' % text # dbg
222 # Another option, seems to work great. Catches things like ''.<tab>
222 # Another option, seems to work great. Catches things like ''.<tab>
@@ -27,7 +27,7 b' from weakref import WeakValueDictionary'
27 from IPython.utils.importstring import import_item
27 from IPython.utils.importstring import import_item
28 from IPython.config.loader import Config
28 from IPython.config.loader import Config
29 from IPython.utils.traitlets import (
29 from IPython.utils.traitlets import (
30 HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This
30 HasTraitlets, MetaHasTraitlets, Instance, This
31 )
31 )
32
32
33
33
@@ -1,124 +1,113 b''
1 # -*- coding: utf-8 -*-
1 # encoding: utf-8
2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
3
3
4 Authors:
4
5
5 Authors
6 * Fernando Perez
6 -------
7 * Brian E. Granger
7 - Fernando Perez <Fernando.Perez@berkeley.edu>
8 """
8 """
9
9
10 #*****************************************************************************
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2008-2009 The IPython Development Team
11 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
12 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
12 # Copyright (C) 2008-2010 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #*****************************************************************************
16 #-----------------------------------------------------------------------------
17
17
18 #****************************************************************************
18 #-----------------------------------------------------------------------------
19 # Required modules
19 # Imports
20 #-----------------------------------------------------------------------------
20
21
21 # From the standard library
22 import os
22 import os
23 import sys
23 import sys
24 from pprint import pformat
24 from pprint import pformat
25
25
26 # Our own
27 from IPython.core import release
28 from IPython.core import ultratb
26 from IPython.core import ultratb
29 from IPython.utils.genutils import sys_info
30
31 from IPython.external.Itpl import itpl
27 from IPython.external.Itpl import itpl
28 from IPython.utils.sysinfo import sys_info
32
29
33 #****************************************************************************
30 #-----------------------------------------------------------------------------
31 # Code
32 #-----------------------------------------------------------------------------
34
33
35 class CrashHandler(object):
34 # Template for the user message.
36 """Customizable crash handlers for IPython-based systems.
35 _default_message_template = """\
36 Oops, $self.app_name crashed. We do our best to make it stable, but...
37
38 A crash report was automatically generated with the following information:
39 - A verbatim copy of the crash traceback.
40 - A copy of your input history during this session.
41 - Data on your current $self.app_name configuration.
37
42
38 Instances of this class provide a __call__ method which can be used as a
43 It was left in the file named:
39 sys.excepthook, i.e., the __call__ signature is:
44 \t'$self.crash_report_fname'
45 If you can email this file to the developers, the information in it will help
46 them in understanding and correcting the problem.
40
47
41 def __call__(self,etype, evalue, etb)
48 You can mail it to: $self.contact_name at $self.contact_email
49 with the subject '$self.app_name Crash Report'.
42
50
43 """
51 If you want to do it now, the following command will work (under Unix):
52 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
53
54 To ensure accurate tracking of this issue, please file a report about it at:
55 $self.bug_tracker
56 """
44
57
45 def __init__(self,app, app_name, contact_name=None, contact_email=None,
46 bug_tracker=None, crash_report_fname='CrashReport.txt',
47 show_crash_traceback=True, call_pdb=False):
48 """New crash handler.
49
58
50 Inputs:
59 class CrashHandler(object):
60 """Customizable crash handlers for IPython applications.
51
61
52 - app: a running application instance, which will be queried at crash
62 Instances of this class provide a :meth:`__call__` method which can be
53 time for internal information.
63 used as a ``sys.excepthook``. The :meth:`__call__` signature is::
54
64
55 - app_name: a string containing the name of your application.
65 def __call__(self, etype, evalue, etb)
66 """
56
67
57 - contact_name: a string with the name of the person to contact.
68 message_template = _default_message_template
58
69
59 - contact_email: a string with the email address of the contact.
70 def __init__(self, app, contact_name=None, contact_email=None,
71 bug_tracker=None, show_crash_traceback=True, call_pdb=False):
72 """Create a new crash handler
60
73
61 - bug_tracker: a string with the URL for your project's bug tracker.
74 Parameters
75 ----------
76 app : Application
77 A running :class:`Application` instance, which will be queried at
78 crash time for internal information.
62
79
63 - crash_report_fname: a string with the filename for the crash report
80 contact_name : str
64 to be saved in. These reports are left in the ipython user directory
81 A string with the name of the person to contact.
65 as determined by the running IPython instance.
66
82
67 Optional inputs:
83 contact_email : str
68
84 A string with the email address of the contact.
69 - show_crash_traceback(True): if false, don't print the crash
70 traceback on stderr, only generate the on-disk report
71
85
86 bug_tracker : str
87 A string with the URL for your project's bug tracker.
88
89 show_crash_traceback : bool
90 If false, don't print the crash traceback on stderr, only generate
91 the on-disk report
72
92
73 Non-argument instance attributes:
93 Non-argument instance attributes:
74
94
75 These instances contain some non-argument attributes which allow for
95 These instances contain some non-argument attributes which allow for
76 further customization of the crash handler's behavior. Please see the
96 further customization of the crash handler's behavior. Please see the
77 source for further details.
97 source for further details.
78 """
98 """
79
80 # apply args into instance
81 self.app = app
99 self.app = app
82 self.app_name = app_name
100 self.app_name = self.app.name
83 self.contact_name = contact_name
101 self.contact_name = contact_name
84 self.contact_email = contact_email
102 self.contact_email = contact_email
85 self.bug_tracker = bug_tracker
103 self.bug_tracker = bug_tracker
86 self.crash_report_fname = crash_report_fname
104 self.crash_report_fname = "Crash_report_%s.txt" % self.app_name
87 self.show_crash_traceback = show_crash_traceback
105 self.show_crash_traceback = show_crash_traceback
88 self.section_sep = '\n\n'+'*'*75+'\n\n'
106 self.section_sep = '\n\n'+'*'*75+'\n\n'
89 self.call_pdb = call_pdb
107 self.call_pdb = call_pdb
90 #self.call_pdb = True # dbg
108 #self.call_pdb = True # dbg
91
92 # Hardcoded defaults, which can be overridden either by subclasses or
93 # at runtime for the instance.
94
95 # Template for the user message. Subclasses which completely override
96 # this, or user apps, can modify it to suit their tastes. It gets
97 # expanded using itpl, so calls of the kind $self.foo are valid.
98 self.user_message_template = """
99 Oops, $self.app_name crashed. We do our best to make it stable, but...
100
109
101 A crash report was automatically generated with the following information:
110 def __call__(self, etype, evalue, etb):
102 - A verbatim copy of the crash traceback.
103 - A copy of your input history during this session.
104 - Data on your current $self.app_name configuration.
105
106 It was left in the file named:
107 \t'$self.crash_report_fname'
108 If you can email this file to the developers, the information in it will help
109 them in understanding and correcting the problem.
110
111 You can mail it to: $self.contact_name at $self.contact_email
112 with the subject '$self.app_name Crash Report'.
113
114 If you want to do it now, the following command will work (under Unix):
115 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
116
117 To ensure accurate tracking of this issue, please file a report about it at:
118 $self.bug_tracker
119 """
120
121 def __call__(self,etype, evalue, etb):
122 """Handle an exception, call for compatible with sys.excepthook"""
111 """Handle an exception, call for compatible with sys.excepthook"""
123
112
124 # Report tracebacks shouldn't use color in general (safer for users)
113 # Report tracebacks shouldn't use color in general (safer for users)
@@ -137,10 +126,11 b' $self.bug_tracker'
137 # write the report filename into the instance dict so it can get
126 # write the report filename into the instance dict so it can get
138 # properly expanded out in the user message template
127 # properly expanded out in the user message template
139 self.crash_report_fname = report_name
128 self.crash_report_fname = report_name
140 TBhandler = ultratb.VerboseTB(color_scheme=color_scheme,
129 TBhandler = ultratb.VerboseTB(
141 long_header=1,
130 color_scheme=color_scheme,
142 call_pdb=self.call_pdb,
131 long_header=1,
143 )
132 call_pdb=self.call_pdb,
133 )
144 if self.call_pdb:
134 if self.call_pdb:
145 TBhandler(etype,evalue,etb)
135 TBhandler(etype,evalue,etb)
146 return
136 return
@@ -159,7 +149,7 b' $self.bug_tracker'
159 return
149 return
160
150
161 # Inform user on stderr of what happened
151 # Inform user on stderr of what happened
162 msg = itpl('\n'+'*'*70+'\n'+self.user_message_template)
152 msg = itpl('\n'+'*'*70+'\n'+self.message_template)
163 print >> sys.stderr, msg
153 print >> sys.stderr, msg
164
154
165 # Construct report on disk
155 # Construct report on disk
@@ -178,7 +168,9 b' $self.bug_tracker'
178
168
179 try:
169 try:
180 config = pformat(self.app.config)
170 config = pformat(self.app.config)
181 rpt_add(sec_sep+'Current user configuration structure:\n\n')
171 rpt_add(sec_sep)
172 rpt_add('Application name: %s\n\n' % self.app_name)
173 rpt_add('Current user configuration structure:\n\n')
182 rpt_add(config)
174 rpt_add(config)
183 except:
175 except:
184 pass
176 pass
@@ -186,38 +178,3 b' $self.bug_tracker'
186
178
187 return ''.join(report)
179 return ''.join(report)
188
180
189
190 class IPythonCrashHandler(CrashHandler):
191 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
192
193 def __init__(self, app, app_name='IPython'):
194
195 # Set here which of the IPython authors should be listed as contact
196 AUTHOR_CONTACT = 'Fernando'
197
198 # Set argument defaults
199 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
200 contact_name,contact_email = release.authors[AUTHOR_CONTACT][:2]
201 crash_report_fname = 'IPython_crash_report.txt'
202 # Call parent constructor
203 CrashHandler.__init__(self,app,app_name,contact_name,contact_email,
204 bug_tracker,crash_report_fname)
205
206 def make_report(self,traceback):
207 """Return a string containing a crash report."""
208
209 sec_sep = self.section_sep
210 # Start with parent report
211 report = [super(IPythonCrashHandler, self).make_report(traceback)]
212 # Add interactive-specific info we may have
213 rpt_add = report.append
214 try:
215 rpt_add(sec_sep+"History of session input:")
216 for line in self.app.shell.user_ns['_ih']:
217 rpt_add(line)
218 rpt_add('\n*** Last line of input (may not be in above history):\n')
219 rpt_add(self.app.shell._last_input_line+'\n')
220 except:
221 pass
222
223 return ''.join(report)
@@ -26,15 +26,13 b' http://www.python.org/2.2.3/license.html"""'
26 #*****************************************************************************
26 #*****************************************************************************
27
27
28 import bdb
28 import bdb
29 import cmd
30 import linecache
29 import linecache
31 import os
32 import sys
30 import sys
33
31
34 from IPython.utils import PyColorize
32 from IPython.utils import PyColorize
35 from IPython.core import ipapi
33 from IPython.core import ipapi
36 from IPython.utils import coloransi
34 from IPython.utils import coloransi
37 from IPython.utils.genutils import Term
35 from IPython.utils.io import Term
38 from IPython.core.excolors import exception_colors
36 from IPython.core.excolors import exception_colors
39
37
40 # See if we can use pydb.
38 # See if we can use pydb.
@@ -24,8 +24,6 b' import sys'
24
24
25 from IPython.core.component import Component
25 from IPython.core.component import Component
26
26
27 from IPython.utils.autoattr import auto_attr
28
29 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
30 # Classes and functions
28 # Classes and functions
31 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
@@ -24,6 +24,7 b' Notes'
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 from __future__ import with_statement
26 from __future__ import with_statement
27 import __main__
27
28
28 import sys
29 import sys
29 from contextlib import nested
30 from contextlib import nested
@@ -33,7 +34,7 b' from IPython.core.iplib import InteractiveShell'
33 from IPython.core.ipapp import load_default_config
34 from IPython.core.ipapp import load_default_config
34
35
35 from IPython.utils.traitlets import Bool, Str, CBool
36 from IPython.utils.traitlets import Bool, Str, CBool
36 from IPython.utils.genutils import ask_yes_no
37 from IPython.utils.io import ask_yes_no
37
38
38
39
39 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
@@ -10,8 +10,6 b' Color schemes for exception handling code in IPython.'
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #*****************************************************************************
11 #*****************************************************************************
12
12
13 #****************************************************************************
14 # Required modules
15 from IPython.utils.coloransi import ColorSchemeTable, TermColors, ColorScheme
13 from IPython.utils.coloransi import ColorSchemeTable, TermColors, ColorScheme
16
14
17 def exception_colors():
15 def exception_colors():
@@ -5,7 +5,8 b''
5 import fnmatch
5 import fnmatch
6 import os
6 import os
7
7
8 from IPython.utils.genutils import Term, ask_yes_no, warn
8 from IPython.utils.io import Term, ask_yes_no
9 from IPython.utils.warn import warn
9 from IPython.core import ipapi
10 from IPython.core import ipapi
10
11
11 def magic_history(self, parameter_s = ''):
12 def magic_history(self, parameter_s = ''):
@@ -43,9 +43,12 b' somewhere in your configuration files or ipython command line.'
43
43
44 import os, bisect
44 import os, bisect
45 import sys
45 import sys
46 from IPython.utils.genutils import Term, shell
46
47 from pprint import PrettyPrinter
47 from pprint import PrettyPrinter
48
48
49 from IPython.utils.io import Term
50 from IPython.utils.process import shell
51
49 from IPython.core.error import TryNext
52 from IPython.core.error import TryNext
50
53
51 # List here all the default hooks. For now it's just the editor functions
54 # List here all the default hooks. For now it's just the editor functions
@@ -18,8 +18,6 b' has been made into a component, this module will be sent to deathrow.'
18 # Imports
18 # Imports
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 from IPython.core.error import TryNext, UsageError, IPythonCoreError
22
23 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
24 # Classes and functions
22 # Classes and functions
25 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
This diff has been collapsed as it changes many lines, (690 lines changed) Show them Hide them
@@ -21,374 +21,376 b' Authors'
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24 from __future__ import absolute_import
25 from __future__ import absolute_import
25
26
26 import logging
27 import logging
27 import os
28 import os
28 import sys
29 import sys
29
30
30 from IPython.core import crashhandler
31 from IPython.core import release
31 from IPython.core.application import Application
32 from IPython.core.crashhandler import CrashHandler
33 from IPython.core.application import Application, BaseAppConfigLoader
32 from IPython.core.iplib import InteractiveShell
34 from IPython.core.iplib import InteractiveShell
33 from IPython.config.loader import (
35 from IPython.config.loader import (
34 Config,
36 Config,
35 PyFileConfigLoader,
37 PyFileConfigLoader
36 # NoConfigDefault,
37 )
38 )
38 from IPython.lib import inputhook
39 from IPython.lib import inputhook
39 from IPython.utils.genutils import filefind, get_ipython_dir
40 from IPython.utils.path import filefind, get_ipython_dir
40 from . import usage
41 from . import usage
41
42
42 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
43 # Globals, utilities and helpers
44 # Globals, utilities and helpers
44 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
45
46
47 #: The default config file name for this application.
46 default_config_file_name = u'ipython_config.py'
48 default_config_file_name = u'ipython_config.py'
47
49
48 cl_args = (
50
49 (('--autocall',), dict(
51 class IPAppConfigLoader(BaseAppConfigLoader):
50 type=int, dest='InteractiveShell.autocall',
52
51 help=
53 def _add_arguments(self):
52 """Make IPython automatically call any callable object even if you
54 super(IPAppConfigLoader, self)._add_arguments()
53 didn't type explicit parentheses. For example, 'str 43' becomes
55 paa = self.parser.add_argument
54 'str(43)' automatically. The value can be '0' to disable the feature,
56 paa('-p',
55 '1' for 'smart' autocall, where it is not applied if there are no more
57 '--profile', dest='Global.profile', type=unicode,
56 arguments on the line, and '2' for 'full' autocall, where all callable
58 help=
57 objects are automatically called (even if no arguments are present).
59 """The string name of the ipython profile to be used. Assume that your
58 The default is '1'.""",
60 config file is ipython_config-<name>.py (looks in current dir first,
59 metavar='InteractiveShell.autocall')
61 then in IPYTHON_DIR). This is a quick way to keep and load multiple
60 ),
62 config files for different tasks, especially if include your basic one
61 (('--autoindent',), dict(
63 in your more specialized ones. You can keep a basic
62 action='store_true', dest='InteractiveShell.autoindent',
64 IPYTHON_DIR/ipython_config.py file and then have other 'profiles' which
63 help='Turn on autoindenting.')
65 include this one and load extra things for particular tasks.""",
64 ),
66 metavar='Global.profile')
65 (('--no-autoindent',), dict(
67 paa('--config-file',
66 action='store_false', dest='InteractiveShell.autoindent',
68 dest='Global.config_file', type=unicode,
67 help='Turn off autoindenting.')
69 help=
68 ),
70 """Set the config file name to override default. Normally IPython
69 (('--automagic',), dict(
71 loads ipython_config.py (from current directory) or
70 action='store_true', dest='InteractiveShell.automagic',
72 IPYTHON_DIR/ipython_config.py. If the loading of your config file
71 help='Turn on the auto calling of magic commands.'
73 fails, IPython starts with a bare bones configuration (no modules
72 'Type %%magic at the IPython prompt for more information.')
74 loaded at all).""",
73 ),
75 metavar='Global.config_file')
74 (('--no-automagic',), dict(
76 paa('--autocall',
75 action='store_false', dest='InteractiveShell.automagic',
77 dest='InteractiveShell.autocall', type=int,
76 help='Turn off the auto calling of magic commands.')
78 help=
77 ),
79 """Make IPython automatically call any callable object even if you
78 (('--autoedit-syntax',), dict(
80 didn't type explicit parentheses. For example, 'str 43' becomes
79 action='store_true', dest='InteractiveShell.autoedit_syntax',
81 'str(43)' automatically. The value can be '0' to disable the feature,
80 help='Turn on auto editing of files with syntax errors.')
82 '1' for 'smart' autocall, where it is not applied if there are no more
81 ),
83 arguments on the line, and '2' for 'full' autocall, where all callable
82 (('--no-autoedit-syntax',), dict(
84 objects are automatically called (even if no arguments are present).
83 action='store_false', dest='InteractiveShell.autoedit_syntax',
85 The default is '1'.""",
84 help='Turn off auto editing of files with syntax errors.')
86 metavar='InteractiveShell.autocall')
85 ),
87 paa('--autoindent',
86 (('--banner',), dict(
88 action='store_true', dest='InteractiveShell.autoindent',
87 action='store_true', dest='Global.display_banner',
89 help='Turn on autoindenting.')
88 help='Display a banner upon starting IPython.')
90 paa('--no-autoindent',
89 ),
91 action='store_false', dest='InteractiveShell.autoindent',
90 (('--no-banner',), dict(
92 help='Turn off autoindenting.')
91 action='store_false', dest='Global.display_banner',
93 paa('--automagic',
92 help="Don't display a banner upon starting IPython.")
94 action='store_true', dest='InteractiveShell.automagic',
93 ),
95 help=
94 (('--cache-size',), dict(
96 """Turn on the auto calling of magic commands. Type %%magic at the
95 type=int, dest='InteractiveShell.cache_size',
97 IPython prompt for more information.""")
96 help=
98 paa('--no-automagic',
97 """Set the size of the output cache. The default is 1000, you can
99 action='store_false', dest='InteractiveShell.automagic',
98 change it permanently in your config file. Setting it to 0 completely
100 help='Turn off the auto calling of magic commands.')
99 disables the caching system, and the minimum value accepted is 20 (if
101 paa('--autoedit-syntax',
100 you provide a value less than 20, it is reset to 0 and a warning is
102 action='store_true', dest='InteractiveShell.autoedit_syntax',
101 issued). This limit is defined because otherwise you'll spend more
103 help='Turn on auto editing of files with syntax errors.')
102 time re-flushing a too small cache than working.
104 paa('--no-autoedit-syntax',
103 """,
105 action='store_false', dest='InteractiveShell.autoedit_syntax',
104 metavar='InteractiveShell.cache_size')
106 help='Turn off auto editing of files with syntax errors.')
105 ),
107 paa('--banner',
106 (('--classic',), dict(
108 action='store_true', dest='Global.display_banner',
107 action='store_true', dest='Global.classic',
109 help='Display a banner upon starting IPython.')
108 help="Gives IPython a similar feel to the classic Python prompt.")
110 paa('--no-banner',
109 ),
111 action='store_false', dest='Global.display_banner',
110 (('--colors',), dict(
112 help="Don't display a banner upon starting IPython.")
111 type=str, dest='InteractiveShell.colors',
113 paa('--cache-size',
112 help="Set the color scheme (NoColor, Linux, and LightBG).",
114 type=int, dest='InteractiveShell.cache_size',
113 metavar='InteractiveShell.colors')
115 help=
114 ),
116 """Set the size of the output cache. The default is 1000, you can
115 (('--color-info',), dict(
117 change it permanently in your config file. Setting it to 0 completely
116 action='store_true', dest='InteractiveShell.color_info',
118 disables the caching system, and the minimum value accepted is 20 (if
117 help=
119 you provide a value less than 20, it is reset to 0 and a warning is
118 """IPython can display information about objects via a set of func-
120 issued). This limit is defined because otherwise you'll spend more
119 tions, and optionally can use colors for this, syntax highlighting
121 time re-flushing a too small cache than working""",
120 source code and various other elements. However, because this
122 metavar='InteractiveShell.cache_size')
121 information is passed through a pager (like 'less') and many pagers get
123 paa('--classic',
122 confused with color codes, this option is off by default. You can test
124 action='store_true', dest='Global.classic',
123 it and turn it on permanently in your ipython_config.py file if it
125 help="Gives IPython a similar feel to the classic Python prompt.")
124 works for you. Test it and turn it on permanently if it works with
126 paa('--colors',
125 your system. The magic function %%color_info allows you to toggle this
127 type=str, dest='InteractiveShell.colors',
126 inter- actively for testing."""
128 help="Set the color scheme (NoColor, Linux, and LightBG).",
127 )
129 metavar='InteractiveShell.colors')
128 ),
130 paa('--color-info',
129 (('--no-color-info',), dict(
131 action='store_true', dest='InteractiveShell.color_info',
130 action='store_false', dest='InteractiveShell.color_info',
132 help=
131 help="Disable using colors for info related things.")
133 """IPython can display information about objects via a set of func-
132 ),
134 tions, and optionally can use colors for this, syntax highlighting
133 (('--confirm-exit',), dict(
135 source code and various other elements. However, because this
134 action='store_true', dest='InteractiveShell.confirm_exit',
136 information is passed through a pager (like 'less') and many pagers get
135 help=
137 confused with color codes, this option is off by default. You can test
136 """Set to confirm when you try to exit IPython with an EOF (Control-D
138 it and turn it on permanently in your ipython_config.py file if it
137 in Unix, Control-Z/Enter in Windows). By typing 'exit', 'quit' or
139 works for you. Test it and turn it on permanently if it works with
138 '%%Exit', you can force a direct exit without any confirmation.
140 your system. The magic function %%color_info allows you to toggle this
139 """
141 inter- actively for testing.""")
140 )
142 paa('--no-color-info',
141 ),
143 action='store_false', dest='InteractiveShell.color_info',
142 (('--no-confirm-exit',), dict(
144 help="Disable using colors for info related things.")
143 action='store_false', dest='InteractiveShell.confirm_exit',
145 paa('--confirm-exit',
144 help="Don't prompt the user when exiting.")
146 action='store_true', dest='InteractiveShell.confirm_exit',
145 ),
147 help=
146 (('--deep-reload',), dict(
148 """Set to confirm when you try to exit IPython with an EOF (Control-D
147 action='store_true', dest='InteractiveShell.deep_reload',
149 in Unix, Control-Z/Enter in Windows). By typing 'exit', 'quit' or
148 help=
150 '%%Exit', you can force a direct exit without any confirmation.""")
149 """Enable deep (recursive) reloading by default. IPython can use the
151 paa('--no-confirm-exit',
150 deep_reload module which reloads changes in modules recursively (it
152 action='store_false', dest='InteractiveShell.confirm_exit',
151 replaces the reload() function, so you don't need to change anything to
153 help="Don't prompt the user when exiting.")
152 use it). deep_reload() forces a full reload of modules whose code may
154 paa('--deep-reload',
153 have changed, which the default reload() function does not. When
155 action='store_true', dest='InteractiveShell.deep_reload',
154 deep_reload is off, IPython will use the normal reload(), but
156 help=
155 deep_reload will still be available as dreload(). This fea- ture is off
157 """Enable deep (recursive) reloading by default. IPython can use the
156 by default [which means that you have both normal reload() and
158 deep_reload module which reloads changes in modules recursively (it
157 dreload()].""")
159 replaces the reload() function, so you don't need to change anything to
158 ),
160 use it). deep_reload() forces a full reload of modules whose code may
159 (('--no-deep-reload',), dict(
161 have changed, which the default reload() function does not. When
160 action='store_false', dest='InteractiveShell.deep_reload',
162 deep_reload is off, IPython will use the normal reload(), but
161 help="Disable deep (recursive) reloading by default.")
163 deep_reload will still be available as dreload(). This fea- ture is off
162 ),
164 by default [which means that you have both normal reload() and
163 (('--editor',), dict(
165 dreload()].""")
164 type=str, dest='InteractiveShell.editor',
166 paa('--no-deep-reload',
165 help="Set the editor used by IPython (default to $EDITOR/vi/notepad).",
167 action='store_false', dest='InteractiveShell.deep_reload',
166 metavar='InteractiveShell.editor')
168 help="Disable deep (recursive) reloading by default.")
167 ),
169 paa('--editor',
168 (('--log','-l'), dict(
170 type=str, dest='InteractiveShell.editor',
169 action='store_true', dest='InteractiveShell.logstart',
171 help="Set the editor used by IPython (default to $EDITOR/vi/notepad).",
170 help="Start logging to the default log file (./ipython_log.py).")
172 metavar='InteractiveShell.editor')
171 ),
173 paa('--log','-l',
172 (('--logfile','-lf'), dict(
174 action='store_true', dest='InteractiveShell.logstart',
173 type=unicode, dest='InteractiveShell.logfile',
175 help="Start logging to the default log file (./ipython_log.py).")
174 help="Start logging to logfile with this name.",
176 paa('--logfile','-lf',
175 metavar='InteractiveShell.logfile')
177 type=unicode, dest='InteractiveShell.logfile',
176 ),
178 help="Start logging to logfile with this name.",
177 (('--log-append','-la'), dict(
179 metavar='InteractiveShell.logfile')
178 type=unicode, dest='InteractiveShell.logappend',
180 paa('--log-append','-la',
179 help="Start logging to the given file in append mode.",
181 type=unicode, dest='InteractiveShell.logappend',
180 metavar='InteractiveShell.logfile')
182 help="Start logging to the given file in append mode.",
181 ),
183 metavar='InteractiveShell.logfile')
182 (('--pdb',), dict(
184 paa('--pdb',
183 action='store_true', dest='InteractiveShell.pdb',
185 action='store_true', dest='InteractiveShell.pdb',
184 help="Enable auto calling the pdb debugger after every exception.")
186 help="Enable auto calling the pdb debugger after every exception.")
185 ),
187 paa('--no-pdb',
186 (('--no-pdb',), dict(
188 action='store_false', dest='InteractiveShell.pdb',
187 action='store_false', dest='InteractiveShell.pdb',
189 help="Disable auto calling the pdb debugger after every exception.")
188 help="Disable auto calling the pdb debugger after every exception.")
190 paa('--pprint',
189 ),
191 action='store_true', dest='InteractiveShell.pprint',
190 (('--pprint',), dict(
192 help="Enable auto pretty printing of results.")
191 action='store_true', dest='InteractiveShell.pprint',
193 paa('--no-pprint',
192 help="Enable auto pretty printing of results.")
194 action='store_false', dest='InteractiveShell.pprint',
193 ),
195 help="Disable auto auto pretty printing of results.")
194 (('--no-pprint',), dict(
196 paa('--prompt-in1','-pi1',
195 action='store_false', dest='InteractiveShell.pprint',
197 type=str, dest='InteractiveShell.prompt_in1',
196 help="Disable auto auto pretty printing of results.")
198 help=
197 ),
199 """Set the main input prompt ('In [\#]: '). Note that if you are using
198 (('--prompt-in1','-pi1'), dict(
200 numbered prompts, the number is represented with a '\#' in the string.
199 type=str, dest='InteractiveShell.prompt_in1',
201 Don't forget to quote strings with spaces embedded in them. Most
200 help=
202 bash-like escapes can be used to customize IPython's prompts, as well
201 """Set the main input prompt ('In [\#]: '). Note that if you are using
203 as a few additional ones which are IPython-spe- cific. All valid
202 numbered prompts, the number is represented with a '\#' in the string.
204 prompt escapes are described in detail in the Customization section of
203 Don't forget to quote strings with spaces embedded in them. Most
205 the IPython manual.""",
204 bash-like escapes can be used to customize IPython's prompts, as well
206 metavar='InteractiveShell.prompt_in1')
205 as a few additional ones which are IPython-spe- cific. All valid
207 paa('--prompt-in2','-pi2',
206 prompt escapes are described in detail in the Customization section of
208 type=str, dest='InteractiveShell.prompt_in2',
207 the IPython manual.""",
209 help=
208 metavar='InteractiveShell.prompt_in1')
210 """Set the secondary input prompt (' .\D.: '). Similar to the previous
209 ),
211 option, but used for the continuation prompts. The special sequence
210 (('--prompt-in2','-pi2'), dict(
212 '\D' is similar to '\#', but with all digits replaced by dots (so you
211 type=str, dest='InteractiveShell.prompt_in2',
213 can have your continuation prompt aligned with your input prompt).
212 help=
214 Default: ' .\D.: ' (note three spaces at the start for alignment with
213 """Set the secondary input prompt (' .\D.: '). Similar to the previous
215 'In [\#]')""",
214 option, but used for the continuation prompts. The special sequence
216 metavar='InteractiveShell.prompt_in2')
215 '\D' is similar to '\#', but with all digits replaced by dots (so you
217 paa('--prompt-out','-po',
216 can have your continuation prompt aligned with your input prompt).
218 type=str, dest='InteractiveShell.prompt_out',
217 Default: ' .\D.: ' (note three spaces at the start for alignment with
219 help="Set the output prompt ('Out[\#]:')",
218 'In [\#]')""",
220 metavar='InteractiveShell.prompt_out')
219 metavar='InteractiveShell.prompt_in2')
221 paa('--quick',
220 ),
222 action='store_true', dest='Global.quick',
221 (('--prompt-out','-po'), dict(
223 help="Enable quick startup with no config files.")
222 type=str, dest='InteractiveShell.prompt_out',
224 paa('--readline',
223 help="Set the output prompt ('Out[\#]:')",
225 action='store_true', dest='InteractiveShell.readline_use',
224 metavar='InteractiveShell.prompt_out')
226 help="Enable readline for command line usage.")
225 ),
227 paa('--no-readline',
226 (('--quick',), dict(
228 action='store_false', dest='InteractiveShell.readline_use',
227 action='store_true', dest='Global.quick',
229 help="Disable readline for command line usage.")
228 help="Enable quick startup with no config files.")
230 paa('--screen-length','-sl',
229 ),
231 type=int, dest='InteractiveShell.screen_length',
230 (('--readline',), dict(
232 help=
231 action='store_true', dest='InteractiveShell.readline_use',
233 """Number of lines of your screen, used to control printing of very
232 help="Enable readline for command line usage.")
234 long strings. Strings longer than this number of lines will be sent
233 ),
235 through a pager instead of directly printed. The default value for
234 (('--no-readline',), dict(
236 this is 0, which means IPython will auto-detect your screen size every
235 action='store_false', dest='InteractiveShell.readline_use',
237 time it needs to print certain potentially long strings (this doesn't
236 help="Disable readline for command line usage.")
238 change the behavior of the 'print' keyword, it's only triggered
237 ),
239 internally). If for some reason this isn't working well (it needs
238 (('--screen-length','-sl'), dict(
240 curses support), specify it yourself. Otherwise don't change the
239 type=int, dest='InteractiveShell.screen_length',
241 default.""",
240 help=
242 metavar='InteractiveShell.screen_length')
241 """Number of lines of your screen, used to control printing of very
243 paa('--separate-in','-si',
242 long strings. Strings longer than this number of lines will be sent
244 type=str, dest='InteractiveShell.separate_in',
243 through a pager instead of directly printed. The default value for
245 help="Separator before input prompts. Default '\\n'.",
244 this is 0, which means IPython will auto-detect your screen size every
246 metavar='InteractiveShell.separate_in')
245 time it needs to print certain potentially long strings (this doesn't
247 paa('--separate-out','-so',
246 change the behavior of the 'print' keyword, it's only triggered
248 type=str, dest='InteractiveShell.separate_out',
247 internally). If for some reason this isn't working well (it needs
249 help="Separator before output prompts. Default 0 (nothing).",
248 curses support), specify it yourself. Otherwise don't change the
250 metavar='InteractiveShell.separate_out')
249 default.""",
251 paa('--separate-out2','-so2',
250 metavar='InteractiveShell.screen_length')
252 type=str, dest='InteractiveShell.separate_out2',
251 ),
253 help="Separator after output prompts. Default 0 (nonight).",
252 (('--separate-in','-si'), dict(
254 metavar='InteractiveShell.separate_out2')
253 type=str, dest='InteractiveShell.separate_in',
255 paa('--no-sep',
254 help="Separator before input prompts. Default '\\n'.",
256 action='store_true', dest='Global.nosep',
255 metavar='InteractiveShell.separate_in')
257 help="Eliminate all spacing between prompts.")
256 ),
258 paa('--term-title',
257 (('--separate-out','-so'), dict(
259 action='store_true', dest='InteractiveShell.term_title',
258 type=str, dest='InteractiveShell.separate_out',
260 help="Enable auto setting the terminal title.")
259 help="Separator before output prompts. Default 0 (nothing).",
261 paa('--no-term-title',
260 metavar='InteractiveShell.separate_out')
262 action='store_false', dest='InteractiveShell.term_title',
261 ),
263 help="Disable auto setting the terminal title.")
262 (('--separate-out2','-so2'), dict(
264 paa('--xmode',
263 type=str, dest='InteractiveShell.separate_out2',
265 type=str, dest='InteractiveShell.xmode',
264 help="Separator after output prompts. Default 0 (nonight).",
266 help=
265 metavar='InteractiveShell.separate_out2')
267 """Exception reporting mode ('Plain','Context','Verbose'). Plain:
266 ),
268 similar to python's normal traceback printing. Context: prints 5 lines
267 (('-no-sep',), dict(
269 of context source code around each line in the traceback. Verbose:
268 action='store_true', dest='Global.nosep',
270 similar to Context, but additionally prints the variables currently
269 help="Eliminate all spacing between prompts.")
271 visible where the exception happened (shortening their strings if too
270 ),
272 long). This can potentially be very slow, if you happen to have a huge
271 (('--term-title',), dict(
273 data structure whose string representation is complex to compute.
272 action='store_true', dest='InteractiveShell.term_title',
274 Your computer may appear to freeze for a while with cpu usage at 100%%.
273 help="Enable auto setting the terminal title.")
275 If this occurs, you can cancel the traceback with Ctrl-C (maybe hitting
274 ),
276 it more than once).
275 (('--no-term-title',), dict(
277 """,
276 action='store_false', dest='InteractiveShell.term_title',
278 metavar='InteractiveShell.xmode')
277 help="Disable auto setting the terminal title.")
279 paa('--ext',
278 ),
280 type=str, dest='Global.extra_extension',
279 (('--xmode',), dict(
281 help="The dotted module name of an IPython extension to load.",
280 type=str, dest='InteractiveShell.xmode',
282 metavar='Global.extra_extension')
281 help=
283 paa('-c',
282 """Exception reporting mode ('Plain','Context','Verbose'). Plain:
284 type=str, dest='Global.code_to_run',
283 similar to python's normal traceback printing. Context: prints 5 lines
285 help="Execute the given command string.",
284 of context source code around each line in the traceback. Verbose:
286 metavar='Global.code_to_run')
285 similar to Context, but additionally prints the variables currently
287 paa('-i',
286 visible where the exception happened (shortening their strings if too
288 action='store_true', dest='Global.force_interact',
287 long). This can potentially be very slow, if you happen to have a huge
289 help=
288 data structure whose string representation is complex to compute.
290 "If running code from the command line, become interactive afterwards.")
289 Your computer may appear to freeze for a while with cpu usage at 100%%.
291
290 If this occurs, you can cancel the traceback with Ctrl-C (maybe hitting
292 # Options to start with GUI control enabled from the beginning
291 it more than once).
293 paa('--gui',
292 """,
294 type=str, dest='Global.gui',
293 metavar='InteractiveShell.xmode')
295 help="Enable GUI event loop integration ('qt', 'wx', 'gtk').",
294 ),
296 metavar='gui-mode')
295 (('--ext',), dict(
297 paa('--pylab','-pylab',
296 type=str, dest='Global.extra_extension',
298 type=str, dest='Global.pylab',
297 help="The dotted module name of an IPython extension to load.",
299 nargs='?', const='auto', metavar='gui-mode',
298 metavar='Global.extra_extension')
300 help="Pre-load matplotlib and numpy for interactive use. "+
299 ),
301 "If no value is given, the gui backend is matplotlib's, else use "+
300 (('-c',), dict(
302 "one of: ['tk', 'qt', 'wx', 'gtk'].")
301 type=str, dest='Global.code_to_run',
303
302 help="Execute the given command string.",
304 # Legacy GUI options. Leave them in for backwards compatibility, but the
303 metavar='Global.code_to_run')
305 # 'thread' names are really a misnomer now.
304 ),
306 paa('--wthread', '-wthread',
305 (('-i',), dict(
307 action='store_true', dest='Global.wthread',
306 action='store_true', dest='Global.force_interact',
308 help=
307 help=
309 """Enable wxPython event loop integration. (DEPRECATED, use --gui wx)""")
308 "If running code from the command line, become interactive afterwards."
310 paa('--q4thread', '--qthread', '-q4thread', '-qthread',
309 )
311 action='store_true', dest='Global.q4thread',
310 ),
312 help=
311
313 """Enable Qt4 event loop integration. Qt3 is no longer supported.
312 # Options to start with GUI control enabled from the beginning
314 (DEPRECATED, use --gui qt)""")
313 (('--gui',), dict(
315 paa('--gthread', '-gthread',
314 type=str, dest='Global.gui',
316 action='store_true', dest='Global.gthread',
315 help="Enable GUI event loop integration ('qt', 'wx', 'gtk').",
317 help=
316 metavar='gui-mode')
318 """Enable GTK event loop integration. (DEPRECATED, use --gui gtk)""")
317 ),
319
318
319 (('--pylab','-pylab'), dict(
320 type=str, dest='Global.pylab',
321 nargs='?', const='auto', metavar='gui-mode',
322 help="Pre-load matplotlib and numpy for interactive use. "+
323 "If no value is given, the gui backend is matplotlib's, else use "+
324 "one of: ['tk', 'qt', 'wx', 'gtk'].")
325 ),
326
327 # Legacy GUI options. Leave them in for backwards compatibility, but the
328 # 'thread' names are really a misnomer now.
329 (('--wthread','-wthread'), dict(
330 action='store_true', dest='Global.wthread',
331 help="Enable wxPython event loop integration "+
332 "(DEPRECATED, use --gui wx)")
333 ),
334 (('--q4thread','--qthread','-q4thread','-qthread'), dict(
335 action='store_true', dest='Global.q4thread',
336 help="Enable Qt4 event loop integration. Qt3 is no longer supported. "+
337 "(DEPRECATED, use --gui qt)")
338 ),
339 (('--gthread','-gthread'), dict(
340 action='store_true', dest='Global.gthread',
341 help="Enable GTK event loop integration. "+
342 "(DEPRECATED, use --gui gtk)")
343 ),
344 )
345
320
346 #-----------------------------------------------------------------------------
321 #-----------------------------------------------------------------------------
347 # Main classes and functions
322 # Crash handler for this application
348 #-----------------------------------------------------------------------------
323 #-----------------------------------------------------------------------------
349
324
350 class IPythonApp(Application):
351 name = u'ipython'
352 #: argparse formats better the 'usage' than the 'description' field
353 description = None
354 #: usage message printed by argparse. If None, auto-generate
355 usage = usage.cl_usage
356
325
357 config_file_name = default_config_file_name
326 _message_template = """\
327 Oops, $self.app_name crashed. We do our best to make it stable, but...
328
329 A crash report was automatically generated with the following information:
330 - A verbatim copy of the crash traceback.
331 - A copy of your input history during this session.
332 - Data on your current $self.app_name configuration.
358
333
359 cl_arguments = Application.cl_arguments + cl_args
334 It was left in the file named:
335 \t'$self.crash_report_fname'
336 If you can email this file to the developers, the information in it will help
337 them in understanding and correcting the problem.
360
338
361 # Private and configuration attributes
339 You can mail it to: $self.contact_name at $self.contact_email
362 _CrashHandler = crashhandler.IPythonCrashHandler
340 with the subject '$self.app_name Crash Report'.
363
364 def __init__(self, argv=None,
365 constructor_config=None, override_config=None,
366 **shell_params):
367 """Create a new IPythonApp.
368
341
369 See the parent class for details on how configuration is handled.
342 If you want to do it now, the following command will work (under Unix):
343 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
370
344
371 Parameters
345 To ensure accurate tracking of this issue, please file a report about it at:
372 ----------
346 $self.bug_tracker
373 argv : optional, list
347 """
374 If given, used as the command-line argv environment to read arguments
375 from.
376
348
377 constructor_config : optional, Config
349 class IPAppCrashHandler(CrashHandler):
378 If given, additional config that is merged last, after internal
350 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
379 defaults, command-line and file-based configs.
380
351
381 override_config : optional, Config
352 message_template = _message_template
382 If given, config that overrides all others unconditionally (except
383 for internal defaults, which ensure that all parameters exist).
384
353
385 shell_params : optional, dict
354 def __init__(self, app):
386 All other keywords are passed to the :class:`iplib.InteractiveShell`
355 contact_name = release.authors['Fernando'][0]
387 constructor.
356 contact_email = release.authors['Fernando'][1]
388 """
357 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
389 super(IPythonApp, self).__init__(argv, constructor_config,
358 super(IPAppCrashHandler,self).__init__(
390 override_config)
359 app, contact_name, contact_email, bug_tracker
391 self.shell_params = shell_params
360 )
361
362 def make_report(self,traceback):
363 """Return a string containing a crash report."""
364
365 sec_sep = self.section_sep
366 # Start with parent report
367 report = [super(IPAppCrashHandler, self).make_report(traceback)]
368 # Add interactive-specific info we may have
369 rpt_add = report.append
370 try:
371 rpt_add(sec_sep+"History of session input:")
372 for line in self.app.shell.user_ns['_ih']:
373 rpt_add(line)
374 rpt_add('\n*** Last line of input (may not be in above history):\n')
375 rpt_add(self.app.shell._last_input_line+'\n')
376 except:
377 pass
378
379 return ''.join(report)
380
381
382 #-----------------------------------------------------------------------------
383 # Main classes and functions
384 #-----------------------------------------------------------------------------
385
386 class IPythonApp(Application):
387 name = u'ipython'
388 #: argparse formats better the 'usage' than the 'description' field
389 description = None
390 usage = usage.cl_usage
391 command_line_loader = IPAppConfigLoader
392 default_config_file_name = default_config_file_name
393 crash_handler_class = IPAppCrashHandler
392
394
393 def create_default_config(self):
395 def create_default_config(self):
394 super(IPythonApp, self).create_default_config()
396 super(IPythonApp, self).create_default_config()
@@ -474,8 +476,7 b' class IPythonApp(Application):'
474 sys.path.insert(0, '')
476 sys.path.insert(0, '')
475
477
476 # Create an InteractiveShell instance
478 # Create an InteractiveShell instance
477 self.shell = InteractiveShell(None, self.master_config,
479 self.shell = InteractiveShell(None, self.master_config)
478 **self.shell_params )
479
480
480 def post_construct(self):
481 def post_construct(self):
481 """Do actions after construct, but before starting the app."""
482 """Do actions after construct, but before starting the app."""
@@ -485,7 +486,6 b' class IPythonApp(Application):'
485 # based app, because we call shell.show_banner() by hand below
486 # based app, because we call shell.show_banner() by hand below
486 # so the banner shows *before* all extension loading stuff.
487 # so the banner shows *before* all extension loading stuff.
487 self.shell.display_banner = False
488 self.shell.display_banner = False
488
489 if config.Global.display_banner and \
489 if config.Global.display_banner and \
490 config.Global.interact:
490 config.Global.interact:
491 self.shell.show_banner()
491 self.shell.show_banner()
@@ -499,7 +499,6 b' class IPythonApp(Application):'
499 self._run_exec_lines()
499 self._run_exec_lines()
500 self._run_exec_files()
500 self._run_exec_files()
501 self._run_cmd_line_code()
501 self._run_cmd_line_code()
502 self._configure_xmode()
503
502
504 def _enable_gui_pylab(self):
503 def _enable_gui_pylab(self):
505 """Enable GUI event loop integration, taking pylab into account."""
504 """Enable GUI event loop integration, taking pylab into account."""
@@ -624,11 +623,6 b' class IPythonApp(Application):'
624 self.log.warn("Error in executing file in user namespace: %s" % fname)
623 self.log.warn("Error in executing file in user namespace: %s" % fname)
625 self.shell.showtraceback()
624 self.shell.showtraceback()
626
625
627 def _configure_xmode(self):
628 # XXX - shouldn't this be read from the config? I'm still a little
629 # lost with all the details of handling the new config guys...
630 self.shell.InteractiveTB.set_mode(mode=self.shell.xmode)
631
632 def start_app(self):
626 def start_app(self):
633 if self.master_config.Global.interact:
627 if self.master_config.Global.interact:
634 self.log.debug("Starting IPython's mainloop...")
628 self.log.debug("Starting IPython's mainloop...")
@@ -653,3 +647,7 b' def launch_new_instance():'
653 """Create and run a full blown IPython instance"""
647 """Create and run a full blown IPython instance"""
654 app = IPythonApp()
648 app = IPythonApp()
655 app.start()
649 app.start()
650
651
652 if __name__ == '__main__':
653 launch_new_instance()
@@ -20,7 +20,6 b' from __future__ import with_statement'
20 from __future__ import absolute_import
20 from __future__ import absolute_import
21
21
22 import __builtin__
22 import __builtin__
23 import StringIO
24 import bdb
23 import bdb
25 import codeop
24 import codeop
26 import exceptions
25 import exceptions
@@ -47,29 +46,35 b' from IPython.core.logger import Logger'
47 from IPython.core.magic import Magic
46 from IPython.core.magic import Magic
48 from IPython.core.prefilter import PrefilterManager
47 from IPython.core.prefilter import PrefilterManager
49 from IPython.core.prompts import CachedOutput
48 from IPython.core.prompts import CachedOutput
50 from IPython.core.pylabtools import pylab_activate
51 from IPython.core.usage import interactive_usage, default_banner
49 from IPython.core.usage import interactive_usage, default_banner
50 import IPython.core.hooks
52 from IPython.external.Itpl import ItplNS
51 from IPython.external.Itpl import ItplNS
53 from IPython.lib.inputhook import enable_gui
52 from IPython.lib.inputhook import enable_gui
54 from IPython.lib.backgroundjobs import BackgroundJobManager
53 from IPython.lib.backgroundjobs import BackgroundJobManager
54 from IPython.lib.pylabtools import pylab_activate
55 from IPython.utils import PyColorize
55 from IPython.utils import PyColorize
56 from IPython.utils import pickleshare
56 from IPython.utils import pickleshare
57 from IPython.utils.genutils import get_ipython_dir
57 from IPython.utils.doctestreload import doctest_reload
58 from IPython.utils.ipstruct import Struct
58 from IPython.utils.ipstruct import Struct
59 from IPython.utils.platutils import toggle_set_term_title, set_term_title
59 from IPython.utils.io import Term, ask_yes_no
60 from IPython.utils.path import get_home_dir, get_ipython_dir, HomeDirError
61 from IPython.utils.process import (
62 abbrev_cwd,
63 getoutput,
64 getoutputerror
65 )
66 # import IPython.utils.rlineimpl as readline
60 from IPython.utils.strdispatch import StrDispatch
67 from IPython.utils.strdispatch import StrDispatch
61 from IPython.utils.syspathcontext import prepended_to_syspath
68 from IPython.utils.syspathcontext import prepended_to_syspath
62
69 from IPython.utils.terminal import toggle_set_term_title, set_term_title
63 # XXX - need to clean up this import * line
70 from IPython.utils.warn import warn, error, fatal
64 from IPython.utils.genutils import *
65
66 # from IPython.utils import growl
67 # growl.start("IPython")
68
69 from IPython.utils.traitlets import (
71 from IPython.utils.traitlets import (
70 Int, Str, CBool, CaselessStrEnum, Enum, List, Unicode
72 Int, Str, CBool, CaselessStrEnum, Enum, List, Unicode
71 )
73 )
72
74
75 # from IPython.utils import growl
76 # growl.start("IPython")
77
73 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
74 # Globals
79 # Globals
75 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
@@ -186,64 +191,6 b' class SeparateStr(Str):'
186 return super(SeparateStr, self).validate(obj, value)
191 return super(SeparateStr, self).validate(obj, value)
187
192
188
193
189 def make_user_namespaces(user_ns=None, user_global_ns=None):
190 """Return a valid local and global user interactive namespaces.
191
192 This builds a dict with the minimal information needed to operate as a
193 valid IPython user namespace, which you can pass to the various
194 embedding classes in ipython. The default implementation returns the
195 same dict for both the locals and the globals to allow functions to
196 refer to variables in the namespace. Customized implementations can
197 return different dicts. The locals dictionary can actually be anything
198 following the basic mapping protocol of a dict, but the globals dict
199 must be a true dict, not even a subclass. It is recommended that any
200 custom object for the locals namespace synchronize with the globals
201 dict somehow.
202
203 Raises TypeError if the provided globals namespace is not a true dict.
204
205 Parameters
206 ----------
207 user_ns : dict-like, optional
208 The current user namespace. The items in this namespace should
209 be included in the output. If None, an appropriate blank
210 namespace should be created.
211 user_global_ns : dict, optional
212 The current user global namespace. The items in this namespace
213 should be included in the output. If None, an appropriate
214 blank namespace should be created.
215
216 Returns
217 -------
218 A pair of dictionary-like object to be used as the local namespace
219 of the interpreter and a dict to be used as the global namespace.
220 """
221
222
223 # We must ensure that __builtin__ (without the final 's') is always
224 # available and pointing to the __builtin__ *module*. For more details:
225 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
226
227 if user_ns is None:
228 # Set __name__ to __main__ to better match the behavior of the
229 # normal interpreter.
230 user_ns = {'__name__' :'__main__',
231 '__builtin__' : __builtin__,
232 '__builtins__' : __builtin__,
233 }
234 else:
235 user_ns.setdefault('__name__','__main__')
236 user_ns.setdefault('__builtin__',__builtin__)
237 user_ns.setdefault('__builtins__',__builtin__)
238
239 if user_global_ns is None:
240 user_global_ns = user_ns
241 if type(user_global_ns) is not dict:
242 raise TypeError("user_global_ns must be a true dict; got %r"
243 % type(user_global_ns))
244
245 return user_ns, user_global_ns
246
247 #-----------------------------------------------------------------------------
194 #-----------------------------------------------------------------------------
248 # Main IPython class
195 # Main IPython class
249 #-----------------------------------------------------------------------------
196 #-----------------------------------------------------------------------------
@@ -658,7 +605,6 b' class InteractiveShell(Component, Magic):'
658 self.strdispatchers = {}
605 self.strdispatchers = {}
659
606
660 # Set all default hooks, defined in the IPython.hooks module.
607 # Set all default hooks, defined in the IPython.hooks module.
661 import IPython.core.hooks
662 hooks = IPython.core.hooks
608 hooks = IPython.core.hooks
663 for hook_name in hooks.__all__:
609 for hook_name in hooks.__all__:
664 # default hooks have priority 100, i.e. low; user hooks should have
610 # default hooks have priority 100, i.e. low; user hooks should have
@@ -876,7 +822,7 b' class InteractiveShell(Component, Magic):'
876 # These routines return properly built dicts as needed by the rest of
822 # These routines return properly built dicts as needed by the rest of
877 # the code, and can also be used by extension writers to generate
823 # the code, and can also be used by extension writers to generate
878 # properly initialized namespaces.
824 # properly initialized namespaces.
879 user_ns, user_global_ns = make_user_namespaces(user_ns, user_global_ns)
825 user_ns, user_global_ns = self.make_user_namespaces(user_ns, user_global_ns)
880
826
881 # Assign namespaces
827 # Assign namespaces
882 # This is the namespace where all normal user variables live
828 # This is the namespace where all normal user variables live
@@ -887,7 +833,7 b' class InteractiveShell(Component, Magic):'
887 # loaded at startup, so we can list later only variables defined in
833 # loaded at startup, so we can list later only variables defined in
888 # actual interactive use. Since it is always a subset of user_ns, it
834 # actual interactive use. Since it is always a subset of user_ns, it
889 # doesn't need to be separately tracked in the ns_table.
835 # doesn't need to be separately tracked in the ns_table.
890 self.user_config_ns = {}
836 self.user_ns_hidden = {}
891
837
892 # A namespace to keep track of internal data structures to prevent
838 # A namespace to keep track of internal data structures to prevent
893 # them from cluttering user-visible stuff. Will be updated later
839 # them from cluttering user-visible stuff. Will be updated later
@@ -933,9 +879,67 b' class InteractiveShell(Component, Magic):'
933 # Similarly, track all namespaces where references can be held and that
879 # Similarly, track all namespaces where references can be held and that
934 # we can safely clear (so it can NOT include builtin). This one can be
880 # we can safely clear (so it can NOT include builtin). This one can be
935 # a simple list.
881 # a simple list.
936 self.ns_refs_table = [ user_ns, user_global_ns, self.user_config_ns,
882 self.ns_refs_table = [ user_ns, user_global_ns, self.user_ns_hidden,
937 self.internal_ns, self._main_ns_cache ]
883 self.internal_ns, self._main_ns_cache ]
938
884
885 def make_user_namespaces(self, user_ns=None, user_global_ns=None):
886 """Return a valid local and global user interactive namespaces.
887
888 This builds a dict with the minimal information needed to operate as a
889 valid IPython user namespace, which you can pass to the various
890 embedding classes in ipython. The default implementation returns the
891 same dict for both the locals and the globals to allow functions to
892 refer to variables in the namespace. Customized implementations can
893 return different dicts. The locals dictionary can actually be anything
894 following the basic mapping protocol of a dict, but the globals dict
895 must be a true dict, not even a subclass. It is recommended that any
896 custom object for the locals namespace synchronize with the globals
897 dict somehow.
898
899 Raises TypeError if the provided globals namespace is not a true dict.
900
901 Parameters
902 ----------
903 user_ns : dict-like, optional
904 The current user namespace. The items in this namespace should
905 be included in the output. If None, an appropriate blank
906 namespace should be created.
907 user_global_ns : dict, optional
908 The current user global namespace. The items in this namespace
909 should be included in the output. If None, an appropriate
910 blank namespace should be created.
911
912 Returns
913 -------
914 A pair of dictionary-like object to be used as the local namespace
915 of the interpreter and a dict to be used as the global namespace.
916 """
917
918
919 # We must ensure that __builtin__ (without the final 's') is always
920 # available and pointing to the __builtin__ *module*. For more details:
921 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
922
923 if user_ns is None:
924 # Set __name__ to __main__ to better match the behavior of the
925 # normal interpreter.
926 user_ns = {'__name__' :'__main__',
927 '__builtin__' : __builtin__,
928 '__builtins__' : __builtin__,
929 }
930 else:
931 user_ns.setdefault('__name__','__main__')
932 user_ns.setdefault('__builtin__',__builtin__)
933 user_ns.setdefault('__builtins__',__builtin__)
934
935 if user_global_ns is None:
936 user_global_ns = user_ns
937 if type(user_global_ns) is not dict:
938 raise TypeError("user_global_ns must be a true dict; got %r"
939 % type(user_global_ns))
940
941 return user_ns, user_global_ns
942
939 def init_sys_modules(self):
943 def init_sys_modules(self):
940 # We need to insert into sys.modules something that looks like a
944 # We need to insert into sys.modules something that looks like a
941 # module but which accesses the IPython namespace, for shelve and
945 # module but which accesses the IPython namespace, for shelve and
@@ -974,7 +978,7 b' class InteractiveShell(Component, Magic):'
974 therm.
978 therm.
975 """
979 """
976 # This function works in two parts: first we put a few things in
980 # This function works in two parts: first we put a few things in
977 # user_ns, and we sync that contents into user_config_ns so that these
981 # user_ns, and we sync that contents into user_ns_hidden so that these
978 # initial variables aren't shown by %who. After the sync, we add the
982 # initial variables aren't shown by %who. After the sync, we add the
979 # rest of what we *do* want the user to see with %who even on a new
983 # rest of what we *do* want the user to see with %who even on a new
980 # session (probably nothing, so theye really only see their own stuff)
984 # session (probably nothing, so theye really only see their own stuff)
@@ -1014,9 +1018,9 b' class InteractiveShell(Component, Magic):'
1014 # Store myself as the public api!!!
1018 # Store myself as the public api!!!
1015 ns['get_ipython'] = self.get_ipython
1019 ns['get_ipython'] = self.get_ipython
1016
1020
1017 # Sync what we've added so far to user_config_ns so these aren't seen
1021 # Sync what we've added so far to user_ns_hidden so these aren't seen
1018 # by %who
1022 # by %who
1019 self.user_config_ns.update(ns)
1023 self.user_ns_hidden.update(ns)
1020
1024
1021 # Anything put into ns now would show up in %who. Think twice before
1025 # Anything put into ns now would show up in %who. Think twice before
1022 # putting anything here, as we really want %who to show the user their
1026 # putting anything here, as we really want %who to show the user their
@@ -1089,7 +1093,7 b' class InteractiveShell(Component, Magic):'
1089 self.user_ns.update(vdict)
1093 self.user_ns.update(vdict)
1090
1094
1091 # And configure interactive visibility
1095 # And configure interactive visibility
1092 config_ns = self.user_config_ns
1096 config_ns = self.user_ns_hidden
1093 if interactive:
1097 if interactive:
1094 for name, val in vdict.iteritems():
1098 for name, val in vdict.iteritems():
1095 config_ns.pop(name, None)
1099 config_ns.pop(name, None)
@@ -1164,7 +1168,9 b' class InteractiveShell(Component, Magic):'
1164 Convert func into callable that saves & restores
1168 Convert func into callable that saves & restores
1165 history around the call """
1169 history around the call """
1166
1170
1167 if not self.has_readline:
1171 if self.has_readline:
1172 from IPython.utils import rlineimpl as readline
1173 else:
1168 return func
1174 return func
1169
1175
1170 def wrapper():
1176 def wrapper():
@@ -1198,6 +1204,9 b' class InteractiveShell(Component, Magic):'
1198 # and add any custom exception handlers the user may have specified
1204 # and add any custom exception handlers the user may have specified
1199 self.set_custom_exc(*custom_exceptions)
1205 self.set_custom_exc(*custom_exceptions)
1200
1206
1207 # Set the exception mode
1208 self.InteractiveTB.set_mode(mode=self.xmode)
1209
1201 def set_custom_exc(self,exc_tuple,handler):
1210 def set_custom_exc(self,exc_tuple,handler):
1202 """set_custom_exc(exc_tuple,handler)
1211 """set_custom_exc(exc_tuple,handler)
1203
1212
@@ -2351,6 +2360,9 b' class InteractiveShell(Component, Magic):'
2351 to make it easy to write extensions, you can also put your extensions
2360 to make it easy to write extensions, you can also put your extensions
2352 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
2361 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
2353 is added to ``sys.path`` automatically.
2362 is added to ``sys.path`` automatically.
2363
2364 If :func:`load_ipython_extension` returns anything, this function
2365 will return that object.
2354 """
2366 """
2355 from IPython.utils.syspathcontext import prepended_to_syspath
2367 from IPython.utils.syspathcontext import prepended_to_syspath
2356
2368
@@ -2495,11 +2507,11 b' class InteractiveShell(Component, Magic):'
2495 # We want to prevent the loading of pylab to pollute the user's
2507 # We want to prevent the loading of pylab to pollute the user's
2496 # namespace as shown by the %who* magics, so we execute the activation
2508 # namespace as shown by the %who* magics, so we execute the activation
2497 # code in an empty namespace, and we update *both* user_ns and
2509 # code in an empty namespace, and we update *both* user_ns and
2498 # user_config_ns with this information.
2510 # user_ns_hidden with this information.
2499 ns = {}
2511 ns = {}
2500 gui = pylab_activate(ns, gui)
2512 gui = pylab_activate(ns, gui)
2501 self.user_ns.update(ns)
2513 self.user_ns.update(ns)
2502 self.user_config_ns.update(ns)
2514 self.user_ns_hidden.update(ns)
2503 # Now we must activate the gui pylab wants to use, and fix %run to take
2515 # Now we must activate the gui pylab wants to use, and fix %run to take
2504 # plot updates into account
2516 # plot updates into account
2505 enable_gui(gui)
2517 enable_gui(gui)
@@ -7,7 +7,7 b''
7 # the file COPYING, distributed as part of this software.
7 # the file COPYING, distributed as part of this software.
8 #*****************************************************************************
8 #*****************************************************************************
9
9
10 from IPython.utils.genutils import Term
10 from IPython.utils.io import Term
11 from IPython.core.autocall import IPyAutocall
11 from IPython.core.autocall import IPyAutocall
12
12
13 class Macro(IPyAutocall):
13 class Macro(IPyAutocall):
@@ -1,35 +1,33 b''
1 # -*- coding: utf-8 -*-
1 # encoding: utf-8
2 """Magic functions for InteractiveShell.
2 """Magic functions for InteractiveShell.
3 """
3 """
4
4
5 #*****************************************************************************
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
6 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
7 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
7 # Copyright (C) 2001-2007 Fernando Perez <fperez@colorado.edu>
8 #
8 # Copyright (C) 2008-2009 The IPython Development Team
9
9 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
11 #*****************************************************************************
12 #-----------------------------------------------------------------------------
12
13
13 #****************************************************************************
14 #-----------------------------------------------------------------------------
14 # Modules and globals
15 # Imports
16 #-----------------------------------------------------------------------------
15
17
16 # Python standard modules
17 import __builtin__
18 import __builtin__
18 import bdb
19 import bdb
19 import inspect
20 import inspect
20 import os
21 import os
21 import pdb
22 import pydoc
23 import sys
22 import sys
24 import shutil
23 import shutil
25 import re
24 import re
26 import tempfile
27 import time
25 import time
28 import cPickle as pickle
29 import textwrap
26 import textwrap
27 import types
30 from cStringIO import StringIO
28 from cStringIO import StringIO
31 from getopt import getopt,GetoptError
29 from getopt import getopt,GetoptError
32 from pprint import pprint, pformat
30 from pprint import pformat
33
31
34 # cProfile was added in Python2.5
32 # cProfile was added in Python2.5
35 try:
33 try:
@@ -42,10 +40,7 b' except ImportError:'
42 except ImportError:
40 except ImportError:
43 profile = pstats = None
41 profile = pstats = None
44
42
45 # Homebrewed
46 import IPython
43 import IPython
47 import IPython.utils.generics
48
49 from IPython.core import debugger, oinspect
44 from IPython.core import debugger, oinspect
50 from IPython.core.error import TryNext
45 from IPython.core.error import TryNext
51 from IPython.core.error import UsageError
46 from IPython.core.error import UsageError
@@ -53,20 +48,24 b' from IPython.core.fakemodule import FakeModule'
53 from IPython.core.macro import Macro
48 from IPython.core.macro import Macro
54 from IPython.core.page import page
49 from IPython.core.page import page
55 from IPython.core.prefilter import ESC_MAGIC
50 from IPython.core.prefilter import ESC_MAGIC
56 from IPython.core.pylabtools import mpl_runner
51 from IPython.lib.pylabtools import mpl_runner
57 from IPython.lib.inputhook import enable_gui
52 from IPython.lib.inputhook import enable_gui
58 from IPython.external.Itpl import Itpl, itpl, printpl,itplns
53 from IPython.external.Itpl import itpl, printpl
59 from IPython.testing import decorators as testdec
54 from IPython.testing import decorators as testdec
60 from IPython.utils import platutils
55 from IPython.utils.io import Term, file_read, nlprint
61 from IPython.utils import wildcard
56 from IPython.utils.path import get_py_filename
62 from IPython.utils.PyColorize import Parser
57 from IPython.utils.process import arg_split, abbrev_cwd
58 from IPython.utils.terminal import set_term_title
59 from IPython.utils.text import LSString, SList, StringTypes
60 from IPython.utils.timing import clock, clock2
61 from IPython.utils.warn import warn, error
63 from IPython.utils.ipstruct import Struct
62 from IPython.utils.ipstruct import Struct
63 import IPython.utils.generics
64
64
65 # XXX - We need to switch to explicit imports here with genutils
65 #-----------------------------------------------------------------------------
66 from IPython.utils.genutils import *
67
68 #***************************************************************************
69 # Utility functions
66 # Utility functions
67 #-----------------------------------------------------------------------------
68
70 def on_off(tag):
69 def on_off(tag):
71 """Return an ON/OFF string for a 1/0 input. Simple utility function."""
70 """Return an ON/OFF string for a 1/0 input. Simple utility function."""
72 return ['OFF','ON'][tag]
71 return ['OFF','ON'][tag]
@@ -94,6 +93,9 b' def compress_dhist(dh):'
94 # on construction of the main InteractiveShell object. Something odd is going
93 # on construction of the main InteractiveShell object. Something odd is going
95 # on with super() calls, Component and the MRO... For now leave it as-is, but
94 # on with super() calls, Component and the MRO... For now leave it as-is, but
96 # eventually this needs to be clarified.
95 # eventually this needs to be clarified.
96 # BG: This is because InteractiveShell inherits from this, but is itself a
97 # Component. This messes up the MRO in some way. The fix is that we need to
98 # make Magic a component that InteractiveShell does not subclass.
97
99
98 class Magic:
100 class Magic:
99 """Magic functions for InteractiveShell.
101 """Magic functions for InteractiveShell.
@@ -277,7 +279,7 b' python-profiler package from non-free.""")'
277 def arg_err(self,func):
279 def arg_err(self,func):
278 """Print docstring if incorrect arguments were passed"""
280 """Print docstring if incorrect arguments were passed"""
279 print 'Error in arguments:'
281 print 'Error in arguments:'
280 print OInspect.getdoc(func)
282 print oinspect.getdoc(func)
281
283
282 def format_latex(self,strng):
284 def format_latex(self,strng):
283 """Format a string for latex inclusion."""
285 """Format a string for latex inclusion."""
@@ -884,10 +886,10 b' Currently the magic system has the following functions:\\n"""'
884
886
885 user_ns = self.shell.user_ns
887 user_ns = self.shell.user_ns
886 internal_ns = self.shell.internal_ns
888 internal_ns = self.shell.internal_ns
887 user_config_ns = self.shell.user_config_ns
889 user_ns_hidden = self.shell.user_ns_hidden
888 out = [ i for i in user_ns
890 out = [ i for i in user_ns
889 if not i.startswith('_') \
891 if not i.startswith('_') \
890 and not (i in internal_ns or i in user_config_ns) ]
892 and not (i in internal_ns or i in user_ns_hidden) ]
891
893
892 typelist = parameter_s.split()
894 typelist = parameter_s.split()
893 if typelist:
895 if typelist:
@@ -1170,7 +1172,7 b' Currently the magic system has the following functions:\\n"""'
1170 started = logger.logstart(logfname,loghead,logmode,
1172 started = logger.logstart(logfname,loghead,logmode,
1171 log_output,timestamp,log_raw_input)
1173 log_output,timestamp,log_raw_input)
1172 except:
1174 except:
1173 rc.opts.logfile = old_logfile
1175 self.shell.logfile = old_logfile
1174 warn("Couldn't start log: %s" % sys.exc_info()[1])
1176 warn("Couldn't start log: %s" % sys.exc_info()[1])
1175 else:
1177 else:
1176 # log input history up to this point, optionally interleaving
1178 # log input history up to this point, optionally interleaving
@@ -2811,7 +2813,7 b' Defaulting color scheme to \'NoColor\'"""'
2811 try:
2813 try:
2812 os.chdir(os.path.expanduser(ps))
2814 os.chdir(os.path.expanduser(ps))
2813 if self.shell.term_title:
2815 if self.shell.term_title:
2814 platutils.set_term_title('IPython: ' + abbrev_cwd())
2816 set_term_title('IPython: ' + abbrev_cwd())
2815 except OSError:
2817 except OSError:
2816 print sys.exc_info()[1]
2818 print sys.exc_info()[1]
2817 else:
2819 else:
@@ -2824,7 +2826,7 b' Defaulting color scheme to \'NoColor\'"""'
2824 else:
2826 else:
2825 os.chdir(self.shell.home_dir)
2827 os.chdir(self.shell.home_dir)
2826 if self.shell.term_title:
2828 if self.shell.term_title:
2827 platutils.set_term_title('IPython: ' + '~')
2829 set_term_title('IPython: ' + '~')
2828 cwd = os.getcwd()
2830 cwd = os.getcwd()
2829 dhist = self.shell.user_ns['_dh']
2831 dhist = self.shell.user_ns['_dh']
2830
2832
@@ -27,10 +27,11 b' import sys'
27 import types
27 import types
28
28
29 # IPython's own
29 # IPython's own
30 from IPython.utils import PyColorize
31 from IPython.utils.genutils import indent, Term
32 from IPython.core.page import page
30 from IPython.core.page import page
33 from IPython.external.Itpl import itpl
31 from IPython.external.Itpl import itpl
32 from IPython.utils import PyColorize
33 from IPython.utils.io import Term
34 from IPython.utils.text import indent
34 from IPython.utils.wildcard import list_namespace
35 from IPython.utils.wildcard import list_namespace
35 from IPython.utils.coloransi import *
36 from IPython.utils.coloransi import *
36
37
@@ -30,15 +30,15 b' rid of that dependency, we could move it there.'
30 import os
30 import os
31 import re
31 import re
32 import sys
32 import sys
33 import tempfile
33
34
34 from IPython.core import ipapi
35 from IPython.core import ipapi
35 from IPython.core.error import TryNext
36 from IPython.core.error import TryNext
36 from IPython.utils.genutils import (
37 from IPython.utils.cursesimport import use_curses
37 chop, Term, USE_CURSES
38 from IPython.utils.data import chop
38 )
39 from IPython.utils.io import Term
39
40 from IPython.utils.process import xsys
40 if os.name == "nt":
41 from IPython.utils.terminal import get_terminal_size
41 from IPython.utils.winconsole import get_console_size
42
42
43
43
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
@@ -47,7 +47,7 b' if os.name == "nt":'
47
47
48 esc_re = re.compile(r"(\x1b[^m]+m)")
48 esc_re = re.compile(r"(\x1b[^m]+m)")
49
49
50 def page_dumb(strng,start=0,screen_lines=25):
50 def page_dumb(strng, start=0, screen_lines=25):
51 """Very dumb 'pager' in Python, for when nothing else works.
51 """Very dumb 'pager' in Python, for when nothing else works.
52
52
53 Only moves forward, same interface as page(), except for pager_cmd and
53 Only moves forward, same interface as page(), except for pager_cmd and
@@ -69,8 +69,8 b' def page_dumb(strng,start=0,screen_lines=25):'
69 last_escape = esc_list[-1]
69 last_escape = esc_list[-1]
70 print >>Term.cout, last_escape + os.linesep.join(screens[-1])
70 print >>Term.cout, last_escape + os.linesep.join(screens[-1])
71
71
72 #----------------------------------------------------------------------------
72
73 def page(strng,start=0,screen_lines=0,pager_cmd = None):
73 def page(strng, start=0, screen_lines=0, pager_cmd=None):
74 """Print a string, piping through a pager after a certain length.
74 """Print a string, piping through a pager after a certain length.
75
75
76 The screen_lines parameter specifies the number of *usable* lines of your
76 The screen_lines parameter specifies the number of *usable* lines of your
@@ -93,7 +93,7 b' def page(strng,start=0,screen_lines=0,pager_cmd = None):'
93
93
94 # Some routines may auto-compute start offsets incorrectly and pass a
94 # Some routines may auto-compute start offsets incorrectly and pass a
95 # negative value. Offset to 0 for robustness.
95 # negative value. Offset to 0 for robustness.
96 start = max(0,start)
96 start = max(0, start)
97
97
98 # first, try the hook
98 # first, try the hook
99 ip = ipapi.get()
99 ip = ipapi.get()
@@ -120,19 +120,16 b' def page(strng,start=0,screen_lines=0,pager_cmd = None):'
120 # terminals. If someone later feels like refining it, it's not hard.
120 # terminals. If someone later feels like refining it, it's not hard.
121 numlines = max(num_newlines,int(len_str/80)+1)
121 numlines = max(num_newlines,int(len_str/80)+1)
122
122
123 if os.name == "nt":
123 screen_lines_def = get_terminal_size()[1]
124 screen_lines_def = get_console_size(defaulty=25)[1]
125 else:
126 screen_lines_def = 25 # default value if we can't auto-determine
127
124
128 # auto-determine screen size
125 # auto-determine screen size
129 if screen_lines <= 0:
126 if screen_lines <= 0:
130 if TERM=='xterm' or TERM=='xterm-color':
127 if TERM=='xterm' or TERM=='xterm-color':
131 use_curses = USE_CURSES
128 local_use_curses = use_curses
132 else:
129 else:
133 # curses causes problems on many terminals other than xterm.
130 # curses causes problems on many terminals other than xterm.
134 use_curses = False
131 local_use_curses = False
135 if use_curses:
132 if local_use_curses:
136 import termios
133 import termios
137 import curses
134 import curses
138 # There is a bug in curses, where *sometimes* it fails to properly
135 # There is a bug in curses, where *sometimes* it fails to properly
@@ -201,8 +198,8 b' def page(strng,start=0,screen_lines=0,pager_cmd = None):'
201 if retval is not None:
198 if retval is not None:
202 page_dumb(strng,screen_lines=screen_lines)
199 page_dumb(strng,screen_lines=screen_lines)
203
200
204 #----------------------------------------------------------------------------
201
205 def page_file(fname,start = 0, pager_cmd = None):
202 def page_file(fname, start=0, pager_cmd=None):
206 """Page a file, using an optional pager command and starting line.
203 """Page a file, using an optional pager command and starting line.
207 """
204 """
208
205
@@ -221,12 +218,12 b' def page_file(fname,start = 0, pager_cmd = None):'
221 except:
218 except:
222 print 'Unable to show file',`fname`
219 print 'Unable to show file',`fname`
223
220
224 #----------------------------------------------------------------------------
225 def get_pager_cmd(pager_cmd = None):
226 """Return a pager command.
227
221
228 Makes some attempts at finding an OS-correct one."""
222 def get_pager_cmd(pager_cmd=None):
223 """Return a pager command.
229
224
225 Makes some attempts at finding an OS-correct one.
226 """
230 if os.name == 'posix':
227 if os.name == 'posix':
231 default_pager_cmd = 'less -r' # -r for color control sequences
228 default_pager_cmd = 'less -r' # -r for color control sequences
232 elif os.name in ['nt','dos']:
229 elif os.name in ['nt','dos']:
@@ -239,8 +236,8 b' def get_pager_cmd(pager_cmd = None):'
239 pager_cmd = default_pager_cmd
236 pager_cmd = default_pager_cmd
240 return pager_cmd
237 return pager_cmd
241
238
242 #-----------------------------------------------------------------------------
239
243 def get_pager_start(pager,start):
240 def get_pager_start(pager, start):
244 """Return the string for paging files with an offset.
241 """Return the string for paging files with an offset.
245
242
246 This is the '+N' argument which less and more (under Unix) accept.
243 This is the '+N' argument which less and more (under Unix) accept.
@@ -255,8 +252,8 b' def get_pager_start(pager,start):'
255 start_string = ''
252 start_string = ''
256 return start_string
253 return start_string
257
254
258 #----------------------------------------------------------------------------
255
259 # (X)emacs on W32 doesn't like to be bypassed with msvcrt.getch()
256 # (X)emacs on win32 doesn't like to be bypassed with msvcrt.getch()
260 if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
257 if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
261 import msvcrt
258 import msvcrt
262 def page_more():
259 def page_more():
@@ -280,7 +277,7 b' else:'
280 else:
277 else:
281 return True
278 return True
282
279
283 #----------------------------------------------------------------------------
280
284 def snip_print(str,width = 75,print_full = 0,header = ''):
281 def snip_print(str,width = 75,print_full = 0,header = ''):
285 """Print a string snipping the midsection to fit in width.
282 """Print a string snipping the midsection to fit in width.
286
283
@@ -305,4 +302,5 b" def snip_print(str,width = 75,print_full = 0,header = ''):"
305 if snip and print_full == 2:
302 if snip and print_full == 2:
306 if raw_input(header+' Snipped. View (y/n)? [N]').lower() == 'y':
303 if raw_input(header+' Snipped. View (y/n)? [N]').lower() == 'y':
307 page(str)
304 page(str)
308 return snip No newline at end of file
305 return snip
306
@@ -27,10 +27,7 b' Authors:'
27
27
28 import __builtin__
28 import __builtin__
29 import codeop
29 import codeop
30 import keyword
31 import os
32 import re
30 import re
33 import sys
34
31
35 from IPython.core.alias import AliasManager
32 from IPython.core.alias import AliasManager
36 from IPython.core.autocall import IPyAutocall
33 from IPython.core.autocall import IPyAutocall
@@ -39,7 +36,8 b' from IPython.core.splitinput import split_user_input'
39 from IPython.core.page import page
36 from IPython.core.page import page
40
37
41 from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool
38 from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool
42 from IPython.utils.genutils import make_quoted_expr, Term
39 from IPython.utils.io import Term
40 from IPython.utils.text import make_quoted_expr
43 from IPython.utils.autoattr import auto_attr
41 from IPython.utils.autoattr import auto_attr
44
42
45 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
@@ -158,11 +156,12 b' class LineInfo(object):'
158 without worrying about *further* damaging state.
156 without worrying about *further* damaging state.
159 """
157 """
160 if not self._oinfo:
158 if not self._oinfo:
159 # ip.shell._ofind is actually on the Magic class!
161 self._oinfo = ip.shell._ofind(self.ifun)
160 self._oinfo = ip.shell._ofind(self.ifun)
162 return self._oinfo
161 return self._oinfo
163
162
164 def __str__(self):
163 def __str__(self):
165 return "Lineinfo [%s|%s|%s]" %(self.pre,self.ifun,self.the_rest)
164 return "Lineinfo [%s|%s|%s]" %(self.pre, self.ifun, self.the_rest)
166
165
167
166
168 #-----------------------------------------------------------------------------
167 #-----------------------------------------------------------------------------
@@ -12,23 +12,20 b' Classes for handling input/output prompts.'
12 #*****************************************************************************
12 #*****************************************************************************
13
13
14 #****************************************************************************
14 #****************************************************************************
15 # Required modules
15
16 import __builtin__
16 import __builtin__
17 import os
17 import os
18 import re
18 import socket
19 import socket
19 import sys
20 import sys
20 import time
21
21
22 # IPython's own
23 from IPython.utils import coloransi
24 from IPython.core import release
22 from IPython.core import release
25 from IPython.external.Itpl import ItplNS
23 from IPython.external.Itpl import ItplNS
26 from IPython.core.error import TryNext
24 from IPython.core.error import TryNext
27 from IPython.utils.ipstruct import Struct
25 from IPython.utils import coloransi
28 from IPython.core.macro import Macro
29 import IPython.utils.generics
26 import IPython.utils.generics
30
27 from IPython.utils.warn import warn
31 from IPython.utils.genutils import *
28 from IPython.utils.io import Term
32
29
33 #****************************************************************************
30 #****************************************************************************
34 #Color schemes for Prompts.
31 #Color schemes for Prompts.
@@ -19,7 +19,11 b' Authors'
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import sys
22
23 #-----------------------------------------------------------------------------
24 # Code
25 #-----------------------------------------------------------------------------
26
23
27
24 class Quitter(object):
28 class Quitter(object):
25 """Simple class to handle exit, similar to Python 2.5's.
29 """Simple class to handle exit, similar to Python 2.5's.
@@ -40,4 +44,4 b' class Quitter(object):'
40 # Repr MUST return a string, else display like pprint hooks get confused
44 # Repr MUST return a string, else display like pprint hooks get confused
41 def __repr__(self):
45 def __repr__(self):
42 self.shell.ask_exit()
46 self.shell.ask_exit()
43 return 'Bye.'
47 return ''
@@ -21,9 +21,9 b" name = 'ipython'"
21 # bdist_deb does not accept underscores (a Debian convention).
21 # bdist_deb does not accept underscores (a Debian convention).
22
22
23 development = True # change this to False to do a release
23 development = True # change this to False to do a release
24 version_base = '0.11'
24 version_base = '0.11.alpha1'
25 branch = 'ipython'
25 branch = 'ipython'
26 revision = '1346'
26 revision = '1223'
27
27
28 if development:
28 if development:
29 if branch == 'ipython':
29 if branch == 'ipython':
@@ -30,9 +30,9 b' ip = get_ipython()'
30 @dec.parametric
30 @dec.parametric
31 def test_reset():
31 def test_reset():
32 """reset must clear most namespaces."""
32 """reset must clear most namespaces."""
33 # The number of variables in the private user_config_ns is not zero, but it
33 # The number of variables in the private user_ns_hidden is not zero, but it
34 # should be constant regardless of what we do
34 # should be constant regardless of what we do
35 nvars_config_ns = len(ip.user_config_ns)
35 nvars_config_ns = len(ip.user_ns_hidden)
36
36
37 # Check that reset runs without error
37 # Check that reset runs without error
38 ip.reset()
38 ip.reset()
@@ -51,7 +51,7 b' def test_reset():'
51 for ns in ip.ns_refs_table:
51 for ns in ip.ns_refs_table:
52 if ns is ip.user_ns:
52 if ns is ip.user_ns:
53 nvars_expected = nvars_user_ns
53 nvars_expected = nvars_user_ns
54 elif ns is ip.user_config_ns:
54 elif ns is ip.user_ns_hidden:
55 nvars_expected = nvars_config_ns
55 nvars_expected = nvars_config_ns
56 else:
56 else:
57 nvars_expected = 0
57 nvars_expected = 0
@@ -8,19 +8,15 b' from __future__ import absolute_import'
8 # Imports
8 # Imports
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 # stdlib
12 import os
11 import os
13 import sys
12 import sys
14 import tempfile
13 import tempfile
15 import types
14 import types
16 from cStringIO import StringIO
15 from cStringIO import StringIO
17
16
18 # third-party
19 import nose.tools as nt
17 import nose.tools as nt
20
18
21 # our own
19 from IPython.utils.path import get_long_path_name
22 from IPython.utils import genutils
23 from IPython.utils.platutils import find_cmd, get_long_path_name
24 from IPython.testing import decorators as dec
20 from IPython.testing import decorators as dec
25 from IPython.testing import tools as tt
21 from IPython.testing import tools as tt
26
22
@@ -12,17 +12,12 b' from __future__ import absolute_import'
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 # stdlib
16 import os
15 import os
17 import sys
16 import sys
18 import tempfile
17 import tempfile
19
18
20 # third-party
21 import nose.tools as nt
19 import nose.tools as nt
22
20
23 # our own
24 from IPython.utils.platutils import find_cmd
25 from IPython.utils import genutils
26 from IPython.testing import decorators as dec
21 from IPython.testing import decorators as dec
27 from IPython.testing import tools as tt
22 from IPython.testing import tools as tt
28
23
@@ -142,10 +137,10 b' class TestMagicRunSimple(tt.TempFileMixin):'
142 _ip.runlines('t = isinstance(f(), foo)')
137 _ip.runlines('t = isinstance(f(), foo)')
143 nt.assert_true(_ip.user_ns['t'])
138 nt.assert_true(_ip.user_ns['t'])
144
139
145 # We have to skip these in win32 because genutils.getoutputerr() crashes,
140 # We have to skip these in win32 because getoutputerr() crashes,
146 # due to the fact that subprocess does not support close_fds when
141 # due to the fact that subprocess does not support close_fds when
147 # redirecting stdout/err. So unless someone who knows more tells us how to
142 # redirecting stdout/err. So unless someone who knows more tells us how to
148 # implement genutils.getoutputerr() in win32, we're stuck avoiding these.
143 # implement getoutputerr() in win32, we're stuck avoiding these.
149 @dec.skip_win32
144 @dec.skip_win32
150 def test_obj_del(self):
145 def test_obj_del(self):
151 """Test that object's __del__ methods are called on exit."""
146 """Test that object's __del__ methods are called on exit."""
@@ -93,9 +93,10 b' from inspect import getsourcefile, getfile, getmodule,\\'
93 from IPython.utils import PyColorize
93 from IPython.utils import PyColorize
94 from IPython.core import debugger, ipapi
94 from IPython.core import debugger, ipapi
95 from IPython.core.display_trap import DisplayTrap
95 from IPython.core.display_trap import DisplayTrap
96 from IPython.utils.ipstruct import Struct
97 from IPython.core.excolors import exception_colors
96 from IPython.core.excolors import exception_colors
98 from IPython.utils.genutils import Term, uniq_stable, error, info
97 from IPython.utils.data import uniq_stable
98 from IPython.utils.io import Term
99 from IPython.utils.warn import info, error
99
100
100 # Globals
101 # Globals
101 # amount of space to put line numbers before verbose tracebacks
102 # amount of space to put line numbers before verbose tracebacks
@@ -384,7 +385,8 b' class ListTB(TBTools):'
384
385
385 def __call__(self, etype, value, elist):
386 def __call__(self, etype, value, elist):
386 Term.cout.flush()
387 Term.cout.flush()
387 Term.cerr.writeln(self.text(etype,value,elist))
388 Term.cerr.write(self.text(etype,value,elist))
389 Term.cerr.write('\n')
388
390
389 def text(self, etype, value, elist, context=5):
391 def text(self, etype, value, elist, context=5):
390 """Return a color formatted string with the traceback info.
392 """Return a color formatted string with the traceback info.
@@ -909,7 +911,8 b' class VerboseTB(TBTools):'
909 (etype, evalue, etb) = info or sys.exc_info()
911 (etype, evalue, etb) = info or sys.exc_info()
910 self.tb = etb
912 self.tb = etb
911 Term.cout.flush()
913 Term.cout.flush()
912 Term.cerr.writeln(self.text(etype, evalue, etb))
914 Term.cerr.write(self.text(etype, evalue, etb))
915 Term.cerr.write('\n')
913
916
914 # Changed so an instance can just be called as VerboseTB_inst() and print
917 # Changed so an instance can just be called as VerboseTB_inst() and print
915 # out the right info on its own.
918 # out the right info on its own.
@@ -53,7 +53,7 b" __all__ = ['Gnuplot','gp','gp_new','Data','File','Func','GridData',"
53 'pm3d_config','eps_fix_bbox']
53 'pm3d_config','eps_fix_bbox']
54
54
55 import os,tempfile,sys
55 import os,tempfile,sys
56 from IPython.utils.genutils import getoutput
56 from IPython.utils.process import getoutput
57
57
58 #---------------------------------------------------------------------------
58 #---------------------------------------------------------------------------
59 # Notes on mouse support for Gnuplot.py
59 # Notes on mouse support for Gnuplot.py
@@ -133,10 +133,10 b' from IPython.external import simplegeneric'
133 from IPython.external import path
133 from IPython.external import path
134
134
135 try:
135 try:
136 from IPython.utils import genutils
136 from IPython.utils.io import Term
137 from IPython.utils import generics
137 from IPython.utils import generics
138 except ImportError:
138 except ImportError:
139 genutils = None
139 Term = None
140 generics = None
140 generics = None
141
141
142 from IPython.core import ipapi
142 from IPython.core import ipapi
@@ -2168,7 +2168,7 b' class idump(Display):'
2168 self.datasepchar = "|"
2168 self.datasepchar = "|"
2169
2169
2170 def display(self):
2170 def display(self):
2171 stream = genutils.Term.cout
2171 stream = Term.cout
2172 allattrs = []
2172 allattrs = []
2173 attrset = set()
2173 attrset = set()
2174 colwidths = {}
2174 colwidths = {}
@@ -54,7 +54,7 b' from enthought.traits import api as T'
54 # IPython imports
54 # IPython imports
55 from IPython.core.error import TryNext
55 from IPython.core.error import TryNext
56 from IPython.core.ipapi import get as ipget
56 from IPython.core.ipapi import get as ipget
57 from IPython.utils.genutils import dir2
57 from IPython.utils.dir2 import dir2
58 try:
58 try:
59 set
59 set
60 except:
60 except:
@@ -14,7 +14,9 b' from IPython.core.iplib import InteractiveShell'
14 from IPython.utils.ipstruct import Struct
14 from IPython.utils.ipstruct import Struct
15 import Queue,thread,threading,signal
15 import Queue,thread,threading,signal
16 from signal import signal, SIGINT
16 from signal import signal, SIGINT
17 from IPython.utils.genutils import Term,warn,error,flag_calls, ask_yes_no
17 from IPython.utils.io import Term, ask_yes_no
18 from IPython.utils.warn import warn, error
19 from IPython.utils.decorators import flag_calls
18 from IPython.core import shellglobals
20 from IPython.core import shellglobals
19
21
20 def install_gtk2():
22 def install_gtk2():
@@ -39,7 +39,7 b' from IPython.core.error import TryNext'
39 from IPython.external import pretty
39 from IPython.external import pretty
40 from IPython.core.component import Component
40 from IPython.core.component import Component
41 from IPython.utils.traitlets import Bool, List
41 from IPython.utils.traitlets import Bool, List
42 from IPython.utils.genutils import Term
42 from IPython.utils.io import Term
43 from IPython.utils.autoattr import auto_attr
43 from IPython.utils.autoattr import auto_attr
44 from IPython.utils.importstring import import_item
44 from IPython.utils.importstring import import_item
45
45
@@ -128,9 +128,8 b' class PrettyResultDisplay(Component):'
128 #-----------------------------------------------------------------------------
128 #-----------------------------------------------------------------------------
129
129
130
130
131 def load_ipython_extension(ip=None):
131 def load_ipython_extension(ip):
132 """Load the extension in IPython as a hook."""
132 """Load the extension in IPython as a hook."""
133 if ip is None: ip = get_ipython()
134 global _loaded
133 global _loaded
135 if not _loaded:
134 if not _loaded:
136 prd = PrettyResultDisplay(ip, name='pretty_result_display')
135 prd = PrettyResultDisplay(ip, name='pretty_result_display')
@@ -213,7 +213,7 b' def main():'
213 print "\n".join(expand(sys.argv[1:])),
213 print "\n".join(expand(sys.argv[1:])),
214
214
215 def mglob_f(self, arg):
215 def mglob_f(self, arg):
216 from IPython.utils.genutils import SList
216 from IPython.utils.text import SList
217 if arg.strip():
217 if arg.strip():
218 return SList(expand(arg))
218 return SList(expand(arg))
219 print "Please specify pattern!"
219 print "Please specify pattern!"
@@ -39,9 +39,10 b' def common_prefix(strings):'
39
39
40 return prefix
40 return prefix
41
41
42 #-------------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Base class for the line-oriented front ends
43 # Base class for the line-oriented front ends
44 #-------------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45 class LineFrontEndBase(FrontEndBase):
46 class LineFrontEndBase(FrontEndBase):
46 """ Concrete implementation of the FrontEndBase class. This is meant
47 """ Concrete implementation of the FrontEndBase class. This is meant
47 to be the base class behind all the frontend that are line-oriented,
48 to be the base class behind all the frontend that are line-oriented,
@@ -26,12 +26,12 b' import os'
26 import re
26 import re
27 import __builtin__
27 import __builtin__
28
28
29 from IPython.core.ipapp import IPythonApp
29 from IPython.core.iplib import InteractiveShell
30 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
30 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
31
31
32 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
32 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
33
33
34 from IPython.utils.genutils import Term
34 from IPython.utils.io import Term
35
35
36 from linefrontendbase import LineFrontEndBase, common_prefix
36 from linefrontendbase import LineFrontEndBase, common_prefix
37
37
@@ -50,9 +50,10 b' def mk_system_call(system_call_function, command):'
50 my_system_call.__doc__ = "Calls %s" % command
50 my_system_call.__doc__ = "Calls %s" % command
51 return my_system_call
51 return my_system_call
52
52
53 #-------------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 # Frontend class using ipython0 to do the prefiltering.
54 # Frontend class using ipython0 to do the prefiltering.
55 #-------------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56 class PrefilterFrontEnd(LineFrontEndBase):
57 class PrefilterFrontEnd(LineFrontEndBase):
57 """ Class that uses ipython0 to do prefilter the input, do the
58 """ Class that uses ipython0 to do prefilter the input, do the
58 completion and the magics.
59 completion and the magics.
@@ -65,19 +66,13 b' class PrefilterFrontEnd(LineFrontEndBase):'
65
66
66 debug = False
67 debug = False
67
68
68 def __init__(self, ipython0=None, argv=None, *args, **kwargs):
69 def __init__(self, ipython0=None, *args, **kwargs):
69 """ Parameters
70 """ Parameters
70 ----------
71 ----------
71
72
72 ipython0: an optional ipython0 instance to use for command
73 ipython0: an optional ipython0 instance to use for command
73 prefiltering and completion.
74 prefiltering and completion.
74
75 argv : list, optional
76 Used as the instance's argv value. If not given, [] is used.
77 """
75 """
78 if argv is None:
79 argv = ['--no-banner']
80
81 LineFrontEndBase.__init__(self, *args, **kwargs)
76 LineFrontEndBase.__init__(self, *args, **kwargs)
82 self.shell.output_trap = RedirectorOutputTrap(
77 self.shell.output_trap = RedirectorOutputTrap(
83 out_callback=self.write,
78 out_callback=self.write,
@@ -90,22 +85,19 b' class PrefilterFrontEnd(LineFrontEndBase):'
90 # Start the ipython0 instance:
85 # Start the ipython0 instance:
91 self.save_output_hooks()
86 self.save_output_hooks()
92 if ipython0 is None:
87 if ipython0 is None:
93 # Instanciate an IPython0 interpreter to be able to use the
88 # Instanciate an IPython0 InteractiveShell to be able to use the
94 # prefiltering.
89 # prefiltering.
95 # Suppress all key input, to avoid waiting
90 # Suppress all key input, to avoid waiting
96 def my_rawinput(x=None):
91 def my_rawinput(x=None):
97 return '\n'
92 return '\n'
98 old_rawinput = __builtin__.raw_input
93 old_rawinput = __builtin__.raw_input
99 __builtin__.raw_input = my_rawinput
94 __builtin__.raw_input = my_rawinput
100 ipython0 = IPythonApp(argv=argv,
95 ipython0 = InteractiveShell(
101 user_ns=self.shell.user_ns,
96 parent=None, user_ns=self.shell.user_ns,
102 user_global_ns=self.shell.user_global_ns)
97 user_global_ns=self.shell.user_global_ns
103 ipython0.initialize()
98 )
104 __builtin__.raw_input = old_rawinput
99 __builtin__.raw_input = old_rawinput
105 # XXX This will need to be updated as we refactor things, but for now,
100 self.ipython0 = ipython0
106 # the .shell attribute of the ipythonapp instance conforms to the old
107 # api.
108 self.ipython0 = ipython0.shell
109 # Set the pager:
101 # Set the pager:
110 self.ipython0.set_hook('show_in_pager',
102 self.ipython0.set_hook('show_in_pager',
111 lambda s, string: self.write("\n" + string))
103 lambda s, string: self.write("\n" + string))
@@ -1,14 +1,8 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
3 """This file contains unittests for the asyncfrontendbase module."""
2 """This file contains unittests for the asyncfrontendbase module."""
4
3
5 __docformat__ = "restructuredtext en"
6
7 # Tell nose to skip this module
8 __test__ = {}
9
10 #---------------------------------------------------------------------------
4 #---------------------------------------------------------------------------
11 # Copyright (C) 2008 The IPython Development Team
5 # Copyright (C) 2008-2009 The IPython Development Team
12 #
6 #
13 # Distributed under the terms of the BSD License. The full license is in
7 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
@@ -21,7 +21,6 b' from nose.tools import assert_equal'
21
21
22 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
22 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
23 from IPython.testing.globalipapp import get_ipython
23 from IPython.testing.globalipapp import get_ipython
24 from IPython.testing.tools import default_argv
25
24
26 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
27 # Support utilities
26 # Support utilities
@@ -35,7 +34,7 b' class TestPrefilterFrontEnd(PrefilterFrontEnd):'
35
34
36 def __init__(self):
35 def __init__(self):
37 self.out = StringIO()
36 self.out = StringIO()
38 PrefilterFrontEnd.__init__(self,argv=default_argv())
37 PrefilterFrontEnd.__init__(self)
39 # Some more code for isolation (yeah, crazy)
38 # Some more code for isolation (yeah, crazy)
40 self._on_enter()
39 self._on_enter()
41 self.out.flush()
40 self.out.flush()
@@ -47,6 +47,7 b' def test_io():'
47 assert result == test_string
47 assert result == test_string
48
48
49
49
50 @testdec.skip_win32
50 def test_kill():
51 def test_kill():
51 """ Check that we can kill a process, and its subprocess.
52 """ Check that we can kill a process, and its subprocess.
52 """
53 """
@@ -135,9 +135,10 b' else:'
135 }
135 }
136
136
137
137
138 #-------------------------------------------------------------------------------
138 #-----------------------------------------------------------------------------
139 # The console widget class
139 # The console widget class
140 #-------------------------------------------------------------------------------
140 #-----------------------------------------------------------------------------
141
141 class ConsoleWidget(editwindow.EditWindow):
142 class ConsoleWidget(editwindow.EditWindow):
142 """ Specialized styled text control view for console-like workflow.
143 """ Specialized styled text control view for console-like workflow.
143
144
@@ -47,7 +47,7 b' class IPythonXController(WxController):'
47 self._input_state = 'subprocess'
47 self._input_state = 'subprocess'
48 self.write('\n', refresh=False)
48 self.write('\n', refresh=False)
49 self.capture_output()
49 self.capture_output()
50 self.ipython0.shell.exit()
50 self.ipython0.exit()
51 self.release_output()
51 self.release_output()
52 if not self.ipython0.exit_now:
52 if not self.ipython0.exit_now:
53 wx.CallAfter(self.new_prompt,
53 wx.CallAfter(self.new_prompt,
@@ -23,9 +23,8 b' import os'
23 import locale
23 import locale
24 from thread_ex import ThreadEx
24 from thread_ex import ThreadEx
25
25
26 import IPython
26 from IPython.core import iplib
27 from IPython.core import iplib, ipapp
27 from IPython.utils.io import Term
28 from IPython.utils import genutils
29
28
30 ##############################################################################
29 ##############################################################################
31 class _Helper(object):
30 class _Helper(object):
@@ -88,12 +87,10 b' class NonBlockingIPShell(object):'
88 via raise_exc()
87 via raise_exc()
89 '''
88 '''
90
89
91 def __init__(self, argv=[], user_ns={}, user_global_ns=None,
90 def __init__(self, user_ns={}, user_global_ns=None,
92 cin=None, cout=None, cerr=None,
91 cin=None, cout=None, cerr=None,
93 ask_exit_handler=None):
92 ask_exit_handler=None):
94 '''
93 '''
95 @param argv: Command line options for IPython
96 @type argv: list
97 @param user_ns: User namespace.
94 @param user_ns: User namespace.
98 @type user_ns: dictionary
95 @type user_ns: dictionary
99 @param user_global_ns: User global namespace.
96 @param user_global_ns: User global namespace.
@@ -111,9 +108,9 b' class NonBlockingIPShell(object):'
111 '''
108 '''
112 #ipython0 initialisation
109 #ipython0 initialisation
113 self._IP = None
110 self._IP = None
114 self.init_ipython0(argv, user_ns, user_global_ns,
111 self.init_ipython0(user_ns, user_global_ns,
115 cin, cout, cerr,
112 cin, cout, cerr,
116 ask_exit_handler)
113 ask_exit_handler)
117
114
118 #vars used by _execute
115 #vars used by _execute
119 self._iter_more = 0
116 self._iter_more = 0
@@ -131,7 +128,7 b' class NonBlockingIPShell(object):'
131 self._help_text = None
128 self._help_text = None
132 self._add_button = None
129 self._add_button = None
133
130
134 def init_ipython0(self, argv=[], user_ns={}, user_global_ns=None,
131 def init_ipython0(self, user_ns={}, user_global_ns=None,
135 cin=None, cout=None, cerr=None,
132 cin=None, cout=None, cerr=None,
136 ask_exit_handler=None):
133 ask_exit_handler=None):
137 ''' Initialize an ipython0 instance '''
134 ''' Initialize an ipython0 instance '''
@@ -141,27 +138,22 b' class NonBlockingIPShell(object):'
141 #only one instance can be instanciated else tehre will be
138 #only one instance can be instanciated else tehre will be
142 #cin/cout/cerr clash...
139 #cin/cout/cerr clash...
143 if cin:
140 if cin:
144 genutils.Term.cin = cin
141 Term.cin = cin
145 if cout:
142 if cout:
146 genutils.Term.cout = cout
143 Term.cout = cout
147 if cerr:
144 if cerr:
148 genutils.Term.cerr = cerr
145 Term.cerr = cerr
149
146
150 excepthook = sys.excepthook
147 excepthook = sys.excepthook
151
148
152 #Hack to save sys.displayhook, because ipython seems to overwrite it...
149 #Hack to save sys.displayhook, because ipython seems to overwrite it...
153 self.sys_displayhook_ori = sys.displayhook
150 self.sys_displayhook_ori = sys.displayhook
154
151 ipython0 = iplib.InteractiveShell(
155 ipython0 = ipapp.IPythonApp(argv,user_ns=user_ns,
152 parent=None, config=None,
156 user_global_ns=user_global_ns)
153 user_ns=user_ns,
157 ipython0.initialize()
154 user_global_ns=user_global_ns
158 self._IP = ipython0.shell
155 )
159
156 self._IP = ipython0
160 ## self._IP = IPython.shell.make_IPython(
161 ## argv,user_ns=user_ns,
162 ## user_global_ns=user_global_ns,
163 ## embedded=True,
164 ## shell_class=IPython.shell.InteractiveShell)
165
157
166 #we save ipython0 displayhook and we restore sys.displayhook
158 #we save ipython0 displayhook and we restore sys.displayhook
167 self.displayhook = sys.displayhook
159 self.displayhook = sys.displayhook
@@ -185,12 +177,10 b' class NonBlockingIPShell(object):'
185 #we replace the help command
177 #we replace the help command
186 self._IP.user_ns['help'] = _Helper(self._pager_help)
178 self._IP.user_ns['help'] = _Helper(self._pager_help)
187
179
188 #we disable cpase magic... until we found a way to use it properly.
180 #we disable cpaste magic... until we found a way to use it properly.
189 from IPython.core import ipapi
190 ip = ipapi.get()
191 def bypass_magic(self, arg):
181 def bypass_magic(self, arg):
192 print '%this magic is currently disabled.'
182 print '%this magic is currently disabled.'
193 ip.define_magic('cpaste', bypass_magic)
183 ipython0.define_magic('cpaste', bypass_magic)
194
184
195 import __builtin__
185 import __builtin__
196 __builtin__.raw_input = self._raw_input
186 __builtin__.raw_input = self._raw_input
@@ -471,7 +461,7 b' class NonBlockingIPShell(object):'
471 '''
461 '''
472
462
473 orig_stdout = sys.stdout
463 orig_stdout = sys.stdout
474 sys.stdout = genutils.Term.cout
464 sys.stdout = Term.cout
475 #self.sys_displayhook_ori = sys.displayhook
465 #self.sys_displayhook_ori = sys.displayhook
476 #sys.displayhook = self.displayhook
466 #sys.displayhook = self.displayhook
477
467
@@ -11,6 +11,7 b' __author__ = "Laurent Dufrechou"'
11 __email__ = "laurent.dufrechou _at_ gmail.com"
11 __email__ = "laurent.dufrechou _at_ gmail.com"
12 __license__ = "BSD"
12 __license__ = "BSD"
13 #-----------------------------------------
13 #-----------------------------------------
14
14 class IPythonHistoryPanel(wx.Panel):
15 class IPythonHistoryPanel(wx.Panel):
15
16
16 def __init__(self, parent,flt_empty=True,
17 def __init__(self, parent,flt_empty=True,
@@ -24,16 +24,25 b' The main classes in this module are:'
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Imports
27 # Warnings control
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 from cStringIO import StringIO
31 import sys
32 import warnings
30 import warnings
33
31
34 # from IPython.utils import growl
32 # Twisted generates annoying warnings with Python 2.6, as will do other code
35 # growl.start("IPython1 Client")
33 # that imports 'sets' as of today
34 warnings.filterwarnings('ignore', 'the sets module is deprecated',
35 DeprecationWarning )
36
37 # This one also comes from Twisted
38 warnings.filterwarnings('ignore', 'the sha module is deprecated',
39 DeprecationWarning)
40
41 #-----------------------------------------------------------------------------
42 # Imports
43 #-----------------------------------------------------------------------------
36
44
45 import sys
37
46
38 from twisted.internet import reactor
47 from twisted.internet import reactor
39 from twisted.internet.error import PotentialZombieWarning
48 from twisted.internet.error import PotentialZombieWarning
@@ -74,8 +83,6 b' rit.setDaemon(True)'
74 rit.start()
83 rit.start()
75
84
76
85
77
78
79 __all__ = [
86 __all__ = [
80 'MapTask',
87 'MapTask',
81 'StringTask',
88 'StringTask',
@@ -1,6 +1,4 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
1 # encoding: utf-8
3
4 """Facilities for handling client connections to the controller."""
2 """Facilities for handling client connections to the controller."""
5
3
6 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
@@ -20,6 +18,8 b' import os'
20 from IPython.kernel.fcutil import (
18 from IPython.kernel.fcutil import (
21 Tub,
19 Tub,
22 find_furl,
20 find_furl,
21 is_valid_furl,
22 is_valid_furl_file,
23 is_valid_furl_or_file,
23 is_valid_furl_or_file,
24 validate_furl_or_file,
24 validate_furl_or_file,
25 FURLError
25 FURLError
@@ -33,7 +33,7 b' from IPython.kernel.twistedutil import ('
33 sleep_deferred
33 sleep_deferred
34 )
34 )
35 from IPython.utils.importstring import import_item
35 from IPython.utils.importstring import import_item
36 from IPython.utils.genutils import get_ipython_dir
36 from IPython.utils.path import get_ipython_dir
37
37
38 from twisted.internet import defer
38 from twisted.internet import defer
39 from twisted.internet.defer import inlineCallbacks, returnValue
39 from twisted.internet.defer import inlineCallbacks, returnValue
@@ -66,18 +66,30 b' class AsyncClientConnector(object):'
66 def _find_furl(self, profile='default', cluster_dir=None,
66 def _find_furl(self, profile='default', cluster_dir=None,
67 furl_or_file=None, furl_file_name=None,
67 furl_or_file=None, furl_file_name=None,
68 ipython_dir=None):
68 ipython_dir=None):
69 """Find a FURL file by profile+ipython_dir or cluster dir.
69 """Find a FURL file.
70
71 If successful, this returns a FURL file that exists on the file
72 system. The contents of the file have not been checked though. This
73 is because we often have to deal with FURL file whose buffers have
74 not been flushed.
70
75
71 This raises an :exc:`~IPython.kernel.fcutil.FURLError` exception
76 This raises an :exc:`~IPython.kernel.fcutil.FURLError` exception
72 if a FURL file can't be found.
77 if a FURL file can't be found.
78
79 This tries the following:
80
81 1. By the name ``furl_or_file``.
82 2. By ``cluster_dir`` and ``furl_file_name``.
83 3. By cluster profile with a default of ``default``. This uses
84 ``ipython_dir``.
73 """
85 """
74 # Try by furl_or_file
86 # Try by furl_or_file
75 if furl_or_file is not None:
87 if furl_or_file is not None:
76 validate_furl_or_file(furl_or_file)
88 if is_valid_furl_or_file(furl_or_file):
77 return furl_or_file
89 return furl_or_file
78
90
79 if furl_file_name is None:
91 if furl_file_name is None:
80 raise FURLError('A furl_file_name must be provided')
92 raise FURLError('A furl_file_name must be provided if furl_or_file is not')
81
93
82 # Try by cluster_dir
94 # Try by cluster_dir
83 if cluster_dir is not None:
95 if cluster_dir is not None:
@@ -151,7 +163,7 b' class AsyncClientConnector(object):'
151 The full path to a cluster directory. This is useful if profiles
163 The full path to a cluster directory. This is useful if profiles
152 are not being used.
164 are not being used.
153 furl_or_file : str
165 furl_or_file : str
154 A furl or a filename containing a FURLK. This is useful if you
166 A furl or a filename containing a FURL. This is useful if you
155 simply know the location of the FURL file.
167 simply know the location of the FURL file.
156 ipython_dir : str
168 ipython_dir : str
157 The location of the ipython_dir if different from the default.
169 The location of the ipython_dir if different from the default.
@@ -193,7 +205,7 b' class AsyncClientConnector(object):'
193 The full path to a cluster directory. This is useful if profiles
205 The full path to a cluster directory. This is useful if profiles
194 are not being used.
206 are not being used.
195 furl_or_file : str
207 furl_or_file : str
196 A furl or a filename containing a FURLK. This is useful if you
208 A furl or a filename containing a FURL. This is useful if you
197 simply know the location of the FURL file.
209 simply know the location of the FURL file.
198 ipython_dir : str
210 ipython_dir : str
199 The location of the ipython_dir if different from the default.
211 The location of the ipython_dir if different from the default.
@@ -259,6 +271,9 b' class AsyncClientConnector(object):'
259 profile, cluster_dir, furl_or_file,
271 profile, cluster_dir, furl_or_file,
260 furl_file_name, ipython_dir
272 furl_file_name, ipython_dir
261 )
273 )
274 # If this succeeds, we know the furl file exists and has a .furl
275 # extension, but it could still be empty. That is checked each
276 # connection attempt.
262 except FURLError:
277 except FURLError:
263 return defer.fail(failure.Failure())
278 return defer.fail(failure.Failure())
264
279
@@ -349,7 +364,7 b' class ClientConnector(object):'
349 The full path to a cluster directory. This is useful if profiles
364 The full path to a cluster directory. This is useful if profiles
350 are not being used.
365 are not being used.
351 furl_or_file : str
366 furl_or_file : str
352 A furl or a filename containing a FURLK. This is useful if you
367 A furl or a filename containing a FURL. This is useful if you
353 simply know the location of the FURL file.
368 simply know the location of the FURL file.
354 ipython_dir : str
369 ipython_dir : str
355 The location of the ipython_dir if different from the default.
370 The location of the ipython_dir if different from the default.
@@ -390,7 +405,7 b' class ClientConnector(object):'
390 The full path to a cluster directory. This is useful if profiles
405 The full path to a cluster directory. This is useful if profiles
391 are not being used.
406 are not being used.
392 furl_or_file : str
407 furl_or_file : str
393 A furl or a filename containing a FURLK. This is useful if you
408 A furl or a filename containing a FURL. This is useful if you
394 simply know the location of the FURL file.
409 simply know the location of the FURL file.
395 ipython_dir : str
410 ipython_dir : str
396 The location of the ipython_dir if different from the default.
411 The location of the ipython_dir if different from the default.
@@ -15,7 +15,7 b' __docformat__ = "restructuredtext en"'
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 from zope.interface import Interface, implements
18 from zope.interface import Interface
19
19
20 class IFCClientInterfaceProvider(Interface):
20 class IFCClientInterfaceProvider(Interface):
21
21
@@ -24,13 +24,16 b' import warnings'
24
24
25 from twisted.python import log
25 from twisted.python import log
26
26
27 from IPython.core import release
28 from IPython.config.loader import PyFileConfigLoader
27 from IPython.config.loader import PyFileConfigLoader
29 from IPython.core.application import Application
28 from IPython.core.application import Application, BaseAppConfigLoader
30 from IPython.core.component import Component
29 from IPython.core.component import Component
31 from IPython.utils.genutils import get_ipython_dir, get_ipython_package_dir
30 from IPython.core.crashhandler import CrashHandler
32 from IPython.utils.traitlets import Unicode, Bool
31 from IPython.core import release
33 from IPython.utils import genutils
32 from IPython.utils.path import (
33 get_ipython_package_dir,
34 expand_path
35 )
36 from IPython.utils.traitlets import Unicode
34
37
35 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
36 # Warnings control
39 # Warnings control
@@ -45,7 +48,7 b" warnings.filterwarnings('ignore', 'the sha module is deprecated',"
45 DeprecationWarning)
48 DeprecationWarning)
46
49
47 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
48 # Classes and functions
51 # Module errors
49 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
50
53
51 class ClusterDirError(Exception):
54 class ClusterDirError(Exception):
@@ -56,6 +59,10 b' class PIDFileError(Exception):'
56 pass
59 pass
57
60
58
61
62 #-----------------------------------------------------------------------------
63 # Class for managing cluster directories
64 #-----------------------------------------------------------------------------
65
59 class ClusterDir(Component):
66 class ClusterDir(Component):
60 """An object to manage the cluster directory and its resources.
67 """An object to manage the cluster directory and its resources.
61
68
@@ -225,43 +232,109 b' class ClusterDir(Component):'
225 The path of the cluster directory. This is expanded using
232 The path of the cluster directory. This is expanded using
226 :func:`IPython.utils.genutils.expand_path`.
233 :func:`IPython.utils.genutils.expand_path`.
227 """
234 """
228 cluster_dir = genutils.expand_path(cluster_dir)
235 cluster_dir = expand_path(cluster_dir)
229 if not os.path.isdir(cluster_dir):
236 if not os.path.isdir(cluster_dir):
230 raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
237 raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
231 return ClusterDir(cluster_dir)
238 return ClusterDir(cluster_dir)
232
239
233
240
234 # Default command line options for IPython cluster applications.
241 #-----------------------------------------------------------------------------
235 cl_args = (
242 # Command line options
236 (('--ipython-dir',), dict(
243 #-----------------------------------------------------------------------------
237 dest='Global.ipython_dir',type=unicode,
244
238 help='Set to override default location of Global.ipython_dir.',
245 class ClusterDirConfigLoader(BaseAppConfigLoader):
239 metavar='Global.ipython_dir') ),
246
240 (('-p', '--profile',), dict(
247 def _add_cluster_profile(self, parser):
241 dest='Global.profile',type=unicode,
248 paa = parser.add_argument
242 help=
249 paa('-p', '--profile',
243 """The string name of the profile to be used. This determines the name
250 dest='Global.profile',type=unicode,
244 of the cluster dir as: cluster_<profile>. The default profile is named
251 help=
245 'default'. The cluster directory is resolve this way if the
252 """The string name of the profile to be used. This determines the name
246 --cluster-dir option is not used.""",
253 of the cluster dir as: cluster_<profile>. The default profile is named
247 metavar='Global.profile') ),
254 'default'. The cluster directory is resolve this way if the
248 (('--cluster-dir',), dict(
255 --cluster-dir option is not used.""",
249 dest='Global.cluster_dir',type=unicode,
256 metavar='Global.profile')
250 help="""Set the cluster dir. This overrides the logic used by the
257
251 --profile option.""",
258 def _add_cluster_dir(self, parser):
252 metavar='Global.cluster_dir') ),
259 paa = parser.add_argument
253 (('--work-dir',), dict(
260 paa('--cluster-dir',
254 dest='Global.work_dir',type=unicode,
261 dest='Global.cluster_dir',type=unicode,
255 help='Set the working dir for the process.',
262 help="""Set the cluster dir. This overrides the logic used by the
256 metavar='Global.work_dir') ),
263 --profile option.""",
257 (('--clean-logs',), dict(
264 metavar='Global.cluster_dir')
258 dest='Global.clean_logs', action='store_true',
265
259 help='Delete old log flies before starting.') ),
266 def _add_work_dir(self, parser):
260 (('--no-clean-logs',), dict(
267 paa = parser.add_argument
261 dest='Global.clean_logs', action='store_false',
268 paa('--work-dir',
262 help="Don't Delete old log flies before starting.") ),
269 dest='Global.work_dir',type=unicode,
263 )
270 help='Set the working dir for the process.',
271 metavar='Global.work_dir')
272
273 def _add_clean_logs(self, parser):
274 paa = parser.add_argument
275 paa('--clean-logs',
276 dest='Global.clean_logs', action='store_true',
277 help='Delete old log flies before starting.')
278
279 def _add_no_clean_logs(self, parser):
280 paa = parser.add_argument
281 paa('--no-clean-logs',
282 dest='Global.clean_logs', action='store_false',
283 help="Don't Delete old log flies before starting.")
284
285 def _add_arguments(self):
286 super(ClusterDirConfigLoader, self)._add_arguments()
287 self._add_cluster_profile(self.parser)
288 self._add_cluster_dir(self.parser)
289 self._add_work_dir(self.parser)
290 self._add_clean_logs(self.parser)
291 self._add_no_clean_logs(self.parser)
292
293
294 #-----------------------------------------------------------------------------
295 # Crash handler for this application
296 #-----------------------------------------------------------------------------
297
298
299 _message_template = """\
300 Oops, $self.app_name crashed. We do our best to make it stable, but...
301
302 A crash report was automatically generated with the following information:
303 - A verbatim copy of the crash traceback.
304 - Data on your current $self.app_name configuration.
264
305
306 It was left in the file named:
307 \t'$self.crash_report_fname'
308 If you can email this file to the developers, the information in it will help
309 them in understanding and correcting the problem.
310
311 You can mail it to: $self.contact_name at $self.contact_email
312 with the subject '$self.app_name Crash Report'.
313
314 If you want to do it now, the following command will work (under Unix):
315 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
316
317 To ensure accurate tracking of this issue, please file a report about it at:
318 $self.bug_tracker
319 """
320
321 class ClusterDirCrashHandler(CrashHandler):
322 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
323
324 message_template = _message_template
325
326 def __init__(self, app):
327 contact_name = release.authors['Brian'][0]
328 contact_email = release.authors['Brian'][1]
329 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
330 super(ClusterDirCrashHandler,self).__init__(
331 app, contact_name, contact_email, bug_tracker
332 )
333
334
335 #-----------------------------------------------------------------------------
336 # Main application
337 #-----------------------------------------------------------------------------
265
338
266 class ApplicationWithClusterDir(Application):
339 class ApplicationWithClusterDir(Application):
267 """An application that puts everything into a cluster directory.
340 """An application that puts everything into a cluster directory.
@@ -282,10 +355,10 b' class ApplicationWithClusterDir(Application):'
282 dir and named the value of the ``config_file_name`` class attribute.
355 dir and named the value of the ``config_file_name`` class attribute.
283 """
356 """
284
357
358 command_line_loader = ClusterDirConfigLoader
359 crash_handler_class = ClusterDirCrashHandler
285 auto_create_cluster_dir = True
360 auto_create_cluster_dir = True
286
361
287 cl_arguments = Application.cl_arguments + cl_args
288
289 def create_default_config(self):
362 def create_default_config(self):
290 super(ApplicationWithClusterDir, self).create_default_config()
363 super(ApplicationWithClusterDir, self).create_default_config()
291 self.default_config.Global.profile = u'default'
364 self.default_config.Global.profile = u'default'
@@ -316,7 +389,7 b' class ApplicationWithClusterDir(Application):'
316 cluster_dir = self.command_line_config.Global.cluster_dir
389 cluster_dir = self.command_line_config.Global.cluster_dir
317 except AttributeError:
390 except AttributeError:
318 cluster_dir = self.default_config.Global.cluster_dir
391 cluster_dir = self.default_config.Global.cluster_dir
319 cluster_dir = genutils.expand_path(cluster_dir)
392 cluster_dir = expand_path(cluster_dir)
320 try:
393 try:
321 self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
394 self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
322 except ClusterDirError:
395 except ClusterDirError:
@@ -368,15 +441,17 b' class ApplicationWithClusterDir(Application):'
368 def find_config_file_name(self):
441 def find_config_file_name(self):
369 """Find the config file name for this application."""
442 """Find the config file name for this application."""
370 # For this type of Application it should be set as a class attribute.
443 # For this type of Application it should be set as a class attribute.
371 if not hasattr(self, 'config_file_name'):
444 if not hasattr(self, 'default_config_file_name'):
372 self.log.critical("No config filename found")
445 self.log.critical("No config filename found")
446 else:
447 self.config_file_name = self.default_config_file_name
373
448
374 def find_config_file_paths(self):
449 def find_config_file_paths(self):
375 # Include our own config directory last, so that users can still find
450 # Set the search path to to the cluster directory. We should NOT
376 # our shipped copies of builtin config files even if they don't have
451 # include IPython.config.default here as the default config files
377 # them in their ipython cluster directory.
452 # are ALWAYS automatically moved to the cluster directory.
378 conf_dir = os.path.join(get_ipython_package_dir(), 'config', 'default')
453 conf_dir = os.path.join(get_ipython_package_dir(), 'config', 'default')
379 self.config_file_paths = (self.cluster_dir, conf_dir)
454 self.config_file_paths = (self.cluster_dir,)
380
455
381 def pre_construct(self):
456 def pre_construct(self):
382 # The log and security dirs were set earlier, but here we put them
457 # The log and security dirs were set earlier, but here we put them
@@ -389,7 +464,7 b' class ApplicationWithClusterDir(Application):'
389 pdir = self.cluster_dir_obj.pid_dir
464 pdir = self.cluster_dir_obj.pid_dir
390 self.pid_dir = config.Global.pid_dir = pdir
465 self.pid_dir = config.Global.pid_dir = pdir
391 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
466 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
392 config.Global.work_dir = unicode(genutils.expand_path(config.Global.work_dir))
467 config.Global.work_dir = unicode(expand_path(config.Global.work_dir))
393 # Change to the working directory. We do this just before construct
468 # Change to the working directory. We do this just before construct
394 # is called so all the components there have the right working dir.
469 # is called so all the components there have the right working dir.
395 self.to_work_dir()
470 self.to_work_dir()
@@ -461,3 +536,4 b' class ApplicationWithClusterDir(Application):'
461 return pid
536 return pid
462 else:
537 else:
463 raise PIDFileError('pid file not found: %s' % pid_file)
538 raise PIDFileError('pid file not found: %s' % pid_file)
539
@@ -37,20 +37,18 b' __docformat__ = "restructuredtext en"'
37 # Imports
37 # Imports
38 #-------------------------------------------------------------------------------
38 #-------------------------------------------------------------------------------
39
39
40 import os, sys
40 import os
41
41
42 from twisted.application import service
42 from twisted.application import service
43 from twisted.internet import defer, reactor
43 from twisted.python import log
44 from twisted.python import log, components
45 from zope.interface import Interface, implements, Attribute
44 from zope.interface import Interface, implements, Attribute
46 import zope.interface as zi
47
45
48 from IPython.kernel.engineservice import \
46 from IPython.kernel.engineservice import \
49 IEngineCore, \
47 IEngineCore, \
50 IEngineSerialized, \
48 IEngineSerialized, \
51 IEngineQueued
49 IEngineQueued
52
50
53 from IPython.utils.genutils import get_ipython_dir
51 from IPython.utils.path import get_ipython_dir
54 from IPython.kernel import codeutil
52 from IPython.kernel import codeutil
55
53
56 #-------------------------------------------------------------------------------
54 #-------------------------------------------------------------------------------
@@ -21,6 +21,8 b' __docformat__ = "restructuredtext en"'
21
21
22 # Required modules
22 # Required modules
23 import __builtin__
23 import __builtin__
24 import os
25 import re
24 import socket
26 import socket
25 import sys
27 import sys
26
28
@@ -30,7 +32,8 b' from IPython.external.Itpl import ItplNS'
30 from IPython.utils import coloransi
32 from IPython.utils import coloransi
31 from IPython.core import release
33 from IPython.core import release
32 from IPython.core.error import TryNext
34 from IPython.core.error import TryNext
33 from IPython.utils.genutils import *
35 from IPython.utils.io import Term
36 from IPython.utils.warn import warn
34 import IPython.utils.generics
37 import IPython.utils.generics
35
38
36 #****************************************************************************
39 #****************************************************************************
@@ -240,7 +243,7 b' class BasePrompt(object):'
240 This must be called every time the color settings change, because the
243 This must be called every time the color settings change, because the
241 prompt_specials global may have changed."""
244 prompt_specials global may have changed."""
242
245
243 import os,time # needed in locals for prompt string handling
246 import os, time # needed in locals for prompt string handling
244 loc = locals()
247 loc = locals()
245 self.p_str = ItplNS('%s%s%s' %
248 self.p_str = ItplNS('%s%s%s' %
246 ('${self.sep}${self.col_p}',
249 ('${self.sep}${self.col_p}',
@@ -22,15 +22,15 b' import sys'
22
22
23 from twisted.trial import unittest
23 from twisted.trial import unittest
24
24
25 from IPython.testing import decorators_trial as dec
26
25 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
26 # Tests
28 # Tests
27 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
28
30
29 class TestRedirector(unittest.TestCase):
31 class TestRedirector(unittest.TestCase):
30
32
31 if sys.platform == 'win32':
33 @dec.skip_win32
32 skip = True
33
34 def test_redirector(self):
34 def test_redirector(self):
35 """Checks that the redirector can be used to do synchronous capture.
35 """Checks that the redirector can be used to do synchronous capture.
36 """
36 """
@@ -51,6 +51,7 b' class TestRedirector(unittest.TestCase):'
51 result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10))
51 result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10))
52 self.assertEquals(result1, result2)
52 self.assertEquals(result1, result2)
53
53
54 @dec.skip_win32
54 def test_redirector_output_trap(self):
55 def test_redirector_output_trap(self):
55 """Check the greedy trapping behavior of the traps.
56 """Check the greedy trapping behavior of the traps.
56
57
@@ -17,8 +17,7 b''
17 import os
17 import os
18 import cPickle as pickle
18 import cPickle as pickle
19
19
20 from twisted.python import log, failure
20 from twisted.python import log
21 from twisted.internet import defer
22 from twisted.internet.defer import inlineCallbacks, returnValue
21 from twisted.internet.defer import inlineCallbacks, returnValue
23
22
24 from IPython.kernel.fcutil import find_furl, validate_furl_or_file
23 from IPython.kernel.fcutil import find_furl, validate_furl_or_file
@@ -19,40 +19,36 b' __docformat__ = "restructuredtext en"'
19 # Imports
19 # Imports
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21
21
22 import os, time
23 import cPickle as pickle
22 import cPickle as pickle
24
23
25 from twisted.python import components, log, failure
24 from twisted.python import components, log, failure
26 from twisted.python.failure import Failure
25 from twisted.internet import defer, threads
27 from twisted.internet import defer, reactor, threads
26 from zope.interface import Interface, implements
28 from twisted.internet.interfaces import IProtocolFactory
29 from zope.interface import Interface, implements, Attribute
30
27
31 from twisted.internet.base import DelayedCall
28 from twisted.internet.base import DelayedCall
32 DelayedCall.debug = True
29 DelayedCall.debug = True
33
30
34 from foolscap import Referenceable, DeadReferenceError
31 try:
32 from foolscap.api import Referenceable, DeadReferenceError
33 except ImportError:
34 from foolscap import Referenceable, DeadReferenceError
35 from foolscap.referenceable import RemoteReference
35 from foolscap.referenceable import RemoteReference
36
36
37 from IPython.kernel.pbutil import packageFailure, unpackageFailure
37 from IPython.kernel.pbutil import packageFailure, unpackageFailure
38 from IPython.kernel.util import printer
39 from IPython.kernel.twistedutil import gatherBoth
40 from IPython.kernel import newserialized
41 from IPython.kernel.error import ProtocolError
42 from IPython.kernel import controllerservice
43 from IPython.kernel.controllerservice import IControllerBase
38 from IPython.kernel.controllerservice import IControllerBase
44 from IPython.kernel.engineservice import \
39 from IPython.kernel.engineservice import (
45 IEngineBase, \
40 IEngineBase,
46 IEngineQueued, \
41 IEngineQueued,
47 EngineService, \
48 StrictDict
42 StrictDict
49 from IPython.kernel.pickleutil import \
43 )
50 can, \
44 from IPython.kernel.pickleutil import (
51 canDict, \
45 can,
52 canSequence, \
46 canDict,
53 uncan, \
47 canSequence,
54 uncanDict, \
48 uncan,
49 uncanDict,
55 uncanSequence
50 uncanSequence
51 )
56
52
57
53
58 #-------------------------------------------------------------------------------
54 #-------------------------------------------------------------------------------
@@ -17,6 +17,9 b' __test__ = {}'
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18 # Imports
18 # Imports
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20
21 from twisted.python import failure
22
20 from IPython.kernel.core import error
23 from IPython.kernel.core import error
21
24
22 #-------------------------------------------------------------------------------
25 #-------------------------------------------------------------------------------
@@ -26,6 +29,7 b' from IPython.kernel.core import error'
26 class KernelError(error.IPythonError):
29 class KernelError(error.IPythonError):
27 pass
30 pass
28
31
32
29 class NotDefined(KernelError):
33 class NotDefined(KernelError):
30 def __init__(self, name):
34 def __init__(self, name):
31 self.name = name
35 self.name = name
@@ -36,78 +40,102 b' class NotDefined(KernelError):'
36
40
37 __str__ = __repr__
41 __str__ = __repr__
38
42
43
39 class QueueCleared(KernelError):
44 class QueueCleared(KernelError):
40 pass
45 pass
41
46
47
42 class IdInUse(KernelError):
48 class IdInUse(KernelError):
43 pass
49 pass
44
50
51
45 class ProtocolError(KernelError):
52 class ProtocolError(KernelError):
46 pass
53 pass
47
54
55
48 class ConnectionError(KernelError):
56 class ConnectionError(KernelError):
49 pass
57 pass
50
58
59
51 class InvalidEngineID(KernelError):
60 class InvalidEngineID(KernelError):
52 pass
61 pass
53
62
63
54 class NoEnginesRegistered(KernelError):
64 class NoEnginesRegistered(KernelError):
55 pass
65 pass
56
66
67
57 class InvalidClientID(KernelError):
68 class InvalidClientID(KernelError):
58 pass
69 pass
59
70
71
60 class InvalidDeferredID(KernelError):
72 class InvalidDeferredID(KernelError):
61 pass
73 pass
62
74
75
63 class SerializationError(KernelError):
76 class SerializationError(KernelError):
64 pass
77 pass
65
78
79
66 class MessageSizeError(KernelError):
80 class MessageSizeError(KernelError):
67 pass
81 pass
68
82
83
69 class PBMessageSizeError(MessageSizeError):
84 class PBMessageSizeError(MessageSizeError):
70 pass
85 pass
71
86
87
72 class ResultNotCompleted(KernelError):
88 class ResultNotCompleted(KernelError):
73 pass
89 pass
74
90
91
75 class ResultAlreadyRetrieved(KernelError):
92 class ResultAlreadyRetrieved(KernelError):
76 pass
93 pass
77
94
78 class ClientError(KernelError):
95 class ClientError(KernelError):
79 pass
96 pass
80
97
98
81 class TaskAborted(KernelError):
99 class TaskAborted(KernelError):
82 pass
100 pass
83
101
102
84 class TaskTimeout(KernelError):
103 class TaskTimeout(KernelError):
85 pass
104 pass
86
105
106
87 class NotAPendingResult(KernelError):
107 class NotAPendingResult(KernelError):
88 pass
108 pass
89
109
110
90 class UnpickleableException(KernelError):
111 class UnpickleableException(KernelError):
91 pass
112 pass
92
113
114
93 class AbortedPendingDeferredError(KernelError):
115 class AbortedPendingDeferredError(KernelError):
94 pass
116 pass
95
117
118
96 class InvalidProperty(KernelError):
119 class InvalidProperty(KernelError):
97 pass
120 pass
98
121
122
99 class MissingBlockArgument(KernelError):
123 class MissingBlockArgument(KernelError):
100 pass
124 pass
101
125
126
102 class StopLocalExecution(KernelError):
127 class StopLocalExecution(KernelError):
103 pass
128 pass
104
129
130
105 class SecurityError(KernelError):
131 class SecurityError(KernelError):
106 pass
132 pass
107
133
134
108 class FileTimeoutError(KernelError):
135 class FileTimeoutError(KernelError):
109 pass
136 pass
110
137
138
111 class TaskRejectError(KernelError):
139 class TaskRejectError(KernelError):
112 """Exception to raise when a task should be rejected by an engine.
140 """Exception to raise when a task should be rejected by an engine.
113
141
@@ -122,6 +150,7 b' class TaskRejectError(KernelError):'
122 properties don't have to be managed or tested by the controller.
150 properties don't have to be managed or tested by the controller.
123 """
151 """
124
152
153
125 class CompositeError(KernelError):
154 class CompositeError(KernelError):
126 def __init__(self, message, elist):
155 def __init__(self, message, elist):
127 Exception.__init__(self, *(message, elist))
156 Exception.__init__(self, *(message, elist))
@@ -176,9 +205,8 b' class CompositeError(KernelError):'
176 else:
205 else:
177 raise et, ev, etb
206 raise et, ev, etb
178
207
179 def collect_exceptions(rlist, method):
180 from twisted.python import failure
181
208
209 def collect_exceptions(rlist, method):
182 elist = []
210 elist = []
183 for r in rlist:
211 for r in rlist:
184 if isinstance(r, failure.Failure):
212 if isinstance(r, failure.Failure):
@@ -203,5 +231,4 b' def collect_exceptions(rlist, method):'
203 raise CompositeError(msg, elist)
231 raise CompositeError(msg, elist)
204 except CompositeError, e:
232 except CompositeError, e:
205 raise e
233 raise e
206
207
234
@@ -23,16 +23,19 b' import tempfile'
23 from twisted.internet import reactor, defer
23 from twisted.internet import reactor, defer
24 from twisted.python import log
24 from twisted.python import log
25
25
26 from foolscap import Tub, UnauthenticatedTub
26 import foolscap
27 try:
28 from foolscap.api import Tub, UnauthenticatedTub
29 except ImportError:
30 from foolscap import Tub, UnauthenticatedTub
27
31
28 from IPython.config.loader import Config
32 from IPython.config.loader import Config
29
30 from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory
33 from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory
31
32 from IPython.kernel.error import SecurityError
34 from IPython.kernel.error import SecurityError
33
35
34 from IPython.utils.traitlets import Int, Str, Bool, Instance
35 from IPython.utils.importstring import import_item
36 from IPython.utils.importstring import import_item
37 from IPython.utils.path import expand_path
38 from IPython.utils.traitlets import Int, Str, Bool, Instance
36
39
37 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
38 # Code
41 # Code
@@ -57,17 +60,17 b' class FURLError(Exception):'
57
60
58 def check_furl_file_security(furl_file, secure):
61 def check_furl_file_security(furl_file, secure):
59 """Remove the old furl_file if changing security modes."""
62 """Remove the old furl_file if changing security modes."""
63 furl_file = expand_path(furl_file)
60 if os.path.isfile(furl_file):
64 if os.path.isfile(furl_file):
61 f = open(furl_file, 'r')
65 with open(furl_file, 'r') as f:
62 oldfurl = f.read().strip()
66 oldfurl = f.read().strip()
63 f.close()
64 if (oldfurl.startswith('pb://') and not secure) or (oldfurl.startswith('pbu://') and secure):
67 if (oldfurl.startswith('pb://') and not secure) or (oldfurl.startswith('pbu://') and secure):
65 os.remove(furl_file)
68 os.remove(furl_file)
66
69
67
70
68 def is_secure(furl):
71 def is_secure(furl):
69 """Is the given FURL secure or not."""
72 """Is the given FURL secure or not."""
70 if is_valid(furl):
73 if is_valid_furl(furl):
71 if furl.startswith("pb://"):
74 if furl.startswith("pb://"):
72 return True
75 return True
73 elif furl.startswith("pbu://"):
76 elif furl.startswith("pbu://"):
@@ -76,26 +79,45 b' def is_secure(furl):'
76 raise FURLError("invalid FURL: %s" % furl)
79 raise FURLError("invalid FURL: %s" % furl)
77
80
78
81
79 def is_valid(furl):
82 def is_valid_furl(furl):
80 """Is the str a valid FURL or not."""
83 """Is the str a valid FURL or not."""
81 if isinstance(furl, str):
84 if isinstance(furl, str):
82 if furl.startswith("pb://") or furl.startswith("pbu://"):
85 if furl.startswith("pb://") or furl.startswith("pbu://"):
83 return True
86 return True
87 else:
88 return False
84 else:
89 else:
85 return False
90 return False
86
91
87
92
93 def is_valid_furl_file(furl_or_file):
94 """See if furl_or_file exists and contains a valid FURL.
95
96 This doesn't try to read the contents because often we have to validate
97 FURL files that are created, but don't yet have a FURL written to them.
98 """
99 if isinstance(furl_or_file, (str, unicode)):
100 path, furl_filename = os.path.split(furl_or_file)
101 if os.path.isdir(path) and furl_filename.endswith('.furl'):
102 return True
103 return False
104
105
88 def find_furl(furl_or_file):
106 def find_furl(furl_or_file):
89 """Find, validate and return a FURL in a string or file."""
107 """Find, validate and return a FURL in a string or file.
90 if isinstance(furl_or_file, str):
108
91 if is_valid(furl_or_file):
109 This calls :func:`IPython.utils.path.expand_path` on the argument to
92 return furl_or_file
110 properly handle ``~`` and ``$`` variables in the path.
93 if os.path.isfile(furl_or_file):
111 """
112 if is_valid_furl(furl_or_file):
113 return furl_or_file
114 furl_or_file = expand_path(furl_or_file)
115 if is_valid_furl_file(furl_or_file):
94 with open(furl_or_file, 'r') as f:
116 with open(furl_or_file, 'r') as f:
95 furl = f.read().strip()
117 furl = f.read().strip()
96 if is_valid(furl):
118 if is_valid_furl(furl):
97 return furl
119 return furl
98 raise FURLError("Not a valid FURL or FURL file: %s" % furl_or_file)
120 raise FURLError("Not a valid FURL or FURL file: %r" % furl_or_file)
99
121
100
122
101 def is_valid_furl_or_file(furl_or_file):
123 def is_valid_furl_or_file(furl_or_file):
@@ -106,17 +128,14 b' def is_valid_furl_or_file(furl_or_file):'
106 if the FURL file exists or to read its contents. This is useful for
128 if the FURL file exists or to read its contents. This is useful for
107 cases where auto re-connection is being used.
129 cases where auto re-connection is being used.
108 """
130 """
109 if isinstance(furl_or_file, str):
131 if is_valid_furl(furl_or_file) or is_valid_furl_file(furl_or_file):
110 if is_valid(furl_or_file):
132 return True
111 return True
133 else:
112 if isinstance(furl_or_file, (str, unicode)):
134 return False
113 path, furl_filename = os.path.split(furl_or_file)
114 if os.path.isdir(path) and furl_filename.endswith('.furl'):
115 return True
116 return False
117
135
118
136
119 def validate_furl_or_file(furl_or_file):
137 def validate_furl_or_file(furl_or_file):
138 """Like :func:`is_valid_furl_or_file`, but raises an error."""
120 if not is_valid_furl_or_file(furl_or_file):
139 if not is_valid_furl_or_file(furl_or_file):
121 raise FURLError('Not a valid FURL or FURL file: %r' % furl_or_file)
140 raise FURLError('Not a valid FURL or FURL file: %r' % furl_or_file)
122
141
@@ -265,9 +284,13 b' class FCServiceFactory(AdaptedConfiguredObjectFactory):'
265 """Register the reference with the FURL file.
284 """Register the reference with the FURL file.
266
285
267 The FURL file is created and then moved to make sure that when the
286 The FURL file is created and then moved to make sure that when the
268 file appears, the buffer has been flushed and the file closed.
287 file appears, the buffer has been flushed and the file closed. This
288 is not done if we are re-using FURLS however.
269 """
289 """
270 temp_furl_file = get_temp_furlfile(furl_file)
290 if self.reuse_furls:
271 self.tub.registerReference(ref, furlFile=temp_furl_file)
291 self.tub.registerReference(ref, furlFile=furl_file)
272 os.rename(temp_furl_file, furl_file)
292 else:
293 temp_furl_file = get_temp_furlfile(furl_file)
294 self.tub.registerReference(ref, furlFile=temp_furl_file)
295 os.rename(temp_furl_file, furl_file)
273
296
@@ -22,161 +22,203 b' import signal'
22 if os.name=='posix':
22 if os.name=='posix':
23 from twisted.scripts._twistd_unix import daemonize
23 from twisted.scripts._twistd_unix import daemonize
24
24
25 from IPython.core import release
25 from twisted.internet import reactor, defer
26 from IPython.external.argparse import ArgumentParser
26 from twisted.python import log, failure
27 from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
27
28 from IPython.utils.importstring import import_item
29
28
29 from IPython.external.argparse import ArgumentParser, SUPPRESS
30 from IPython.utils.importstring import import_item
30 from IPython.kernel.clusterdir import (
31 from IPython.kernel.clusterdir import (
31 ApplicationWithClusterDir, ClusterDirError, PIDFileError
32 ApplicationWithClusterDir, ClusterDirConfigLoader,
33 ClusterDirError, PIDFileError
32 )
34 )
33
35
34 from twisted.internet import reactor, defer
35 from twisted.python import log, failure
36
37
36
38 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
39 # The ipcluster application
38 # Module level variables
40 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
41
40
42
41
42 default_config_file_name = u'ipcluster_config.py'
43
44
45 _description = """\
46 Start an IPython cluster for parallel computing.\n\n
47
48 An IPython cluster consists of 1 controller and 1 or more engines.
49 This command automates the startup of these processes using a wide
50 range of startup methods (SSH, local processes, PBS, mpiexec,
51 Windows HPC Server 2008). To start a cluster with 4 engines on your
52 local host simply do 'ipcluster start -n 4'. For more complex usage
53 you will typically do 'ipcluster create -p mycluster', then edit
54 configuration files, followed by 'ipcluster start -p mycluster -n 4'.
55 """
56
57
43 # Exit codes for ipcluster
58 # Exit codes for ipcluster
44
59
45 # This will be the exit code if the ipcluster appears to be running because
60 # This will be the exit code if the ipcluster appears to be running because
46 # a .pid file exists
61 # a .pid file exists
47 ALREADY_STARTED = 10
62 ALREADY_STARTED = 10
48
63
64
49 # This will be the exit code if ipcluster stop is run, but there is not .pid
65 # This will be the exit code if ipcluster stop is run, but there is not .pid
50 # file to be found.
66 # file to be found.
51 ALREADY_STOPPED = 11
67 ALREADY_STOPPED = 11
52
68
53
69
54 class IPClusterCLLoader(ArgParseConfigLoader):
70 #-----------------------------------------------------------------------------
71 # Command line options
72 #-----------------------------------------------------------------------------
73
55
74
56 def _add_other_arguments(self):
75 class IPClusterAppConfigLoader(ClusterDirConfigLoader):
76
77 def _add_arguments(self):
78 # Don't call ClusterDirConfigLoader._add_arguments as we don't want
79 # its defaults on self.parser. Instead, we will put those on
80 # default options on our subparsers.
81
57 # This has all the common options that all subcommands use
82 # This has all the common options that all subcommands use
58 parent_parser1 = ArgumentParser(add_help=False,
83 parent_parser1 = ArgumentParser(
59 argument_default=NoConfigDefault)
84 add_help=False,
60 parent_parser1.add_argument('--ipython-dir',
85 argument_default=SUPPRESS
61 dest='Global.ipython_dir',type=unicode,
86 )
62 help='Set to override default location of Global.ipython_dir.',
87 self._add_ipython_dir(parent_parser1)
63 metavar='Global.ipython_dir')
88 self._add_log_level(parent_parser1)
64 parent_parser1.add_argument('--log-level',
65 dest="Global.log_level",type=int,
66 help='Set the log level (0,10,20,30,40,50). Default is 30.',
67 metavar='Global.log_level')
68
89
69 # This has all the common options that other subcommands use
90 # This has all the common options that other subcommands use
70 parent_parser2 = ArgumentParser(add_help=False,
91 parent_parser2 = ArgumentParser(
71 argument_default=NoConfigDefault)
92 add_help=False,
72 parent_parser2.add_argument('-p','--profile',
93 argument_default=SUPPRESS
73 dest='Global.profile',type=unicode,
74 help='The string name of the profile to be used. This determines '
75 'the name of the cluster dir as: cluster_<profile>. The default profile '
76 'is named "default". The cluster directory is resolve this way '
77 'if the --cluster-dir option is not used.',
78 metavar='Global.profile')
79 parent_parser2.add_argument('--cluster-dir',
80 dest='Global.cluster_dir',type=unicode,
81 help='Set the cluster dir. This overrides the logic used by the '
82 '--profile option.',
83 metavar='Global.cluster_dir'),
84 parent_parser2.add_argument('--work-dir',
85 dest='Global.work_dir',type=unicode,
86 help='Set the working dir for the process.',
87 metavar='Global.work_dir')
88 parent_parser2.add_argument('--log-to-file',
89 action='store_true', dest='Global.log_to_file',
90 help='Log to a file in the log directory (default is stdout)'
91 )
94 )
95 self._add_cluster_profile(parent_parser2)
96 self._add_cluster_dir(parent_parser2)
97 self._add_work_dir(parent_parser2)
98 paa = parent_parser2.add_argument
99 paa('--log-to-file',
100 action='store_true', dest='Global.log_to_file',
101 help='Log to a file in the log directory (default is stdout)')
92
102
103 # Create the object used to create the subparsers.
93 subparsers = self.parser.add_subparsers(
104 subparsers = self.parser.add_subparsers(
94 dest='Global.subcommand',
105 dest='Global.subcommand',
95 title='ipcluster subcommands',
106 title='ipcluster subcommands',
96 description='ipcluster has a variety of subcommands. '
107 description=
97 'The general way of running ipcluster is "ipcluster <cmd> '
108 """ipcluster has a variety of subcommands. The general way of
98 ' [options]""',
109 running ipcluster is 'ipcluster <cmd> [options]'. To get help
99 help='For more help, type "ipcluster <cmd> -h"')
110 on a particular subcommand do 'ipcluster <cmd> -h'."""
111 # help="For more help, type 'ipcluster <cmd> -h'",
112 )
100
113
114 # The "list" subcommand parser
101 parser_list = subparsers.add_parser(
115 parser_list = subparsers.add_parser(
102 'list',
116 'list',
103 help='List all clusters in cwd and ipython_dir.',
117 parents=[parent_parser1],
104 parents=[parent_parser1]
118 argument_default=SUPPRESS,
119 help="List all clusters in cwd and ipython_dir.",
120 description=
121 """List all available clusters, by cluster directory, that can
122 be found in the current working directly or in the ipython
123 directory. Cluster directories are named using the convention
124 'cluster_<profile>'."""
105 )
125 )
106
126
127 # The "create" subcommand parser
107 parser_create = subparsers.add_parser(
128 parser_create = subparsers.add_parser(
108 'create',
129 'create',
109 help='Create a new cluster directory.',
130 parents=[parent_parser1, parent_parser2],
110 parents=[parent_parser1, parent_parser2]
131 argument_default=SUPPRESS,
132 help="Create a new cluster directory.",
133 description=
134 """Create an ipython cluster directory by its profile name or
135 cluster directory path. Cluster directories contain
136 configuration, log and security related files and are named
137 using the convention 'cluster_<profile>'. By default they are
138 located in your ipython directory. Once created, you will
139 probably need to edit the configuration files in the cluster
140 directory to configure your cluster. Most users will create a
141 cluster directory by profile name,
142 'ipcluster create -p mycluster', which will put the directory
143 in '<ipython_dir>/cluster_mycluster'.
144 """
111 )
145 )
112 parser_create.add_argument(
146 paa = parser_create.add_argument
113 '--reset-config',
147 paa('--reset-config',
114 dest='Global.reset_config', action='store_true',
148 dest='Global.reset_config', action='store_true',
115 default=NoConfigDefault,
149 help=
116 help='Recopy the default config files to the cluster directory. '
150 """Recopy the default config files to the cluster directory.
117 'You will loose any modifications you have made to these files.'
151 You will loose any modifications you have made to these files.""")
118 )
119
152
153 # The "start" subcommand parser
120 parser_start = subparsers.add_parser(
154 parser_start = subparsers.add_parser(
121 'start',
155 'start',
122 help='Start a cluster.',
156 parents=[parent_parser1, parent_parser2],
123 parents=[parent_parser1, parent_parser2]
157 argument_default=SUPPRESS,
158 help="Start a cluster.",
159 description=
160 """Start an ipython cluster by its profile name or cluster
161 directory. Cluster directories contain configuration, log and
162 security related files and are named using the convention
163 'cluster_<profile>' and should be creating using the 'start'
164 subcommand of 'ipcluster'. If your cluster directory is in
165 the cwd or the ipython directory, you can simply refer to it
166 using its profile name, 'ipcluster start -n 4 -p <profile>`,
167 otherwise use the '--cluster-dir' option.
168 """
124 )
169 )
125 parser_start.add_argument(
170 paa = parser_start.add_argument
126 '-n', '--number',
171 paa('-n', '--number',
127 type=int, dest='Global.n',
172 type=int, dest='Global.n',
128 help='The number of engines to start.',
173 help='The number of engines to start.',
129 metavar='Global.n'
174 metavar='Global.n')
130 )
175 paa('--clean-logs',
131 parser_start.add_argument('--clean-logs',
132 dest='Global.clean_logs', action='store_true',
176 dest='Global.clean_logs', action='store_true',
133 help='Delete old log flies before starting.',
177 help='Delete old log flies before starting.')
134 )
178 paa('--no-clean-logs',
135 parser_start.add_argument('--no-clean-logs',
136 dest='Global.clean_logs', action='store_false',
179 dest='Global.clean_logs', action='store_false',
137 help="Don't delete old log flies before starting.",
180 help="Don't delete old log flies before starting.")
138 )
181 paa('--daemon',
139 parser_start.add_argument('--daemon',
140 dest='Global.daemonize', action='store_true',
182 dest='Global.daemonize', action='store_true',
141 help='Daemonize the ipcluster program. This implies --log-to-file',
183 help='Daemonize the ipcluster program. This implies --log-to-file')
142 )
184 paa('--no-daemon',
143 parser_start.add_argument('--no-daemon',
144 dest='Global.daemonize', action='store_false',
185 dest='Global.daemonize', action='store_false',
145 help="Dont't daemonize the ipcluster program.",
186 help="Dont't daemonize the ipcluster program.")
146 )
147
187
148 parser_start = subparsers.add_parser(
188 # The "stop" subcommand parser
189 parser_stop = subparsers.add_parser(
149 'stop',
190 'stop',
150 help='Stop a cluster.',
191 parents=[parent_parser1, parent_parser2],
151 parents=[parent_parser1, parent_parser2]
192 argument_default=SUPPRESS,
193 help="Stop a running cluster.",
194 description=
195 """Stop a running ipython cluster by its profile name or cluster
196 directory. Cluster directories are named using the convention
197 'cluster_<profile>'. If your cluster directory is in
198 the cwd or the ipython directory, you can simply refer to it
199 using its profile name, 'ipcluster stop -p <profile>`, otherwise
200 use the '--cluster-dir' option.
201 """
152 )
202 )
153 parser_start.add_argument('--signal',
203 paa = parser_stop.add_argument
204 paa('--signal',
154 dest='Global.signal', type=int,
205 dest='Global.signal', type=int,
155 help="The signal number to use in stopping the cluster (default=2).",
206 help="The signal number to use in stopping the cluster (default=2).",
156 metavar="Global.signal",
207 metavar="Global.signal")
157 )
158
159
208
160 default_config_file_name = u'ipcluster_config.py'
161
209
162
210 #-----------------------------------------------------------------------------
163 _description = """Start an IPython cluster for parallel computing.\n\n
211 # Main application
164
212 #-----------------------------------------------------------------------------
165 An IPython cluster consists of 1 controller and 1 or more engines.
166 This command automates the startup of these processes using a wide
167 range of startup methods (SSH, local processes, PBS, mpiexec,
168 Windows HPC Server 2008). To start a cluster with 4 engines on your
169 local host simply do "ipcluster start -n 4". For more complex usage
170 you will typically do "ipcluster create -p mycluster", then edit
171 configuration files, followed by "ipcluster start -p mycluster -n 4".
172 """
173
213
174
214
175 class IPClusterApp(ApplicationWithClusterDir):
215 class IPClusterApp(ApplicationWithClusterDir):
176
216
177 name = u'ipcluster'
217 name = u'ipcluster'
178 description = _description
218 description = _description
179 config_file_name = default_config_file_name
219 usage = None
220 command_line_loader = IPClusterAppConfigLoader
221 default_config_file_name = default_config_file_name
180 default_log_level = logging.INFO
222 default_log_level = logging.INFO
181 auto_create_cluster_dir = False
223 auto_create_cluster_dir = False
182
224
@@ -192,13 +234,6 b' class IPClusterApp(ApplicationWithClusterDir):'
192 self.default_config.Global.signal = 2
234 self.default_config.Global.signal = 2
193 self.default_config.Global.daemonize = False
235 self.default_config.Global.daemonize = False
194
236
195 def create_command_line_config(self):
196 """Create and return a command line config loader."""
197 return IPClusterCLLoader(
198 description=self.description,
199 version=release.version
200 )
201
202 def find_resources(self):
237 def find_resources(self):
203 subcommand = self.command_line_config.Global.subcommand
238 subcommand = self.command_line_config.Global.subcommand
204 if subcommand=='list':
239 if subcommand=='list':
@@ -361,8 +396,11 b' class IPClusterApp(ApplicationWithClusterDir):'
361 log.msg('Unexpected error in ipcluster:')
396 log.msg('Unexpected error in ipcluster:')
362 log.msg(r.getTraceback())
397 log.msg(r.getTraceback())
363 log.msg("IPython cluster: stopping")
398 log.msg("IPython cluster: stopping")
364 self.stop_engines()
399 # These return deferreds. We are not doing anything with them
365 self.stop_controller()
400 # but we are holding refs to them as a reminder that they
401 # do return deferreds.
402 d1 = self.stop_engines()
403 d2 = self.stop_controller()
366 # Wait a few seconds to let things shut down.
404 # Wait a few seconds to let things shut down.
367 reactor.callLater(4.0, reactor.stop)
405 reactor.callLater(4.0, reactor.stop)
368
406
@@ -449,6 +487,7 b' class IPClusterApp(ApplicationWithClusterDir):'
449 # old .pid files.
487 # old .pid files.
450 self.remove_pid_file()
488 self.remove_pid_file()
451
489
490
452 def launch_new_instance():
491 def launch_new_instance():
453 """Create and run the IPython cluster."""
492 """Create and run the IPython cluster."""
454 app = IPClusterApp()
493 app = IPClusterApp()
@@ -18,20 +18,40 b' The IPython controller application.'
18 from __future__ import with_statement
18 from __future__ import with_statement
19
19
20 import copy
20 import copy
21 import os
22 import sys
21 import sys
23
22
24 from twisted.application import service
23 from twisted.application import service
25 from twisted.internet import reactor
24 from twisted.internet import reactor
26 from twisted.python import log
25 from twisted.python import log
27
26
28 from IPython.config.loader import Config, NoConfigDefault
27 from IPython.config.loader import Config
29 from IPython.core import release
30 from IPython.core.application import Application
31 from IPython.kernel import controllerservice
28 from IPython.kernel import controllerservice
32 from IPython.kernel.clusterdir import ApplicationWithClusterDir
29 from IPython.kernel.clusterdir import (
33 from IPython.kernel.fcutil import FCServiceFactory
30 ApplicationWithClusterDir,
34 from IPython.utils.traitlets import Str, Instance, Unicode
31 ClusterDirConfigLoader
32 )
33 from IPython.kernel.fcutil import FCServiceFactory, FURLError
34 from IPython.utils.traitlets import Instance, Unicode
35
36
37 #-----------------------------------------------------------------------------
38 # Module level variables
39 #-----------------------------------------------------------------------------
40
41
42 #: The default config file name for this application
43 default_config_file_name = u'ipcontroller_config.py'
44
45
46 _description = """Start the IPython controller for parallel computing.
47
48 The IPython controller provides a gateway between the IPython engines and
49 clients. The controller needs to be started before the engines and can be
50 configured using command line options or using a cluster directory. Cluster
51 directories contain config, log and security files and are usually located in
52 your .ipython directory and named as "cluster_<profile>". See the --profile
53 and --cluster-dir options for details.
54 """
35
55
36 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
37 # Default interfaces
57 # Default interfaces
@@ -92,108 +112,96 b' class FCEngineServiceFactory(FCServiceFactory):'
92
112
93
113
94 #-----------------------------------------------------------------------------
114 #-----------------------------------------------------------------------------
95 # The main application
115 # Command line options
96 #-----------------------------------------------------------------------------
116 #-----------------------------------------------------------------------------
97
117
98
118
99 cl_args = (
119 class IPControllerAppConfigLoader(ClusterDirConfigLoader):
100 # Client config
120
101 (('--client-ip',), dict(
121 def _add_arguments(self):
102 type=str, dest='FCClientServiceFactory.ip',
122 super(IPControllerAppConfigLoader, self)._add_arguments()
103 help='The IP address or hostname the controller will listen on for '
123 paa = self.parser.add_argument
104 'client connections.',
124 # Client config
105 metavar='FCClientServiceFactory.ip')
125 paa('--client-ip',
106 ),
126 type=str, dest='FCClientServiceFactory.ip',
107 (('--client-port',), dict(
127 help='The IP address or hostname the controller will listen on for '
108 type=int, dest='FCClientServiceFactory.port',
128 'client connections.',
109 help='The port the controller will listen on for client connections. '
129 metavar='FCClientServiceFactory.ip')
110 'The default is to use 0, which will autoselect an open port.',
130 paa('--client-port',
111 metavar='FCClientServiceFactory.port')
131 type=int, dest='FCClientServiceFactory.port',
112 ),
132 help='The port the controller will listen on for client connections. '
113 (('--client-location',), dict(
133 'The default is to use 0, which will autoselect an open port.',
114 type=str, dest='FCClientServiceFactory.location',
134 metavar='FCClientServiceFactory.port')
115 help='The hostname or IP that clients should connect to. This does '
135 paa('--client-location',), dict(
116 'not control which interface the controller listens on. Instead, this '
136 type=str, dest='FCClientServiceFactory.location',
117 'determines the hostname/IP that is listed in the FURL, which is how '
137 help='The hostname or IP that clients should connect to. This does '
118 'clients know where to connect. Useful if the controller is listening '
138 'not control which interface the controller listens on. Instead, this '
119 'on multiple interfaces.',
139 'determines the hostname/IP that is listed in the FURL, which is how '
120 metavar='FCClientServiceFactory.location')
140 'clients know where to connect. Useful if the controller is listening '
121 ),
141 'on multiple interfaces.',
122 # Engine config
142 metavar='FCClientServiceFactory.location')
123 (('--engine-ip',), dict(
143 # Engine config
124 type=str, dest='FCEngineServiceFactory.ip',
144 paa('--engine-ip',
125 help='The IP address or hostname the controller will listen on for '
145 type=str, dest='FCEngineServiceFactory.ip',
126 'engine connections.',
146 help='The IP address or hostname the controller will listen on for '
127 metavar='FCEngineServiceFactory.ip')
147 'engine connections.',
128 ),
148 metavar='FCEngineServiceFactory.ip')
129 (('--engine-port',), dict(
149 paa('--engine-port',
130 type=int, dest='FCEngineServiceFactory.port',
150 type=int, dest='FCEngineServiceFactory.port',
131 help='The port the controller will listen on for engine connections. '
151 help='The port the controller will listen on for engine connections. '
132 'The default is to use 0, which will autoselect an open port.',
152 'The default is to use 0, which will autoselect an open port.',
133 metavar='FCEngineServiceFactory.port')
153 metavar='FCEngineServiceFactory.port')
134 ),
154 paa('--engine-location',
135 (('--engine-location',), dict(
155 type=str, dest='FCEngineServiceFactory.location',
136 type=str, dest='FCEngineServiceFactory.location',
156 help='The hostname or IP that engines should connect to. This does '
137 help='The hostname or IP that engines should connect to. This does '
157 'not control which interface the controller listens on. Instead, this '
138 'not control which interface the controller listens on. Instead, this '
158 'determines the hostname/IP that is listed in the FURL, which is how '
139 'determines the hostname/IP that is listed in the FURL, which is how '
159 'engines know where to connect. Useful if the controller is listening '
140 'engines know where to connect. Useful if the controller is listening '
160 'on multiple interfaces.',
141 'on multiple interfaces.',
161 metavar='FCEngineServiceFactory.location')
142 metavar='FCEngineServiceFactory.location')
162 # Global config
143 ),
163 paa('--log-to-file',
144 # Global config
164 action='store_true', dest='Global.log_to_file',
145 (('--log-to-file',), dict(
165 help='Log to a file in the log directory (default is stdout)')
146 action='store_true', dest='Global.log_to_file',
166 paa('-r','--reuse-furls',
147 help='Log to a file in the log directory (default is stdout)')
167 action='store_true', dest='Global.reuse_furls',
148 ),
168 help='Try to reuse all FURL files. If this is not set all FURL files '
149 (('-r','--reuse-furls'), dict(
169 'are deleted before the controller starts. This must be set if '
150 action='store_true', dest='Global.reuse_furls',
170 'specific ports are specified by --engine-port or --client-port.')
151 help='Try to reuse all FURL files. If this is not set all FURL files '
171 paa('--no-secure',
152 'are deleted before the controller starts. This must be set if '
172 action='store_false', dest='Global.secure',
153 'specific ports are specified by --engine-port or --client-port.')
173 help='Turn off SSL encryption for all connections.')
154 ),
174 paa('--secure',
155 (('--no-secure',), dict(
175 action='store_true', dest='Global.secure',
156 action='store_false', dest='Global.secure',
176 help='Turn off SSL encryption for all connections.')
157 help='Turn off SSL encryption for all connections.')
158 ),
159 (('--secure',), dict(
160 action='store_true', dest='Global.secure',
161 help='Turn off SSL encryption for all connections.')
162 )
163 )
164
177
165
178
166 _description = """Start the IPython controller for parallel computing.
179 #-----------------------------------------------------------------------------
167
180 # The main application
168 The IPython controller provides a gateway between the IPython engines and
181 #-----------------------------------------------------------------------------
169 clients. The controller needs to be started before the engines and can be
170 configured using command line options or using a cluster directory. Cluster
171 directories contain config, log and security files and are usually located in
172 your .ipython directory and named as "cluster_<profile>". See the --profile
173 and --cluster-dir options for details.
174 """
175
176 default_config_file_name = u'ipcontroller_config.py'
177
182
178
183
179 class IPControllerApp(ApplicationWithClusterDir):
184 class IPControllerApp(ApplicationWithClusterDir):
180
185
181 name = u'ipcontroller'
186 name = u'ipcontroller'
182 description = _description
187 description = _description
183 config_file_name = default_config_file_name
188 command_line_loader = IPControllerAppConfigLoader
189 default_config_file_name = default_config_file_name
184 auto_create_cluster_dir = True
190 auto_create_cluster_dir = True
185 cl_arguments = Application.cl_arguments + cl_args
186
191
187 def create_default_config(self):
192 def create_default_config(self):
188 super(IPControllerApp, self).create_default_config()
193 super(IPControllerApp, self).create_default_config()
189 self.default_config.Global.reuse_furls = False
194 # Don't set defaults for Global.secure or Global.reuse_furls
190 self.default_config.Global.secure = True
195 # as those are set in a component.
191 self.default_config.Global.import_statements = []
196 self.default_config.Global.import_statements = []
192 self.default_config.Global.clean_logs = True
197 self.default_config.Global.clean_logs = True
193
198
194 def post_load_command_line_config(self):
199 def pre_construct(self):
195 # Now setup reuse_furls
200 super(IPControllerApp, self).pre_construct()
196 c = self.command_line_config
201 c = self.master_config
202 # The defaults for these are set in FCClientServiceFactory and
203 # FCEngineServiceFactory, so we only set them here if the global
204 # options have be set to override the class level defaults.
197 if hasattr(c.Global, 'reuse_furls'):
205 if hasattr(c.Global, 'reuse_furls'):
198 c.FCClientServiceFactory.reuse_furls = c.Global.reuse_furls
206 c.FCClientServiceFactory.reuse_furls = c.Global.reuse_furls
199 c.FCEngineServiceFactory.reuse_furls = c.Global.reuse_furls
207 c.FCEngineServiceFactory.reuse_furls = c.Global.reuse_furls
@@ -216,11 +224,19 b' class IPControllerApp(ApplicationWithClusterDir):'
216 controller_service = controllerservice.ControllerService()
224 controller_service = controllerservice.ControllerService()
217 controller_service.setServiceParent(self.main_service)
225 controller_service.setServiceParent(self.main_service)
218 # The client tub and all its refereceables
226 # The client tub and all its refereceables
219 csfactory = FCClientServiceFactory(self.master_config, controller_service)
227 try:
228 csfactory = FCClientServiceFactory(self.master_config, controller_service)
229 except FURLError, e:
230 log.err(e)
231 self.exit(0)
220 client_service = csfactory.create()
232 client_service = csfactory.create()
221 client_service.setServiceParent(self.main_service)
233 client_service.setServiceParent(self.main_service)
222 # The engine tub
234 # The engine tub
223 esfactory = FCEngineServiceFactory(self.master_config, controller_service)
235 try:
236 esfactory = FCEngineServiceFactory(self.master_config, controller_service)
237 except FURLError, e:
238 log.err(e)
239 self.exit(0)
224 engine_service = esfactory.create()
240 engine_service = esfactory.create()
225 engine_service.setServiceParent(self.main_service)
241 engine_service.setServiceParent(self.main_service)
226
242
@@ -22,39 +22,21 b' from twisted.application import service'
22 from twisted.internet import reactor
22 from twisted.internet import reactor
23 from twisted.python import log
23 from twisted.python import log
24
24
25 from IPython.core.application import Application
25 from IPython.kernel.clusterdir import (
26 from IPython.kernel.clusterdir import ApplicationWithClusterDir
26 ApplicationWithClusterDir,
27 ClusterDirConfigLoader
28 )
27 from IPython.kernel.engineconnector import EngineConnector
29 from IPython.kernel.engineconnector import EngineConnector
28 from IPython.kernel.engineservice import EngineService
30 from IPython.kernel.engineservice import EngineService
29 from IPython.kernel.fcutil import Tub
31 from IPython.kernel.fcutil import Tub
30 from IPython.utils.importstring import import_item
32 from IPython.utils.importstring import import_item
31
33
32 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
33 # The main application
35 # Module level variables
34 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
35
37
36 cl_args = (
38 #: The default config file name for this application
37 # Controller config
39 default_config_file_name = u'ipengine_config.py'
38 (('--furl-file',), dict(
39 type=unicode, dest='Global.furl_file',
40 help='The full location of the file containing the FURL of the '
41 'controller. If this is not given, the FURL file must be in the '
42 'security directory of the cluster directory. This location is '
43 'resolved using the --profile and --app-dir options.',
44 metavar='Global.furl_file')
45 ),
46 # MPI
47 (('--mpi',), dict(
48 type=str, dest='MPI.use',
49 help='How to enable MPI (mpi4py, pytrilinos, or empty string to disable).',
50 metavar='MPI.use')
51 ),
52 # Global config
53 (('--log-to-file',), dict(
54 action='store_true', dest='Global.log_to_file',
55 help='Log to a file in the log directory (default is stdout)')
56 )
57 )
58
40
59
41
60 mpi4py_init = """from mpi4py import MPI as mpi
42 mpi4py_init = """from mpi4py import MPI as mpi
@@ -62,6 +44,7 b' mpi.size = mpi.COMM_WORLD.Get_size()'
62 mpi.rank = mpi.COMM_WORLD.Get_rank()
44 mpi.rank = mpi.COMM_WORLD.Get_rank()
63 """
45 """
64
46
47
65 pytrilinos_init = """from PyTrilinos import Epetra
48 pytrilinos_init = """from PyTrilinos import Epetra
66 class SimpleStruct:
49 class SimpleStruct:
67 pass
50 pass
@@ -71,9 +54,6 b' mpi.size = 0'
71 """
54 """
72
55
73
56
74 default_config_file_name = u'ipengine_config.py'
75
76
77 _description = """Start an IPython engine for parallel computing.\n\n
57 _description = """Start an IPython engine for parallel computing.\n\n
78
58
79 IPython engines run in parallel and perform computations on behalf of a client
59 IPython engines run in parallel and perform computations on behalf of a client
@@ -84,14 +64,47 b' usually located in your .ipython directory and named as "cluster_<profile>".'
84 See the --profile and --cluster-dir options for details.
64 See the --profile and --cluster-dir options for details.
85 """
65 """
86
66
67 #-----------------------------------------------------------------------------
68 # Command line options
69 #-----------------------------------------------------------------------------
70
71
72 class IPEngineAppConfigLoader(ClusterDirConfigLoader):
73
74 def _add_arguments(self):
75 super(IPEngineAppConfigLoader, self)._add_arguments()
76 paa = self.parser.add_argument
77 # Controller config
78 paa('--furl-file',
79 type=unicode, dest='Global.furl_file',
80 help='The full location of the file containing the FURL of the '
81 'controller. If this is not given, the FURL file must be in the '
82 'security directory of the cluster directory. This location is '
83 'resolved using the --profile and --app-dir options.',
84 metavar='Global.furl_file')
85 # MPI
86 paa('--mpi',
87 type=str, dest='MPI.use',
88 help='How to enable MPI (mpi4py, pytrilinos, or empty string to disable).',
89 metavar='MPI.use')
90 # Global config
91 paa('--log-to-file',
92 action='store_true', dest='Global.log_to_file',
93 help='Log to a file in the log directory (default is stdout)')
94
95
96 #-----------------------------------------------------------------------------
97 # Main application
98 #-----------------------------------------------------------------------------
99
87
100
88 class IPEngineApp(ApplicationWithClusterDir):
101 class IPEngineApp(ApplicationWithClusterDir):
89
102
90 name = u'ipengine'
103 name = u'ipengine'
91 description = _description
104 description = _description
92 config_file_name = default_config_file_name
105 command_line_loader = IPEngineAppConfigLoader
106 default_config_file_name = default_config_file_name
93 auto_create_cluster_dir = True
107 auto_create_cluster_dir = True
94 cl_arguments = Application.cl_arguments + cl_args
95
108
96 def create_default_config(self):
109 def create_default_config(self):
97 super(IPEngineApp, self).create_default_config()
110 super(IPEngineApp, self).create_default_config()
@@ -21,11 +21,15 b' import sys'
21
21
22 from IPython.core.component import Component
22 from IPython.core.component import Component
23 from IPython.external import Itpl
23 from IPython.external import Itpl
24 from IPython.utils.traitlets import Str, Int, List, Unicode, Enum
24 from IPython.utils.traitlets import Str, Int, List, Unicode
25 from IPython.utils.platutils import find_cmd
25 from IPython.utils.path import get_ipython_module_path
26 from IPython.kernel.twistedutil import gatherBoth, make_deferred, sleep_deferred
26 from IPython.utils.process import find_cmd, pycmd2argv, FindCmdError
27 from IPython.kernel.twistedutil import (
28 gatherBoth,
29 make_deferred,
30 sleep_deferred
31 )
27 from IPython.kernel.winhpcjob import (
32 from IPython.kernel.winhpcjob import (
28 WinHPCJob, WinHPCTask,
29 IPControllerTask, IPEngineTask,
33 IPControllerTask, IPEngineTask,
30 IPControllerJob, IPEngineSetJob
34 IPControllerJob, IPEngineSetJob
31 )
35 )
@@ -38,46 +42,23 b' from twisted.internet.error import ProcessDone, ProcessTerminated'
38 from twisted.python import log
42 from twisted.python import log
39 from twisted.python.failure import Failure
43 from twisted.python.failure import Failure
40
44
45
41 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
42 # Utilities
47 # Paths to the kernel apps
43 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
44
49
45
50
46 def find_controller_cmd():
51 ipcluster_cmd_argv = pycmd2argv(get_ipython_module_path(
47 """Find the command line ipcontroller program in a cross platform way."""
52 'IPython.kernel.ipclusterapp'
48 if sys.platform == 'win32':
53 ))
49 # This logic is needed because the ipcontroller script doesn't
54
50 # always get installed in the same way or in the same location.
55 ipengine_cmd_argv = pycmd2argv(get_ipython_module_path(
51 from IPython.kernel import ipcontrollerapp
56 'IPython.kernel.ipengineapp'
52 script_location = ipcontrollerapp.__file__.replace('.pyc', '.py')
57 ))
53 # The -u option here turns on unbuffered output, which is required
54 # on Win32 to prevent wierd conflict and problems with Twisted.
55 # Also, use sys.executable to make sure we are picking up the
56 # right python exe.
57 cmd = [sys.executable, '-u', script_location]
58 else:
59 # ipcontroller has to be on the PATH in this case.
60 cmd = ['ipcontroller']
61 return cmd
62
63
64 def find_engine_cmd():
65 """Find the command line ipengine program in a cross platform way."""
66 if sys.platform == 'win32':
67 # This logic is needed because the ipengine script doesn't
68 # always get installed in the same way or in the same location.
69 from IPython.kernel import ipengineapp
70 script_location = ipengineapp.__file__.replace('.pyc', '.py')
71 # The -u option here turns on unbuffered output, which is required
72 # on Win32 to prevent wierd conflict and problems with Twisted.
73 # Also, use sys.executable to make sure we are picking up the
74 # right python exe.
75 cmd = [sys.executable, '-u', script_location]
76 else:
77 # ipcontroller has to be on the PATH in this case.
78 cmd = ['ipengine']
79 return cmd
80
58
59 ipcontroller_cmd_argv = pycmd2argv(get_ipython_module_path(
60 'IPython.kernel.ipcontrollerapp'
61 ))
81
62
82 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
83 # Base launchers and errors
64 # Base launchers and errors
@@ -333,7 +314,7 b' class LocalProcessLauncher(BaseLauncher):'
333 class LocalControllerLauncher(LocalProcessLauncher):
314 class LocalControllerLauncher(LocalProcessLauncher):
334 """Launch a controller as a regular external process."""
315 """Launch a controller as a regular external process."""
335
316
336 controller_cmd = List(find_controller_cmd(), config=True)
317 controller_cmd = List(ipcontroller_cmd_argv, config=True)
337 # Command line arguments to ipcontroller.
318 # Command line arguments to ipcontroller.
338 controller_args = List(['--log-to-file','--log-level', '40'], config=True)
319 controller_args = List(['--log-to-file','--log-level', '40'], config=True)
339
320
@@ -351,7 +332,7 b' class LocalControllerLauncher(LocalProcessLauncher):'
351 class LocalEngineLauncher(LocalProcessLauncher):
332 class LocalEngineLauncher(LocalProcessLauncher):
352 """Launch a single engine as a regular externall process."""
333 """Launch a single engine as a regular externall process."""
353
334
354 engine_cmd = List(find_engine_cmd(), config=True)
335 engine_cmd = List(ipengine_cmd_argv, config=True)
355 # Command line arguments for ipengine.
336 # Command line arguments for ipengine.
356 engine_args = List(
337 engine_args = List(
357 ['--log-to-file','--log-level', '40'], config=True
338 ['--log-to-file','--log-level', '40'], config=True
@@ -462,7 +443,7 b' class MPIExecLauncher(LocalProcessLauncher):'
462 class MPIExecControllerLauncher(MPIExecLauncher):
443 class MPIExecControllerLauncher(MPIExecLauncher):
463 """Launch a controller using mpiexec."""
444 """Launch a controller using mpiexec."""
464
445
465 controller_cmd = List(find_controller_cmd(), config=True)
446 controller_cmd = List(ipcontroller_cmd_argv, config=True)
466 # Command line arguments to ipcontroller.
447 # Command line arguments to ipcontroller.
467 controller_args = List(['--log-to-file','--log-level', '40'], config=True)
448 controller_args = List(['--log-to-file','--log-level', '40'], config=True)
468 n = Int(1, config=False)
449 n = Int(1, config=False)
@@ -481,7 +462,7 b' class MPIExecControllerLauncher(MPIExecLauncher):'
481
462
482 class MPIExecEngineSetLauncher(MPIExecLauncher):
463 class MPIExecEngineSetLauncher(MPIExecLauncher):
483
464
484 engine_cmd = List(find_engine_cmd(), config=True)
465 engine_cmd = List(ipengine_cmd_argv, config=True)
485 # Command line arguments for ipengine.
466 # Command line arguments for ipengine.
486 engine_args = List(
467 engine_args = List(
487 ['--log-to-file','--log-level', '40'], config=True
468 ['--log-to-file','--log-level', '40'], config=True
@@ -557,7 +538,10 b' class SSHEngineSetLauncher(BaseLauncher):'
557 # This is only used on Windows.
538 # This is only used on Windows.
558 def find_job_cmd():
539 def find_job_cmd():
559 if os.name=='nt':
540 if os.name=='nt':
560 return find_cmd('job')
541 try:
542 return find_cmd('job')
543 except FindCmdError:
544 return 'job'
561 else:
545 else:
562 return 'job'
546 return 'job'
563
547
@@ -831,28 +815,10 b' class PBSEngineSetLauncher(PBSLauncher):'
831 #-----------------------------------------------------------------------------
815 #-----------------------------------------------------------------------------
832
816
833
817
834 def find_ipcluster_cmd():
835 """Find the command line ipcluster program in a cross platform way."""
836 if sys.platform == 'win32':
837 # This logic is needed because the ipcluster script doesn't
838 # always get installed in the same way or in the same location.
839 from IPython.kernel import ipclusterapp
840 script_location = ipclusterapp.__file__.replace('.pyc', '.py')
841 # The -u option here turns on unbuffered output, which is required
842 # on Win32 to prevent wierd conflict and problems with Twisted.
843 # Also, use sys.executable to make sure we are picking up the
844 # right python exe.
845 cmd = [sys.executable, '-u', script_location]
846 else:
847 # ipcontroller has to be on the PATH in this case.
848 cmd = ['ipcluster']
849 return cmd
850
851
852 class IPClusterLauncher(LocalProcessLauncher):
818 class IPClusterLauncher(LocalProcessLauncher):
853 """Launch the ipcluster program in an external process."""
819 """Launch the ipcluster program in an external process."""
854
820
855 ipcluster_cmd = List(find_ipcluster_cmd(), config=True)
821 ipcluster_cmd = List(ipcluster_cmd_argv, config=True)
856 # Command line arguments to pass to ipcluster.
822 # Command line arguments to pass to ipcluster.
857 ipcluster_args = List(
823 ipcluster_args = List(
858 ['--clean-logs', '--log-to-file', '--log-level', '40'], config=True)
824 ['--clean-logs', '--log-to-file', '--log-level', '40'], config=True)
@@ -21,7 +21,7 b' __docformat__ = "restructuredtext en"'
21
21
22 import types
22 import types
23
23
24 from IPython.utils.genutils import flatten as genutil_flatten
24 from IPython.utils.data import flatten as utils_flatten
25
25
26 #-------------------------------------------------------------------------------
26 #-------------------------------------------------------------------------------
27 # Figure out which array packages are present and their array types
27 # Figure out which array packages are present and their array types
@@ -87,7 +87,7 b' class Map:'
87 return m['module'].concatenate(listOfPartitions)
87 return m['module'].concatenate(listOfPartitions)
88 # Next try for Python sequence types
88 # Next try for Python sequence types
89 if isinstance(testObject, (types.ListType, types.TupleType)):
89 if isinstance(testObject, (types.ListType, types.TupleType)):
90 return genutil_flatten(listOfPartitions)
90 return utils_flatten(listOfPartitions)
91 # If we have scalars, just return listOfPartitions
91 # If we have scalars, just return listOfPartitions
92 return listOfPartitions
92 return listOfPartitions
93
93
@@ -18,8 +18,7 b' __docformat__ = "restructuredtext en"'
18 from types import FunctionType
18 from types import FunctionType
19 from zope.interface import Interface, implements
19 from zope.interface import Interface, implements
20 from IPython.kernel.task import MapTask
20 from IPython.kernel.task import MapTask
21 from IPython.kernel.twistedutil import DeferredList, gatherBoth
21 from IPython.kernel.twistedutil import gatherBoth
22 from IPython.kernel.util import printer
23 from IPython.kernel.error import collect_exceptions
22 from IPython.kernel.error import collect_exceptions
24
23
25 #----------------------------------------------------------------------------
24 #----------------------------------------------------------------------------
@@ -27,24 +27,17 b' __docformat__ = "restructuredtext en"'
27 # Imports
27 # Imports
28 #-------------------------------------------------------------------------------
28 #-------------------------------------------------------------------------------
29
29
30 from new import instancemethod
31 from types import FunctionType
32
33 from twisted.application import service
34 from twisted.internet import defer, reactor
30 from twisted.internet import defer, reactor
35 from twisted.python import log, components, failure
31 from twisted.python import log, components, failure
36 from zope.interface import Interface, implements, Attribute
32 from zope.interface import Interface, implements
37
33
38 from IPython.utils import growl
39 from IPython.kernel.util import printer
40 from IPython.kernel.twistedutil import gatherBoth
34 from IPython.kernel.twistedutil import gatherBoth
41 from IPython.kernel import map as Map
42 from IPython.kernel import error
35 from IPython.kernel import error
43 from IPython.kernel.pendingdeferred import PendingDeferredManager, two_phase
36 from IPython.kernel.pendingdeferred import PendingDeferredManager, two_phase
44 from IPython.kernel.controllerservice import \
37 from IPython.kernel.controllerservice import (
45 ControllerAdapterBase, \
38 ControllerAdapterBase,
46 ControllerService, \
47 IControllerBase
39 IControllerBase
40 )
48
41
49
42
50 #-------------------------------------------------------------------------------
43 #-------------------------------------------------------------------------------
@@ -17,13 +17,17 b' __docformat__ = "restructuredtext en"'
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 import sys
19 import sys
20 import linecache
21 import warnings
20 import warnings
22
21
23 from twisted.python import components
22 from twisted.python import components
24 from twisted.python.failure import Failure
23 from twisted.python.failure import Failure
25 from zope.interface import Interface, implements, Attribute
24 from zope.interface import Interface, implements, Attribute
26
25
26 try:
27 from foolscap.api import DeadReferenceError
28 except ImportError:
29 from foolscap import DeadReferenceError
30
27 from IPython.utils.coloransi import TermColors
31 from IPython.utils.coloransi import TermColors
28
32
29 from IPython.kernel.twistedutil import blockingCallFromThread
33 from IPython.kernel.twistedutil import blockingCallFromThread
@@ -306,85 +310,6 b' class InteractiveMultiEngineClient(object):'
306 def __len__(self):
310 def __len__(self):
307 """Return the number of available engines."""
311 """Return the number of available engines."""
308 return len(self.get_ids())
312 return len(self.get_ids())
309
310 #---------------------------------------------------------------------------
311 # Make this a context manager for with
312 #---------------------------------------------------------------------------
313
314 def findsource_file(self,f):
315 linecache.checkcache()
316 s = findsource(f.f_code) # findsource is not defined!
317 lnum = f.f_lineno
318 wsource = s[0][f.f_lineno:]
319 return strip_whitespace(wsource)
320
321 def findsource_ipython(self,f):
322 from IPython.core import ipapi
323 self.ip = ipapi.get()
324 wsource = [l+'\n' for l in
325 self.ip.input_hist_raw[-1].splitlines()[1:]]
326 return strip_whitespace(wsource)
327
328 def __enter__(self):
329 f = sys._getframe(1)
330 local_ns = f.f_locals
331 global_ns = f.f_globals
332 if f.f_code.co_filename == '<ipython console>':
333 s = self.findsource_ipython(f)
334 else:
335 s = self.findsource_file(f)
336
337 self._with_context_result = self.execute(s)
338
339 def __exit__ (self, etype, value, tb):
340 if issubclass(etype,error.StopLocalExecution):
341 return True
342
343
344 def remote():
345 m = 'Special exception to stop local execution of parallel code.'
346 raise error.StopLocalExecution(m)
347
348 def strip_whitespace(source):
349 # Expand tabs to avoid any confusion.
350 wsource = [l.expandtabs(4) for l in source]
351 # Detect the indentation level
352 done = False
353 for line in wsource:
354 if line.isspace():
355 continue
356 for col,char in enumerate(line):
357 if char != ' ':
358 done = True
359 break
360 if done:
361 break
362 # Now we know how much leading space there is in the code. Next, we
363 # extract up to the first line that has less indentation.
364 # WARNINGS: we skip comments that may be misindented, but we do NOT yet
365 # detect triple quoted strings that may have flush left text.
366 for lno,line in enumerate(wsource):
367 lead = line[:col]
368 if lead.isspace():
369 continue
370 else:
371 if not lead.lstrip().startswith('#'):
372 break
373 # The real 'with' source is up to lno
374 src_lines = [l[col:] for l in wsource[:lno+1]]
375
376 # Finally, check that the source's first non-comment line begins with the
377 # special call 'remote()'
378 for nline,line in enumerate(src_lines):
379 if line.isspace() or line.startswith('#'):
380 continue
381 if 'remote()' in line:
382 break
383 else:
384 raise ValueError('remote() call missing at the start of code')
385 src = ''.join(src_lines[nline+1:])
386 #print 'SRC:\n<<<<<<<>>>>>>>\n%s<<<<<>>>>>>' % src # dbg
387 return src
388
313
389
314
390 #-------------------------------------------------------------------------------
315 #-------------------------------------------------------------------------------
@@ -444,18 +369,31 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
444
369
445 def _findTargetsAndBlock(self, targets=None, block=None):
370 def _findTargetsAndBlock(self, targets=None, block=None):
446 return self._findTargets(targets), self._findBlock(block)
371 return self._findTargets(targets), self._findBlock(block)
447
372
373 def _bcft(self, *args, **kwargs):
374 try:
375 result = blockingCallFromThread(*args, **kwargs)
376 except DeadReferenceError:
377 raise error.ConnectionError(
378 """A connection error has occurred in trying to connect to the
379 controller. This is usually caused by the controller dying or
380 being restarted. To resolve this issue try recreating the
381 multiengine client."""
382 )
383 else:
384 return result
385
448 def _blockFromThread(self, function, *args, **kwargs):
386 def _blockFromThread(self, function, *args, **kwargs):
449 block = kwargs.get('block', None)
387 block = kwargs.get('block', None)
450 if block is None:
388 if block is None:
451 raise error.MissingBlockArgument("'block' keyword argument is missing")
389 raise error.MissingBlockArgument("'block' keyword argument is missing")
452 result = blockingCallFromThread(function, *args, **kwargs)
390 result = self._bcft(function, *args, **kwargs)
453 if not block:
391 if not block:
454 result = PendingResult(self, result)
392 result = PendingResult(self, result)
455 return result
393 return result
456
394
457 def get_pending_deferred(self, deferredID, block):
395 def get_pending_deferred(self, deferredID, block):
458 return blockingCallFromThread(self.smultiengine.get_pending_deferred, deferredID, block)
396 return self._bcft(self.smultiengine.get_pending_deferred, deferredID, block)
459
397
460 def barrier(self, pendingResults):
398 def barrier(self, pendingResults):
461 """Synchronize a set of `PendingResults`.
399 """Synchronize a set of `PendingResults`.
@@ -505,7 +443,7 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
505 controller. This method allows the user to clear out all un-retrieved
443 controller. This method allows the user to clear out all un-retrieved
506 results on the controller.
444 results on the controller.
507 """
445 """
508 r = blockingCallFromThread(self.smultiengine.clear_pending_deferreds)
446 r = self._bcft(self.smultiengine.clear_pending_deferreds)
509 return r
447 return r
510
448
511 clear_pending_results = flush
449 clear_pending_results = flush
@@ -529,7 +467,7 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
529 at a later time.
467 at a later time.
530 """
468 """
531 targets, block = self._findTargetsAndBlock(targets, block)
469 targets, block = self._findTargetsAndBlock(targets, block)
532 result = blockingCallFromThread(self.smultiengine.execute, lines,
470 result = self._bcft(self.smultiengine.execute, lines,
533 targets=targets, block=block)
471 targets=targets, block=block)
534 if block:
472 if block:
535 result = ResultList(result)
473 result = ResultList(result)
@@ -647,7 +585,7 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
647 at a later time.
585 at a later time.
648 """
586 """
649 targets, block = self._findTargetsAndBlock(targets, block)
587 targets, block = self._findTargetsAndBlock(targets, block)
650 result = blockingCallFromThread(self.smultiengine.get_result, i, targets=targets, block=block)
588 result = self._bcft(self.smultiengine.get_result, i, targets=targets, block=block)
651 if block:
589 if block:
652 result = ResultList(result)
590 result = ResultList(result)
653 else:
591 else:
@@ -773,7 +711,7 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
773 """
711 """
774 Returns the ids of currently registered engines.
712 Returns the ids of currently registered engines.
775 """
713 """
776 result = blockingCallFromThread(self.smultiengine.get_ids)
714 result = self._bcft(self.smultiengine.get_ids)
777 return result
715 return result
778
716
779 #---------------------------------------------------------------------------
717 #---------------------------------------------------------------------------
@@ -22,12 +22,14 b' from types import FunctionType'
22
22
23 from zope.interface import Interface, implements
23 from zope.interface import Interface, implements
24 from twisted.internet import defer
24 from twisted.internet import defer
25 from twisted.python import components, failure, log
25 from twisted.python import components, failure
26
26
27 from foolscap import Referenceable
27 try:
28 from foolscap.api import Referenceable
29 except ImportError:
30 from foolscap import Referenceable
28
31
29 from IPython.kernel import error
32 from IPython.kernel import error
30 from IPython.kernel.util import printer
31 from IPython.kernel import map as Map
33 from IPython.kernel import map as Map
32 from IPython.kernel.parallelfunction import ParallelFunction
34 from IPython.kernel.parallelfunction import ParallelFunction
33 from IPython.kernel.mapper import (
35 from IPython.kernel.mapper import (
@@ -36,14 +38,15 b' from IPython.kernel.mapper import ('
36 IMapper
38 IMapper
37 )
39 )
38 from IPython.kernel.twistedutil import gatherBoth
40 from IPython.kernel.twistedutil import gatherBoth
39 from IPython.kernel.multiengine import (MultiEngine,
41 from IPython.kernel.multiengine import (
40 IMultiEngine,
42 IMultiEngine,
41 IFullSynchronousMultiEngine,
43 IFullSynchronousMultiEngine,
42 ISynchronousMultiEngine)
44 ISynchronousMultiEngine)
43 from IPython.kernel.multiengineclient import wrapResultList
44 from IPython.kernel.pendingdeferred import PendingDeferredManager
45 from IPython.kernel.pendingdeferred import PendingDeferredManager
45 from IPython.kernel.pickleutil import (can, canDict,
46 from IPython.kernel.pickleutil import (
46 canSequence, uncan, uncanDict, uncanSequence)
47 canDict,
48 canSequence, uncanDict, uncanSequence
49 )
47
50
48 from IPython.kernel.clientinterfaces import (
51 from IPython.kernel.clientinterfaces import (
49 IFCClientInterfaceProvider,
52 IFCClientInterfaceProvider,
@@ -19,7 +19,6 b' import cPickle as pickle'
19
19
20 from twisted.python.failure import Failure
20 from twisted.python.failure import Failure
21 from twisted.python import failure
21 from twisted.python import failure
22 import threading, sys
23
22
24 from IPython.kernel import pbconfig
23 from IPython.kernel import pbconfig
25 from IPython.kernel.error import PBMessageSizeError, UnpickleableException
24 from IPython.kernel.error import PBMessageSizeError, UnpickleableException
@@ -58,7 +57,7 b' def unpackageFailure(r):'
58 result = pickle.loads(r[8:])
57 result = pickle.loads(r[8:])
59 except pickle.PickleError:
58 except pickle.PickleError:
60 return failure.Failure( \
59 return failure.Failure( \
61 FailureUnpickleable("Could not unpickle failure."))
60 UnpickleableException("Could not unpickle failure."))
62 else:
61 else:
63 return result
62 return result
64 return r
63 return r
@@ -22,15 +22,11 b' __docformat__ = "restructuredtext en"'
22 # Imports
22 # Imports
23 #-------------------------------------------------------------------------------
23 #-------------------------------------------------------------------------------
24
24
25 from twisted.application import service
25 from twisted.internet import defer
26 from twisted.internet import defer, reactor
26 from twisted.python import failure
27 from twisted.python import log, components, failure
28 from zope.interface import Interface, implements, Attribute
29
27
30 from IPython.kernel.twistedutil import gatherBoth
31 from IPython.kernel import error
28 from IPython.kernel import error
32 from IPython.external import guid
29 from IPython.external import guid
33 from IPython.utils import growl
34
30
35 class PendingDeferredManager(object):
31 class PendingDeferredManager(object):
36 """A class to track pending deferreds.
32 """A class to track pending deferreds.
@@ -16,7 +16,6 b' __docformat__ = "restructuredtext en"'
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 from types import FunctionType
18 from types import FunctionType
19 from twisted.python import log
20
19
21 class CannedObject(object):
20 class CannedObject(object):
22 pass
21 pass
@@ -19,19 +19,18 b' __docformat__ = "restructuredtext en"'
19 # Tell nose to skip the testing of this module
19 # Tell nose to skip the testing of this module
20 __test__ = {}
20 __test__ = {}
21
21
22 import copy, time
22 import time
23 from types import FunctionType
23 from types import FunctionType
24
24
25 import zope.interface as zi, string
25 import zope.interface as zi
26 from twisted.internet import defer, reactor
26 from twisted.internet import defer, reactor
27 from twisted.python import components, log, failure
27 from twisted.python import components, log, failure
28
28
29 from IPython.kernel.util import printer
30 from IPython.kernel import engineservice as es, error
29 from IPython.kernel import engineservice as es, error
31 from IPython.kernel import controllerservice as cs
30 from IPython.kernel import controllerservice as cs
32 from IPython.kernel.twistedutil import gatherBoth, DeferredList
31 from IPython.kernel.twistedutil import DeferredList
33
32
34 from IPython.kernel.pickleutil import can, uncan, CannedFunction
33 from IPython.kernel.pickleutil import can, uncan
35
34
36 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
37 # Definition of the Task objects
36 # Definition of the Task objects
@@ -19,7 +19,12 b' __docformat__ = "restructuredtext en"'
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20
20
21 from zope.interface import Interface, implements
21 from zope.interface import Interface, implements
22 from twisted.python import components, log
22 from twisted.python import components
23
24 try:
25 from foolscap.api import DeadReferenceError
26 except ImportError:
27 from foolscap import DeadReferenceError
23
28
24 from IPython.kernel.twistedutil import blockingCallFromThread
29 from IPython.kernel.twistedutil import blockingCallFromThread
25 from IPython.kernel import task, error
30 from IPython.kernel import task, error
@@ -58,7 +63,20 b' class BlockingTaskClient(object):'
58 def __init__(self, task_controller):
63 def __init__(self, task_controller):
59 self.task_controller = task_controller
64 self.task_controller = task_controller
60 self.block = True
65 self.block = True
61
66
67 def _bcft(self, *args, **kwargs):
68 try:
69 result = blockingCallFromThread(*args, **kwargs)
70 except DeadReferenceError:
71 raise error.ConnectionError(
72 """A connection error has occurred in trying to connect to the
73 controller. This is usually caused by the controller dying or
74 being restarted. To resolve this issue try recreating the
75 task client."""
76 )
77 else:
78 return result
79
62 def run(self, task, block=False):
80 def run(self, task, block=False):
63 """Run a task on the `TaskController`.
81 """Run a task on the `TaskController`.
64
82
@@ -71,7 +89,7 b' class BlockingTaskClient(object):'
71 :Returns: The int taskid of the submitted task. Pass this to
89 :Returns: The int taskid of the submitted task. Pass this to
72 `get_task_result` to get the `TaskResult` object.
90 `get_task_result` to get the `TaskResult` object.
73 """
91 """
74 tid = blockingCallFromThread(self.task_controller.run, task)
92 tid = self._bcft(self.task_controller.run, task)
75 if block:
93 if block:
76 return self.get_task_result(tid, block=True)
94 return self.get_task_result(tid, block=True)
77 else:
95 else:
@@ -89,7 +107,7 b' class BlockingTaskClient(object):'
89
107
90 :Returns: A `TaskResult` object that encapsulates the task result.
108 :Returns: A `TaskResult` object that encapsulates the task result.
91 """
109 """
92 return blockingCallFromThread(self.task_controller.get_task_result,
110 return self._bcft(self.task_controller.get_task_result,
93 taskid, block)
111 taskid, block)
94
112
95 def abort(self, taskid):
113 def abort(self, taskid):
@@ -100,7 +118,7 b' class BlockingTaskClient(object):'
100 taskid : int
118 taskid : int
101 The taskid of the task to be aborted.
119 The taskid of the task to be aborted.
102 """
120 """
103 return blockingCallFromThread(self.task_controller.abort, taskid)
121 return self._bcft(self.task_controller.abort, taskid)
104
122
105 def barrier(self, taskids):
123 def barrier(self, taskids):
106 """Block until a set of tasks are completed.
124 """Block until a set of tasks are completed.
@@ -109,7 +127,7 b' class BlockingTaskClient(object):'
109 taskids : list, tuple
127 taskids : list, tuple
110 A sequence of taskids to block on.
128 A sequence of taskids to block on.
111 """
129 """
112 return blockingCallFromThread(self.task_controller.barrier, taskids)
130 return self._bcft(self.task_controller.barrier, taskids)
113
131
114 def spin(self):
132 def spin(self):
115 """
133 """
@@ -118,7 +136,7 b' class BlockingTaskClient(object):'
118 This method only needs to be called in unusual situations where the
136 This method only needs to be called in unusual situations where the
119 scheduler is idle for some reason.
137 scheduler is idle for some reason.
120 """
138 """
121 return blockingCallFromThread(self.task_controller.spin)
139 return self._bcft(self.task_controller.spin)
122
140
123 def queue_status(self, verbose=False):
141 def queue_status(self, verbose=False):
124 """
142 """
@@ -132,7 +150,7 b' class BlockingTaskClient(object):'
132 :Returns:
150 :Returns:
133 A dict with the queue status.
151 A dict with the queue status.
134 """
152 """
135 return blockingCallFromThread(self.task_controller.queue_status, verbose)
153 return self._bcft(self.task_controller.queue_status, verbose)
136
154
137 def clear(self):
155 def clear(self):
138 """
156 """
@@ -143,7 +161,7 b' class BlockingTaskClient(object):'
143 tasks. Users should call this periodically to clean out these
161 tasks. Users should call this periodically to clean out these
144 cached task results.
162 cached task results.
145 """
163 """
146 return blockingCallFromThread(self.task_controller.clear)
164 return self._bcft(self.task_controller.clear)
147
165
148 def map(self, func, *sequences):
166 def map(self, func, *sequences):
149 """
167 """
@@ -19,17 +19,17 b' __docformat__ = "restructuredtext en"'
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20
20
21 import cPickle as pickle
21 import cPickle as pickle
22 import xmlrpclib, copy
23
22
24 from zope.interface import Interface, implements
23 from zope.interface import Interface, implements
25 from twisted.internet import defer
24 from twisted.internet import defer
26 from twisted.python import components, failure
25 from twisted.python import components
27
26
28 from foolscap import Referenceable
27 try:
28 from foolscap.api import Referenceable
29 except ImportError:
30 from foolscap import Referenceable
29
31
30 from IPython.kernel.twistedutil import blockingCallFromThread
32 from IPython.kernel import task as taskmodule
31 from IPython.kernel import error, task as taskmodule, taskclient
32 from IPython.kernel.pickleutil import can, uncan
33 from IPython.kernel.clientinterfaces import (
33 from IPython.kernel.clientinterfaces import (
34 IFCClientInterfaceProvider,
34 IFCClientInterfaceProvider,
35 IBlockingClientAdaptor
35 IBlockingClientAdaptor
@@ -53,8 +53,8 b' class EngineFCTest(DeferredTestCase,'
53 # Start a server and append to self.servers
53 # Start a server and append to self.servers
54 self.controller_reference = FCRemoteEngineRefFromService(self)
54 self.controller_reference = FCRemoteEngineRefFromService(self)
55 self.controller_tub = Tub()
55 self.controller_tub = Tub()
56 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
56 self.controller_tub.listenOn('tcp:10111:interface=127.0.0.1')
57 self.controller_tub.setLocation('127.0.0.1:10105')
57 self.controller_tub.setLocation('127.0.0.1:10111')
58
58
59 furl = self.controller_tub.registerReference(self.controller_reference)
59 furl = self.controller_tub.registerReference(self.controller_reference)
60 self.controller_tub.startService()
60 self.controller_tub.startService()
@@ -27,7 +27,7 b' from IPython.kernel.multiengine import IMultiEngine'
27 from IPython.kernel.tests.multienginetest import IFullSynchronousMultiEngineTestCase
27 from IPython.kernel.tests.multienginetest import IFullSynchronousMultiEngineTestCase
28 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
28 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
29 from IPython.kernel import multiengine as me
29 from IPython.kernel import multiengine as me
30 from IPython.kernel.clientconnector import ClientConnector
30 from IPython.kernel.clientconnector import AsyncClientConnector
31 from IPython.kernel.parallelfunction import ParallelFunction
31 from IPython.kernel.parallelfunction import ParallelFunction
32 from IPython.kernel.error import CompositeError
32 from IPython.kernel.error import CompositeError
33 from IPython.kernel.util import printer
33 from IPython.kernel.util import printer
@@ -40,37 +40,30 b' def _raise_it(f):'
40 e.raise_exception()
40 e.raise_exception()
41
41
42
42
43 class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMultiEngineTestCase):
43 class FullSynchronousMultiEngineTestCase(
44 DeferredTestCase, IFullSynchronousMultiEngineTestCase):
44
45
45 # XXX (fperez) this is awful: I'm fully disabling this entire test class.
46 # Right now it's blocking the tests from running at all, and I don't know
47 # how to fix it. I hope Brian can have a stab at it, but at least by doing
48 # this we can run the entire suite to completion.
49 # Once the problem is cleared, remove this skip method.
50 skip = True
51 # END XXX
52
53 def setUp(self):
46 def setUp(self):
54
47
55 self.engines = []
48 self.engines = []
56
49
57 self.controller = ControllerService()
50 self.controller = ControllerService()
58 self.controller.startService()
51 self.controller.startService()
59 self.imultiengine = IMultiEngine(self.controller)
52 self.imultiengine = IMultiEngine(self.controller)
60 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
53 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
61
54
62 self.controller_tub = Tub()
55 self.controller_tub = Tub()
63 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
56 self.controller_tub.listenOn('tcp:10111:interface=127.0.0.1')
64 self.controller_tub.setLocation('127.0.0.1:10105')
57 self.controller_tub.setLocation('127.0.0.1:10111')
65
58
66 furl = self.controller_tub.registerReference(self.mec_referenceable)
59 furl = self.controller_tub.registerReference(self.mec_referenceable)
67 self.controller_tub.startService()
60 self.controller_tub.startService()
68
61
69 self.client_tub = ClientConnector()
62 self.client_tub = AsyncClientConnector()
70 d = self.client_tub.get_multiengine_client(furl)
63 d = self.client_tub.get_multiengine_client(furl_or_file=furl)
71 d.addCallback(self.handle_got_client)
64 d.addCallback(self.handle_got_client)
72 return d
65 return d
73
66
74 def handle_got_client(self, client):
67 def handle_got_client(self, client):
75 self.multiengine = client
68 self.multiengine = client
76
69
@@ -95,7 +88,7 b' class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMulti'
95 self.assertEquals(m.dist,'b')
88 self.assertEquals(m.dist,'b')
96 self.assertEquals(m.targets,'all')
89 self.assertEquals(m.targets,'all')
97 self.assertEquals(m.block,True)
90 self.assertEquals(m.block,True)
98
91
99 def test_map_default(self):
92 def test_map_default(self):
100 self.addEngine(4)
93 self.addEngine(4)
101 m = self.multiengine.mapper()
94 m = self.multiengine.mapper()
@@ -104,7 +97,7 b' class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMulti'
104 d.addCallback(lambda _: self.multiengine.map(lambda x: 2*x, range(10)))
97 d.addCallback(lambda _: self.multiengine.map(lambda x: 2*x, range(10)))
105 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
98 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
106 return d
99 return d
107
100
108 def test_map_noblock(self):
101 def test_map_noblock(self):
109 self.addEngine(4)
102 self.addEngine(4)
110 m = self.multiengine.mapper(block=False)
103 m = self.multiengine.mapper(block=False)
@@ -112,14 +105,14 b' class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMulti'
112 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
105 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
113 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
106 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
114 return d
107 return d
115
108
116 def test_mapper_fail(self):
109 def test_mapper_fail(self):
117 self.addEngine(4)
110 self.addEngine(4)
118 m = self.multiengine.mapper()
111 m = self.multiengine.mapper()
119 d = m.map(lambda x: 1/0, range(10))
112 d = m.map(lambda x: 1/0, range(10))
120 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
113 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
121 return d
114 return d
122
115
123 def test_parallel(self):
116 def test_parallel(self):
124 self.addEngine(4)
117 self.addEngine(4)
125 p = self.multiengine.parallel()
118 p = self.multiengine.parallel()
@@ -129,7 +122,7 b' class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMulti'
129 d = f(range(10))
122 d = f(range(10))
130 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
123 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
131 return d
124 return d
132
125
133 def test_parallel_noblock(self):
126 def test_parallel_noblock(self):
134 self.addEngine(1)
127 self.addEngine(1)
135 p = self.multiengine.parallel(block=False)
128 p = self.multiengine.parallel(block=False)
@@ -140,7 +133,7 b' class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMulti'
140 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
133 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
141 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
134 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
142 return d
135 return d
143
136
144 def test_parallel_fail(self):
137 def test_parallel_fail(self):
145 self.addEngine(4)
138 self.addEngine(4)
146 p = self.multiengine.parallel()
139 p = self.multiengine.parallel()
@@ -150,3 +143,4 b' class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMulti'
150 d = f(range(10))
143 d = f(range(10))
151 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
144 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
152 return d
145 return d
146
@@ -31,7 +31,7 b' from IPython.kernel.multienginefc import IFCSynchronousMultiEngine'
31 from IPython.kernel.taskfc import IFCTaskController
31 from IPython.kernel.taskfc import IFCTaskController
32 from IPython.kernel.util import printer
32 from IPython.kernel.util import printer
33 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
33 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
34 from IPython.kernel.clientconnector import ClientConnector
34 from IPython.kernel.clientconnector import AsyncClientConnector
35 from IPython.kernel.error import CompositeError
35 from IPython.kernel.error import CompositeError
36 from IPython.kernel.parallelfunction import ParallelFunction
36 from IPython.kernel.parallelfunction import ParallelFunction
37
37
@@ -48,42 +48,34 b' def _raise_it(f):'
48
48
49 class TaskTest(DeferredTestCase, ITaskControllerTestCase):
49 class TaskTest(DeferredTestCase, ITaskControllerTestCase):
50
50
51 # XXX (fperez) this is awful: I'm fully disabling this entire test class.
52 # Right now it's blocking the tests from running at all, and I don't know
53 # how to fix it. I hope Brian can have a stab at it, but at least by doing
54 # this we can run the entire suite to completion.
55 # Once the problem is cleared, remove this skip method.
56 skip = True
57 # END XXX
58
59 def setUp(self):
51 def setUp(self):
60
52
61 self.engines = []
53 self.engines = []
62
54
63 self.controller = cs.ControllerService()
55 self.controller = cs.ControllerService()
64 self.controller.startService()
56 self.controller.startService()
65 self.imultiengine = me.IMultiEngine(self.controller)
57 self.imultiengine = me.IMultiEngine(self.controller)
66 self.itc = taskmodule.ITaskController(self.controller)
58 self.itc = taskmodule.ITaskController(self.controller)
67 self.itc.failurePenalty = 0
59 self.itc.failurePenalty = 0
68
60
69 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
61 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
70 self.tc_referenceable = IFCTaskController(self.itc)
62 self.tc_referenceable = IFCTaskController(self.itc)
71
63
72 self.controller_tub = Tub()
64 self.controller_tub = Tub()
73 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
65 self.controller_tub.listenOn('tcp:10111:interface=127.0.0.1')
74 self.controller_tub.setLocation('127.0.0.1:10105')
66 self.controller_tub.setLocation('127.0.0.1:10111')
75
67
76 mec_furl = self.controller_tub.registerReference(self.mec_referenceable)
68 mec_furl = self.controller_tub.registerReference(self.mec_referenceable)
77 tc_furl = self.controller_tub.registerReference(self.tc_referenceable)
69 tc_furl = self.controller_tub.registerReference(self.tc_referenceable)
78 self.controller_tub.startService()
70 self.controller_tub.startService()
79
71
80 self.client_tub = ClientConnector()
72 self.client_tub = AsyncClientConnector()
81 d = self.client_tub.get_multiengine_client(mec_furl)
73 d = self.client_tub.get_multiengine_client(furl_or_file=mec_furl)
82 d.addCallback(self.handle_mec_client)
74 d.addCallback(self.handle_mec_client)
83 d.addCallback(lambda _: self.client_tub.get_task_client(tc_furl))
75 d.addCallback(lambda _: self.client_tub.get_task_client(furl_or_file=tc_furl))
84 d.addCallback(self.handle_tc_client)
76 d.addCallback(self.handle_tc_client)
85 return d
77 return d
86
78
87 def handle_mec_client(self, client):
79 def handle_mec_client(self, client):
88 self.multiengine = client
80 self.multiengine = client
89
81
@@ -103,7 +95,7 b' class TaskTest(DeferredTestCase, ITaskControllerTestCase):'
103 d.addBoth(lambda _: self.controller.stopService())
95 d.addBoth(lambda _: self.controller.stopService())
104 dlist.append(d)
96 dlist.append(d)
105 return defer.DeferredList(dlist)
97 return defer.DeferredList(dlist)
106
98
107 def test_mapper(self):
99 def test_mapper(self):
108 self.addEngine(1)
100 self.addEngine(1)
109 m = self.tc.mapper()
101 m = self.tc.mapper()
@@ -114,7 +106,7 b' class TaskTest(DeferredTestCase, ITaskControllerTestCase):'
114 self.assertEquals(m.recovery_task,None)
106 self.assertEquals(m.recovery_task,None)
115 self.assertEquals(m.depend,None)
107 self.assertEquals(m.depend,None)
116 self.assertEquals(m.block,True)
108 self.assertEquals(m.block,True)
117
109
118 def test_map_default(self):
110 def test_map_default(self):
119 self.addEngine(1)
111 self.addEngine(1)
120 m = self.tc.mapper()
112 m = self.tc.mapper()
@@ -123,21 +115,21 b' class TaskTest(DeferredTestCase, ITaskControllerTestCase):'
123 d.addCallback(lambda _: self.tc.map(lambda x: 2*x, range(10)))
115 d.addCallback(lambda _: self.tc.map(lambda x: 2*x, range(10)))
124 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
116 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
125 return d
117 return d
126
118
127 def test_map_noblock(self):
119 def test_map_noblock(self):
128 self.addEngine(1)
120 self.addEngine(1)
129 m = self.tc.mapper(block=False)
121 m = self.tc.mapper(block=False)
130 d = m.map(lambda x: 2*x, range(10))
122 d = m.map(lambda x: 2*x, range(10))
131 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
123 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
132 return d
124 return d
133
125
134 def test_mapper_fail(self):
126 def test_mapper_fail(self):
135 self.addEngine(1)
127 self.addEngine(1)
136 m = self.tc.mapper()
128 m = self.tc.mapper()
137 d = m.map(lambda x: 1/0, range(10))
129 d = m.map(lambda x: 1/0, range(10))
138 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
130 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
139 return d
131 return d
140
132
141 def test_parallel(self):
133 def test_parallel(self):
142 self.addEngine(1)
134 self.addEngine(1)
143 p = self.tc.parallel()
135 p = self.tc.parallel()
@@ -147,7 +139,7 b' class TaskTest(DeferredTestCase, ITaskControllerTestCase):'
147 d = f(range(10))
139 d = f(range(10))
148 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
140 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
149 return d
141 return d
150
142
151 def test_parallel_noblock(self):
143 def test_parallel_noblock(self):
152 self.addEngine(1)
144 self.addEngine(1)
153 p = self.tc.parallel(block=False)
145 p = self.tc.parallel(block=False)
@@ -157,7 +149,7 b' class TaskTest(DeferredTestCase, ITaskControllerTestCase):'
157 d = f(range(10))
149 d = f(range(10))
158 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
150 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
159 return d
151 return d
160
152
161 def test_parallel_fail(self):
153 def test_parallel_fail(self):
162 self.addEngine(1)
154 self.addEngine(1)
163 p = self.tc.parallel()
155 p = self.tc.parallel()
@@ -167,3 +159,4 b' class TaskTest(DeferredTestCase, ITaskControllerTestCase):'
167 d = f(range(10))
159 d = f(range(10))
168 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
160 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
169 return d
161 return d
162
@@ -15,7 +15,7 b''
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import os, sys
17 import os, sys
18 import threading, Queue, atexit
18 import threading, Queue
19
19
20 import twisted
20 import twisted
21 from twisted.internet import defer, reactor
21 from twisted.internet import defer, reactor
@@ -23,12 +23,10 b' import re'
23 import uuid
23 import uuid
24
24
25 from xml.etree import ElementTree as ET
25 from xml.etree import ElementTree as ET
26 from xml.dom import minidom
27
26
28 from IPython.core.component import Component
27 from IPython.core.component import Component
29 from IPython.external import Itpl
30 from IPython.utils.traitlets import (
28 from IPython.utils.traitlets import (
31 Str, Int, List, Unicode, Instance,
29 Str, Int, List, Instance,
32 Enum, Bool, CStr
30 Enum, Bool, CStr
33 )
31 )
34
32
@@ -31,7 +31,7 b' import sys'
31 import threading
31 import threading
32
32
33 from IPython.core.ultratb import AutoFormattedTB
33 from IPython.core.ultratb import AutoFormattedTB
34 from IPython.utils.genutils import warn,error
34 from IPython.utils.warn import warn, error
35
35
36 class BackgroundJobManager:
36 class BackgroundJobManager:
37 """Class to manage a pool of backgrounded threaded jobs.
37 """Class to manage a pool of backgrounded threaded jobs.
@@ -1,15 +1,18 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 A module to change reload() so that it acts recursively.
3 A module to change reload() so that it acts recursively.
4 To enable it type:
4 To enable it type::
5 >>> import __builtin__, deepreload
6 >>> __builtin__.reload = deepreload.reload
7
5
8 You can then disable it with:
6 import __builtin__, deepreload
9 >>> __builtin__.reload = deepreload.original_reload
7 __builtin__.reload = deepreload.reload
8
9 You can then disable it with::
10
11 __builtin__.reload = deepreload.original_reload
10
12
11 Alternatively, you can add a dreload builtin alongside normal reload with:
13 Alternatively, you can add a dreload builtin alongside normal reload with::
12 >>> __builtin__.dreload = deepreload.reload
14
15 __builtin__.dreload = deepreload.reload
13
16
14 This code is almost entirely based on knee.py from the standard library.
17 This code is almost entirely based on knee.py from the standard library.
15 """
18 """
@@ -176,7 +176,8 b' import shlex'
176 import sys
176 import sys
177
177
178 from IPython.utils.PyColorize import Parser
178 from IPython.utils.PyColorize import Parser
179 from IPython.utils.genutils import marquee, file_read, file_readlines, Term
179 from IPython.utils.io import file_read, file_readlines, Term
180 from IPython.utils.text import marquee
180
181
181 __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError']
182 __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError']
182
183
@@ -543,7 +544,7 b' class ClearMixin(object):'
543 """Method called before executing each block.
544 """Method called before executing each block.
544
545
545 This one simply clears the screen."""
546 This one simply clears the screen."""
546 from IPython.utils.platutils import term_clear
547 from IPython.utils.terminal import term_clear
547 term_clear()
548 term_clear()
548
549
549 class ClearDemo(ClearMixin,Demo):
550 class ClearDemo(ClearMixin,Demo):
@@ -12,10 +12,12 b' Fernando Perez.'
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
16 # Imports
17 # Imports
17 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
18 from IPython.utils.genutils import flag_calls
19
20 from IPython.utils.decorators import flag_calls
19
21
20 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
21 # Main classes and functions
23 # Main classes and functions
@@ -50,7 +50,7 b' del InteractiveShell,prefilter_shell'
50
50
51 # Provide pysh and further shell-oriented services
51 # Provide pysh and further shell-oriented services
52 import os,sys,shutil
52 import os,sys,shutil
53 from IPython.utils.genutils import system,shell,getoutput,getoutputerror
53 from IPython.utils.process import system,shell,getoutput,getoutputerror
54
54
55 # Short aliases for getting shell output as a string and a list
55 # Short aliases for getting shell output as a string and a list
56 sout = getoutput
56 sout = getoutput
@@ -10,6 +10,7 b' var = !ls'
10
10
11 from IPython.core import ipapi
11 from IPython.core import ipapi
12 from IPython.core.error import TryNext
12 from IPython.core.error import TryNext
13 from IPython.utils.text import make_quoted_expr
13 from IPython.utils.genutils import *
14 from IPython.utils.genutils import *
14
15
15 ip = ipapi.get()
16 ip = ipapi.get()
@@ -12,7 +12,7 b' do the same in default completer.'
12 from IPython.core import ipapi
12 from IPython.core import ipapi
13 from IPython.core.error import TryNext
13 from IPython.core.error import TryNext
14 from IPython.utils import generics
14 from IPython.utils import generics
15 from IPython.utils.genutils import dir2
15 from IPython.utils.dir2 import dir2
16
16
17 def attr_matches(self, text):
17 def attr_matches(self, text):
18 """Compute matches when text contains a dot.
18 """Compute matches when text contains a dot.
@@ -16,6 +16,7 b' import pickleshare'
16 import inspect,pickle,os,sys,textwrap
16 import inspect,pickle,os,sys,textwrap
17 from IPython.core.fakemodule import FakeModule
17 from IPython.core.fakemodule import FakeModule
18 from IPython.utils.ipstruct import Struct
18 from IPython.utils.ipstruct import Struct
19 from IPython.utils.warn import error
19
20
20
21
21 def refresh_variables(ip, key=None):
22 def refresh_variables(ip, key=None):
@@ -1,6 +1,6 b''
1 import inspect
1 import inspect
2 from IPython.core import ipapi
2 from IPython.core import ipapi
3 from IPython.utils.genutils import arg_split
3 from IPython.utils.process import arg_split
4 ip = ipapi.get()
4 ip = ipapi.get()
5
5
6 from IPython.core import debugger
6 from IPython.core import debugger
@@ -45,10 +45,9 b' from subprocess import *'
45 import os,shlex,sys,time
45 import os,shlex,sys,time
46 import threading,Queue
46 import threading,Queue
47
47
48 from IPython.utils import genutils
49
50 from IPython.core import ipapi
48 from IPython.core import ipapi
51 from IPython.core.error import TryNext
49 from IPython.core.error import TryNext
50 from IPython.utils.text import make_quoted_expr
52
51
53 if os.name == 'nt':
52 if os.name == 'nt':
54 def kill_process(pid):
53 def kill_process(pid):
@@ -126,8 +125,8 b' def jobctrl_prefilter_f(self,line):'
126
125
127 line = ip.expand_aliases(fn,rest)
126 line = ip.expand_aliases(fn,rest)
128 if not _jobq:
127 if not _jobq:
129 return 'get_ipython().startjob(%s)' % genutils.make_quoted_expr(line)
128 return 'get_ipython().startjob(%s)' % make_quoted_expr(line)
130 return 'get_ipython().jobq(%s)' % genutils.make_quoted_expr(line)
129 return 'get_ipython().jobq(%s)' % make_quoted_expr(line)
131
130
132 raise TryNext
131 raise TryNext
133
132
@@ -1,6 +1,17 b''
1 """Testing support (tools to test IPython itself).
1 """Testing support (tools to test IPython itself).
2 """
2 """
3
3
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2009 The IPython Development Team
6 #
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
10
11 #-----------------------------------------------------------------------------
12 # Functions
13 #-----------------------------------------------------------------------------
14
4 # User-level entry point for testing
15 # User-level entry point for testing
5 def test():
16 def test():
6 """Run the entire IPython test suite.
17 """Run the entire IPython test suite.
@@ -4,6 +4,17 b' This is just so we can use 2.6 features when running in 2.5, the code below is'
4 copied verbatim from the stdlib's collections and doctest modules.
4 copied verbatim from the stdlib's collections and doctest modules.
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2009 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
7 from keyword import iskeyword as _iskeyword
18 from keyword import iskeyword as _iskeyword
8 from operator import itemgetter as _itemgetter
19 from operator import itemgetter as _itemgetter
9 import sys as _sys
20 import sys as _sys
@@ -1,10 +1,17 b''
1 """Implementation of the parametric test support for Python 2.x
1 """Implementation of the parametric test support for Python 2.x
2 """
2 """
3
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2009 The IPython Development Team
6 #
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
10
3 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
4 # Imports
12 # Imports
5 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
6
14
7 # Stdlib
8 import unittest
15 import unittest
9 from compiler.consts import CO_GENERATOR
16 from compiler.consts import CO_GENERATOR
10
17
@@ -3,11 +3,18 b''
3 Thanks for the py3 version to Robert Collins, from the Testing in Python
3 Thanks for the py3 version to Robert Collins, from the Testing in Python
4 mailing list.
4 mailing list.
5 """
5 """
6
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2009 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
6 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
7 # Imports
15 # Imports
8 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
9
17
10 # Stdlib
11 import unittest
18 import unittest
12 from unittest import TestSuite
19 from unittest import TestSuite
13
20
@@ -9,18 +9,22 b' done.'
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Module imports
12 # Copyright (C) 2009 The IPython Development Team
13 #
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
17
18 #-----------------------------------------------------------------------------
19 # Imports
13 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
14
21
15 # From the standard library
16 import __builtin__
22 import __builtin__
17 import commands
23 import commands
18 import new
19 import os
24 import os
20 import sys
25 import sys
21
26
22 from . import tools
27 from . import tools
23 from IPython.utils.genutils import Term
24
28
25 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
26 # Functions
30 # Functions
@@ -35,7 +39,7 b' class py_file_finder(object):'
35 self.test_filename = test_filename
39 self.test_filename = test_filename
36
40
37 def __call__(self,name):
41 def __call__(self,name):
38 from IPython.utils.genutils import get_py_filename
42 from IPython.utils.path import get_py_filename
39 try:
43 try:
40 return get_py_filename(name)
44 return get_py_filename(name)
41 except IOError:
45 except IOError:
@@ -93,24 +97,25 b' class ipnsdict(dict):'
93 # correct for that ourselves, to ensure consitency with the 'real'
97 # correct for that ourselves, to ensure consitency with the 'real'
94 # ipython.
98 # ipython.
95 self['__builtins__'] = __builtin__
99 self['__builtins__'] = __builtin__
96
100
97
101
98 def get_ipython():
102 def get_ipython():
99 # This will get replaced by the real thing once we start IPython below
103 # This will get replaced by the real thing once we start IPython below
100 return start_ipython()
104 return start_ipython()
101
105
106
102 def start_ipython():
107 def start_ipython():
103 """Start a global IPython shell, which we need for IPython-specific syntax.
108 """Start a global IPython shell, which we need for IPython-specific syntax.
104 """
109 """
105 global get_ipython
110 global get_ipython
106
111
107 # This function should only ever run once!
112 # This function should only ever run once!
108 if hasattr(start_ipython,'already_called'):
113 if hasattr(start_ipython, 'already_called'):
109 return
114 return
110 start_ipython.already_called = True
115 start_ipython.already_called = True
111
116
112 # Ok, first time we're called, go ahead
117 # Ok, first time we're called, go ahead
113 from IPython.core import ipapp, iplib
118 from IPython.core import iplib
114
119
115 def xsys(cmd):
120 def xsys(cmd):
116 """Execute a command and print its output.
121 """Execute a command and print its output.
@@ -128,26 +133,27 b' def start_ipython():'
128 _main = sys.modules.get('__main__')
133 _main = sys.modules.get('__main__')
129
134
130 # Create custom argv and namespaces for our IPython to be test-friendly
135 # Create custom argv and namespaces for our IPython to be test-friendly
131 argv = tools.default_argv()
136 config = tools.default_config()
132 user_ns, global_ns = iplib.make_user_namespaces(ipnsdict(), {})
137
133
134 # Create and initialize our test-friendly IPython instance.
138 # Create and initialize our test-friendly IPython instance.
135 ip = ipapp.IPythonApp(argv, user_ns=user_ns, user_global_ns=global_ns)
139 shell = iplib.InteractiveShell(
136 ip.initialize()
140 parent=None, config=config,
141 user_ns=ipnsdict(), user_global_ns={}
142 )
137
143
138 # A few more tweaks needed for playing nicely with doctests...
144 # A few more tweaks needed for playing nicely with doctests...
139
145
140 # These traps are normally only active for interactive use, set them
146 # These traps are normally only active for interactive use, set them
141 # permanently since we'll be mocking interactive sessions.
147 # permanently since we'll be mocking interactive sessions.
142 ip.shell.builtin_trap.set()
148 shell.builtin_trap.set()
143
149
144 # Set error printing to stdout so nose can doctest exceptions
150 # Set error printing to stdout so nose can doctest exceptions
145 ip.shell.InteractiveTB.out_stream = 'stdout'
151 shell.InteractiveTB.out_stream = 'stdout'
146
152
147 # Modify the IPython system call with one that uses getoutput, so that we
153 # Modify the IPython system call with one that uses getoutput, so that we
148 # can capture subcommands and print them to Python's stdout, otherwise the
154 # can capture subcommands and print them to Python's stdout, otherwise the
149 # doctest machinery would miss them.
155 # doctest machinery would miss them.
150 ip.shell.system = xsys
156 shell.system = xsys
151
157
152 # IPython is ready, now clean up some global state...
158 # IPython is ready, now clean up some global state...
153
159
@@ -160,7 +166,7 b' def start_ipython():'
160 # So that ipython magics and aliases can be doctested (they work by making
166 # So that ipython magics and aliases can be doctested (they work by making
161 # a call into a global _ip object). Also make the top-level get_ipython
167 # a call into a global _ip object). Also make the top-level get_ipython
162 # now return this without recursively calling here again.
168 # now return this without recursively calling here again.
163 _ip = ip.shell
169 _ip = shell
164 get_ipython = _ip.get_ipython
170 get_ipython = _ip.get_ipython
165 __builtin__._ip = _ip
171 __builtin__._ip = _ip
166 __builtin__.get_ipython = get_ipython
172 __builtin__.get_ipython = get_ipython
@@ -17,13 +17,19 b' will change in the future.'
17 """
17 """
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Module imports
20 # Copyright (C) 2009 The IPython Development Team
21 #
22 # Distributed under the terms of the BSD License. The full license is in
23 # the file COPYING, distributed as part of this software.
24 #-----------------------------------------------------------------------------
25
26 #-----------------------------------------------------------------------------
27 # Imports
21 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
22
29
23 # Stdlib
30 # Stdlib
24 import os
31 import os
25 import os.path as path
32 import os.path as path
26 import platform
27 import signal
33 import signal
28 import sys
34 import sys
29 import subprocess
35 import subprocess
@@ -31,15 +37,6 b' import tempfile'
31 import time
37 import time
32 import warnings
38 import warnings
33
39
34
35 # Ugly, but necessary hack to ensure the test suite finds our version of
36 # IPython and not a possibly different one that may exist system-wide.
37 # Note that this must be done here, so the imports that come next work
38 # correctly even if IPython isn't installed yet.
39 p = os.path
40 ippath = p.abspath(p.join(p.dirname(__file__),'..','..'))
41 sys.path.insert(0, ippath)
42
43 # Note: monkeypatch!
40 # Note: monkeypatch!
44 # We need to monkeypatch a small problem in nose itself first, before importing
41 # We need to monkeypatch a small problem in nose itself first, before importing
45 # it for actual use. This should get into nose upstream, but its release cycle
42 # it for actual use. This should get into nose upstream, but its release cycle
@@ -50,11 +47,11 b' import nose.plugins.builtin'
50 from nose.core import TestProgram
47 from nose.core import TestProgram
51
48
52 # Our own imports
49 # Our own imports
53 from IPython.core import release
50 from IPython.utils.path import get_ipython_module_path
54 from IPython.utils import genutils
51 from IPython.utils.process import find_cmd, pycmd2argv
55 from IPython.utils.platutils import find_cmd, FindCmdError
52 from IPython.utils.sysinfo import sys_info
53
56 from IPython.testing import globalipapp
54 from IPython.testing import globalipapp
57 from IPython.testing import tools
58 from IPython.testing.plugin.ipdoctest import IPythonDoctest
55 from IPython.testing.plugin.ipdoctest import IPythonDoctest
59
56
60 pjoin = path.join
57 pjoin = path.join
@@ -64,16 +61,11 b' pjoin = path.join'
64 # Globals
61 # Globals
65 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
66
63
67 # By default, we assume IPython has been installed. But if the test suite is
68 # being run from a source tree that has NOT been installed yet, this flag can
69 # be set to False by the entry point scripts, to let us know that we must call
70 # the source tree versions of the scripts which manipulate sys.path instead of
71 # assuming that things exist system-wide.
72 INSTALLED = True
73
64
74 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
75 # Warnings control
66 # Warnings control
76 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
68
77 # Twisted generates annoying warnings with Python 2.6, as will do other code
69 # Twisted generates annoying warnings with Python 2.6, as will do other code
78 # that imports 'sets' as of today
70 # that imports 'sets' as of today
79 warnings.filterwarnings('ignore', 'the sets module is deprecated',
71 warnings.filterwarnings('ignore', 'the sets module is deprecated',
@@ -124,9 +116,7 b" have['gobject'] = test_for('gobject')"
124 def report():
116 def report():
125 """Return a string with a summary report of test-related variables."""
117 """Return a string with a summary report of test-related variables."""
126
118
127 out = [ genutils.sys_info() ]
119 out = [ sys_info() ]
128
129 out.append('\nRunning from an installed IPython: %s\n' % INSTALLED)
130
120
131 avail = []
121 avail = []
132 not_avail = []
122 not_avail = []
@@ -152,11 +142,11 b' def report():'
152
142
153 def make_exclude():
143 def make_exclude():
154 """Make patterns of modules and packages to exclude from testing.
144 """Make patterns of modules and packages to exclude from testing.
155
145
156 For the IPythonDoctest plugin, we need to exclude certain patterns that
146 For the IPythonDoctest plugin, we need to exclude certain patterns that
157 cause testing problems. We should strive to minimize the number of
147 cause testing problems. We should strive to minimize the number of
158 skipped modules, since this means untested code. As the testing
148 skipped modules, since this means untested code.
159 machinery solidifies, this list should eventually become empty.
149
160 These modules and packages will NOT get scanned by nose at all for tests.
150 These modules and packages will NOT get scanned by nose at all for tests.
161 """
151 """
162 # Simple utility to make IPython paths more readably, we need a lot of
152 # Simple utility to make IPython paths more readably, we need a lot of
@@ -198,18 +188,12 b' def make_exclude():'
198 if not have['objc']:
188 if not have['objc']:
199 exclusions.append(ipjoin('frontend', 'cocoa'))
189 exclusions.append(ipjoin('frontend', 'cocoa'))
200
190
201 if not sys.platform == 'win32':
202 exclusions.append(ipjoin('utils', 'platutils_win32'))
203
204 # These have to be skipped on win32 because the use echo, rm, cd, etc.
191 # These have to be skipped on win32 because the use echo, rm, cd, etc.
205 # See ticket https://bugs.launchpad.net/bugs/366982
192 # See ticket https://bugs.launchpad.net/bugs/366982
206 if sys.platform == 'win32':
193 if sys.platform == 'win32':
207 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
194 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
208 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
195 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
209
196
210 if not os.name == 'posix':
211 exclusions.append(ipjoin('utils', 'platutils_posix'))
212
213 if not have['pexpect']:
197 if not have['pexpect']:
214 exclusions.extend([ipjoin('scripts', 'irunner'),
198 exclusions.extend([ipjoin('scripts', 'irunner'),
215 ipjoin('lib', 'irunner')])
199 ipjoin('lib', 'irunner')])
@@ -255,20 +239,13 b' class IPTester(object):'
255 """Create new test runner."""
239 """Create new test runner."""
256 p = os.path
240 p = os.path
257 if runner == 'iptest':
241 if runner == 'iptest':
258 if INSTALLED:
242 iptest_app = get_ipython_module_path('IPython.testing.iptest')
259 self.runner = tools.cmd2argv(
243 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
260 p.abspath(find_cmd('iptest'))) + sys.argv[1:]
244 elif runner == 'trial':
261 else:
262 # Find our own 'iptest' script OS-level entry point. Don't
263 # look system-wide, so we are sure we pick up *this one*. And
264 # pass through to subprocess call our own sys.argv
265 ippath = p.abspath(p.join(p.dirname(__file__),'..','..'))
266 script = p.join(ippath, 'iptest.py')
267 self.runner = tools.cmd2argv(script) + sys.argv[1:]
268
269 else:
270 # For trial, it needs to be installed system-wide
245 # For trial, it needs to be installed system-wide
271 self.runner = tools.cmd2argv(p.abspath(find_cmd('trial')))
246 self.runner = pycmd2argv(p.abspath(find_cmd('trial')))
247 else:
248 raise Exception('Not a valid test runner: %s' % repr(runner))
272 if params is None:
249 if params is None:
273 params = []
250 params = []
274 if isinstance(params, str):
251 if isinstance(params, str):
@@ -290,6 +267,8 b' class IPTester(object):'
290 # fashioned' way to do it, but it works just fine. If someone
267 # fashioned' way to do it, but it works just fine. If someone
291 # later can clean this up that's fine, as long as the tests run
268 # later can clean this up that's fine, as long as the tests run
292 # reliably in win32.
269 # reliably in win32.
270 # What types of problems are you having. They may be related to
271 # running Python in unboffered mode. BG.
293 return os.system(' '.join(self.call_args))
272 return os.system(' '.join(self.call_args))
294 else:
273 else:
295 def _run_cmd(self):
274 def _run_cmd(self):
@@ -343,9 +322,9 b' def make_runners():'
343
322
344 # And add twisted ones if conditions are met
323 # And add twisted ones if conditions are met
345 if have['zope.interface'] and have['twisted'] and have['foolscap']:
324 if have['zope.interface'] and have['twisted'] and have['foolscap']:
346 # Note that we list the kernel here, though the bulk of it is
325 # We only list IPython.kernel for testing using twisted.trial as
347 # twisted-based, because nose picks up doctests that twisted doesn't.
326 # nose and twisted.trial have conflicts that make the testing system
348 nose_pkg_names.append('kernel')
327 # unstable.
349 trial_pkg_names.append('kernel')
328 trial_pkg_names.append('kernel')
350
329
351 # For debugging this code, only load quick stuff
330 # For debugging this code, only load quick stuff
@@ -31,7 +31,6 b' from __future__ import absolute_import'
31 # the file COPYING, distributed as part of this software.
31 # the file COPYING, distributed as part of this software.
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33
33
34
35 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
36 # Imports
35 # Imports
37 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
@@ -38,7 +38,7 b' import tempfile'
38
38
39 # IPython-specific libraries
39 # IPython-specific libraries
40 from IPython.lib import irunner
40 from IPython.lib import irunner
41 from IPython.utils.genutils import fatal
41 from IPython.utils.warn import fatal
42
42
43 class IndentOut(object):
43 class IndentOut(object):
44 """A simple output stream that indents all output by a fixed amount.
44 """A simple output stream that indents all output by a fixed amount.
@@ -5,10 +5,25 b' Once this is fixed in upstream nose we can disable it.'
5
5
6 Note: merely importing this module causes the monkeypatch to be applied."""
6 Note: merely importing this module causes the monkeypatch to be applied."""
7
7
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2009 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18
8 import unittest
19 import unittest
9 import nose.loader
20 import nose.loader
10 from inspect import ismethod, isfunction
21 from inspect import ismethod, isfunction
11
22
23 #-----------------------------------------------------------------------------
24 # Classes and functions
25 #-----------------------------------------------------------------------------
26
12 def getTestCaseNames(self, testCaseClass):
27 def getTestCaseNames(self, testCaseClass):
13 """Override to select with selector, unless
28 """Override to select with selector, unless
14 config.getTestCaseNamesCompat is True
29 config.getTestCaseNamesCompat is True
@@ -5,10 +5,27 b" parametric code. We just need to double-check that the new code doesn't clash"
5 with Twisted (we know it works with nose and unittest).
5 with Twisted (we know it works with nose and unittest).
6 """
6 """
7
7
8 __all__ = ['parametric','Parametric']
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2009 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18
9
19
10 from twisted.trial.unittest import TestCase
20 from twisted.trial.unittest import TestCase
11
21
22 #-----------------------------------------------------------------------------
23 # Classes and functions
24 #-----------------------------------------------------------------------------
25
26 __all__ = ['parametric','Parametric']
27
28
12 def partial(f, *partial_args, **partial_kwargs):
29 def partial(f, *partial_args, **partial_kwargs):
13 """Generate a partial class method.
30 """Generate a partial class method.
14
31
@@ -20,6 +37,7 b' def partial(f, *partial_args, **partial_kwargs):'
20
37
21 return partial_func
38 return partial_func
22
39
40
23 def parametric(f):
41 def parametric(f):
24 """Mark f as a parametric test.
42 """Mark f as a parametric test.
25
43
@@ -27,6 +45,7 b' def parametric(f):'
27 f._parametric = True
45 f._parametric = True
28 return classmethod(f)
46 return classmethod(f)
29
47
48
30 def Parametric(cls):
49 def Parametric(cls):
31 """Register parametric tests with a class.
50 """Register parametric tests with a class.
32
51
@@ -56,3 +75,4 b' def Parametric(cls):'
56
75
57 # rename test generator so it isn't called again by nose
76 # rename test generator so it isn't called again by nose
58 test_gen.im_func.func_name = '__done_' + test_name
77 test_gen.im_func.func_name = '__done_' + test_name
78
@@ -1,6 +1,6 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Tests for decorators.py compatibility with Twisted.trial
3 Tests for decorators_trial.py
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
@@ -14,24 +14,19 b' Tests for decorators.py compatibility with Twisted.trial'
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 # Tell nose to skip this module, since this is for twisted only
17 # Tell nose to skip this module
18 __test__ = {}
18 __test__ = {}
19
19
20 import os
20 import os
21 import sys
21 import sys
22
22
23 from twisted.trial import unittest
23 from twisted.trial import unittest
24 import IPython.testing.decorators as dec
24 import IPython.testing.decorators_trial as dec
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Tests
27 # Tests
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 # Note: this code is identical to that in test_decorators, but that one uses
31 # stdlib unittest, not the one from twisted, which we are using here. While
32 # somewhat redundant, we want to check both with the stdlib and with twisted,
33 # so the duplication is OK.
34
35 class TestDecoratorsTrial(unittest.TestCase):
30 class TestDecoratorsTrial(unittest.TestCase):
36
31
37 @dec.skip()
32 @dec.skip()
@@ -15,22 +15,22 b' Authors'
15 - Fernando Perez <Fernando.Perez@berkeley.edu>
15 - Fernando Perez <Fernando.Perez@berkeley.edu>
16 """
16 """
17
17
18 #*****************************************************************************
18 from __future__ import absolute_import
19 # Copyright (C) 2009 The IPython Development Team
19
20 #-----------------------------------------------------------------------------
21 # Copyright (C) 2009 The IPython Development Team
20 #
22 #
21 # Distributed under the terms of the BSD License. The full license is in
23 # Distributed under the terms of the BSD License. The full license is in
22 # the file COPYING, distributed as part of this software.
24 # the file COPYING, distributed as part of this software.
23 #*****************************************************************************
25 #-----------------------------------------------------------------------------
24
26
25 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
26 # Required modules and packages
28 # Imports
27 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
28 from __future__ import absolute_import
29
30
30 import os
31 import os
31 import re
32 import re
32 import sys
33 import sys
33 import tempfile
34
34
35 try:
35 try:
36 # These tools are used by parts of the runtime, so we make the nose
36 # These tools are used by parts of the runtime, so we make the nose
@@ -41,7 +41,10 b' try:'
41 except ImportError:
41 except ImportError:
42 has_nose = False
42 has_nose = False
43
43
44 from IPython.utils import genutils, platutils
44 from IPython.config.loader import Config
45 from IPython.utils.process import find_cmd, getoutputerror
46 from IPython.utils.text import list_strings
47 from IPython.utils.io import temp_pyfile
45
48
46 from . import decorators as dec
49 from . import decorators as dec
47
50
@@ -49,13 +52,6 b' from . import decorators as dec'
49 # Globals
52 # Globals
50 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
51
54
52 # By default, we assume IPython has been installed. But if the test suite is
53 # being run from a source tree that has NOT been installed yet, this flag can
54 # be set to False by the entry point scripts, to let us know that we must call
55 # the source tree versions of the scripts which manipulate sys.path instead of
56 # assuming that things exist system-wide.
57 INSTALLED = True
58
59 # Make a bunch of nose.tools assert wrappers that can be used in test
55 # Make a bunch of nose.tools assert wrappers that can be used in test
60 # generators. This will expose an assert* function for each one in nose.tools.
56 # generators. This will expose an assert* function for each one in nose.tools.
61
57
@@ -107,7 +103,7 b' def full_path(startPath,files):'
107 ['/a.txt']
103 ['/a.txt']
108 """
104 """
109
105
110 files = genutils.list_strings(files)
106 files = list_strings(files)
111 base = os.path.split(startPath)[0]
107 base = os.path.split(startPath)[0]
112 return [ os.path.join(base,f) for f in files ]
108 return [ os.path.join(base,f) for f in files ]
113
109
@@ -156,63 +152,6 b' def parse_test_output(txt):'
156 parse_test_output.__test__ = False
152 parse_test_output.__test__ = False
157
153
158
154
159 def cmd2argv(cmd):
160 r"""Take the path of a command and return a list (argv-style).
161
162 For a given path ``cmd``, this returns [cmd] if cmd's extension is .exe,
163 .com or .bat, and ['python', cmd] otherwise.
164
165 This is mostly a Windows utility, to deal with the fact that the scripts in
166 Windows get wrapped in .exe entry points, so we have to call them
167 differently.
168
169 Parameters
170 ----------
171 cmd : string
172 The path of the command.
173
174 Returns
175 -------
176 argv-style list.
177
178 Examples
179 --------
180 In [2]: cmd2argv('/usr/bin/ipython')
181 Out[2]: ['python', '/usr/bin/ipython']
182
183 In [3]: cmd2argv(r'C:\Python26\Scripts\ipython.exe')
184 Out[3]: ['C:\\Python26\\Scripts\\ipython.exe']
185 """
186 ext = os.path.splitext(cmd)[1]
187 if ext in ['.exe', '.com', '.bat']:
188 return [cmd]
189 else:
190 return ['python', cmd]
191
192
193 def temp_pyfile(src, ext='.py'):
194 """Make a temporary python file, return filename and filehandle.
195
196 Parameters
197 ----------
198 src : string or list of strings (no need for ending newlines if list)
199 Source code to be written to the file.
200
201 ext : optional, string
202 Extension for the generated file.
203
204 Returns
205 -------
206 (filename, open filehandle)
207 It is the caller's responsibility to close the open file and unlink it.
208 """
209 fname = tempfile.mkstemp(ext)[1]
210 f = open(fname,'w')
211 f.write(src)
212 f.flush()
213 return fname, f
214
215
216 def default_argv():
155 def default_argv():
217 """Return a valid default argv for creating testing instances of ipython"""
156 """Return a valid default argv for creating testing instances of ipython"""
218
157
@@ -220,7 +159,16 b' def default_argv():'
220 # Other defaults to minimize side effects on stdout
159 # Other defaults to minimize side effects on stdout
221 '--colors=NoColor', '--no-term-title','--no-banner',
160 '--colors=NoColor', '--no-term-title','--no-banner',
222 '--autocall=0']
161 '--autocall=0']
223
162
163
164 def default_config():
165 """Return a config object with good defaults for testing."""
166 config = Config()
167 config.InteractiveShell.colors = 'NoColor'
168 config.InteractiveShell.term_title = False,
169 config.InteractiveShell.autocall = 0
170 return config
171
224
172
225 def ipexec(fname, options=None):
173 def ipexec(fname, options=None):
226 """Utility to call 'ipython filename'.
174 """Utility to call 'ipython filename'.
@@ -252,20 +200,12 b' def ipexec(fname, options=None):'
252 _ip = get_ipython()
200 _ip = get_ipython()
253 test_dir = os.path.dirname(__file__)
201 test_dir = os.path.dirname(__file__)
254
202
255 # Find the ipython script from the package we're using, so that the test
203 ipython_cmd = find_cmd('ipython')
256 # suite can be run from the source tree without an installed IPython
257 p = os.path
258 if INSTALLED:
259 ipython_cmd = platutils.find_cmd('ipython')
260 else:
261 ippath = p.abspath(p.join(p.dirname(__file__),'..','..'))
262 ipython_script = p.join(ippath, 'ipython.py')
263 ipython_cmd = 'python "%s"' % ipython_script
264 # Absolute path for filename
204 # Absolute path for filename
265 full_fname = p.join(test_dir, fname)
205 full_fname = os.path.join(test_dir, fname)
266 full_cmd = '%s %s %s' % (ipython_cmd, cmdargs, full_fname)
206 full_cmd = '%s %s %s' % (ipython_cmd, cmdargs, full_fname)
267 #print >> sys.stderr, 'FULL CMD:', full_cmd # dbg
207 #print >> sys.stderr, 'FULL CMD:', full_cmd # dbg
268 return genutils.getoutputerror(full_cmd)
208 return getoutputerror(full_cmd)
269
209
270
210
271 def ipexec_validate(fname, expected_out, expected_err='',
211 def ipexec_validate(fname, expected_out, expected_err='',
@@ -1,23 +1,24 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """This file contains utility classes for performing tests with Deferreds.
2 """This file contains utility classes for performing tests with Deferreds.
3 """
3 """
4 __docformat__ = "restructuredtext en"
4 #-----------------------------------------------------------------------------
5 #-------------------------------------------------------------------------------
5 # Copyright (C) 2009 The IPython Development Team
6 # Copyright (C) 2005 Fernando Perez <fperez@colorado.edu>
7 # Brian E Granger <ellisonbg@gmail.com>
8 # Benjamin Ragan-Kelley <benjaminrk@gmail.com>
9 #
6 #
10 # Distributed under the terms of the BSD License. The full license is in
7 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
13
10
14 #-------------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
15 # Imports
12 # Imports
16 #-------------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
17
14
18 from twisted.trial import unittest
15 from twisted.trial import unittest
19 from twisted.internet import defer
16 from twisted.internet import defer
20
17
18 #-----------------------------------------------------------------------------
19 # Classes and functions
20 #-----------------------------------------------------------------------------
21
21 class DeferredTestCase(unittest.TestCase):
22 class DeferredTestCase(unittest.TestCase):
22
23
23 def assertDeferredEquals(self, deferred, expectedResult,
24 def assertDeferredEquals(self, deferred, expectedResult,
@@ -1,8 +1,9 b''
1 # encoding: utf-8
1 """Generic functions for extending IPython.
2 """Generic functions for extending IPython.
2
3
3 See http://cheeseshop.python.org/pypi/simplegeneric.
4 See http://cheeseshop.python.org/pypi/simplegeneric.
4
5
5 Here is an example from genutils.py::
6 Here is an example from IPython.utils.text::
6
7
7 def print_lsstring(arg):
8 def print_lsstring(arg):
8 "Prettier (non-repr-like) and more informative printer for LSString"
9 "Prettier (non-repr-like) and more informative printer for LSString"
@@ -12,19 +13,37 b' Here is an example from genutils.py::'
12 print_lsstring = result_display.when_type(LSString)(print_lsstring)
13 print_lsstring = result_display.when_type(LSString)(print_lsstring)
13 """
14 """
14
15
16 #-----------------------------------------------------------------------------
17 # Copyright (C) 2008-2009 The IPython Development Team
18 #
19 # Distributed under the terms of the BSD License. The full license is in
20 # the file COPYING, distributed as part of this software.
21 #-----------------------------------------------------------------------------
22
23 #-----------------------------------------------------------------------------
24 # Imports
25 #-----------------------------------------------------------------------------
26
15 from IPython.core.error import TryNext
27 from IPython.core.error import TryNext
16 from IPython.external.simplegeneric import generic
28 from IPython.external.simplegeneric import generic
17
29
30 #-----------------------------------------------------------------------------
31 # Imports
32 #-----------------------------------------------------------------------------
33
34
18 @generic
35 @generic
19 def result_display(result):
36 def result_display(result):
20 """Print the result of computation."""
37 """Print the result of computation."""
21 raise TryNext
38 raise TryNext
22
39
40
23 @generic
41 @generic
24 def inspect_object(obj):
42 def inspect_object(obj):
25 """Called when you do obj?"""
43 """Called when you do obj?"""
26 raise TryNext
44 raise TryNext
27
45
46
28 @generic
47 @generic
29 def complete_object(obj, prev_completions):
48 def complete_object(obj, prev_completions):
30 """Custom completer dispatching for python objects.
49 """Custom completer dispatching for python objects.
@@ -41,3 +60,5 b' def complete_object(obj, prev_completions):'
41 own_attrs + prev_completions.
60 own_attrs + prev_completions.
42 """
61 """
43 raise TryNext
62 raise TryNext
63
64
@@ -1,6 +1,22 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
1 # encoding: utf-8
2 """
3 Utilities using Growl on OS X for notifications.
4 """
3
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 #-----------------------------------------------------------------------------
18 # Code
19 #-----------------------------------------------------------------------------
4
20
5 class IPythonGrowlError(Exception):
21 class IPythonGrowlError(Exception):
6 pass
22 pass
@@ -1,4 +1,3 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
1 # encoding: utf-8
3 """
2 """
4 A simple utility to import something by its string name.
3 A simple utility to import something by its string name.
@@ -19,9 +19,7 b' Authors:'
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import pprint
22 from IPython.utils.data import list2dict2
23
24 from IPython.utils.genutils import list2dict2
25
23
26 __all__ = ['Struct']
24 __all__ = ['Struct']
27
25
@@ -37,7 +37,6 b' from IPython.external.path import path as Path'
37 import os,stat,time
37 import os,stat,time
38 import cPickle as pickle
38 import cPickle as pickle
39 import UserDict
39 import UserDict
40 import warnings
41 import glob
40 import glob
42
41
43 def gethashfile(key):
42 def gethashfile(key):
@@ -1,10 +1,12 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """ Imports and provides the 'correct' version of readline for the platform.
2 """ Imports and provides the 'correct' version of readline for the platform.
3
3
4 Readline is used throughout IPython as 'import IPython.utils.rlineimpl as readline'.
4 Readline is used throughout IPython as::
5
6 import IPython.utils.rlineimpl as readline
5
7
6 In addition to normal readline stuff, this module provides have_readline
8 In addition to normal readline stuff, this module provides have_readline
7 boolean and _outputfile variable used in genutils.
9 boolean and _outputfile variable used in IPython.utils.
8 """
10 """
9
11
10 import sys
12 import sys
@@ -19,9 +19,12 b' Authors:'
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22
23 import sys
22 import sys
24
23
24 #-----------------------------------------------------------------------------
25 # Code
26 #-----------------------------------------------------------------------------
27
25 class appended_to_syspath(object):
28 class appended_to_syspath(object):
26 """A context for appending a directory to sys.path for a second."""
29 """A context for appending a directory to sys.path for a second."""
27
30
@@ -3,21 +3,12 b''
3 def test_import_coloransi():
3 def test_import_coloransi():
4 from IPython.utils import coloransi
4 from IPython.utils import coloransi
5
5
6 def test_import_DPyGetOpt():
7 from IPython.utils import DPyGetOpt
8
9 def test_import_generics():
6 def test_import_generics():
10 from IPython.utils import generics
7 from IPython.utils import generics
11
8
12 def test_import_genutils():
13 from IPython.utils import genutils
14
15 def test_import_ipstruct():
9 def test_import_ipstruct():
16 from IPython.utils import ipstruct
10 from IPython.utils import ipstruct
17
11
18 def test_import_platutils():
19 from IPython.utils import platutils
20
21 def test_import_PyColorize():
12 def test_import_PyColorize():
22 from IPython.utils import PyColorize
13 from IPython.utils import PyColorize
23
14
@@ -33,5 +24,3 b' def test_import_upgradedir():'
33 def test_import_wildcard():
24 def test_import_wildcard():
34 from IPython.utils import wildcard
25 from IPython.utils import wildcard
35
26
36 def test_import_winconsole():
37 from IPython.utils import winconsole
@@ -15,11 +15,7 b''
15
15
16 import unittest
16 import unittest
17
17
18 from IPython.utils.notification import (
18 from IPython.utils.notification import shared_center
19 NotificationCenter,
20 NotificationError,
21 shared_center
22 )
23
19
24 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
25 # Support Classes
21 # Support Classes
@@ -1,8 +1,5 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2 """Tests for IPython.utils.path.py"""
3 """Tests for genutils.py"""
4
5 __docformat__ = "restructuredtext en"
6
3
7 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
5 # Copyright (C) 2008 The IPython Development Team
@@ -15,27 +12,21 b' __docformat__ = "restructuredtext en"'
15 # Imports
12 # Imports
16 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
17
14
18 # stdlib
19 import os
15 import os
20 import shutil
16 import shutil
21 import sys
17 import sys
22 import tempfile
18 import tempfile
23 import unittest
24
19
25 from cStringIO import StringIO
26 from os.path import join, abspath, split
20 from os.path import join, abspath, split
27
21
28 # third-party
29 import nose.tools as nt
22 import nose.tools as nt
30
23
31 from nose import with_setup
24 from nose import with_setup
32 from nose.tools import raises
33
25
34 # Our own
35 import IPython
26 import IPython
36 from IPython.utils import genutils
37 from IPython.testing import decorators as dec
27 from IPython.testing import decorators as dec
38 from IPython.testing.decorators import skipif, skip_if_not_win32
28 from IPython.testing.decorators import skip_if_not_win32, skip_win32
29 from IPython.utils import path
39
30
40 # Platform-dependent imports
31 # Platform-dependent imports
41 try:
32 try:
@@ -69,6 +60,7 b' def setup():'
69 # problem because that exception is only defined on Windows...
60 # problem because that exception is only defined on Windows...
70 os.makedirs(IP_TEST_DIR)
61 os.makedirs(IP_TEST_DIR)
71
62
63
72 def teardown():
64 def teardown():
73 """Teardown testenvironment for the module:
65 """Teardown testenvironment for the module:
74
66
@@ -79,7 +71,7 b' def teardown():'
79 # that non-empty directories are all recursively removed.
71 # that non-empty directories are all recursively removed.
80 shutil.rmtree(TMP_TEST_DIR)
72 shutil.rmtree(TMP_TEST_DIR)
81
73
82
74
83 def setup_environment():
75 def setup_environment():
84 """Setup testenvironment for some functions that are tested
76 """Setup testenvironment for some functions that are tested
85 in this module. In particular this functions stores attributes
77 in this module. In particular this functions stores attributes
@@ -88,16 +80,16 b' def setup_environment():'
88 each testfunction needs a pristine environment.
80 each testfunction needs a pristine environment.
89 """
81 """
90 global oldstuff, platformstuff
82 global oldstuff, platformstuff
91 oldstuff = (env.copy(), os.name, genutils.get_home_dir, IPython.__file__)
83 oldstuff = (env.copy(), os.name, path.get_home_dir, IPython.__file__)
92
84
93 if os.name == 'nt':
85 if os.name == 'nt':
94 platformstuff = (wreg.OpenKey, wreg.QueryValueEx,)
86 platformstuff = (wreg.OpenKey, wreg.QueryValueEx,)
95
87
96
88
97 def teardown_environment():
89 def teardown_environment():
98 """Restore things that were remebered by the setup_environment function
90 """Restore things that were remebered by the setup_environment function
99 """
91 """
100 (oldenv, os.name, genutils.get_home_dir, IPython.__file__,) = oldstuff
92 (oldenv, os.name, get_home_dir, IPython.__file__,) = oldstuff
101
93
102 for key in env.keys():
94 for key in env.keys():
103 if key not in oldenv:
95 if key not in oldenv:
@@ -112,10 +104,6 b' def teardown_environment():'
112 with_environment = with_setup(setup_environment, teardown_environment)
104 with_environment = with_setup(setup_environment, teardown_environment)
113
105
114
106
115 #
116 # Tests for get_home_dir
117 #
118
119 @skip_if_not_win32
107 @skip_if_not_win32
120 @with_environment
108 @with_environment
121 def test_get_home_dir_1():
109 def test_get_home_dir_1():
@@ -126,9 +114,10 b' def test_get_home_dir_1():'
126 #fake filename for IPython.__init__
114 #fake filename for IPython.__init__
127 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
115 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
128
116
129 home_dir = genutils.get_home_dir()
117 home_dir = path.get_home_dir()
130 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
118 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
131
119
120
132 @skip_if_not_win32
121 @skip_if_not_win32
133 @with_environment
122 @with_environment
134 def test_get_home_dir_2():
123 def test_get_home_dir_2():
@@ -138,61 +127,78 b' def test_get_home_dir_2():'
138 #fake filename for IPython.__init__
127 #fake filename for IPython.__init__
139 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
128 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
140
129
141 home_dir = genutils.get_home_dir()
130 home_dir = path.get_home_dir()
142 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower())
131 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower())
143
132
133
144 @with_environment
134 @with_environment
135 @skip_win32
145 def test_get_home_dir_3():
136 def test_get_home_dir_3():
146 """Testcase $HOME is set, then use its value as home directory."""
137 """Testcase $HOME is set, then use its value as home directory."""
147 env["HOME"] = HOME_TEST_DIR
138 env["HOME"] = HOME_TEST_DIR
148 home_dir = genutils.get_home_dir()
139 home_dir = path.get_home_dir()
149 nt.assert_equal(home_dir, env["HOME"])
140 nt.assert_equal(home_dir, env["HOME"])
150
141
142
151 @with_environment
143 @with_environment
152 def test_get_home_dir_4():
144 def test_get_home_dir_4():
153 """Testcase $HOME is not set, os=='poix'.
145 """Testcase $HOME is not set, os=='posix'.
154 This should fail with HomeDirError"""
146 This should fail with HomeDirError"""
155
147
156 os.name = 'posix'
148 os.name = 'posix'
157 if 'HOME' in env: del env['HOME']
149 if 'HOME' in env: del env['HOME']
158 nt.assert_raises(genutils.HomeDirError, genutils.get_home_dir)
150 nt.assert_raises(path.HomeDirError, path.get_home_dir)
159
151
152
160 @skip_if_not_win32
153 @skip_if_not_win32
161 @with_environment
154 @with_environment
162 def test_get_home_dir_5():
155 def test_get_home_dir_5():
163 """Testcase $HOME is not set, os=='nt'
156 """Using HOMEDRIVE + HOMEPATH, os=='nt'.
164 env['HOMEDRIVE'],env['HOMEPATH'] points to path."""
157
158 HOMESHARE is missing.
159 """
165
160
166 os.name = 'nt'
161 os.name = 'nt'
167 if 'HOME' in env: del env['HOME']
162 env.pop('HOMESHARE', None)
168 env['HOMEDRIVE'], env['HOMEPATH'] = os.path.splitdrive(HOME_TEST_DIR)
163 env['HOMEDRIVE'], env['HOMEPATH'] = os.path.splitdrive(HOME_TEST_DIR)
169
164 home_dir = path.get_home_dir()
170 home_dir = genutils.get_home_dir()
171 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
165 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
172
166
167
173 @skip_if_not_win32
168 @skip_if_not_win32
174 @with_environment
169 @with_environment
175 def test_get_home_dir_6():
170 def test_get_home_dir_6():
176 """Testcase $HOME is not set, os=='nt'
171 """Using USERPROFILE, os=='nt'.
177 env['HOMEDRIVE'],env['HOMEPATH'] do not point to path.
172
178 env['USERPROFILE'] points to path
173 HOMESHARE, HOMEDRIVE, HOMEPATH are missing.
179 """
174 """
180
175
181 os.name = 'nt'
176 os.name = 'nt'
182 if 'HOME' in env: del env['HOME']
177 env.pop('HOMESHARE', None)
183 env['HOMEDRIVE'], env['HOMEPATH'] = os.path.abspath(TEST_FILE_PATH), "DOES NOT EXIST"
178 env.pop('HOMEDRIVE', None)
179 env.pop('HOMEPATH', None)
184 env["USERPROFILE"] = abspath(HOME_TEST_DIR)
180 env["USERPROFILE"] = abspath(HOME_TEST_DIR)
185
181 home_dir = path.get_home_dir()
186 home_dir = genutils.get_home_dir()
187 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
182 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
188
183
189 # Should we stub wreg fully so we can run the test on all platforms?
184
190 @skip_if_not_win32
185 @skip_if_not_win32
191 @with_environment
186 @with_environment
192 def test_get_home_dir_7():
187 def test_get_home_dir_7():
193 """Testcase $HOME is not set, os=='nt'
188 """Using HOMESHARE, os=='nt'."""
189
190 os.name = 'nt'
191 env["HOMESHARE"] = abspath(HOME_TEST_DIR)
192 home_dir = path.get_home_dir()
193 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
194
194
195 env['HOMEDRIVE'],env['HOMEPATH'], env['USERPROFILE'] and others missing
195 # Should we stub wreg fully so we can run the test on all platforms?
196 @skip_if_not_win32
197 @with_environment
198 def test_get_home_dir_8():
199 """Using registry hack for 'My Documents', os=='nt'
200
201 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
196 """
202 """
197 os.name = 'nt'
203 os.name = 'nt'
198 # Remove from stub environment all keys that may be set
204 # Remove from stub environment all keys that may be set
@@ -211,116 +217,56 b' def test_get_home_dir_7():'
211 wreg.OpenKey = OpenKey
217 wreg.OpenKey = OpenKey
212 wreg.QueryValueEx = QueryValueEx
218 wreg.QueryValueEx = QueryValueEx
213
219
214 home_dir = genutils.get_home_dir()
220 home_dir = path.get_home_dir()
215 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
221 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
216
222
217 #
218 # Tests for get_ipython_dir
219 #
220
223
221 @with_environment
224 @with_environment
222 def test_get_ipython_dir_1():
225 def test_get_ipython_dir_1():
223 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
226 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
224 env['IPYTHON_DIR'] = "someplace/.ipython"
227 env['IPYTHON_DIR'] = "someplace/.ipython"
225 ipdir = genutils.get_ipython_dir()
228 ipdir = path.get_ipython_dir()
226 nt.assert_equal(ipdir, "someplace/.ipython")
229 nt.assert_equal(ipdir, "someplace/.ipython")
227
230
228
231
229 @with_environment
232 @with_environment
230 def test_get_ipython_dir_2():
233 def test_get_ipython_dir_2():
231 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
234 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
232 genutils.get_home_dir = lambda : "someplace"
235 path.get_home_dir = lambda : "someplace"
233 os.name = "posix"
236 os.name = "posix"
234 env.pop('IPYTHON_DIR', None)
237 env.pop('IPYTHON_DIR', None)
235 env.pop('IPYTHONDIR', None)
238 env.pop('IPYTHONDIR', None)
236 ipdir = genutils.get_ipython_dir()
239 ipdir = path.get_ipython_dir()
237 nt.assert_equal(ipdir, os.path.join("someplace", ".ipython"))
240 nt.assert_equal(ipdir, os.path.join("someplace", ".ipython"))
238
241
239 #
240 # Tests for popkey
241 #
242
243 def test_popkey_1():
244 """test_popkey_1, Basic usage test of popkey
245 """
246 dct = dict(a=1, b=2, c=3)
247 nt.assert_equal(genutils.popkey(dct, "a"), 1)
248 nt.assert_equal(dct, dict(b=2, c=3))
249 nt.assert_equal(genutils.popkey(dct, "b"), 2)
250 nt.assert_equal(dct, dict(c=3))
251 nt.assert_equal(genutils.popkey(dct, "c"), 3)
252 nt.assert_equal(dct, dict())
253
254 def test_popkey_2():
255 """test_popkey_2, Test to see that popkey of non occuring keys
256 generates a KeyError exception
257 """
258 dct = dict(a=1, b=2, c=3)
259 nt.assert_raises(KeyError, genutils.popkey, dct, "d")
260
261 def test_popkey_3():
262 """test_popkey_3, Tests to see that popkey calls returns the correct value
263 and that the key/value was removed from the dict.
264 """
265 dct = dict(a=1, b=2, c=3)
266 nt.assert_equal(genutils.popkey(dct, "A", 13), 13)
267 nt.assert_equal(dct, dict(a=1, b=2, c=3))
268 nt.assert_equal(genutils.popkey(dct, "B", 14), 14)
269 nt.assert_equal(dct, dict(a=1, b=2, c=3))
270 nt.assert_equal(genutils.popkey(dct, "C", 15), 15)
271 nt.assert_equal(dct, dict(a=1, b=2, c=3))
272 nt.assert_equal(genutils.popkey(dct, "a"), 1)
273 nt.assert_equal(dct, dict(b=2, c=3))
274 nt.assert_equal(genutils.popkey(dct, "b"), 2)
275 nt.assert_equal(dct, dict(c=3))
276 nt.assert_equal(genutils.popkey(dct, "c"), 3)
277 nt.assert_equal(dct, dict())
278
279
242
280 def test_filefind():
243 def test_filefind():
281 """Various tests for filefind"""
244 """Various tests for filefind"""
282 f = tempfile.NamedTemporaryFile()
245 f = tempfile.NamedTemporaryFile()
283 print 'fname:',f.name
246 # print 'fname:',f.name
284 alt_dirs = genutils.get_ipython_dir()
247 alt_dirs = path.get_ipython_dir()
285 t = genutils.filefind(f.name,alt_dirs)
248 t = path.filefind(f.name, alt_dirs)
286 print 'found:',t
249 # print 'found:',t
287
250
288
251
289 def test_get_ipython_package_dir():
252 def test_get_ipython_package_dir():
290 ipdir = genutils.get_ipython_package_dir()
253 ipdir = path.get_ipython_package_dir()
291 nt.assert_true(os.path.isdir(ipdir))
254 nt.assert_true(os.path.isdir(ipdir))
292
255
293
256
294 def test_tee_simple():
257 def test_get_ipython_module_path():
295 "Very simple check with stdout only"
258 ipapp_path = path.get_ipython_module_path('IPython.core.ipapp')
296 chan = StringIO()
259 nt.assert_true(os.path.isfile(ipapp_path))
297 text = 'Hello'
298 tee = genutils.Tee(chan, channel='stdout')
299 print >> chan, text,
300 nt.assert_equal(chan.getvalue(), text)
301
260
302
261
303 class TeeTestCase(dec.ParametricTestCase):
262 @dec.skip_if_not_win32
263 def test_get_long_path_name_win32():
264 p = path.get_long_path_name('c:\\docume~1')
265 nt.assert_equals(p,u'c:\\Documents and Settings')
266
267
268 @dec.skip_win32
269 def test_get_long_path_name():
270 p = path.get_long_path_name('/usr/local')
271 nt.assert_equals(p,'/usr/local')
304
272
305 def tchan(self, channel, check='close'):
306 trap = StringIO()
307 chan = StringIO()
308 text = 'Hello'
309
310 std_ori = getattr(sys, channel)
311 setattr(sys, channel, trap)
312
313 tee = genutils.Tee(chan, channel=channel)
314 print >> chan, text,
315 setattr(sys, channel, std_ori)
316 trap_val = trap.getvalue()
317 nt.assert_equals(chan.getvalue(), text)
318 if check=='close':
319 tee.close()
320 else:
321 del tee
322
323 def test(self):
324 for chan in ['stdout', 'stderr']:
325 for check in ['close', 'del']:
326 yield self.tchan(chan, check)
@@ -14,12 +14,11 b' Tests for platutils.py'
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import os
18 import sys
17 import sys
19
18
20 import nose.tools as nt
19 import nose.tools as nt
21
20
22 from IPython.utils.platutils import find_cmd, FindCmdError, get_long_path_name
21 from IPython.utils.process import find_cmd, FindCmdError
23 from IPython.testing import decorators as dec
22 from IPython.testing import decorators as dec
24
23
25 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
@@ -60,13 +59,4 b' def test_find_cmd_fail():'
60 nt.assert_raises(FindCmdError,find_cmd,'asdfasdf')
59 nt.assert_raises(FindCmdError,find_cmd,'asdfasdf')
61
60
62
61
63 @dec.skip_if_not_win32
64 def test_get_long_path_name_win32():
65 p = get_long_path_name('c:\\docume~1')
66 nt.assert_equals(p,u'c:\\Documents and Settings')
67
62
68
69 @dec.skip_win32
70 def test_get_long_path_name():
71 p = get_long_path_name('/usr/local')
72 nt.assert_equals(p,'/usr/local')
@@ -22,15 +22,11 b' Authors:'
22 # Imports
22 # Imports
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 import sys
26 import os
27
28
29 from unittest import TestCase
25 from unittest import TestCase
30
26
31 from IPython.utils.traitlets import (
27 from IPython.utils.traitlets import (
32 HasTraitlets, MetaHasTraitlets, TraitletType, Any,
28 HasTraitlets, MetaHasTraitlets, TraitletType, Any,
33 Int, Long, Float, Complex, Str, Unicode, Bool, TraitletError,
29 Int, Long, Float, Complex, Str, Unicode, TraitletError,
34 Undefined, Type, This, Instance
30 Undefined, Type, This, Instance
35 )
31 )
36
32
@@ -11,7 +11,7 b' try:'
11 except ImportError:
11 except ImportError:
12 from path import path
12 from path import path
13
13
14 import md5,pickle
14 import md5, pickle
15
15
16 def showdiff(old,new):
16 def showdiff(old,new):
17 import difflib
17 import difflib
@@ -14,13 +14,10 b' Authors'
14 #*****************************************************************************
14 #*****************************************************************************
15
15
16 import __builtin__
16 import __builtin__
17 import exceptions
18 import pdb
19 import pprint
20 import re
17 import re
21 import types
18 import types
22
19
23 from IPython.utils.genutils import dir2
20 from IPython.utils.dir2 import dir2
24
21
25 def create_typestr2type_dicts(dont_include_in_type2type2str=["lambda"]):
22 def create_typestr2type_dicts(dont_include_in_type2type2str=["lambda"]):
26 """Return dictionaries mapping lower case typename to type objects, from
23 """Return dictionaries mapping lower case typename to type objects, from
@@ -1,5 +1,4 b''
1 include ipython.py
1 include ipython.py
2 include iptest.py
3 include setupbase.py
2 include setupbase.py
4 include setupegg.py
3 include setupegg.py
5
4
@@ -9,7 +8,7 b' graft scripts'
9 graft IPython/kernel
8 graft IPython/kernel
10 graft IPython/config
9 graft IPython/config
11 graft IPython/core
10 graft IPython/core
12 graft IPython/deathrow
11 # graft IPython/deathrow
13 graft IPython/external
12 graft IPython/external
14 graft IPython/frontend
13 graft IPython/frontend
15 graft IPython/gui
14 graft IPython/gui
@@ -1,71 +1,148 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Run a Monte-Carlo options pricer in parallel."""
2 """Run a Monte-Carlo options pricer in parallel."""
3
3
4 #-----------------------------------------------------------------------------
5 # Imports
6 #-----------------------------------------------------------------------------
7
8 import sys
9 import time
4 from IPython.kernel import client
10 from IPython.kernel import client
5 import numpy as np
11 import numpy as np
6 from mcpricer import price_options
12 from mcpricer import price_options
13 from matplotlib import pyplot as plt
14
15 #-----------------------------------------------------------------------------
16 # Setup parameters for the run
17 #-----------------------------------------------------------------------------
18
19 def ask_question(text, the_type, default):
20 s = '%s [%r]: ' % (text, the_type(default))
21 result = raw_input(s)
22 if result:
23 return the_type(result)
24 else:
25 return the_type(default)
26
27 cluster_profile = ask_question("Cluster profile", str, "default")
28 price = ask_question("Initial price", float, 100.0)
29 rate = ask_question("Interest rate", float, 0.05)
30 days = ask_question("Days to expiration", int, 260)
31 paths = ask_question("Number of MC paths", int, 10000)
32 n_strikes = ask_question("Number of strike values", int, 5)
33 min_strike = ask_question("Min strike price", float, 90.0)
34 max_strike = ask_question("Max strike price", float, 110.0)
35 n_sigmas = ask_question("Number of volatility values", int, 5)
36 min_sigma = ask_question("Min volatility", float, 0.1)
37 max_sigma = ask_question("Max volatility", float, 0.4)
38
39 strike_vals = np.linspace(min_strike, max_strike, n_strikes)
40 sigma_vals = np.linspace(min_sigma, max_sigma, n_sigmas)
41
42 #-----------------------------------------------------------------------------
43 # Setup for parallel calculation
44 #-----------------------------------------------------------------------------
7
45
8 # The MultiEngineClient is used to setup the calculation and works with all
46 # The MultiEngineClient is used to setup the calculation and works with all
9 # engine.
47 # engine.
10 mec = client.MultiEngineClient(profile='mycluster')
48 mec = client.MultiEngineClient(profile=cluster_profile)
11
49
12 # The TaskClient is an interface to the engines that provides dynamic load
50 # The TaskClient is an interface to the engines that provides dynamic load
13 # balancing at the expense of not knowing which engine will execute the code.
51 # balancing at the expense of not knowing which engine will execute the code.
14 tc = client.TaskClient(profile='mycluster')
52 tc = client.TaskClient(profile=cluster_profile)
15
53
16 # Initialize the common code on the engines. This Python module has the
54 # Initialize the common code on the engines. This Python module has the
17 # price_options function that prices the options.
55 # price_options function that prices the options.
18 mec.run('mcpricer.py')
56 mec.run('mcpricer.py')
19
57
20 # Define the function that will make up our tasks. We basically want to
58 #-----------------------------------------------------------------------------
21 # call the price_options function with all but two arguments (K, sigma)
59 # Perform parallel calculation
22 # fixed.
60 #-----------------------------------------------------------------------------
23 def my_prices(K, sigma):
61
24 S = 100.0
62 print "Running parallel calculation over strike prices and volatilities..."
25 r = 0.05
63 print "Strike prices: ", strike_vals
26 days = 260
64 print "Volatilities: ", sigma_vals
27 paths = 100000
65 sys.stdout.flush()
28 return price_options(S, K, sigma, r, days, paths)
66
29
67 # Submit tasks to the TaskClient for each (strike, sigma) pair as a MapTask.
30 # Create arrays of strike prices and volatilities
68 t1 = time.time()
31 nK = 10
32 nsigma = 10
33 K_vals = np.linspace(90.0, 100.0, nK)
34 sigma_vals = np.linspace(0.1, 0.4, nsigma)
35
36 # Submit tasks to the TaskClient for each (K, sigma) pair as a MapTask.
37 # The MapTask simply applies a function (my_prices) to the arguments:
38 # my_prices(K, sigma) and returns the result.
39 taskids = []
69 taskids = []
40 for K in K_vals:
70 for strike in strike_vals:
41 for sigma in sigma_vals:
71 for sigma in sigma_vals:
42 t = client.MapTask(my_prices, args=(K, sigma))
72 t = client.MapTask(
73 price_options,
74 args=(price, strike, sigma, rate, days, paths)
75 )
43 taskids.append(tc.run(t))
76 taskids.append(tc.run(t))
44
77
45 print "Submitted tasks: ", len(taskids)
78 print "Submitted tasks: ", len(taskids)
79 sys.stdout.flush()
46
80
47 # Block until all tasks are completed.
81 # Block until all tasks are completed.
48 tc.barrier(taskids)
82 tc.barrier(taskids)
83 t2 = time.time()
84 t = t2-t1
85
86 print "Parallel calculation completed, time = %s s" % t
87 print "Collecting results..."
49
88
50 # Get the results using TaskClient.get_task_result.
89 # Get the results using TaskClient.get_task_result.
51 results = [tc.get_task_result(tid) for tid in taskids]
90 results = [tc.get_task_result(tid) for tid in taskids]
52
91
53 # Assemble the result into a structured NumPy array.
92 # Assemble the result into a structured NumPy array.
54 prices = np.empty(nK*nsigma,
93 prices = np.empty(n_strikes*n_sigmas,
55 dtype=[('ecall',float),('eput',float),('acall',float),('aput',float)]
94 dtype=[('ecall',float),('eput',float),('acall',float),('aput',float)]
56 )
95 )
96
57 for i, price_tuple in enumerate(results):
97 for i, price_tuple in enumerate(results):
58 prices[i] = price_tuple
98 prices[i] = price_tuple
59 prices.shape = (nK, nsigma)
99
60 K_vals, sigma_vals = np.meshgrid(K_vals, sigma_vals)
100 prices.shape = (n_strikes, n_sigmas)
101 strike_mesh, sigma_mesh = np.meshgrid(strike_vals, sigma_vals)
61
102
62 def plot_options(sigma_vals, K_vals, prices):
103 print "Results are available: strike_mesh, sigma_mesh, prices"
104 print "To plot results type 'plot_options(sigma_mesh, strike_mesh, prices)'"
105
106 #-----------------------------------------------------------------------------
107 # Utilities
108 #-----------------------------------------------------------------------------
109
110 def plot_options(sigma_mesh, strike_mesh, prices):
63 """
111 """
64 Make a contour plot of the option price in (sigma, K) space.
112 Make a contour plot of the option price in (sigma, strike) space.
65 """
113 """
66 from matplotlib import pyplot as plt
114 plt.figure(1)
67 plt.contourf(sigma_vals, K_vals, prices)
115
116 plt.subplot(221)
117 plt.contourf(sigma_mesh, strike_mesh, prices['ecall'])
118 plt.axis('tight')
68 plt.colorbar()
119 plt.colorbar()
69 plt.title("Option Price")
120 plt.title('European Call')
121 plt.ylabel("Strike Price")
122
123 plt.subplot(222)
124 plt.contourf(sigma_mesh, strike_mesh, prices['acall'])
125 plt.axis('tight')
126 plt.colorbar()
127 plt.title("Asian Call")
128
129 plt.subplot(223)
130 plt.contourf(sigma_mesh, strike_mesh, prices['eput'])
131 plt.axis('tight')
132 plt.colorbar()
133 plt.title("European Put")
70 plt.xlabel("Volatility")
134 plt.xlabel("Volatility")
71 plt.ylabel("Strike Price")
135 plt.ylabel("Strike Price")
136
137 plt.subplot(224)
138 plt.contourf(sigma_mesh, strike_mesh, prices['aput'])
139 plt.axis('tight')
140 plt.colorbar()
141 plt.title("Asian Put")
142 plt.xlabel("Volatility")
143
144
145
146
147
148
@@ -18,7 +18,7 b' overhead of a single task is about 0.001-0.01 seconds.'
18 import random, sys
18 import random, sys
19 from optparse import OptionParser
19 from optparse import OptionParser
20
20
21 from IPython.genutils import time
21 from IPython.utils.timing import time
22 from IPython.kernel import client
22 from IPython.kernel import client
23
23
24 def main():
24 def main():
@@ -51,7 +51,7 b' def main():'
51 print tc.task_controller
51 print tc.task_controller
52 rc.block=True
52 rc.block=True
53 nengines = len(rc.get_ids())
53 nengines = len(rc.get_ids())
54 rc.execute('from IPython.genutils import time')
54 rc.execute('from IPython.utils.timing import time')
55
55
56 # the jobs should take a random time within a range
56 # the jobs should take a random time within a range
57 times = [random.random()*(opts.tmax-opts.tmin)+opts.tmin for i in range(opts.n)]
57 times = [random.random()*(opts.tmax-opts.tmin)+opts.tmin for i in range(opts.n)]
@@ -245,7 +245,7 b' this directory is determined by the following algorithm:'
245
245
246 * If the ``--ipython-dir`` command line flag is given, its value is used.
246 * If the ``--ipython-dir`` command line flag is given, its value is used.
247
247
248 * If not, the value returned by :func:`IPython.utils.genutils.get_ipython_dir`
248 * If not, the value returned by :func:`IPython.utils.path.get_ipython_dir`
249 is used. This function will first look at the :envvar:`IPYTHON_DIR`
249 is used. This function will first look at the :envvar:`IPYTHON_DIR`
250 environment variable and then default to the directory
250 environment variable and then default to the directory
251 :file:`$HOME/.ipython`.
251 :file:`$HOME/.ipython`.
@@ -41,8 +41,8 b' A bit of Python code::'
41
41
42 An interactive Python session::
42 An interactive Python session::
43
43
44 >>> from IPython import genutils
44 >>> from IPython.utils.path import get_ipython_dir
45 >>> genutils.get_ipython_dir()
45 >>> get_ipython_dir()
46 '/home/fperez/.ipython'
46 '/home/fperez/.ipython'
47
47
48 An IPython session:
48 An IPython session:
@@ -57,3 +57,5 b' methods in :class:`InteractiveShell` that manage code execution::'
57 nx.draw_spectral(g, node_size=100, alpha=0.6, node_color='r',
57 nx.draw_spectral(g, node_size=100, alpha=0.6, node_color='r',
58 font_size=10, node_shape='o')
58 font_size=10, node_shape='o')
59 plt.show()
59 plt.show()
60
61
@@ -244,7 +244,7 b' and an example of ``# all-random``::'
244
244
245 When writing docstrings, you can use the ``@skip_doctest`` decorator to
245 When writing docstrings, you can use the ``@skip_doctest`` decorator to
246 indicate that a docstring should *not* be treated as a doctest at all. The
246 indicate that a docstring should *not* be treated as a doctest at all. The
247 difference betwee ``# all-random`` and ``@skip_doctest`` is that the former
247 difference between ``# all-random`` and ``@skip_doctest`` is that the former
248 executes the example but ignores output, while the latter doesn't execute any
248 executes the example but ignores output, while the latter doesn't execute any
249 code. ``@skip_doctest`` should be used for docstrings whose examples are
249 code. ``@skip_doctest`` should be used for docstrings whose examples are
250 purely informational.
250 purely informational.
@@ -166,7 +166,7 b' ipy_user_conf.py.'
166 String lists
166 String lists
167 ============
167 ============
168
168
169 String lists (IPython.genutils.SList) are handy way to process output
169 String lists (IPython.utils.text.SList) are handy way to process output
170 from system commands. They are produced by ``var = !cmd`` syntax.
170 from system commands. They are produced by ``var = !cmd`` syntax.
171
171
172 First, we acquire the output of 'ls -l'::
172 First, we acquire the output of 'ls -l'::
@@ -8,7 +8,7 b' import re'
8 import pydoc
8 import pydoc
9 from StringIO import StringIO
9 from StringIO import StringIO
10 from warnings import warn
10 from warnings import warn
11 4
11
12 class Reader(object):
12 class Reader(object):
13 """A line-based string reader.
13 """A line-based string reader.
14
14
@@ -59,12 +59,9 b' Authors'
59
59
60 # Stdlib
60 # Stdlib
61 import cStringIO
61 import cStringIO
62 import imp
63 import os
62 import os
64 import re
63 import re
65 import shutil
66 import sys
64 import sys
67 import warnings
68
65
69 # To keep compatibility with various python versions
66 # To keep compatibility with various python versions
70 try:
67 try:
@@ -80,8 +77,8 b' from docutils.parsers.rst import directives'
80 matplotlib.use('Agg')
77 matplotlib.use('Agg')
81
78
82 # Our own
79 # Our own
83 from IPython import Config, IPythonApp
80 from IPython import Config, InteractiveShell
84 from IPython.utils.genutils import Term, Tee
81 from IPython.utils.io import Term
85
82
86 #-----------------------------------------------------------------------------
83 #-----------------------------------------------------------------------------
87 # Globals
84 # Globals
@@ -208,8 +205,9 b' class EmbeddedSphinxShell(object):'
208 Term.cerr = self.cout
205 Term.cerr = self.cout
209
206
210 # For debugging, so we can see normal output, use this:
207 # For debugging, so we can see normal output, use this:
211 #Term.cout = genutils.Tee(self.cout, channel='stdout') # dbg
208 # from IPython.utils.io import Tee
212 #Term.cerr = genutils.Tee(self.cout, channel='stderr') # dbg
209 #Term.cout = Tee(self.cout, channel='stdout') # dbg
210 #Term.cerr = Tee(self.cout, channel='stderr') # dbg
213
211
214 # Create config object for IPython
212 # Create config object for IPython
215 config = Config()
213 config = Config()
@@ -221,15 +219,11 b' class EmbeddedSphinxShell(object):'
221 config.InteractiveShell.autoindent = False
219 config.InteractiveShell.autoindent = False
222 config.InteractiveShell.colors = 'NoColor'
220 config.InteractiveShell.colors = 'NoColor'
223
221
224 # Merge global config which can be used to override.
225 config._merge(CONFIG)
226
227 # Create and initialize ipython, but don't start its mainloop
222 # Create and initialize ipython, but don't start its mainloop
228 IP = IPythonApp(override_config=config)
223 IP = InteractiveShell(parent=None, config=config)
229 IP.initialize()
230
224
231 # Store a few parts of IPython we'll need.
225 # Store a few parts of IPython we'll need.
232 self.IP = IP.shell
226 self.IP = IP
233 self.user_ns = self.IP.user_ns
227 self.user_ns = self.IP.user_ns
234 self.user_global_ns = self.IP.user_global_ns
228 self.user_global_ns = self.IP.user_global_ns
235
229
@@ -47,7 +47,7 b" if os.path.exists('MANIFEST'): os.remove('MANIFEST')"
47 from distutils.core import setup
47 from distutils.core import setup
48
48
49 # Our own imports
49 # Our own imports
50 from IPython.utils.genutils import target_update
50 from IPython.utils.path import target_update
51
51
52 from setupbase import (
52 from setupbase import (
53 setup_args,
53 setup_args,
@@ -1,62 +0,0 b''
1 from os.path import join
2 pjoin = join
3
4 from IPython.utils.genutils import get_ipython_dir, get_security_dir
5 security_dir = get_security_dir()
6
7
8 ENGINE_LOGFILE = ''
9
10 ENGINE_FURL_FILE = 'ipcontroller-engine.furl'
11
12 MPI_CONFIG_MPI4PY = """from mpi4py import MPI as mpi
13 mpi.size = mpi.COMM_WORLD.Get_size()
14 mpi.rank = mpi.COMM_WORLD.Get_rank()
15 """
16
17 MPI_CONFIG_PYTRILINOS = """from PyTrilinos import Epetra
18 class SimpleStruct:
19 pass
20 mpi = SimpleStruct()
21 mpi.rank = 0
22 mpi.size = 0
23 """
24
25 MPI_DEFAULT = ''
26
27 CONTROLLER_LOGFILE = ''
28 CONTROLLER_IMPORT_STATEMENT = ''
29 CONTROLLER_REUSE_FURLS = False
30
31 ENGINE_TUB_IP = ''
32 ENGINE_TUB_PORT = 0
33 ENGINE_TUB_LOCATION = ''
34 ENGINE_TUB_SECURE = True
35 ENGINE_TUB_CERT_FILE = 'ipcontroller-engine.pem'
36 ENGINE_FC_INTERFACE = 'IPython.kernel.enginefc.IFCControllerBase'
37 ENGINE_FURL_FILE = 'ipcontroller-engine.furl'
38
39 CONTROLLER_INTERFACES = dict(
40 TASK = dict(
41 CONTROLLER_INTERFACE = 'IPython.kernel.task.ITaskController',
42 FC_INTERFACE = 'IPython.kernel.taskfc.IFCTaskController',
43 FURL_FILE = pjoin(security_dir, 'ipcontroller-tc.furl')
44 ),
45 MULTIENGINE = dict(
46 CONTROLLER_INTERFACE = 'IPython.kernel.multiengine.IMultiEngine',
47 FC_INTERFACE = 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine',
48 FURL_FILE = pjoin(security_dir, 'ipcontroller-mec.furl')
49 )
50 )
51
52 CLIENT_TUB_IP = ''
53 CLIENT_TUB_PORT = 0
54 CLIENT_TUB_LOCATION = ''
55 CLIENT_TUB_SECURE = True
56 CLIENT_TUB_CERT_FILE = 'ipcontroller-client.pem'
57
58 CLIENT_INTERFACES = dict(
59 TASK = dict(FURL_FILE = 'ipcontroller-tc.furl'),
60 MULTIENGINE = dict(FURLFILE='ipcontroller-mec.furl')
61 )
62
@@ -1,141 +0,0 b''
1 # encoding: utf-8
2 # -*- test-case-name: IPython.kernel.test.test_contexts -*-
3 """Context managers for IPython.
4
5 Python 2.5 introduced the `with` statement, which is based on the context
6 manager protocol. This module offers a few context managers for common cases,
7 which can also be useful as templates for writing new, application-specific
8 managers.
9 """
10
11 __docformat__ = "restructuredtext en"
12
13 #-------------------------------------------------------------------------------
14 # Copyright (C) 2008 The IPython Development Team
15 #
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
18 #-------------------------------------------------------------------------------
19
20 #-------------------------------------------------------------------------------
21 # Imports
22 #-------------------------------------------------------------------------------
23
24 import linecache
25 import sys
26
27 from twisted.internet.error import ConnectionRefusedError
28
29 from IPython.core.ultratb import _fixed_getinnerframes, findsource
30 from IPython.core import ipapi
31
32 from IPython.kernel import error
33
34 #---------------------------------------------------------------------------
35 # Utility functions needed by all context managers.
36 #---------------------------------------------------------------------------
37
38 def remote():
39 """Raises a special exception meant to be caught by context managers.
40 """
41 m = 'Special exception to stop local execution of parallel code.'
42 raise error.StopLocalExecution(m)
43
44
45 def strip_whitespace(source,require_remote=True):
46 """strip leading whitespace from input source.
47
48 :Parameters:
49
50 """
51 remote_mark = 'remote()'
52 # Expand tabs to avoid any confusion.
53 wsource = [l.expandtabs(4) for l in source]
54 # Detect the indentation level
55 done = False
56 for line in wsource:
57 if line.isspace():
58 continue
59 for col,char in enumerate(line):
60 if char != ' ':
61 done = True
62 break
63 if done:
64 break
65 # Now we know how much leading space there is in the code. Next, we
66 # extract up to the first line that has less indentation.
67 # WARNINGS: we skip comments that may be misindented, but we do NOT yet
68 # detect triple quoted strings that may have flush left text.
69 for lno,line in enumerate(wsource):
70 lead = line[:col]
71 if lead.isspace():
72 continue
73 else:
74 if not lead.lstrip().startswith('#'):
75 break
76 # The real 'with' source is up to lno
77 src_lines = [l[col:] for l in wsource[:lno+1]]
78
79 # Finally, check that the source's first non-comment line begins with the
80 # special call 'remote()'
81 if require_remote:
82 for nline,line in enumerate(src_lines):
83 if line.isspace() or line.startswith('#'):
84 continue
85 if line.startswith(remote_mark):
86 break
87 else:
88 raise ValueError('%s call missing at the start of code' %
89 remote_mark)
90 out_lines = src_lines[nline+1:]
91 else:
92 # If the user specified that the remote() call wasn't mandatory
93 out_lines = src_lines
94
95 # src = ''.join(out_lines) # dbg
96 #print 'SRC:\n<<<<<<<>>>>>>>\n%s<<<<<>>>>>>' % src # dbg
97 return ''.join(out_lines)
98
99 class RemoteContextBase(object):
100 def __init__(self):
101 self.ip = ipapi.get()
102
103 def _findsource_file(self,f):
104 linecache.checkcache()
105 s = findsource(f.f_code)
106 lnum = f.f_lineno
107 wsource = s[0][f.f_lineno:]
108 return strip_whitespace(wsource)
109
110 def _findsource_ipython(self,f):
111 from IPython.core import ipapi
112 self.ip = ipapi.get()
113 buf = self.ip.input_hist_raw[-1].splitlines()[1:]
114 wsource = [l+'\n' for l in buf ]
115
116 return strip_whitespace(wsource)
117
118 def findsource(self,frame):
119 local_ns = frame.f_locals
120 global_ns = frame.f_globals
121 if frame.f_code.co_filename == '<ipython console>':
122 src = self._findsource_ipython(frame)
123 else:
124 src = self._findsource_file(frame)
125 return src
126
127 def __enter__(self):
128 raise NotImplementedError
129
130 def __exit__ (self, etype, value, tb):
131 if issubclass(etype,error.StopLocalExecution):
132 return True
133
134 class RemoteMultiEngine(RemoteContextBase):
135 def __init__(self,mec):
136 self.mec = mec
137 RemoteContextBase.__init__(self)
138
139 def __enter__(self):
140 src = self.findsource(sys._getframe(1))
141 return self.mec.execute(src)
@@ -1,46 +0,0 b''
1 # Tell nose to skip this module
2 __test__ = {}
3
4 #from __future__ import with_statement
5
6 # XXX This file is currently disabled to preserve 2.4 compatibility.
7
8 #def test_simple():
9 if 0:
10
11 # XXX - for now, we need a running cluster to be started separately. The
12 # daemon work is almost finished, and will make much of this unnecessary.
13 from IPython.kernel import client
14 mec = client.MultiEngineClient(('127.0.0.1',10105))
15
16 try:
17 mec.get_ids()
18 except ConnectionRefusedError:
19 import os, time
20 os.system('ipcluster -n 2 &')
21 time.sleep(2)
22 mec = client.MultiEngineClient(('127.0.0.1',10105))
23
24 mec.block = False
25
26 import itertools
27 c = itertools.count()
28
29 parallel = RemoteMultiEngine(mec)
30
31 mec.pushAll()
32
33 ## with parallel as pr:
34 ## # A comment
35 ## remote() # this means the code below only runs remotely
36 ## print 'Hello remote world'
37 ## x = range(10)
38 ## # Comments are OK
39 ## # Even misindented.
40 ## y = x+1
41
42
43 ## with pfor('i',sequence) as pr:
44 ## print x[i]
45
46 print pr.x + pr.y
This diff has been collapsed as it changes many lines, (690 lines changed) Show them Hide them
@@ -1,690 +0,0 b''
1 # -*- coding: utf-8 -*-
2 """DPyGetOpt -- Demiurge Python GetOptions Module
3
4 This module is modeled after perl's Getopt::Long module-- which
5 is, in turn, modeled after GNU's extended getopt() function.
6
7 Upon instantiation, the option specification should be a sequence
8 (list) of option definitions.
9
10 Options that take no arguments should simply contain the name of
11 the option. If a ! is post-pended, the option can be negated by
12 prepending 'no'; ie 'debug!' specifies that -debug and -nodebug
13 should be accepted.
14
15 Mandatory arguments to options are specified using a postpended
16 '=' + a type specifier. '=s' specifies a mandatory string
17 argument, '=i' specifies a mandatory integer argument, and '=f'
18 specifies a mandatory real number. In all cases, the '=' can be
19 substituted with ':' to specify that the argument is optional.
20
21 Dashes '-' in option names are allowed.
22
23 If an option has the character '@' postpended (after the
24 argumentation specification), it can appear multiple times within
25 each argument list that is processed. The results will be stored
26 in a list.
27
28 The option name can actually be a list of names separated by '|'
29 characters; ie-- 'foo|bar|baz=f@' specifies that all -foo, -bar,
30 and -baz options that appear on within the parsed argument list
31 must have a real number argument and that the accumulated list
32 of values will be available under the name 'foo'
33 """
34
35 #*****************************************************************************
36 #
37 # Copyright (c) 2001 Bill Bumgarner <bbum@friday.com>
38 #
39 #
40 # Published under the terms of the MIT license, hereby reproduced:
41 #
42 # Permission is hereby granted, free of charge, to any person obtaining a copy
43 # of this software and associated documentation files (the "Software"), to
44 # deal in the Software without restriction, including without limitation the
45 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
46 # sell copies of the Software, and to permit persons to whom the Software is
47 # furnished to do so, subject to the following conditions:
48 #
49 # The above copyright notice and this permission notice shall be included in
50 # all copies or substantial portions of the Software.
51 #
52 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
53 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
54 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
55 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
56 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
57 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
58 # IN THE SOFTWARE.
59 #
60 #*****************************************************************************
61
62 __author__ = 'Bill Bumgarner <bbum@friday.com>'
63 __license__ = 'MIT'
64 __version__ = '1.2'
65
66 # Modified to use re instead of regex and regsub modules.
67 # 2001/5/7, Jonathan Hogg <jonathan@onegoodidea.com>
68
69 import re
70 import string
71 import sys
72 import types
73
74 class Error(Exception):
75 """Base class for exceptions in the DPyGetOpt module."""
76
77 class ArgumentError(Error):
78 """Exception indicating an error in the arguments passed to
79 DPyGetOpt.processArguments."""
80
81 class SpecificationError(Error):
82 """Exception indicating an error with an option specification."""
83
84 class TerminationError(Error):
85 """Exception indicating an error with an option processing terminator."""
86
87 specificationExpr = re.compile('(?P<required>.)(?P<type>.)(?P<multi>@?)')
88
89 ArgRequired = 'Requires an Argument'
90 ArgOptional = 'Argument Optional'
91
92 # The types modules is not used for these identifiers because there
93 # is no identifier for 'boolean' or 'generic'
94 StringArgType = 'String Argument Type'
95 IntegerArgType = 'Integer Argument Type'
96 RealArgType = 'Real Argument Type'
97 BooleanArgType = 'Boolean Argument Type'
98 GenericArgType = 'Generic Argument Type'
99
100 # dictionary of conversion functions-- boolean and generic options
101 # do not accept arguments and do not need conversion functions;
102 # the identity function is used purely for convenience.
103 ConversionFunctions = {
104 StringArgType : lambda x: x,
105 IntegerArgType : string.atoi,
106 RealArgType : string.atof,
107 BooleanArgType : lambda x: x,
108 GenericArgType : lambda x: x,
109 }
110
111 class DPyGetOpt:
112
113 def __init__(self, spec = None, terminators = ['--']):
114 """
115 Declare and intialize instance variables
116
117 Yes, declaration is not necessary... but one of the things
118 I sorely miss from C/Obj-C is the concept of having an
119 interface definition that clearly declares all instance
120 variables and methods without providing any implementation
121 details. it is a useful reference!
122
123 all instance variables are initialized to 0/Null/None of
124 the appropriate type-- not even the default value...
125 """
126
127 # sys.stderr.write(string.join(spec) + "\n")
128
129 self.allowAbbreviations = 1 # boolean, 1 if abbreviations will
130 # be expanded
131 self.freeValues = [] # list, contains free values
132 self.ignoreCase = 0 # boolean, YES if ignoring case
133 self.needsParse = 0 # boolean, YES if need to reparse parameter spec
134 self.optionNames = {} # dict, all option names-- value is index of tuple
135 self.optionStartExpr = None # regexp defining the start of an option (ie; '-', '--')
136 self.optionTuples = [] # list o' tuples containing defn of options AND aliases
137 self.optionValues = {} # dict, option names (after alias expansion) -> option value(s)
138 self.orderMixed = 0 # boolean, YES if options can be mixed with args
139 self.posixCompliance = 0 # boolean, YES indicates posix like behaviour
140 self.spec = [] # list, raw specs (in case it must be reparsed)
141 self.terminators = terminators # list, strings that terminate argument processing
142 self.termValues = [] # list, values after terminator
143 self.terminator = None # full name of terminator that ended
144 # option processing
145
146 # set up defaults
147 self.setPosixCompliance()
148 self.setIgnoreCase()
149 self.setAllowAbbreviations()
150
151 # parse spec-- if present
152 if spec:
153 self.parseConfiguration(spec)
154
155 def setPosixCompliance(self, aFlag = 0):
156 """
157 Enables and disables posix compliance.
158
159 When enabled, '+' can be used as an option prefix and free
160 values can be mixed with options.
161 """
162 self.posixCompliance = aFlag
163 self.needsParse = 1
164
165 if self.posixCompliance:
166 self.optionStartExpr = re.compile('(--|-)(?P<option>[A-Za-z0-9_-]+)(?P<arg>=.*)?')
167 self.orderMixed = 0
168 else:
169 self.optionStartExpr = re.compile('(--|-|\+)(?P<option>[A-Za-z0-9_-]+)(?P<arg>=.*)?')
170 self.orderMixed = 1
171
172 def isPosixCompliant(self):
173 """
174 Returns the value of the posix compliance flag.
175 """
176 return self.posixCompliance
177
178 def setIgnoreCase(self, aFlag = 1):
179 """
180 Enables and disables ignoring case during option processing.
181 """
182 self.needsParse = 1
183 self.ignoreCase = aFlag
184
185 def ignoreCase(self):
186 """
187 Returns 1 if the option processor will ignore case when
188 processing options.
189 """
190 return self.ignoreCase
191
192 def setAllowAbbreviations(self, aFlag = 1):
193 """
194 Enables and disables the expansion of abbreviations during
195 option processing.
196 """
197 self.allowAbbreviations = aFlag
198
199 def willAllowAbbreviations(self):
200 """
201 Returns 1 if abbreviated options will be automatically
202 expanded to the non-abbreviated form (instead of causing an
203 unrecognized option error).
204 """
205 return self.allowAbbreviations
206
207 def addTerminator(self, newTerm):
208 """
209 Adds newTerm as terminator of option processing.
210
211 Whenever the option processor encounters one of the terminators
212 during option processing, the processing of options terminates
213 immediately, all remaining options are stored in the termValues
214 instance variable and the full name of the terminator is stored
215 in the terminator instance variable.
216 """
217 self.terminators = self.terminators + [newTerm]
218
219 def _addOption(self, oTuple):
220 """
221 Adds the option described by oTuple (name, (type, mode,
222 default), alias) to optionTuples. Adds index keyed under name
223 to optionNames. Raises SpecificationError if name already in
224 optionNames
225 """
226 (name, (type, mode, default, multi), realName) = oTuple
227
228 # verify name and add to option names dictionary
229 if self.optionNames.has_key(name):
230 if realName:
231 raise SpecificationError('Alias \'' + name + '\' for \'' +
232 realName +
233 '\' already used for another option or alias.')
234 else:
235 raise SpecificationError('Option named \'' + name +
236 '\' specified more than once. Specification: '
237 + option)
238
239 # validated. add to optionNames
240 self.optionNames[name] = self.tupleIndex
241 self.tupleIndex = self.tupleIndex + 1
242
243 # add to optionTuples
244 self.optionTuples = self.optionTuples + [oTuple]
245
246 # if type is boolean, add negation
247 if type == BooleanArgType:
248 alias = 'no' + name
249 specTuple = (type, mode, 0, multi)
250 oTuple = (alias, specTuple, name)
251
252 # verify name and add to option names dictionary
253 if self.optionNames.has_key(alias):
254 if realName:
255 raise SpecificationError('Negated alias \'' + name +
256 '\' for \'' + realName +
257 '\' already used for another option or alias.')
258 else:
259 raise SpecificationError('Negated option named \'' + name +
260 '\' specified more than once. Specification: '
261 + option)
262
263 # validated. add to optionNames
264 self.optionNames[alias] = self.tupleIndex
265 self.tupleIndex = self.tupleIndex + 1
266
267 # add to optionTuples
268 self.optionTuples = self.optionTuples + [oTuple]
269
270 def addOptionConfigurationTuple(self, oTuple):
271 (name, argSpec, realName) = oTuple
272 if self.ignoreCase:
273 name = string.lower(name)
274 if realName:
275 realName = string.lower(realName)
276 else:
277 realName = name
278
279 oTuple = (name, argSpec, realName)
280
281 # add option
282 self._addOption(oTuple)
283
284 def addOptionConfigurationTuples(self, oTuple):
285 if type(oTuple) is ListType:
286 for t in oTuple:
287 self.addOptionConfigurationTuple(t)
288 else:
289 self.addOptionConfigurationTuple(oTuple)
290
291 def parseConfiguration(self, spec):
292 # destroy previous stored information + store raw spec
293 self.spec = spec
294 self.optionTuples = []
295 self.optionNames = {}
296 self.tupleIndex = 0
297
298 tupleIndex = 0
299
300 # create some regex's for parsing each spec
301 splitExpr = \
302 re.compile('(?P<names>\w+[-A-Za-z0-9|]*)?(?P<spec>!|[=:][infs]@?)?')
303 for option in spec:
304 # push to lower case (does not negatively affect
305 # specification)
306 if self.ignoreCase:
307 option = string.lower(option)
308
309 # break into names, specification
310 match = splitExpr.match(option)
311 if match is None:
312 raise SpecificationError('Invalid specification {' + option +
313 '}')
314
315 names = match.group('names')
316 specification = match.group('spec')
317
318 # break name into name, aliases
319 nlist = string.split(names, '|')
320
321 # get name
322 name = nlist[0]
323 aliases = nlist[1:]
324
325 # specificationExpr = regex.symcomp('\(<required>.\)\(<type>.\)\(<multi>@?\)')
326 if not specification:
327 #spec tuple is ('type', 'arg mode', 'default value', 'multiple')
328 argType = GenericArgType
329 argMode = None
330 argDefault = 1
331 argMultiple = 0
332 elif specification == '!':
333 argType = BooleanArgType
334 argMode = None
335 argDefault = 1
336 argMultiple = 0
337 else:
338 # parse
339 match = specificationExpr.match(specification)
340 if match is None:
341 # failed to parse, die
342 raise SpecificationError('Invalid configuration for option \''
343 + option + '\'')
344
345 # determine mode
346 required = match.group('required')
347 if required == '=':
348 argMode = ArgRequired
349 elif required == ':':
350 argMode = ArgOptional
351 else:
352 raise SpecificationError('Unknown requirement configuration \''
353 + required + '\'')
354
355 # determine type
356 type = match.group('type')
357 if type == 's':
358 argType = StringArgType
359 argDefault = ''
360 elif type == 'i':
361 argType = IntegerArgType
362 argDefault = 1
363 elif type == 'f' or type == 'n':
364 argType = RealArgType
365 argDefault = 1
366 else:
367 raise SpecificationError('Unknown type specifier \'' +
368 type + '\'')
369
370 # determine quantity
371 if match.group('multi') == '@':
372 argMultiple = 1
373 else:
374 argMultiple = 0
375 ## end else (of not specification)
376
377 # construct specification tuple
378 specTuple = (argType, argMode, argDefault, argMultiple)
379
380 # add the option-- option tuple is (name, specTuple, real name)
381 oTuple = (name, specTuple, name)
382 self._addOption(oTuple)
383
384 for alias in aliases:
385 # drop to all lower (if configured to do so)
386 if self.ignoreCase:
387 alias = string.lower(alias)
388 # create configuration tuple
389 oTuple = (alias, specTuple, name)
390 # add
391 self._addOption(oTuple)
392
393 # successfully parsed....
394 self.needsParse = 0
395
396 def _getArgTuple(self, argName):
397 """
398 Returns a list containing all the specification tuples that
399 match argName. If none match, None is returned. If one
400 matches, a list with one tuple is returned. If more than one
401 match, a list containing all the tuples that matched is
402 returned.
403
404 In other words, this function does not pass judgement upon the
405 validity of multiple matches.
406 """
407 # is it in the optionNames dict?
408
409 try:
410 # sys.stderr.write(argName + string.join(self.optionNames.keys()) + "\n")
411
412 # yes, get index
413 tupleIndex = self.optionNames[argName]
414 # and return tuple as element of list
415 return [self.optionTuples[tupleIndex]]
416 except KeyError:
417 # are abbreviations allowed?
418 if not self.allowAbbreviations:
419 # No! terefore, this cannot be valid argument-- nothing found
420 return None
421
422 # argName might be an abbreviation (and, abbreviations must
423 # be allowed... or this would not have been reached!)
424
425 # create regex for argName
426 argExpr = re.compile('^' + argName)
427
428 tuples = filter(lambda x, argExpr=argExpr: argExpr.search(x[0]) is not None,
429 self.optionTuples)
430
431 if not len(tuples):
432 return None
433 else:
434 return tuples
435
436 def _isTerminator(self, optionName):
437 """
438 Returns the full name of the terminator if optionName is a valid
439 terminator. If it is, sets self.terminator to the full name of
440 the terminator.
441
442 If more than one terminator matched, raises a TerminationError with a
443 string describing the ambiguity.
444 """
445
446 # sys.stderr.write(optionName + "\n")
447 # sys.stderr.write(repr(self.terminators))
448
449 if optionName in self.terminators:
450 self.terminator = optionName
451 elif not self.allowAbbreviations:
452 return None
453
454 # regex thing in bogus
455 # termExpr = regex.compile('^' + optionName)
456
457 terms = filter(lambda x, on=optionName: string.find(x,on) == 0, self.terminators)
458
459 if not len(terms):
460 return None
461 elif len(terms) > 1:
462 raise TerminationError('Ambiguous terminator \'' + optionName +
463 '\' matches ' + repr(terms))
464
465 self.terminator = terms[0]
466 return self.terminator
467
468 def processArguments(self, args = None):
469 """
470 Processes args, a list of arguments (including options).
471
472 If args is the same as sys.argv, automatically trims the first
473 argument (the executable name/path).
474
475 If an exception is not raised, the argument list was parsed
476 correctly.
477
478 Upon successful completion, the freeValues instance variable
479 will contain all the arguments that were not associated with an
480 option in the order they were encountered. optionValues is a
481 dictionary containing the value of each option-- the method
482 valueForOption() can be used to query this dictionary.
483 terminator will contain the argument encountered that terminated
484 option processing (or None, if a terminator was never
485 encountered) and termValues will contain all of the options that
486 appeared after the Terminator (or an empty list).
487 """
488
489 if hasattr(sys, "argv") and args == sys.argv:
490 args = sys.argv[1:]
491
492 max = len(args) # maximum index + 1
493 self.freeValues = [] # array to hold return values
494 self.optionValues= {}
495 index = 0 # initial index
496 self.terminator = None
497 self.termValues = []
498
499 while index < max:
500 # obtain argument
501 arg = args[index]
502 # increment index -- REMEMBER; it is NOW incremented
503 index = index + 1
504
505 # terminate immediately if option terminator encountered
506 if self._isTerminator(arg):
507 self.freeValues = self.freeValues + args[index:]
508 self.termValues = args[index:]
509 return
510
511 # is this possibly an option?
512 match = self.optionStartExpr.match(arg)
513 if match is None:
514 # not an option-- add to freeValues
515 self.freeValues = self.freeValues + [arg]
516 if not self.orderMixed:
517 # mixing not allowed; add rest of args as freeValues
518 self.freeValues = self.freeValues + args[index:]
519 # return to caller
520 return
521 else:
522 continue
523
524 # grab name
525 optName = match.group('option')
526
527 # obtain next argument-- index has already been incremented
528 nextArg = match.group('arg')
529 if nextArg:
530 nextArg = nextArg[1:]
531 index = index - 1 # put it back
532 else:
533 try:
534 nextArg = args[index]
535 except:
536 nextArg = None
537
538 # transpose to lower case, if necessary
539 if self.ignoreCase:
540 optName = string.lower(optName)
541
542 # obtain defining tuple
543 tuples = self._getArgTuple(optName)
544
545 if tuples == None:
546 raise ArgumentError('Illegal option \'' + arg + '\'')
547 elif len(tuples) > 1:
548 raise ArgumentError('Ambiguous option \'' + arg +
549 '\'; matches ' +
550 repr(map(lambda x: x[0], tuples)))
551 else:
552 config = tuples[0]
553
554 # config is now set to the configuration tuple for the
555 # argument
556 (fullName, spec, realName) = config
557 (optType, optMode, optDefault, optMultiple) = spec
558
559 # if opt mode required, but nextArg is none, raise an error
560 if (optMode == ArgRequired):
561 if (not nextArg) or self._isTerminator(nextArg):
562 # print nextArg
563 raise ArgumentError('Option \'' + arg +
564 '\' requires an argument of type ' +
565 optType)
566
567 if (not optMode == None) and nextArg and (not self._isTerminator(nextArg)):
568 # nextArg defined, option configured to possibly consume arg
569 try:
570 # grab conversion function-- the try is more for internal diagnostics
571 func = ConversionFunctions[optType]
572 try:
573 optionValue = func(nextArg)
574 index = index + 1
575 except:
576 # only raise conversion error if REQUIRED to consume argument
577 if optMode == ArgRequired:
578 raise ArgumentError('Invalid argument to option \''
579 + arg + '\'; should be \'' +
580 optType + '\'')
581 else:
582 optionValue = optDefault
583 except ArgumentError:
584 raise
585 except:
586 raise ArgumentError('(' + arg +
587 ') Conversion function for \'' +
588 optType + '\' not found.')
589 else:
590 optionValue = optDefault
591
592 # add value to options dictionary
593 if optMultiple:
594 # can be multiple values
595 try:
596 # try to append element
597 self.optionValues[realName] = self.optionValues[realName] + [optionValue]
598 except:
599 # failed-- must not exist; add it
600 self.optionValues[realName] = [optionValue]
601 else:
602 # only one value per
603 if self.isPosixCompliant and self.optionValues.has_key(realName):
604 raise ArgumentError('Argument \'' + arg +
605 '\' occurs multiple times.')
606
607 self.optionValues[realName] = optionValue
608
609 def valueForOption(self, optionName, defaultValue = None):
610 """
611 Return the value associated with optionName. If optionName was
612 not encountered during parsing of the arguments, returns the
613 defaultValue (which defaults to None).
614 """
615 try:
616 optionValue = self.optionValues[optionName]
617 except:
618 optionValue = defaultValue
619
620 return optionValue
621
622 ##
623 ## test/example section
624 ##
625 test_error = 'Test Run Amok!'
626 def _test():
627 """
628 A relatively complete test suite.
629 """
630 try:
631 DPyGetOpt(['foo', 'bar=s', 'foo'])
632 except Error, exc:
633 print 'EXCEPTION (should be \'foo\' already used..): %s' % exc
634
635 try:
636 DPyGetOpt(['foo|bar|apple=s@', 'baz|apple!'])
637 except Error, exc:
638 print 'EXCEPTION (should be duplicate alias/name error): %s' % exc
639
640 x = DPyGetOpt(['apple|atlas=i@', 'application|executable=f@'])
641 try:
642 x.processArguments(['-app', '29.3'])
643 except Error, exc:
644 print 'EXCEPTION (should be ambiguous argument): %s' % exc
645
646 x = DPyGetOpt(['foo'], ['antigravity', 'antithesis'])
647 try:
648 x.processArguments(['-foo', 'anti'])
649 except Error, exc:
650 print 'EXCEPTION (should be ambiguous terminator): %s' % exc
651
652 profile = ['plain-option',
653 'boolean-option!',
654 'list-of-integers=i@',
655 'list-real-option|list-real-alias|list-real-pseudonym=f@',
656 'optional-string-option:s',
657 'abbreviated-string-list=s@']
658
659 terminators = ['terminator']
660
661 args = ['-plain-option',
662 '+noboolean-option',
663 '--list-of-integers', '1',
664 '+list-of-integers', '2',
665 '-list-of-integers', '3',
666 'freeargone',
667 '-list-real-option', '1.1',
668 '+list-real-alias', '1.2',
669 '--list-real-pseudonym', '1.3',
670 'freeargtwo',
671 '-abbreviated-string-list', 'String1',
672 '--abbreviated-s', 'String2',
673 '-abbrev', 'String3',
674 '-a', 'String4',
675 '-optional-string-option',
676 'term',
677 'next option should look like an invalid arg',
678 '-a']
679
680
681 print 'Using profile: ' + repr(profile)
682 print 'With terminator: ' + repr(terminators)
683 print 'Processing arguments: ' + repr(args)
684
685 go = DPyGetOpt(profile, terminators)
686 go.processArguments(args)
687
688 print 'Options (and values): ' + repr(go.optionValues)
689 print 'free args: ' + repr(go.freeValues)
690 print 'term args: ' + repr(go.termValues)
@@ -1,51 +0,0 b''
1 """Base utilities support for IPython.
2
3 Warning: this is a module that other utilities modules will import from, so it
4 can ONLY depend on the standard library, and NOTHING ELSE. In particular, this
5 module can NOT import anything from IPython, or circular dependencies will arise.
6 """
7
8 #-----------------------------------------------------------------------------
9 # Imports
10 #-----------------------------------------------------------------------------
11
12 import subprocess
13
14 #-----------------------------------------------------------------------------
15 # Functions
16 #-----------------------------------------------------------------------------
17
18 def getoutputerror(cmd,verbose=0,debug=0,header='',split=0):
19 """Return (standard output,standard error) of executing cmd in a shell.
20
21 Accepts the same arguments as system(), plus:
22
23 - split(0): if true, each of stdout/err is returned as a list split on
24 newlines.
25
26 Note: a stateful version of this function is available through the
27 SystemExec class."""
28
29 if verbose or debug: print header+cmd
30 if not cmd:
31 if split:
32 return [],[]
33 else:
34 return '',''
35 if not debug:
36 p = subprocess.Popen(cmd, shell=True,
37 stdin=subprocess.PIPE,
38 stdout=subprocess.PIPE,
39 stderr=subprocess.PIPE,
40 close_fds=True)
41 pin, pout, perr = (p.stdin, p.stdout, p.stderr)
42
43 tout = pout.read().rstrip()
44 terr = perr.read().rstrip()
45 pin.close()
46 pout.close()
47 perr.close()
48 if split:
49 return tout.split('\n'),terr.split('\n')
50 else:
51 return tout,terr
This diff has been collapsed as it changes many lines, (1894 lines changed) Show them Hide them
@@ -1,1894 +0,0 b''
1 # -*- coding: utf-8 -*-
2 """General purpose utilities.
3
4 This is a grab-bag of stuff I find useful in most programs I write. Some of
5 these things are also convenient when working at the command line.
6 """
7
8 #*****************************************************************************
9 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #*****************************************************************************
14 from __future__ import absolute_import
15
16 #****************************************************************************
17 # required modules from the Python standard library
18 import __main__
19
20 import os
21 import platform
22 import re
23 import shlex
24 import shutil
25 import subprocess
26 import sys
27 import time
28 import types
29 import warnings
30
31 # Curses and termios are Unix-only modules
32 try:
33 import curses
34 # We need termios as well, so if its import happens to raise, we bail on
35 # using curses altogether.
36 import termios
37 except ImportError:
38 USE_CURSES = False
39 else:
40 # Curses on Solaris may not be complete, so we can't use it there
41 USE_CURSES = hasattr(curses,'initscr')
42
43 # Other IPython utilities
44 import IPython
45 from IPython.core import release
46 from IPython.external.Itpl import itpl,printpl
47 from IPython.utils import platutils
48 from IPython.utils.generics import result_display
49 from IPython.external.path import path
50 from .baseutils import getoutputerror
51
52 #****************************************************************************
53 # Exceptions
54 class Error(Exception):
55 """Base class for exceptions in this module."""
56 pass
57
58 #----------------------------------------------------------------------------
59 class IOStream:
60 def __init__(self,stream,fallback):
61 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
62 stream = fallback
63 self.stream = stream
64 self._swrite = stream.write
65 self.flush = stream.flush
66
67 def write(self,data):
68 try:
69 self._swrite(data)
70 except:
71 try:
72 # print handles some unicode issues which may trip a plain
73 # write() call. Attempt to emulate write() by using a
74 # trailing comma
75 print >> self.stream, data,
76 except:
77 # if we get here, something is seriously broken.
78 print >> sys.stderr, \
79 'ERROR - failed to write data to stream:', self.stream
80
81 def writeln(self, data):
82 self.write(data)
83 self.write('\n')
84
85 def close(self):
86 pass
87
88
89 class IOTerm:
90 """ Term holds the file or file-like objects for handling I/O operations.
91
92 These are normally just sys.stdin, sys.stdout and sys.stderr but for
93 Windows they can can replaced to allow editing the strings before they are
94 displayed."""
95
96 # In the future, having IPython channel all its I/O operations through
97 # this class will make it easier to embed it into other environments which
98 # are not a normal terminal (such as a GUI-based shell)
99 def __init__(self,cin=None,cout=None,cerr=None):
100 self.cin = IOStream(cin,sys.stdin)
101 self.cout = IOStream(cout,sys.stdout)
102 self.cerr = IOStream(cerr,sys.stderr)
103
104 # Global variable to be used for all I/O
105 Term = IOTerm()
106
107 import IPython.utils.rlineimpl as readline
108 # Remake Term to use the readline i/o facilities
109 if sys.platform == 'win32' and readline.have_readline:
110
111 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
112
113
114 class Tee(object):
115 """A class to duplicate an output stream to stdout/err.
116
117 This works in a manner very similar to the Unix 'tee' command.
118
119 When the object is closed or deleted, it closes the original file given to
120 it for duplication.
121 """
122 # Inspired by:
123 # http://mail.python.org/pipermail/python-list/2007-May/442737.html
124
125 def __init__(self, file, mode=None, channel='stdout'):
126 """Construct a new Tee object.
127
128 Parameters
129 ----------
130 file : filename or open filehandle (writable)
131 File that will be duplicated
132
133 mode : optional, valid mode for open().
134 If a filename was give, open with this mode.
135
136 channel : str, one of ['stdout', 'stderr']
137 """
138 if channel not in ['stdout', 'stderr']:
139 raise ValueError('Invalid channel spec %s' % channel)
140
141 if hasattr(file, 'write') and hasattr(file, 'seek'):
142 self.file = file
143 else:
144 self.file = open(name, mode)
145 self.channel = channel
146 self.ostream = getattr(sys, channel)
147 setattr(sys, channel, self)
148 self._closed = False
149
150 def close(self):
151 """Close the file and restore the channel."""
152 self.flush()
153 setattr(sys, self.channel, self.ostream)
154 self.file.close()
155 self._closed = True
156
157 def write(self, data):
158 """Write data to both channels."""
159 self.file.write(data)
160 self.ostream.write(data)
161 self.ostream.flush()
162
163 def flush(self):
164 """Flush both channels."""
165 self.file.flush()
166 self.ostream.flush()
167
168 def __del__(self):
169 if not self._closed:
170 self.close()
171
172
173 #****************************************************************************
174 # Generic warning/error printer, used by everything else
175 def warn(msg,level=2,exit_val=1):
176 """Standard warning printer. Gives formatting consistency.
177
178 Output is sent to Term.cerr (sys.stderr by default).
179
180 Options:
181
182 -level(2): allows finer control:
183 0 -> Do nothing, dummy function.
184 1 -> Print message.
185 2 -> Print 'WARNING:' + message. (Default level).
186 3 -> Print 'ERROR:' + message.
187 4 -> Print 'FATAL ERROR:' + message and trigger a sys.exit(exit_val).
188
189 -exit_val (1): exit value returned by sys.exit() for a level 4
190 warning. Ignored for all other levels."""
191
192 if level>0:
193 header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
194 print >> Term.cerr, '%s%s' % (header[level],msg)
195 if level == 4:
196 print >> Term.cerr,'Exiting.\n'
197 sys.exit(exit_val)
198
199
200 def info(msg):
201 """Equivalent to warn(msg,level=1)."""
202
203 warn(msg,level=1)
204
205
206 def error(msg):
207 """Equivalent to warn(msg,level=3)."""
208
209 warn(msg,level=3)
210
211
212 def fatal(msg,exit_val=1):
213 """Equivalent to warn(msg,exit_val=exit_val,level=4)."""
214
215 warn(msg,exit_val=exit_val,level=4)
216
217 def sys_info():
218 """Return useful information about IPython and the system, as a string.
219
220 Examples
221 --------
222 In [1]: print(sys_info())
223 IPython version: 0.11.bzr.r1340 # random
224 BZR revision : 1340
225 Platform info : os.name -> posix, sys.platform -> linux2
226 : Linux-2.6.31-17-generic-i686-with-Ubuntu-9.10-karmic
227 Python info : 2.6.4 (r264:75706, Dec 7 2009, 18:45:15)
228 [GCC 4.4.1]
229 """
230 import platform
231 out = []
232 out.append('IPython version: %s' % release.version)
233 out.append('BZR revision : %s' % release.revision)
234 out.append('Platform info : os.name -> %s, sys.platform -> %s' %
235 (os.name,sys.platform) )
236 out.append(' : %s' % platform.platform())
237 out.append('Python info : %s' % sys.version)
238 out.append('') # ensure closing newline
239 return '\n'.join(out)
240
241
242 #---------------------------------------------------------------------------
243 # Debugging routines
244 #
245 def debugx(expr,pre_msg=''):
246 """Print the value of an expression from the caller's frame.
247
248 Takes an expression, evaluates it in the caller's frame and prints both
249 the given expression and the resulting value (as well as a debug mark
250 indicating the name of the calling function. The input must be of a form
251 suitable for eval().
252
253 An optional message can be passed, which will be prepended to the printed
254 expr->value pair."""
255
256 cf = sys._getframe(1)
257 print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
258 eval(expr,cf.f_globals,cf.f_locals))
259
260 # deactivate it by uncommenting the following line, which makes it a no-op
261 #def debugx(expr,pre_msg=''): pass
262
263 #----------------------------------------------------------------------------
264 StringTypes = types.StringTypes
265
266 # Basic timing functionality
267
268 # If possible (Unix), use the resource module instead of time.clock()
269 try:
270 import resource
271 def clocku():
272 """clocku() -> floating point number
273
274 Return the *USER* CPU time in seconds since the start of the process.
275 This is done via a call to resource.getrusage, so it avoids the
276 wraparound problems in time.clock()."""
277
278 return resource.getrusage(resource.RUSAGE_SELF)[0]
279
280 def clocks():
281 """clocks() -> floating point number
282
283 Return the *SYSTEM* CPU time in seconds since the start of the process.
284 This is done via a call to resource.getrusage, so it avoids the
285 wraparound problems in time.clock()."""
286
287 return resource.getrusage(resource.RUSAGE_SELF)[1]
288
289 def clock():
290 """clock() -> floating point number
291
292 Return the *TOTAL USER+SYSTEM* CPU time in seconds since the start of
293 the process. This is done via a call to resource.getrusage, so it
294 avoids the wraparound problems in time.clock()."""
295
296 u,s = resource.getrusage(resource.RUSAGE_SELF)[:2]
297 return u+s
298
299 def clock2():
300 """clock2() -> (t_user,t_system)
301
302 Similar to clock(), but return a tuple of user/system times."""
303 return resource.getrusage(resource.RUSAGE_SELF)[:2]
304
305 except ImportError:
306 # There is no distinction of user/system time under windows, so we just use
307 # time.clock() for everything...
308 clocku = clocks = clock = time.clock
309 def clock2():
310 """Under windows, system CPU time can't be measured.
311
312 This just returns clock() and zero."""
313 return time.clock(),0.0
314
315
316 def timings_out(reps,func,*args,**kw):
317 """timings_out(reps,func,*args,**kw) -> (t_total,t_per_call,output)
318
319 Execute a function reps times, return a tuple with the elapsed total
320 CPU time in seconds, the time per call and the function's output.
321
322 Under Unix, the return value is the sum of user+system time consumed by
323 the process, computed via the resource module. This prevents problems
324 related to the wraparound effect which the time.clock() function has.
325
326 Under Windows the return value is in wall clock seconds. See the
327 documentation for the time module for more details."""
328
329 reps = int(reps)
330 assert reps >=1, 'reps must be >= 1'
331 if reps==1:
332 start = clock()
333 out = func(*args,**kw)
334 tot_time = clock()-start
335 else:
336 rng = xrange(reps-1) # the last time is executed separately to store output
337 start = clock()
338 for dummy in rng: func(*args,**kw)
339 out = func(*args,**kw) # one last time
340 tot_time = clock()-start
341 av_time = tot_time / reps
342 return tot_time,av_time,out
343
344
345 def timings(reps,func,*args,**kw):
346 """timings(reps,func,*args,**kw) -> (t_total,t_per_call)
347
348 Execute a function reps times, return a tuple with the elapsed total CPU
349 time in seconds and the time per call. These are just the first two values
350 in timings_out()."""
351
352 return timings_out(reps,func,*args,**kw)[0:2]
353
354
355 def timing(func,*args,**kw):
356 """timing(func,*args,**kw) -> t_total
357
358 Execute a function once, return the elapsed total CPU time in
359 seconds. This is just the first value in timings_out()."""
360
361 return timings_out(1,func,*args,**kw)[0]
362
363 #****************************************************************************
364 # file and system
365
366 def arg_split(s,posix=False):
367 """Split a command line's arguments in a shell-like manner.
368
369 This is a modified version of the standard library's shlex.split()
370 function, but with a default of posix=False for splitting, so that quotes
371 in inputs are respected."""
372
373 # XXX - there may be unicode-related problems here!!! I'm not sure that
374 # shlex is truly unicode-safe, so it might be necessary to do
375 #
376 # s = s.encode(sys.stdin.encoding)
377 #
378 # first, to ensure that shlex gets a normal string. Input from anyone who
379 # knows more about unicode and shlex than I would be good to have here...
380 lex = shlex.shlex(s, posix=posix)
381 lex.whitespace_split = True
382 return list(lex)
383
384
385 def system(cmd,verbose=0,debug=0,header=''):
386 """Execute a system command, return its exit status.
387
388 Options:
389
390 - verbose (0): print the command to be executed.
391
392 - debug (0): only print, do not actually execute.
393
394 - header (''): Header to print on screen prior to the executed command (it
395 is only prepended to the command, no newlines are added).
396
397 Note: a stateful version of this function is available through the
398 SystemExec class."""
399
400 stat = 0
401 if verbose or debug: print header+cmd
402 sys.stdout.flush()
403 if not debug: stat = os.system(cmd)
404 return stat
405
406
407 def abbrev_cwd():
408 """ Return abbreviated version of cwd, e.g. d:mydir """
409 cwd = os.getcwd().replace('\\','/')
410 drivepart = ''
411 tail = cwd
412 if sys.platform == 'win32':
413 if len(cwd) < 4:
414 return cwd
415 drivepart,tail = os.path.splitdrive(cwd)
416
417
418 parts = tail.split('/')
419 if len(parts) > 2:
420 tail = '/'.join(parts[-2:])
421
422 return (drivepart + (
423 cwd == '/' and '/' or tail))
424
425
426 # This function is used by ipython in a lot of places to make system calls.
427 # We need it to be slightly different under win32, due to the vagaries of
428 # 'network shares'. A win32 override is below.
429
430 def shell(cmd,verbose=0,debug=0,header=''):
431 """Execute a command in the system shell, always return None.
432
433 Options:
434
435 - verbose (0): print the command to be executed.
436
437 - debug (0): only print, do not actually execute.
438
439 - header (''): Header to print on screen prior to the executed command (it
440 is only prepended to the command, no newlines are added).
441
442 Note: this is similar to genutils.system(), but it returns None so it can
443 be conveniently used in interactive loops without getting the return value
444 (typically 0) printed many times."""
445
446 stat = 0
447 if verbose or debug: print header+cmd
448 # flush stdout so we don't mangle python's buffering
449 sys.stdout.flush()
450
451 if not debug:
452 platutils.set_term_title("IPy " + cmd)
453 os.system(cmd)
454 platutils.set_term_title("IPy " + abbrev_cwd())
455
456 # override shell() for win32 to deal with network shares
457 if os.name in ('nt','dos'):
458
459 shell_ori = shell
460
461 def shell(cmd,verbose=0,debug=0,header=''):
462 if os.getcwd().startswith(r"\\"):
463 path = os.getcwd()
464 # change to c drive (cannot be on UNC-share when issuing os.system,
465 # as cmd.exe cannot handle UNC addresses)
466 os.chdir("c:")
467 # issue pushd to the UNC-share and then run the command
468 try:
469 shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header)
470 finally:
471 os.chdir(path)
472 else:
473 shell_ori(cmd,verbose,debug,header)
474
475 shell.__doc__ = shell_ori.__doc__
476
477
478 def getoutput(cmd,verbose=0,debug=0,header='',split=0):
479 """Dummy substitute for perl's backquotes.
480
481 Executes a command and returns the output.
482
483 Accepts the same arguments as system(), plus:
484
485 - split(0): if true, the output is returned as a list split on newlines.
486
487 Note: a stateful version of this function is available through the
488 SystemExec class.
489
490 This is pretty much deprecated and rarely used,
491 genutils.getoutputerror may be what you need.
492
493 """
494
495 if verbose or debug: print header+cmd
496 if not debug:
497 pipe = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout
498 output = pipe.read()
499 # stipping last \n is here for backwards compat.
500 if output.endswith('\n'):
501 output = output[:-1]
502 if split:
503 return output.split('\n')
504 else:
505 return output
506
507 # for compatibility with older naming conventions
508 xsys = system
509 bq = getoutput
510
511
512 class SystemExec:
513 """Access the system and getoutput functions through a stateful interface.
514
515 Note: here we refer to the system and getoutput functions from this
516 library, not the ones from the standard python library.
517
518 This class offers the system and getoutput functions as methods, but the
519 verbose, debug and header parameters can be set for the instance (at
520 creation time or later) so that they don't need to be specified on each
521 call.
522
523 For efficiency reasons, there's no way to override the parameters on a
524 per-call basis other than by setting instance attributes. If you need
525 local overrides, it's best to directly call system() or getoutput().
526
527 The following names are provided as alternate options:
528 - xsys: alias to system
529 - bq: alias to getoutput
530
531 An instance can then be created as:
532 >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ')
533 """
534
535 def __init__(self,verbose=0,debug=0,header='',split=0):
536 """Specify the instance's values for verbose, debug and header."""
537 setattr_list(self,'verbose debug header split')
538
539 def system(self,cmd):
540 """Stateful interface to system(), with the same keyword parameters."""
541
542 system(cmd,self.verbose,self.debug,self.header)
543
544 def shell(self,cmd):
545 """Stateful interface to shell(), with the same keyword parameters."""
546
547 shell(cmd,self.verbose,self.debug,self.header)
548
549 xsys = system # alias
550
551 def getoutput(self,cmd):
552 """Stateful interface to getoutput()."""
553
554 return getoutput(cmd,self.verbose,self.debug,self.header,self.split)
555
556 def getoutputerror(self,cmd):
557 """Stateful interface to getoutputerror()."""
558
559 return getoutputerror(cmd,self.verbose,self.debug,self.header,self.split)
560
561 bq = getoutput # alias
562
563 #-----------------------------------------------------------------------------
564 def mutex_opts(dict,ex_op):
565 """Check for presence of mutually exclusive keys in a dict.
566
567 Call: mutex_opts(dict,[[op1a,op1b],[op2a,op2b]...]"""
568 for op1,op2 in ex_op:
569 if op1 in dict and op2 in dict:
570 raise ValueError,'\n*** ERROR in Arguments *** '\
571 'Options '+op1+' and '+op2+' are mutually exclusive.'
572
573 #-----------------------------------------------------------------------------
574 def get_py_filename(name):
575 """Return a valid python filename in the current directory.
576
577 If the given name is not a file, it adds '.py' and searches again.
578 Raises IOError with an informative message if the file isn't found."""
579
580 name = os.path.expanduser(name)
581 if not os.path.isfile(name) and not name.endswith('.py'):
582 name += '.py'
583 if os.path.isfile(name):
584 return name
585 else:
586 raise IOError,'File `%s` not found.' % name
587
588 #-----------------------------------------------------------------------------
589
590
591 def filefind(filename, path_dirs=None):
592 """Find a file by looking through a sequence of paths.
593
594 This iterates through a sequence of paths looking for a file and returns
595 the full, absolute path of the first occurence of the file. If no set of
596 path dirs is given, the filename is tested as is, after running through
597 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
598
599 filefind('myfile.txt')
600
601 will find the file in the current working dir, but::
602
603 filefind('~/myfile.txt')
604
605 Will find the file in the users home directory. This function does not
606 automatically try any paths, such as the cwd or the user's home directory.
607
608 Parameters
609 ----------
610 filename : str
611 The filename to look for.
612 path_dirs : str, None or sequence of str
613 The sequence of paths to look for the file in. If None, the filename
614 need to be absolute or be in the cwd. If a string, the string is
615 put into a sequence and the searched. If a sequence, walk through
616 each element and join with ``filename``, calling :func:`expandvars`
617 and :func:`expanduser` before testing for existence.
618
619 Returns
620 -------
621 Raises :exc:`IOError` or returns absolute path to file.
622 """
623
624 # If paths are quoted, abspath gets confused, strip them...
625 filename = filename.strip('"').strip("'")
626 # If the input is an absolute path, just check it exists
627 if os.path.isabs(filename) and os.path.isfile(filename):
628 return filename
629
630 if path_dirs is None:
631 path_dirs = ("",)
632 elif isinstance(path_dirs, basestring):
633 path_dirs = (path_dirs,)
634
635 for path in path_dirs:
636 if path == '.': path = os.getcwd()
637 testname = expand_path(os.path.join(path, filename))
638 if os.path.isfile(testname):
639 return os.path.abspath(testname)
640
641 raise IOError("File %r does not exist in any of the search paths: %r" %
642 (filename, path_dirs) )
643
644
645 #----------------------------------------------------------------------------
646 def file_read(filename):
647 """Read a file and close it. Returns the file source."""
648 fobj = open(filename,'r');
649 source = fobj.read();
650 fobj.close()
651 return source
652
653 def file_readlines(filename):
654 """Read a file and close it. Returns the file source using readlines()."""
655 fobj = open(filename,'r');
656 lines = fobj.readlines();
657 fobj.close()
658 return lines
659
660 #----------------------------------------------------------------------------
661 def target_outdated(target,deps):
662 """Determine whether a target is out of date.
663
664 target_outdated(target,deps) -> 1/0
665
666 deps: list of filenames which MUST exist.
667 target: single filename which may or may not exist.
668
669 If target doesn't exist or is older than any file listed in deps, return
670 true, otherwise return false.
671 """
672 try:
673 target_time = os.path.getmtime(target)
674 except os.error:
675 return 1
676 for dep in deps:
677 dep_time = os.path.getmtime(dep)
678 if dep_time > target_time:
679 #print "For target",target,"Dep failed:",dep # dbg
680 #print "times (dep,tar):",dep_time,target_time # dbg
681 return 1
682 return 0
683
684 #-----------------------------------------------------------------------------
685 def target_update(target,deps,cmd):
686 """Update a target with a given command given a list of dependencies.
687
688 target_update(target,deps,cmd) -> runs cmd if target is outdated.
689
690 This is just a wrapper around target_outdated() which calls the given
691 command if target is outdated."""
692
693 if target_outdated(target,deps):
694 xsys(cmd)
695
696 #----------------------------------------------------------------------------
697 def unquote_ends(istr):
698 """Remove a single pair of quotes from the endpoints of a string."""
699
700 if not istr:
701 return istr
702 if (istr[0]=="'" and istr[-1]=="'") or \
703 (istr[0]=='"' and istr[-1]=='"'):
704 return istr[1:-1]
705 else:
706 return istr
707
708 #----------------------------------------------------------------------------
709 def flag_calls(func):
710 """Wrap a function to detect and flag when it gets called.
711
712 This is a decorator which takes a function and wraps it in a function with
713 a 'called' attribute. wrapper.called is initialized to False.
714
715 The wrapper.called attribute is set to False right before each call to the
716 wrapped function, so if the call fails it remains False. After the call
717 completes, wrapper.called is set to True and the output is returned.
718
719 Testing for truth in wrapper.called allows you to determine if a call to
720 func() was attempted and succeeded."""
721
722 def wrapper(*args,**kw):
723 wrapper.called = False
724 out = func(*args,**kw)
725 wrapper.called = True
726 return out
727
728 wrapper.called = False
729 wrapper.__doc__ = func.__doc__
730 return wrapper
731
732 #----------------------------------------------------------------------------
733 def dhook_wrap(func,*a,**k):
734 """Wrap a function call in a sys.displayhook controller.
735
736 Returns a wrapper around func which calls func, with all its arguments and
737 keywords unmodified, using the default sys.displayhook. Since IPython
738 modifies sys.displayhook, it breaks the behavior of certain systems that
739 rely on the default behavior, notably doctest.
740 """
741
742 def f(*a,**k):
743
744 dhook_s = sys.displayhook
745 sys.displayhook = sys.__displayhook__
746 try:
747 out = func(*a,**k)
748 finally:
749 sys.displayhook = dhook_s
750
751 return out
752
753 f.__doc__ = func.__doc__
754 return f
755
756 #----------------------------------------------------------------------------
757 def doctest_reload():
758 """Properly reload doctest to reuse it interactively.
759
760 This routine:
761
762 - imports doctest but does NOT reload it (see below).
763
764 - resets its global 'master' attribute to None, so that multiple uses of
765 the module interactively don't produce cumulative reports.
766
767 - Monkeypatches its core test runner method to protect it from IPython's
768 modified displayhook. Doctest expects the default displayhook behavior
769 deep down, so our modification breaks it completely. For this reason, a
770 hard monkeypatch seems like a reasonable solution rather than asking
771 users to manually use a different doctest runner when under IPython.
772
773 Notes
774 -----
775
776 This function *used to* reload doctest, but this has been disabled because
777 reloading doctest unconditionally can cause massive breakage of other
778 doctest-dependent modules already in memory, such as those for IPython's
779 own testing system. The name wasn't changed to avoid breaking people's
780 code, but the reload call isn't actually made anymore."""
781
782 import doctest
783 doctest.master = None
784 doctest.DocTestRunner.run = dhook_wrap(doctest.DocTestRunner.run)
785
786 #----------------------------------------------------------------------------
787 class HomeDirError(Error):
788 pass
789
790 def get_home_dir():
791 """Return the closest possible equivalent to a 'home' directory.
792
793 * On POSIX, we try $HOME.
794 * On Windows we try:
795 - %HOME%: rare, but some people with unix-like setups may have defined it
796 - %HOMESHARE%
797 - %HOMEDRIVE\%HOMEPATH%
798 - %USERPROFILE%
799 - Registry hack
800 * On Dos C:\
801
802 Currently only Posix and NT are implemented, a HomeDirError exception is
803 raised for all other OSes.
804 """
805
806 isdir = os.path.isdir
807 env = os.environ
808
809 # first, check py2exe distribution root directory for _ipython.
810 # This overrides all. Normally does not exist.
811
812 if hasattr(sys, "frozen"): #Is frozen by py2exe
813 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
814 root, rest = IPython.__file__.lower().split('library.zip')
815 else:
816 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
817 root=os.path.abspath(root).rstrip('\\')
818 if isdir(os.path.join(root, '_ipython')):
819 os.environ["IPYKITROOT"] = root
820 return root.decode(sys.getfilesystemencoding())
821
822 if os.name == 'posix':
823 # Linux, Unix, AIX, OS X
824 try:
825 homedir = env['HOME']
826 except KeyError:
827 raise HomeDirError('Undefined $HOME, IPython cannot proceed.')
828 else:
829 return homedir.decode(sys.getfilesystemencoding())
830 elif os.name == 'nt':
831 # Now for win9x, XP, Vista, 7?
832 # For some strange reason all of these return 'nt' for os.name.
833 # First look for a network home directory. This will return the UNC
834 # path (\\server\\Users\%username%) not the mapped path (Z:\). This
835 # is needed when running IPython on cluster where all paths have to
836 # be UNC.
837 try:
838 # A user with a lot of unix tools in win32 may have defined $HOME,
839 # honor it if it exists, but otherwise let the more typical
840 # %HOMESHARE% variable be used.
841 homedir = env.get('HOME')
842 if homedir is None:
843 homedir = env['HOMESHARE']
844 except KeyError:
845 pass
846 else:
847 if isdir(homedir):
848 return homedir.decode(sys.getfilesystemencoding())
849
850 # Now look for a local home directory
851 try:
852 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
853 except KeyError:
854 pass
855 else:
856 if isdir(homedir):
857 return homedir.decode(sys.getfilesystemencoding())
858
859 # Now the users profile directory
860 try:
861 homedir = os.path.join(env['USERPROFILE'])
862 except KeyError:
863 pass
864 else:
865 if isdir(homedir):
866 return homedir.decode(sys.getfilesystemencoding())
867
868 # Use the registry to get the 'My Documents' folder.
869 try:
870 import _winreg as wreg
871 key = wreg.OpenKey(
872 wreg.HKEY_CURRENT_USER,
873 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
874 )
875 homedir = wreg.QueryValueEx(key,'Personal')[0]
876 key.Close()
877 except:
878 pass
879 else:
880 if isdir(homedir):
881 return homedir.decode(sys.getfilesystemencoding())
882
883 # If all else fails, raise HomeDirError
884 raise HomeDirError('No valid home directory could be found')
885 elif os.name == 'dos':
886 # Desperate, may do absurd things in classic MacOS. May work under DOS.
887 return 'C:\\'.decode(sys.getfilesystemencoding())
888 else:
889 raise HomeDirError('No valid home directory could be found for your OS')
890
891
892 def get_ipython_dir():
893 """Get the IPython directory for this platform and user.
894
895 This uses the logic in `get_home_dir` to find the home directory
896 and the adds .ipython to the end of the path.
897 """
898 ipdir_def = '.ipython'
899 home_dir = get_home_dir()
900 #import pdb; pdb.set_trace() # dbg
901 ipdir = os.environ.get(
902 'IPYTHON_DIR', os.environ.get(
903 'IPYTHONDIR', os.path.join(home_dir, ipdir_def)
904 )
905 )
906 return ipdir.decode(sys.getfilesystemencoding())
907
908
909 def get_ipython_package_dir():
910 """Get the base directory where IPython itself is installed."""
911 ipdir = os.path.dirname(IPython.__file__)
912 return ipdir.decode(sys.getfilesystemencoding())
913
914
915 #****************************************************************************
916 # strings and text
917
918 class LSString(str):
919 """String derivative with a special access attributes.
920
921 These are normal strings, but with the special attributes:
922
923 .l (or .list) : value as list (split on newlines).
924 .n (or .nlstr): original value (the string itself).
925 .s (or .spstr): value as whitespace-separated string.
926 .p (or .paths): list of path objects
927
928 Any values which require transformations are computed only once and
929 cached.
930
931 Such strings are very useful to efficiently interact with the shell, which
932 typically only understands whitespace-separated options for commands."""
933
934 def get_list(self):
935 try:
936 return self.__list
937 except AttributeError:
938 self.__list = self.split('\n')
939 return self.__list
940
941 l = list = property(get_list)
942
943 def get_spstr(self):
944 try:
945 return self.__spstr
946 except AttributeError:
947 self.__spstr = self.replace('\n',' ')
948 return self.__spstr
949
950 s = spstr = property(get_spstr)
951
952 def get_nlstr(self):
953 return self
954
955 n = nlstr = property(get_nlstr)
956
957 def get_paths(self):
958 try:
959 return self.__paths
960 except AttributeError:
961 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
962 return self.__paths
963
964 p = paths = property(get_paths)
965
966 def print_lsstring(arg):
967 """ Prettier (non-repr-like) and more informative printer for LSString """
968 print "LSString (.p, .n, .l, .s available). Value:"
969 print arg
970
971 print_lsstring = result_display.when_type(LSString)(print_lsstring)
972
973 #----------------------------------------------------------------------------
974 class SList(list):
975 """List derivative with a special access attributes.
976
977 These are normal lists, but with the special attributes:
978
979 .l (or .list) : value as list (the list itself).
980 .n (or .nlstr): value as a string, joined on newlines.
981 .s (or .spstr): value as a string, joined on spaces.
982 .p (or .paths): list of path objects
983
984 Any values which require transformations are computed only once and
985 cached."""
986
987 def get_list(self):
988 return self
989
990 l = list = property(get_list)
991
992 def get_spstr(self):
993 try:
994 return self.__spstr
995 except AttributeError:
996 self.__spstr = ' '.join(self)
997 return self.__spstr
998
999 s = spstr = property(get_spstr)
1000
1001 def get_nlstr(self):
1002 try:
1003 return self.__nlstr
1004 except AttributeError:
1005 self.__nlstr = '\n'.join(self)
1006 return self.__nlstr
1007
1008 n = nlstr = property(get_nlstr)
1009
1010 def get_paths(self):
1011 try:
1012 return self.__paths
1013 except AttributeError:
1014 self.__paths = [path(p) for p in self if os.path.exists(p)]
1015 return self.__paths
1016
1017 p = paths = property(get_paths)
1018
1019 def grep(self, pattern, prune = False, field = None):
1020 """ Return all strings matching 'pattern' (a regex or callable)
1021
1022 This is case-insensitive. If prune is true, return all items
1023 NOT matching the pattern.
1024
1025 If field is specified, the match must occur in the specified
1026 whitespace-separated field.
1027
1028 Examples::
1029
1030 a.grep( lambda x: x.startswith('C') )
1031 a.grep('Cha.*log', prune=1)
1032 a.grep('chm', field=-1)
1033 """
1034
1035 def match_target(s):
1036 if field is None:
1037 return s
1038 parts = s.split()
1039 try:
1040 tgt = parts[field]
1041 return tgt
1042 except IndexError:
1043 return ""
1044
1045 if isinstance(pattern, basestring):
1046 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
1047 else:
1048 pred = pattern
1049 if not prune:
1050 return SList([el for el in self if pred(match_target(el))])
1051 else:
1052 return SList([el for el in self if not pred(match_target(el))])
1053 def fields(self, *fields):
1054 """ Collect whitespace-separated fields from string list
1055
1056 Allows quick awk-like usage of string lists.
1057
1058 Example data (in var a, created by 'a = !ls -l')::
1059 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
1060 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
1061
1062 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
1063 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
1064 (note the joining by space).
1065 a.fields(-1) is ['ChangeLog', 'IPython']
1066
1067 IndexErrors are ignored.
1068
1069 Without args, fields() just split()'s the strings.
1070 """
1071 if len(fields) == 0:
1072 return [el.split() for el in self]
1073
1074 res = SList()
1075 for el in [f.split() for f in self]:
1076 lineparts = []
1077
1078 for fd in fields:
1079 try:
1080 lineparts.append(el[fd])
1081 except IndexError:
1082 pass
1083 if lineparts:
1084 res.append(" ".join(lineparts))
1085
1086 return res
1087 def sort(self,field= None, nums = False):
1088 """ sort by specified fields (see fields())
1089
1090 Example::
1091 a.sort(1, nums = True)
1092
1093 Sorts a by second field, in numerical order (so that 21 > 3)
1094
1095 """
1096
1097 #decorate, sort, undecorate
1098 if field is not None:
1099 dsu = [[SList([line]).fields(field), line] for line in self]
1100 else:
1101 dsu = [[line, line] for line in self]
1102 if nums:
1103 for i in range(len(dsu)):
1104 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
1105 try:
1106 n = int(numstr)
1107 except ValueError:
1108 n = 0;
1109 dsu[i][0] = n
1110
1111
1112 dsu.sort()
1113 return SList([t[1] for t in dsu])
1114
1115 def print_slist(arg):
1116 """ Prettier (non-repr-like) and more informative printer for SList """
1117 print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
1118 if hasattr(arg, 'hideonce') and arg.hideonce:
1119 arg.hideonce = False
1120 return
1121
1122 nlprint(arg)
1123
1124 print_slist = result_display.when_type(SList)(print_slist)
1125
1126
1127
1128 #----------------------------------------------------------------------------
1129 def esc_quotes(strng):
1130 """Return the input string with single and double quotes escaped out"""
1131
1132 return strng.replace('"','\\"').replace("'","\\'")
1133
1134 #----------------------------------------------------------------------------
1135 def make_quoted_expr(s):
1136 """Return string s in appropriate quotes, using raw string if possible.
1137
1138 XXX - example removed because it caused encoding errors in documentation
1139 generation. We need a new example that doesn't contain invalid chars.
1140
1141 Note the use of raw string and padding at the end to allow trailing
1142 backslash.
1143 """
1144
1145 tail = ''
1146 tailpadding = ''
1147 raw = ''
1148 if "\\" in s:
1149 raw = 'r'
1150 if s.endswith('\\'):
1151 tail = '[:-1]'
1152 tailpadding = '_'
1153 if '"' not in s:
1154 quote = '"'
1155 elif "'" not in s:
1156 quote = "'"
1157 elif '"""' not in s and not s.endswith('"'):
1158 quote = '"""'
1159 elif "'''" not in s and not s.endswith("'"):
1160 quote = "'''"
1161 else:
1162 # give up, backslash-escaped string will do
1163 return '"%s"' % esc_quotes(s)
1164 res = raw + quote + s + tailpadding + quote + tail
1165 return res
1166
1167
1168 #----------------------------------------------------------------------------
1169 def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):
1170 """Take multiple lines of input.
1171
1172 A list with each line of input as a separate element is returned when a
1173 termination string is entered (defaults to a single '.'). Input can also
1174 terminate via EOF (^D in Unix, ^Z-RET in Windows).
1175
1176 Lines of input which end in \\ are joined into single entries (and a
1177 secondary continuation prompt is issued as long as the user terminates
1178 lines with \\). This allows entering very long strings which are still
1179 meant to be treated as single entities.
1180 """
1181
1182 try:
1183 if header:
1184 header += '\n'
1185 lines = [raw_input(header + ps1)]
1186 except EOFError:
1187 return []
1188 terminate = [terminate_str]
1189 try:
1190 while lines[-1:] != terminate:
1191 new_line = raw_input(ps1)
1192 while new_line.endswith('\\'):
1193 new_line = new_line[:-1] + raw_input(ps2)
1194 lines.append(new_line)
1195
1196 return lines[:-1] # don't return the termination command
1197 except EOFError:
1198 print
1199 return lines
1200
1201 #----------------------------------------------------------------------------
1202 def raw_input_ext(prompt='', ps2='... '):
1203 """Similar to raw_input(), but accepts extended lines if input ends with \\."""
1204
1205 line = raw_input(prompt)
1206 while line.endswith('\\'):
1207 line = line[:-1] + raw_input(ps2)
1208 return line
1209
1210 #----------------------------------------------------------------------------
1211 def ask_yes_no(prompt,default=None):
1212 """Asks a question and returns a boolean (y/n) answer.
1213
1214 If default is given (one of 'y','n'), it is used if the user input is
1215 empty. Otherwise the question is repeated until an answer is given.
1216
1217 An EOF is treated as the default answer. If there is no default, an
1218 exception is raised to prevent infinite loops.
1219
1220 Valid answers are: y/yes/n/no (match is not case sensitive)."""
1221
1222 answers = {'y':True,'n':False,'yes':True,'no':False}
1223 ans = None
1224 while ans not in answers.keys():
1225 try:
1226 ans = raw_input(prompt+' ').lower()
1227 if not ans: # response was an empty string
1228 ans = default
1229 except KeyboardInterrupt:
1230 pass
1231 except EOFError:
1232 if default in answers.keys():
1233 ans = default
1234 print
1235 else:
1236 raise
1237
1238 return answers[ans]
1239
1240 #----------------------------------------------------------------------------
1241 class EvalDict:
1242 """
1243 Emulate a dict which evaluates its contents in the caller's frame.
1244
1245 Usage:
1246 >>> number = 19
1247
1248 >>> text = "python"
1249
1250 >>> print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
1251 Python 2.1 rules!
1252 """
1253
1254 # This version is due to sismex01@hebmex.com on c.l.py, and is basically a
1255 # modified (shorter) version of:
1256 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66018 by
1257 # Skip Montanaro (skip@pobox.com).
1258
1259 def __getitem__(self, name):
1260 frame = sys._getframe(1)
1261 return eval(name, frame.f_globals, frame.f_locals)
1262
1263 EvalString = EvalDict # for backwards compatibility
1264 #----------------------------------------------------------------------------
1265 def qw(words,flat=0,sep=None,maxsplit=-1):
1266 """Similar to Perl's qw() operator, but with some more options.
1267
1268 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
1269
1270 words can also be a list itself, and with flat=1, the output will be
1271 recursively flattened.
1272
1273 Examples:
1274
1275 >>> qw('1 2')
1276 ['1', '2']
1277
1278 >>> qw(['a b','1 2',['m n','p q']])
1279 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
1280
1281 >>> qw(['a b','1 2',['m n','p q']],flat=1)
1282 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
1283 """
1284
1285 if type(words) in StringTypes:
1286 return [word.strip() for word in words.split(sep,maxsplit)
1287 if word and not word.isspace() ]
1288 if flat:
1289 return flatten(map(qw,words,[1]*len(words)))
1290 return map(qw,words)
1291
1292 #----------------------------------------------------------------------------
1293 def qwflat(words,sep=None,maxsplit=-1):
1294 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
1295 return qw(words,1,sep,maxsplit)
1296
1297 #----------------------------------------------------------------------------
1298 def qw_lol(indata):
1299 """qw_lol('a b') -> [['a','b']],
1300 otherwise it's just a call to qw().
1301
1302 We need this to make sure the modules_some keys *always* end up as a
1303 list of lists."""
1304
1305 if type(indata) in StringTypes:
1306 return [qw(indata)]
1307 else:
1308 return qw(indata)
1309
1310 #----------------------------------------------------------------------------
1311 def grep(pat,list,case=1):
1312 """Simple minded grep-like function.
1313 grep(pat,list) returns occurrences of pat in list, None on failure.
1314
1315 It only does simple string matching, with no support for regexps. Use the
1316 option case=0 for case-insensitive matching."""
1317
1318 # This is pretty crude. At least it should implement copying only references
1319 # to the original data in case it's big. Now it copies the data for output.
1320 out=[]
1321 if case:
1322 for term in list:
1323 if term.find(pat)>-1: out.append(term)
1324 else:
1325 lpat=pat.lower()
1326 for term in list:
1327 if term.lower().find(lpat)>-1: out.append(term)
1328
1329 if len(out): return out
1330 else: return None
1331
1332 #----------------------------------------------------------------------------
1333 def dgrep(pat,*opts):
1334 """Return grep() on dir()+dir(__builtins__).
1335
1336 A very common use of grep() when working interactively."""
1337
1338 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
1339
1340 #----------------------------------------------------------------------------
1341 def idgrep(pat):
1342 """Case-insensitive dgrep()"""
1343
1344 return dgrep(pat,0)
1345
1346 #----------------------------------------------------------------------------
1347 def igrep(pat,list):
1348 """Synonym for case-insensitive grep."""
1349
1350 return grep(pat,list,case=0)
1351
1352 #----------------------------------------------------------------------------
1353 def indent(str,nspaces=4,ntabs=0):
1354 """Indent a string a given number of spaces or tabstops.
1355
1356 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
1357 """
1358 if str is None:
1359 return
1360 ind = '\t'*ntabs+' '*nspaces
1361 outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
1362 if outstr.endswith(os.linesep+ind):
1363 return outstr[:-len(ind)]
1364 else:
1365 return outstr
1366
1367 #-----------------------------------------------------------------------------
1368 def native_line_ends(filename,backup=1):
1369 """Convert (in-place) a file to line-ends native to the current OS.
1370
1371 If the optional backup argument is given as false, no backup of the
1372 original file is left. """
1373
1374 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
1375
1376 bak_filename = filename + backup_suffixes[os.name]
1377
1378 original = open(filename).read()
1379 shutil.copy2(filename,bak_filename)
1380 try:
1381 new = open(filename,'wb')
1382 new.write(os.linesep.join(original.splitlines()))
1383 new.write(os.linesep) # ALWAYS put an eol at the end of the file
1384 new.close()
1385 except:
1386 os.rename(bak_filename,filename)
1387 if not backup:
1388 try:
1389 os.remove(bak_filename)
1390 except:
1391 pass
1392
1393 #****************************************************************************
1394 # lists, dicts and structures
1395
1396 def belong(candidates,checklist):
1397 """Check whether a list of items appear in a given list of options.
1398
1399 Returns a list of 1 and 0, one for each candidate given."""
1400
1401 return [x in checklist for x in candidates]
1402
1403 #----------------------------------------------------------------------------
1404 def uniq_stable(elems):
1405 """uniq_stable(elems) -> list
1406
1407 Return from an iterable, a list of all the unique elements in the input,
1408 but maintaining the order in which they first appear.
1409
1410 A naive solution to this problem which just makes a dictionary with the
1411 elements as keys fails to respect the stability condition, since
1412 dictionaries are unsorted by nature.
1413
1414 Note: All elements in the input must be valid dictionary keys for this
1415 routine to work, as it internally uses a dictionary for efficiency
1416 reasons."""
1417
1418 unique = []
1419 unique_dict = {}
1420 for nn in elems:
1421 if nn not in unique_dict:
1422 unique.append(nn)
1423 unique_dict[nn] = None
1424 return unique
1425
1426 #----------------------------------------------------------------------------
1427 class NLprinter:
1428 """Print an arbitrarily nested list, indicating index numbers.
1429
1430 An instance of this class called nlprint is available and callable as a
1431 function.
1432
1433 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
1434 and using 'sep' to separate the index from the value. """
1435
1436 def __init__(self):
1437 self.depth = 0
1438
1439 def __call__(self,lst,pos='',**kw):
1440 """Prints the nested list numbering levels."""
1441 kw.setdefault('indent',' ')
1442 kw.setdefault('sep',': ')
1443 kw.setdefault('start',0)
1444 kw.setdefault('stop',len(lst))
1445 # we need to remove start and stop from kw so they don't propagate
1446 # into a recursive call for a nested list.
1447 start = kw['start']; del kw['start']
1448 stop = kw['stop']; del kw['stop']
1449 if self.depth == 0 and 'header' in kw.keys():
1450 print kw['header']
1451
1452 for idx in range(start,stop):
1453 elem = lst[idx]
1454 if type(elem)==type([]):
1455 self.depth += 1
1456 self.__call__(elem,itpl('$pos$idx,'),**kw)
1457 self.depth -= 1
1458 else:
1459 printpl(kw['indent']*self.depth+'$pos$idx$kw["sep"]$elem')
1460
1461 nlprint = NLprinter()
1462 #----------------------------------------------------------------------------
1463 def all_belong(candidates,checklist):
1464 """Check whether a list of items ALL appear in a given list of options.
1465
1466 Returns a single 1 or 0 value."""
1467
1468 return 1-(0 in [x in checklist for x in candidates])
1469
1470 #----------------------------------------------------------------------------
1471 def sort_compare(lst1,lst2,inplace = 1):
1472 """Sort and compare two lists.
1473
1474 By default it does it in place, thus modifying the lists. Use inplace = 0
1475 to avoid that (at the cost of temporary copy creation)."""
1476 if not inplace:
1477 lst1 = lst1[:]
1478 lst2 = lst2[:]
1479 lst1.sort(); lst2.sort()
1480 return lst1 == lst2
1481
1482 #----------------------------------------------------------------------------
1483 def list2dict(lst):
1484 """Takes a list of (key,value) pairs and turns it into a dict."""
1485
1486 dic = {}
1487 for k,v in lst: dic[k] = v
1488 return dic
1489
1490 #----------------------------------------------------------------------------
1491 def list2dict2(lst,default=''):
1492 """Takes a list and turns it into a dict.
1493 Much slower than list2dict, but more versatile. This version can take
1494 lists with sublists of arbitrary length (including sclars)."""
1495
1496 dic = {}
1497 for elem in lst:
1498 if type(elem) in (types.ListType,types.TupleType):
1499 size = len(elem)
1500 if size == 0:
1501 pass
1502 elif size == 1:
1503 dic[elem] = default
1504 else:
1505 k,v = elem[0], elem[1:]
1506 if len(v) == 1: v = v[0]
1507 dic[k] = v
1508 else:
1509 dic[elem] = default
1510 return dic
1511
1512 #----------------------------------------------------------------------------
1513 def flatten(seq):
1514 """Flatten a list of lists (NOT recursive, only works for 2d lists)."""
1515
1516 return [x for subseq in seq for x in subseq]
1517
1518 #----------------------------------------------------------------------------
1519 def get_slice(seq,start=0,stop=None,step=1):
1520 """Get a slice of a sequence with variable step. Specify start,stop,step."""
1521 if stop == None:
1522 stop = len(seq)
1523 item = lambda i: seq[i]
1524 return map(item,xrange(start,stop,step))
1525
1526 #----------------------------------------------------------------------------
1527 def chop(seq,size):
1528 """Chop a sequence into chunks of the given size."""
1529 chunk = lambda i: seq[i:i+size]
1530 return map(chunk,xrange(0,len(seq),size))
1531
1532 #----------------------------------------------------------------------------
1533 # with is a keyword as of python 2.5, so this function is renamed to withobj
1534 # from its old 'with' name.
1535 def with_obj(object, **args):
1536 """Set multiple attributes for an object, similar to Pascal's with.
1537
1538 Example:
1539 with_obj(jim,
1540 born = 1960,
1541 haircolour = 'Brown',
1542 eyecolour = 'Green')
1543
1544 Credit: Greg Ewing, in
1545 http://mail.python.org/pipermail/python-list/2001-May/040703.html.
1546
1547 NOTE: up until IPython 0.7.2, this was called simply 'with', but 'with'
1548 has become a keyword for Python 2.5, so we had to rename it."""
1549
1550 object.__dict__.update(args)
1551
1552 #----------------------------------------------------------------------------
1553 def setattr_list(obj,alist,nspace = None):
1554 """Set a list of attributes for an object taken from a namespace.
1555
1556 setattr_list(obj,alist,nspace) -> sets in obj all the attributes listed in
1557 alist with their values taken from nspace, which must be a dict (something
1558 like locals() will often do) If nspace isn't given, locals() of the
1559 *caller* is used, so in most cases you can omit it.
1560
1561 Note that alist can be given as a string, which will be automatically
1562 split into a list on whitespace. If given as a list, it must be a list of
1563 *strings* (the variable names themselves), not of variables."""
1564
1565 # this grabs the local variables from the *previous* call frame -- that is
1566 # the locals from the function that called setattr_list().
1567 # - snipped from weave.inline()
1568 if nspace is None:
1569 call_frame = sys._getframe().f_back
1570 nspace = call_frame.f_locals
1571
1572 if type(alist) in StringTypes:
1573 alist = alist.split()
1574 for attr in alist:
1575 val = eval(attr,nspace)
1576 setattr(obj,attr,val)
1577
1578 #----------------------------------------------------------------------------
1579 def getattr_list(obj,alist,*args):
1580 """getattr_list(obj,alist[, default]) -> attribute list.
1581
1582 Get a list of named attributes for an object. When a default argument is
1583 given, it is returned when the attribute doesn't exist; without it, an
1584 exception is raised in that case.
1585
1586 Note that alist can be given as a string, which will be automatically
1587 split into a list on whitespace. If given as a list, it must be a list of
1588 *strings* (the variable names themselves), not of variables."""
1589
1590 if type(alist) in StringTypes:
1591 alist = alist.split()
1592 if args:
1593 if len(args)==1:
1594 default = args[0]
1595 return map(lambda attr: getattr(obj,attr,default),alist)
1596 else:
1597 raise ValueError,'getattr_list() takes only one optional argument'
1598 else:
1599 return map(lambda attr: getattr(obj,attr),alist)
1600
1601 #----------------------------------------------------------------------------
1602 def map_method(method,object_list,*argseq,**kw):
1603 """map_method(method,object_list,*args,**kw) -> list
1604
1605 Return a list of the results of applying the methods to the items of the
1606 argument sequence(s). If more than one sequence is given, the method is
1607 called with an argument list consisting of the corresponding item of each
1608 sequence. All sequences must be of the same length.
1609
1610 Keyword arguments are passed verbatim to all objects called.
1611
1612 This is Python code, so it's not nearly as fast as the builtin map()."""
1613
1614 out_list = []
1615 idx = 0
1616 for object in object_list:
1617 try:
1618 handler = getattr(object, method)
1619 except AttributeError:
1620 out_list.append(None)
1621 else:
1622 if argseq:
1623 args = map(lambda lst:lst[idx],argseq)
1624 #print 'ob',object,'hand',handler,'ar',args # dbg
1625 out_list.append(handler(args,**kw))
1626 else:
1627 out_list.append(handler(**kw))
1628 idx += 1
1629 return out_list
1630
1631 #----------------------------------------------------------------------------
1632 def get_class_members(cls):
1633 ret = dir(cls)
1634 if hasattr(cls,'__bases__'):
1635 for base in cls.__bases__:
1636 ret.extend(get_class_members(base))
1637 return ret
1638
1639 #----------------------------------------------------------------------------
1640 def dir2(obj):
1641 """dir2(obj) -> list of strings
1642
1643 Extended version of the Python builtin dir(), which does a few extra
1644 checks, and supports common objects with unusual internals that confuse
1645 dir(), such as Traits and PyCrust.
1646
1647 This version is guaranteed to return only a list of true strings, whereas
1648 dir() returns anything that objects inject into themselves, even if they
1649 are later not really valid for attribute access (many extension libraries
1650 have such bugs).
1651 """
1652
1653 # Start building the attribute list via dir(), and then complete it
1654 # with a few extra special-purpose calls.
1655 words = dir(obj)
1656
1657 if hasattr(obj,'__class__'):
1658 words.append('__class__')
1659 words.extend(get_class_members(obj.__class__))
1660 #if '__base__' in words: 1/0
1661
1662 # Some libraries (such as traits) may introduce duplicates, we want to
1663 # track and clean this up if it happens
1664 may_have_dupes = False
1665
1666 # this is the 'dir' function for objects with Enthought's traits
1667 if hasattr(obj, 'trait_names'):
1668 try:
1669 words.extend(obj.trait_names())
1670 may_have_dupes = True
1671 except TypeError:
1672 # This will happen if `obj` is a class and not an instance.
1673 pass
1674
1675 # Support for PyCrust-style _getAttributeNames magic method.
1676 if hasattr(obj, '_getAttributeNames'):
1677 try:
1678 words.extend(obj._getAttributeNames())
1679 may_have_dupes = True
1680 except TypeError:
1681 # `obj` is a class and not an instance. Ignore
1682 # this error.
1683 pass
1684
1685 if may_have_dupes:
1686 # eliminate possible duplicates, as some traits may also
1687 # appear as normal attributes in the dir() call.
1688 words = list(set(words))
1689 words.sort()
1690
1691 # filter out non-string attributes which may be stuffed by dir() calls
1692 # and poor coding in third-party modules
1693 return [w for w in words if isinstance(w, basestring)]
1694
1695 #----------------------------------------------------------------------------
1696 def import_fail_info(mod_name,fns=None):
1697 """Inform load failure for a module."""
1698
1699 if fns == None:
1700 warn("Loading of %s failed.\n" % (mod_name,))
1701 else:
1702 warn("Loading of %s from %s failed.\n" % (fns,mod_name))
1703
1704 #----------------------------------------------------------------------------
1705 # Proposed popitem() extension, written as a method
1706
1707
1708 class NotGiven: pass
1709
1710 def popkey(dct,key,default=NotGiven):
1711 """Return dct[key] and delete dct[key].
1712
1713 If default is given, return it if dct[key] doesn't exist, otherwise raise
1714 KeyError. """
1715
1716 try:
1717 val = dct[key]
1718 except KeyError:
1719 if default is NotGiven:
1720 raise
1721 else:
1722 return default
1723 else:
1724 del dct[key]
1725 return val
1726
1727 def wrap_deprecated(func, suggest = '<nothing>'):
1728 def newFunc(*args, **kwargs):
1729 warnings.warn("Call to deprecated function %s, use %s instead" %
1730 ( func.__name__, suggest),
1731 category=DeprecationWarning,
1732 stacklevel = 2)
1733 return func(*args, **kwargs)
1734 return newFunc
1735
1736
1737 def _num_cpus_unix():
1738 """Return the number of active CPUs on a Unix system."""
1739 return os.sysconf("SC_NPROCESSORS_ONLN")
1740
1741
1742 def _num_cpus_darwin():
1743 """Return the number of active CPUs on a Darwin system."""
1744 p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE)
1745 return p.stdout.read()
1746
1747
1748 def _num_cpus_windows():
1749 """Return the number of active CPUs on a Windows system."""
1750 return os.environ.get("NUMBER_OF_PROCESSORS")
1751
1752
1753 def num_cpus():
1754 """Return the effective number of CPUs in the system as an integer.
1755
1756 This cross-platform function makes an attempt at finding the total number of
1757 available CPUs in the system, as returned by various underlying system and
1758 python calls.
1759
1760 If it can't find a sensible answer, it returns 1 (though an error *may* make
1761 it return a large positive number that's actually incorrect).
1762 """
1763
1764 # Many thanks to the Parallel Python project (http://www.parallelpython.com)
1765 # for the names of the keys we needed to look up for this function. This
1766 # code was inspired by their equivalent function.
1767
1768 ncpufuncs = {'Linux':_num_cpus_unix,
1769 'Darwin':_num_cpus_darwin,
1770 'Windows':_num_cpus_windows,
1771 # On Vista, python < 2.5.2 has a bug and returns 'Microsoft'
1772 # See http://bugs.python.org/issue1082 for details.
1773 'Microsoft':_num_cpus_windows,
1774 }
1775
1776 ncpufunc = ncpufuncs.get(platform.system(),
1777 # default to unix version (Solaris, AIX, etc)
1778 _num_cpus_unix)
1779
1780 try:
1781 ncpus = max(1,int(ncpufunc()))
1782 except:
1783 ncpus = 1
1784 return ncpus
1785
1786 def extract_vars(*names,**kw):
1787 """Extract a set of variables by name from another frame.
1788
1789 :Parameters:
1790 - `*names`: strings
1791 One or more variable names which will be extracted from the caller's
1792 frame.
1793
1794 :Keywords:
1795 - `depth`: integer (0)
1796 How many frames in the stack to walk when looking for your variables.
1797
1798
1799 Examples:
1800
1801 In [2]: def func(x):
1802 ...: y = 1
1803 ...: print extract_vars('x','y')
1804 ...:
1805
1806 In [3]: func('hello')
1807 {'y': 1, 'x': 'hello'}
1808 """
1809
1810 depth = kw.get('depth',0)
1811
1812 callerNS = sys._getframe(depth+1).f_locals
1813 return dict((k,callerNS[k]) for k in names)
1814
1815
1816 def extract_vars_above(*names):
1817 """Extract a set of variables by name from another frame.
1818
1819 Similar to extractVars(), but with a specified depth of 1, so that names
1820 are exctracted exactly from above the caller.
1821
1822 This is simply a convenience function so that the very common case (for us)
1823 of skipping exactly 1 frame doesn't have to construct a special dict for
1824 keyword passing."""
1825
1826 callerNS = sys._getframe(2).f_locals
1827 return dict((k,callerNS[k]) for k in names)
1828
1829 def expand_path(s):
1830 """Expand $VARS and ~names in a string, like a shell
1831
1832 :Examples:
1833
1834 In [2]: os.environ['FOO']='test'
1835
1836 In [3]: expand_path('variable FOO is $FOO')
1837 Out[3]: 'variable FOO is test'
1838 """
1839 # This is a pretty subtle hack. When expand user is given a UNC path
1840 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
1841 # the $ to get (\\server\share\%username%). I think it considered $
1842 # alone an empty var. But, we need the $ to remains there (it indicates
1843 # a hidden share).
1844 if os.name=='nt':
1845 s = s.replace('$\\', 'IPYTHON_TEMP')
1846 s = os.path.expandvars(os.path.expanduser(s))
1847 if os.name=='nt':
1848 s = s.replace('IPYTHON_TEMP', '$\\')
1849 return s
1850
1851 def list_strings(arg):
1852 """Always return a list of strings, given a string or list of strings
1853 as input.
1854
1855 :Examples:
1856
1857 In [7]: list_strings('A single string')
1858 Out[7]: ['A single string']
1859
1860 In [8]: list_strings(['A single string in a list'])
1861 Out[8]: ['A single string in a list']
1862
1863 In [9]: list_strings(['A','list','of','strings'])
1864 Out[9]: ['A', 'list', 'of', 'strings']
1865 """
1866
1867 if isinstance(arg,basestring): return [arg]
1868 else: return arg
1869
1870
1871 #----------------------------------------------------------------------------
1872 def marquee(txt='',width=78,mark='*'):
1873 """Return the input string centered in a 'marquee'.
1874
1875 :Examples:
1876
1877 In [16]: marquee('A test',40)
1878 Out[16]: '**************** A test ****************'
1879
1880 In [17]: marquee('A test',40,'-')
1881 Out[17]: '---------------- A test ----------------'
1882
1883 In [18]: marquee('A test',40,' ')
1884 Out[18]: ' A test '
1885
1886 """
1887 if not txt:
1888 return (mark*width)[:width]
1889 nmark = (width-len(txt)-2)/len(mark)/2
1890 if nmark < 0: nmark =0
1891 marks = mark*nmark
1892 return '%s %s %s' % (marks,txt,marks)
1893
1894 #*************************** end of file <genutils.py> **********************
@@ -1,102 +0,0 b''
1 # -*- coding: utf-8 -*-
2 """ Proxy module for accessing platform specific utility functions.
3
4 Importing this module should give you the implementations that are correct
5 for your operation system, from platutils_PLATFORMNAME module.
6 """
7
8 #*****************************************************************************
9 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #*****************************************************************************
14
15 import os
16 import sys
17 import warnings
18
19 # Import the platform-specific implementations
20 if os.name == 'posix':
21 import platutils_posix as _platutils
22 elif sys.platform == 'win32':
23 import platutils_win32 as _platutils
24 else:
25 import platutils_dummy as _platutils
26
27 # Functionality that's logically common to all platforms goes here, each
28 # platform-specific module only provides the bits that are OS-dependent.
29
30 # XXX - I'm still not happy with a module global for this, but at least now
31 # there is a public, cross-platform way of toggling the term title control on
32 # and off. We should make this a stateful object later on so that each user
33 # can have its own instance if needed.
34 def term_clear():
35 _platutils.term_clear()
36
37 def toggle_set_term_title(val):
38 """Control whether set_term_title is active or not.
39
40 set_term_title() allows writing to the console titlebar. In embedded
41 widgets this can cause problems, so this call can be used to toggle it on
42 or off as needed.
43
44 The default state of the module is for the function to be disabled.
45
46 Parameters
47 ----------
48 val : bool
49 If True, set_term_title() actually writes to the terminal (using the
50 appropriate platform-specific module). If False, it is a no-op.
51 """
52 _platutils.ignore_termtitle = not(val)
53
54
55 def set_term_title(title):
56 """Set terminal title using the necessary platform-dependent calls."""
57 if _platutils.ignore_termtitle:
58 return
59 _platutils.set_term_title(title)
60
61
62 class FindCmdError(Exception):
63 pass
64
65 def find_cmd(cmd):
66 """Find full path to executable cmd in a cross platform manner.
67
68 This function tries to determine the full path to a command line program
69 using `which` on Unix/Linux/OS X and `win32api` on Windows. Most of the
70 time it will use the version that is first on the users `PATH`. If
71 cmd is `python` return `sys.executable`.
72
73 Parameters
74 ----------
75 cmd : str
76 The command line program to look for.
77 """
78 if cmd == 'python':
79 return sys.executable
80 try:
81 path = _platutils.find_cmd(cmd)
82 except OSError:
83 raise FindCmdError('command could not be found: %s' % cmd)
84 # which returns empty if not found
85 if path == '':
86 raise FindCmdError('command could not be found: %s' % cmd)
87 return path
88
89 def get_long_path_name(path):
90 """Expand a path into its long form.
91
92 On Windows this expands any ~ in the paths. On other platforms, it is
93 a null operation.
94 """
95 return _platutils.get_long_path_name(path)
96
97 #-----------------------------------------------------------------------------
98 # Deprecated functions
99 #-----------------------------------------------------------------------------
100 def freeze_term_title():
101 warnings.warn("This function is deprecated, use toggle_set_term_title()")
102 _platutils.ignore_termtitle = True
@@ -1,33 +0,0 b''
1 # -*- coding: utf-8 -*-
2 """ Platform specific utility functions, dummy version
3
4 This has empty implementation of the platutils functions, used for
5 unsupported operating systems.
6
7 Authors
8 -------
9 - Ville Vainio <vivainio@gmail.com>
10 """
11
12 #*****************************************************************************
13 # Copyright (C) 2008-2009 The IPython Development Team
14 # Copyright (C) 2001-2007 Fernando Perez <fperez@colorado.edu>
15 #
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
18 #*****************************************************************************
19
20 # This variable is part of the expected API of the module:
21 ignore_termtitle = True
22
23 def set_term_title(*args,**kw):
24 """Dummy no-op."""
25 pass
26
27 def find_cmd(cmd):
28 """Find the full path to a command using which."""
29 return os.popen('which %s' % cmd).read().strip()
30
31 def get_long_path_name(path):
32 """Dummy no-op."""
33 return path
@@ -1,61 +0,0 b''
1 # -*- coding: utf-8 -*-
2 """ Platform specific utility functions, posix version
3
4 Importing this module directly is not portable - rather, import platutils
5 to use these functions in platform agnostic fashion.
6 """
7
8 #*****************************************************************************
9 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #*****************************************************************************
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
17 from __future__ import absolute_import
18
19 import sys
20 import os
21
22 from .baseutils import getoutputerror
23
24 #-----------------------------------------------------------------------------
25 # Globals
26 #-----------------------------------------------------------------------------
27
28 ignore_termtitle = True
29
30 #-----------------------------------------------------------------------------
31 # Functions
32 #-----------------------------------------------------------------------------
33
34 def _dummy_op(*a, **b):
35 """ A no-op function """
36
37
38 def _set_term_title_xterm(title):
39 """ Change virtual terminal title in xterm-workalikes """
40 sys.stdout.write('\033]0;%s\007' % title)
41
42 TERM = os.environ.get('TERM','')
43
44 if (TERM == 'xterm') or (TERM == 'xterm-color'):
45 set_term_title = _set_term_title_xterm
46 else:
47 set_term_title = _dummy_op
48
49
50 def find_cmd(cmd):
51 """Find the full path to a command using which."""
52 return getoutputerror('/usr/bin/env which %s' % cmd)[0]
53
54
55 def get_long_path_name(path):
56 """Dummy no-op."""
57 return path
58
59
60 def term_clear():
61 os.system('clear')
@@ -1,95 +0,0 b''
1 # -*- coding: utf-8 -*-
2 """ Platform specific utility functions, win32 version
3
4 Importing this module directly is not portable - rather, import platutils
5 to use these functions in platform agnostic fashion.
6 """
7
8 #*****************************************************************************
9 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #*****************************************************************************
14
15 import os
16
17 ignore_termtitle = True
18
19 try:
20 import ctypes
21
22 SetConsoleTitleW = ctypes.windll.kernel32.SetConsoleTitleW
23 SetConsoleTitleW.argtypes = [ctypes.c_wchar_p]
24
25 def set_term_title(title):
26 """Set terminal title using ctypes to access the Win32 APIs."""
27 SetConsoleTitleW(title)
28
29
30 except ImportError:
31 def set_term_title(title):
32 """Set terminal title using the 'title' command."""
33 global ignore_termtitle
34
35 try:
36 # Cannot be on network share when issuing system commands
37 curr = os.getcwd()
38 os.chdir("C:")
39 ret = os.system("title " + title)
40 finally:
41 os.chdir(curr)
42 if ret:
43 # non-zero return code signals error, don't try again
44 ignore_termtitle = True
45
46
47 def find_cmd(cmd):
48 """Find the full path to a .bat or .exe using the win32api module."""
49 try:
50 from win32api import SearchPath
51 except ImportError:
52 raise ImportError('you need to have pywin32 installed for this to work')
53 else:
54 PATH = os.environ['PATH']
55 extensions = ['.exe', '.com', '.bat', '.py']
56 path = None
57 for ext in extensions:
58 try:
59 path = SearchPath(PATH,cmd + ext)[0]
60 except:
61 pass
62 if path is None:
63 raise OSError("command %r not found" % cmd)
64 else:
65 return path
66
67
68 def get_long_path_name(path):
69 """Get a long path name (expand ~) on Windows using ctypes.
70
71 Examples
72 --------
73
74 >>> get_long_path_name('c:\\docume~1')
75 u'c:\\\\Documents and Settings'
76
77 """
78 try:
79 import ctypes
80 except ImportError:
81 raise ImportError('you need to have ctypes installed for this to work')
82 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
83 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
84 ctypes.c_uint ]
85
86 buf = ctypes.create_unicode_buffer(260)
87 rv = _GetLongPathName(path, buf, 260)
88 if rv == 0 or rv > 260:
89 return path
90 else:
91 return buf.value
92
93
94 def term_clear():
95 os.system('cls')
@@ -1,46 +0,0 b''
1 """Set of functions to work with console on Windows.
2 """
3
4 #*****************************************************************************
5 # Copyright (C) 2005 Alexander Belchenko <bialix@ukr.net>
6 #
7 # This file is placed in the public domain.
8 #
9 #*****************************************************************************
10
11 __author__ = 'Alexander Belchenko (e-mail: bialix AT ukr.net)'
12 __license__ = 'Public domain'
13
14 import struct
15
16 try:
17 import ctypes
18 except ImportError:
19 ctypes = None
20
21 def get_console_size(defaultx=80, defaulty=25):
22 """ Return size of current console.
23
24 This function try to determine actual size of current working
25 console window and return tuple (sizex, sizey) if success,
26 or default size (defaultx, defaulty) otherwise.
27
28 Dependencies: ctypes should be installed.
29 """
30 if ctypes is None:
31 # no ctypes is found
32 return (defaultx, defaulty)
33
34 h = ctypes.windll.kernel32.GetStdHandle(-11)
35 csbi = ctypes.create_string_buffer(22)
36 res = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
37
38 if res:
39 (bufx, bufy, curx, cury, wattr,
40 left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh",
41 csbi.raw)
42 sizex = right - left + 1
43 sizey = bottom - top + 1
44 return (sizex, sizey)
45 else:
46 return (defaultx, defaulty)
@@ -1,26 +0,0 b''
1 #!/usr/bin/env python
2 """Test script for IPython.
3
4 The actual ipython test script to be installed with 'python setup.py install'
5 is in './scripts' directory, and will test IPython from an importable
6 location.
7
8 This file is here (ipython source root directory) to facilitate non-root
9 'zero-installation testing and development' (just copy the source tree
10 somewhere and run iptest.py).
11
12 You can run this script directly, type -h to see all options."""
13
14 # Ensure that the imported IPython packages come from *THIS* IPython, not some
15 # other one that may exist system-wide
16 import os, sys
17 this_dir = os.path.dirname(os.path.abspath(__file__))
18 sys.path.insert(0, this_dir)
19
20 import IPython.testing.tools as t
21 import IPython.testing.iptest as ipt
22 t.INSTALLED = False
23 ipt.INSTALLED = False
24
25 # Now proceed with execution
26 execfile(os.path.join(this_dir, 'IPython', 'scripts', 'iptest'))
General Comments 0
You need to be logged in to leave comments. Login now