##// 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 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 35 sys.path.append(os.path.join(os.path.dirname(__file__), "extensions"))
35 36
36 37 #-----------------------------------------------------------------------------
37 38 # Setup the top level names
38 39 #-----------------------------------------------------------------------------
39 40
40 # In some cases, these are causing circular imports.
41 41 from .config.loader import Config
42 42 from .core import release
43 43 from .core.application import Application
@@ -23,7 +23,7 b' import os'
23 23 import sys
24 24
25 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 29 # Exceptions
@@ -40,6 +40,7 b' class ConfigLoaderError(ConfigError):'
40 40 #-----------------------------------------------------------------------------
41 41 # Argparse fix
42 42 #-----------------------------------------------------------------------------
43
43 44 # Unfortunately argparse by default prints help messages to stderr instead of
44 45 # stdout. This makes it annoying to capture long help screens at the command
45 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 201 self.config = Config()
201 202
202 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 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 211 return self.config
208 212
209 213
@@ -242,6 +246,7 b' class PyFileConfigLoader(FileConfigLoader):'
242 246
243 247 def load_config(self):
244 248 """Load the config from a file and return it as a Struct."""
249 self.clear()
245 250 self._find_file()
246 251 self._read_file_as_dict()
247 252 self._convert_to_config()
@@ -292,21 +297,11 b' class CommandLineConfigLoader(ConfigLoader):'
292 297 """
293 298
294 299
295 class __NoConfigDefault(object): pass
296 NoConfigDefault = __NoConfigDefault()
297
298
299 300 class ArgParseConfigLoader(CommandLineConfigLoader):
300 #: Global default for arguments (see argparse docs for details)
301 argument_default = NoConfigDefault
302 301
303 def __init__(self, argv=None, arguments=(), *args, **kw):
302 def __init__(self, argv=None, *parser_args, **parser_kw):
304 303 """Create a config loader for use with argparse.
305 304
306 With the exception of ``argv`` and ``arguments``, other args and kwargs
307 arguments here are passed onto the constructor of
308 :class:`argparse.ArgumentParser`.
309
310 305 Parameters
311 306 ----------
312 307
@@ -314,19 +309,22 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
314 309 If given, used to read command-line arguments from, otherwise
315 310 sys.argv[1:] is used.
316 311
317 arguments : optional, tuple
318 Description of valid command-line arguments, to be called in sequence
319 with parser.add_argument() to configure the parser.
312 parser_args : tuple
313 A tuple of positional arguments that will be passed to the
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 320 super(CommandLineConfigLoader, self).__init__()
322 321 if argv == None:
323 322 argv = sys.argv[1:]
324 323 self.argv = argv
325 self.arguments = arguments
326 self.args = args
327 kwargs = dict(argument_default=self.argument_default)
328 kwargs.update(kw)
329 self.kw = kwargs
324 self.parser_args = parser_args
325 kwargs = dict(argument_default=argparse.SUPPRESS)
326 kwargs.update(parser_kw)
327 self.parser_kw = kwargs
330 328
331 329 def load_config(self, args=None):
332 330 """Parse command line arguments and return as a Struct.
@@ -335,10 +333,10 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
335 333 ----------
336 334
337 335 args : optional, list
338 If given, a list with the structure of sys.argv[1:] to parse arguments
339 from. If not given, the instance's self.argv attribute (given at
340 construction time) is used."""
341
336 If given, a list with the structure of sys.argv[1:] to parse
337 arguments from. If not given, the instance's self.argv attribute
338 (given at construction time) is used."""
339 self.clear()
342 340 if args is None:
343 341 args = self.argv
344 342 self._create_parser()
@@ -353,17 +351,11 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
353 351 return []
354 352
355 353 def _create_parser(self):
356 self.parser = ArgumentParser(*self.args, **self.kw)
354 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
357 355 self._add_arguments()
358 self._add_other_arguments()
359 356
360 357 def _add_arguments(self):
361 for argument in self.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
358 raise NotImplementedError("subclasses must implement _add_arguments")
367 359
368 360 def _parse_args(self, args):
369 361 """self.parser->self.parsed_data"""
@@ -372,6 +364,7 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
372 364 def _convert_to_config(self):
373 365 """self.parsed_data->self.config"""
374 366 for k, v in vars(self.parsed_data).items():
375 if v is not NoConfigDefault:
376 367 exec_str = 'self.config.' + k + '= v'
377 368 exec exec_str in locals(), globals()
369
370
@@ -61,35 +61,38 b' class TestPyFileCL(TestCase):'
61 61 self.assertEquals(config.Foo.Bam.value, range(10))
62 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 80 class TestArgParseCL(TestCase):
66 81
67 82 def test_basic(self):
68
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)
83 cl = MyLoader1()
76 84 config = cl.load_config('-f hi -b 10 -n wow'.split())
77 85 self.assertEquals(config.Global.foo, 'hi')
78 86 self.assertEquals(config.MyClass.bar, 10)
79 87 self.assertEquals(config.n, True)
80 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 94 def test_add_arguments(self):
83
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()
95 cl = MyLoader2()
93 96 config = cl.load_config('2 frobble'.split())
94 97 self.assertEquals(config.subparser_name, '2')
95 98 self.assertEquals(config.y, 'frobble')
@@ -97,6 +100,15 b' class TestArgParseCL(TestCase):'
97 100 self.assertEquals(config.subparser_name, '1')
98 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 112 class TestConfig(TestCase):
101 113
102 114 def test_setget(self):
@@ -28,9 +28,9 b' import sys'
28 28 from IPython.core.component import Component
29 29 from IPython.core.splitinput import split_user_input
30 30
31 from IPython.utils.traitlets import CBool, List, Instance
32 from IPython.utils.genutils import error
31 from IPython.utils.traitlets import List
33 32 from IPython.utils.autoattr import auto_attr
33 from IPython.utils.warn import warn, error
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Utilities
@@ -33,7 +33,7 b' import os'
33 33 import sys
34 34
35 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 37 from IPython.config.loader import (
38 38 PyFileConfigLoader,
39 39 ArgParseConfigLoader,
@@ -48,108 +48,86 b' class ApplicationError(Exception):'
48 48 pass
49 49
50 50
51 app_cl_args = (
52 (('--ipython-dir', ), dict(
51 class BaseAppConfigLoader(ArgParseConfigLoader):
52 """Default command line options for IPython based applications."""
53
54 def _add_ipython_dir(self, parser):
55 """Add the --ipython-dir option to the parser."""
56 paa = parser.add_argument
57 paa('--ipython-dir',
53 58 dest='Global.ipython_dir',type=unicode,
54 59 help=
55 60 """Set to override default location of the IPython directory
56 IPYTHON_DIR, stored as Global.ipython_dir. This can also be specified
57 through the environment variable IPYTHON_DIR.""",
58 metavar='Global.ipython_dir') ),
59 (('-p', '--profile',), dict(
60 dest='Global.profile',type=unicode,
61 help=
62 """The string name of the ipython profile to be used. Assume that your
63 config file is ipython_config-<name>.py (looks in current dir first,
64 then in IPYTHON_DIR). This is a quick way to keep and load multiple
65 config files for different tasks, especially if include your basic one
66 in your more specialized ones. You can keep a basic
67 IPYTHON_DIR/ipython_config.py file and then have other 'profiles' which
68 include this one and load extra things for particular tasks.""",
69 metavar='Global.profile') ),
70 (('--log-level',), dict(
61 IPYTHON_DIR, stored as Global.ipython_dir. This can also be
62 specified through the environment variable IPYTHON_DIR.""",
63 metavar='Global.ipython_dir')
64
65 def _add_log_level(self, parser):
66 """Add the --log-level option to the parser."""
67 paa = parser.add_argument
68 paa('--log-level',
71 69 dest="Global.log_level",type=int,
72 70 help='Set the log level (0,10,20,30,40,50). Default is 30.',
73 metavar='Global.log_level')),
74 (('--config-file',), dict(
75 dest='Global.config_file',type=unicode,
76 help=
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 )
71 metavar='Global.log_level')
72
73 def _add_arguments(self):
74 self._add_ipython_dir(self.parser)
75 self._add_log_level(self.parser)
76
84 77
85 78 class Application(object):
86 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
89 objects, which are loaded and ultimately merged into a single one used from
90 that point on by the app. These are:
81 The configuration of an application can be done via three different Config
82 objects, which are loaded and ultimately merged into a single one used
83 from that point on by the app. These are:
91 84
92 85 1. default_config: internal defaults, implemented in code.
93 86 2. file_config: read from the filesystem.
94 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 89 During initialization, 3 is actually read before 2, since at the
98 90 command-line one may override the location of the file to be read. But the
99 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 94 name = u'ipython'
111 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 97 usage = None
114 config_file_name = u'ipython_config.py'
115 #: Track the default and actual separately because some messages are
116 #: only printed if we aren't using the default.
117 default_config_file_name = config_file_name
98 #: The command line config loader. Subclass of ArgParseConfigLoader.
99 command_line_loader = BaseAppConfigLoader
100 #: The name of the config file to load, determined at runtime
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 105 default_log_level = logging.WARN
119 106 #: Set by --profile option
120 107 profile_name = None
121 108 #: User's ipython directory, typically ~/.ipython/
122 109 ipython_dir = None
123 #: internal defaults, implemented in code.
110 #: Internal defaults, implemented in code.
124 111 default_config = None
125 #: read from the filesystem
112 #: Read from the filesystem.
126 113 file_config = None
127 #: read from the system's command line flags
114 #: Read from the system's command line flags.
128 115 command_line_config = None
129 #: passed parametrically to the constructor.
130 constructor_config = None
131 #: final override, if given supercedes file/command/constructor configs
132 override_config = None
116 #: The final config that will be passed to the component.
117 master_config = None
133 118 #: A reference to the argv to be used (typically ends up being sys.argv[1:])
134 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 120 #: extra arguments computed by the command-line loader
140 121 extra_args = None
122 #: The class to use as the crash handler.
123 crash_handler_class = crashhandler.CrashHandler
141 124
142 125 # Private attributes
143 126 _exiting = False
144 127 _initialized = False
145 128
146 # Class choices for things that will be instantiated at runtime.
147 _CrashHandler = crashhandler.CrashHandler
148
149 def __init__(self, argv=None, constructor_config=None, override_config=None):
129 def __init__(self, argv=None):
150 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 131 self.init_logger()
154 132
155 133 def init_logger(self):
@@ -194,7 +172,6 b' class Application(object):'
194 172 self.log_default_config()
195 173 self.set_default_config_log_level()
196 174
197 if self.override_config is None:
198 175 # Command-line config
199 176 self.pre_load_command_line_config()
200 177 self.load_command_line_config()
@@ -209,7 +186,6 b' class Application(object):'
209 186 self.find_config_file_name()
210 187 self.find_config_file_paths()
211 188
212 if self.override_config is None:
213 189 # File-based config
214 190 self.pre_load_file_config()
215 191 self.load_file_config()
@@ -240,7 +216,7 b' class Application(object):'
240 216
241 217 def create_crash_handler(self):
242 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 220 sys.excepthook = self.crash_handler
245 221
246 222 def create_default_config(self):
@@ -270,10 +246,11 b' class Application(object):'
270 246
271 247 def create_command_line_config(self):
272 248 """Create and return a command line config loader."""
273 return ArgParseConfigLoader(self.argv, self.cl_arguments,
249 return self.command_line_loader(
250 self.argv,
274 251 description=self.description,
275 252 version=release.version,
276 usage=self.usage,
253 usage=self.usage
277 254 )
278 255
279 256 def pre_load_command_line_config(self):
@@ -338,18 +315,22 b' class Application(object):'
338 315
339 316 If a profile has been set at the command line, this will resolve it.
340 317 """
341
342 318 try:
343 319 self.config_file_name = self.command_line_config.Global.config_file
344 320 except AttributeError:
345 321 pass
322 else:
323 return
346 324
347 325 try:
348 326 self.profile_name = self.command_line_config.Global.profile
349 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 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 334 name_parts.insert(1, u'_' + self.profile_name + u'.')
354 335 self.config_file_name = ''.join(name_parts)
355 336
@@ -418,13 +399,9 b' class Application(object):'
418 399 """Merge the default, command line and file config objects."""
419 400 config = Config()
420 401 config._merge(self.default_config)
421 if self.override_config is None:
422 402 config._merge(self.file_config)
423 403 config._merge(self.command_line_config)
424 if self.constructor_config is not None:
425 config._merge(self.constructor_config)
426 else:
427 config._merge(self.override_config)
404
428 405 # XXX fperez - propose to Brian we rename master_config to simply
429 406 # config, I think this is going to be heavily used in examples and
430 407 # application code and the name is shorter/easier to find/remember.
@@ -456,15 +433,6 b' class Application(object):'
456 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 436 def exit(self, exit_status=0):
469 437 if self._exiting:
470 438 pass
@@ -473,17 +441,13 b' class Application(object):'
473 441 self._exiting = True
474 442 sys.exit(exit_status)
475 443
476 def attempt(self, func, action='abort'):
444 def attempt(self, func):
477 445 try:
478 446 func()
479 447 except SystemExit:
480 448 raise
481 449 except:
482 if action == 'abort':
483 450 self.log.critical("Aborting application: %s" % self.name,
484 451 exc_info=True)
485 self.abort()
486 raise
487 elif action == 'exit':
488 452 self.exit(0)
489 453
@@ -69,19 +69,20 b' used, and this module (and the readline module) are silently inactive.'
69 69 import __builtin__
70 70 import __main__
71 71 import glob
72 import inspect
72 73 import itertools
73 74 import keyword
74 75 import os
75 76 import re
76 77 import shlex
77 78 import sys
78 import types
79 79
80 import IPython.utils.rlineimpl as readline
81 80 from IPython.core.error import TryNext
82 81 from IPython.core.prefilter import ESC_MAGIC
83 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 88 # Globals
@@ -216,7 +217,6 b' class Completer:'
216 217 with a __getattr__ hook is evaluated.
217 218
218 219 """
219 import re
220 220
221 221 #print 'Completer->attr_matches, txt=%r' % text # dbg
222 222 # Another option, seems to work great. Catches things like ''.<tab>
@@ -27,7 +27,7 b' from weakref import WeakValueDictionary'
27 27 from IPython.utils.importstring import import_item
28 28 from IPython.config.loader import Config
29 29 from IPython.utils.traitlets import (
30 HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This
30 HasTraitlets, MetaHasTraitlets, Instance, This
31 31 )
32 32
33 33
@@ -1,74 +1,94 b''
1 # -*- coding: utf-8 -*-
1 # encoding: utf-8
2 2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
3 3
4 Authors:
4 5
5 Authors
6 -------
7 - Fernando Perez <Fernando.Perez@berkeley.edu>
6 * Fernando Perez
7 * Brian E. Granger
8 8 """
9 9
10 #*****************************************************************************
11 # Copyright (C) 2008-2009 The IPython Development Team
10 #-----------------------------------------------------------------------------
12 11 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
12 # Copyright (C) 2008-2010 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 #*****************************************************************************
16 #-----------------------------------------------------------------------------
17 17
18 #****************************************************************************
19 # Required modules
18 #-----------------------------------------------------------------------------
19 # Imports
20 #-----------------------------------------------------------------------------
20 21
21 # From the standard library
22 22 import os
23 23 import sys
24 24 from pprint import pformat
25 25
26 # Our own
27 from IPython.core import release
28 26 from IPython.core import ultratb
29 from IPython.utils.genutils import sys_info
30
31 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):
36 """Customizable crash handlers for IPython-based systems.
34 # Template for the user message.
35 _default_message_template = """\
36 Oops, $self.app_name crashed. We do our best to make it stable, but...
37 37
38 Instances of this class provide a __call__ method which can be used as a
39 sys.excepthook, i.e., the __call__ signature is:
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.
40 42
41 def __call__(self,etype, evalue, etb)
43 It was left in the file named:
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.
42 47
48 You can mail it to: $self.contact_name at $self.contact_email
49 with the subject '$self.app_name Crash Report'.
50
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
43 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
53 time for internal information.
62 Instances of this class provide a :meth:`__call__` method which can be
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
64 to be saved in. These reports are left in the ipython user directory
65 as determined by the running IPython instance.
80 contact_name : str
81 A string with the name of the person to contact.
66 82
67 Optional inputs:
83 contact_email : str
84 A string with the email address of the contact.
68 85
69 - show_crash_traceback(True): if false, don't print the crash
70 traceback on stderr, only generate the on-disk report
86 bug_tracker : str
87 A string with the URL for your project's bug tracker.
71 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 93 Non-argument instance attributes:
74 94
@@ -76,48 +96,17 b' class CrashHandler(object):'
76 96 further customization of the crash handler's behavior. Please see the
77 97 source for further details.
78 98 """
79
80 # apply args into instance
81 99 self.app = app
82 self.app_name = app_name
100 self.app_name = self.app.name
83 101 self.contact_name = contact_name
84 102 self.contact_email = contact_email
85 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 105 self.show_crash_traceback = show_crash_traceback
88 106 self.section_sep = '\n\n'+'*'*75+'\n\n'
89 107 self.call_pdb = call_pdb
90 108 #self.call_pdb = True # dbg
91 109
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
101 A crash report was automatically generated with the following information:
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 110 def __call__(self,etype, evalue, etb):
122 111 """Handle an exception, call for compatible with sys.excepthook"""
123 112
@@ -137,7 +126,8 b' $self.bug_tracker'
137 126 # write the report filename into the instance dict so it can get
138 127 # properly expanded out in the user message template
139 128 self.crash_report_fname = report_name
140 TBhandler = ultratb.VerboseTB(color_scheme=color_scheme,
129 TBhandler = ultratb.VerboseTB(
130 color_scheme=color_scheme,
141 131 long_header=1,
142 132 call_pdb=self.call_pdb,
143 133 )
@@ -159,7 +149,7 b' $self.bug_tracker'
159 149 return
160 150
161 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 153 print >> sys.stderr, msg
164 154
165 155 # Construct report on disk
@@ -178,7 +168,9 b' $self.bug_tracker'
178 168
179 169 try:
180 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 174 rpt_add(config)
183 175 except:
184 176 pass
@@ -186,38 +178,3 b' $self.bug_tracker'
186 178
187 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 28 import bdb
29 import cmd
30 29 import linecache
31 import os
32 30 import sys
33 31
34 32 from IPython.utils import PyColorize
35 33 from IPython.core import ipapi
36 34 from IPython.utils import coloransi
37 from IPython.utils.genutils import Term
35 from IPython.utils.io import Term
38 36 from IPython.core.excolors import exception_colors
39 37
40 38 # See if we can use pydb.
@@ -24,8 +24,6 b' import sys'
24 24
25 25 from IPython.core.component import Component
26 26
27 from IPython.utils.autoattr import auto_attr
28
29 27 #-----------------------------------------------------------------------------
30 28 # Classes and functions
31 29 #-----------------------------------------------------------------------------
@@ -24,6 +24,7 b' Notes'
24 24 #-----------------------------------------------------------------------------
25 25
26 26 from __future__ import with_statement
27 import __main__
27 28
28 29 import sys
29 30 from contextlib import nested
@@ -33,7 +34,7 b' from IPython.core.iplib import InteractiveShell'
33 34 from IPython.core.ipapp import load_default_config
34 35
35 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 10 # the file COPYING, distributed as part of this software.
11 11 #*****************************************************************************
12 12
13 #****************************************************************************
14 # Required modules
15 13 from IPython.utils.coloransi import ColorSchemeTable, TermColors, ColorScheme
16 14
17 15 def exception_colors():
@@ -5,7 +5,8 b''
5 5 import fnmatch
6 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 10 from IPython.core import ipapi
10 11
11 12 def magic_history(self, parameter_s = ''):
@@ -43,9 +43,12 b' somewhere in your configuration files or ipython command line.'
43 43
44 44 import os, bisect
45 45 import sys
46 from IPython.utils.genutils import Term, shell
46
47 47 from pprint import PrettyPrinter
48 48
49 from IPython.utils.io import Term
50 from IPython.utils.process import shell
51
49 52 from IPython.core.error import TryNext
50 53
51 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 18 # Imports
19 19 #-----------------------------------------------------------------------------
20 20
21 from IPython.core.error import TryNext, UsageError, IPythonCoreError
22
23 21 #-----------------------------------------------------------------------------
24 22 # Classes and functions
25 23 #-----------------------------------------------------------------------------
@@ -21,33 +21,60 b' Authors'
21 21 #-----------------------------------------------------------------------------
22 22 # Imports
23 23 #-----------------------------------------------------------------------------
24
24 25 from __future__ import absolute_import
25 26
26 27 import logging
27 28 import os
28 29 import sys
29 30
30 from IPython.core import crashhandler
31 from IPython.core.application import Application
31 from IPython.core import release
32 from IPython.core.crashhandler import CrashHandler
33 from IPython.core.application import Application, BaseAppConfigLoader
32 34 from IPython.core.iplib import InteractiveShell
33 35 from IPython.config.loader import (
34 36 Config,
35 PyFileConfigLoader,
36 # NoConfigDefault,
37 PyFileConfigLoader
37 38 )
38 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 41 from . import usage
41 42
42 43 #-----------------------------------------------------------------------------
43 44 # Globals, utilities and helpers
44 45 #-----------------------------------------------------------------------------
45 46
47 #: The default config file name for this application.
46 48 default_config_file_name = u'ipython_config.py'
47 49
48 cl_args = (
49 (('--autocall',), dict(
50 type=int, dest='InteractiveShell.autocall',
50
51 class IPAppConfigLoader(BaseAppConfigLoader):
52
53 def _add_arguments(self):
54 super(IPAppConfigLoader, self)._add_arguments()
55 paa = self.parser.add_argument
56 paa('-p',
57 '--profile', dest='Global.profile', type=unicode,
58 help=
59 """The string name of the ipython profile to be used. Assume that your
60 config file is ipython_config-<name>.py (looks in current dir first,
61 then in IPYTHON_DIR). This is a quick way to keep and load multiple
62 config files for different tasks, especially if include your basic one
63 in your more specialized ones. You can keep a basic
64 IPYTHON_DIR/ipython_config.py file and then have other 'profiles' which
65 include this one and load extra things for particular tasks.""",
66 metavar='Global.profile')
67 paa('--config-file',
68 dest='Global.config_file', type=unicode,
69 help=
70 """Set the config file name to override default. Normally IPython
71 loads ipython_config.py (from current directory) or
72 IPYTHON_DIR/ipython_config.py. If the loading of your config file
73 fails, IPython starts with a bare bones configuration (no modules
74 loaded at all).""",
75 metavar='Global.config_file')
76 paa('--autocall',
77 dest='InteractiveShell.autocall', type=int,
51 78 help=
52 79 """Make IPython automatically call any callable object even if you
53 80 didn't type explicit parentheses. For example, 'str 43' becomes
@@ -57,41 +84,33 b' cl_args = ('
57 84 objects are automatically called (even if no arguments are present).
58 85 The default is '1'.""",
59 86 metavar='InteractiveShell.autocall')
60 ),
61 (('--autoindent',), dict(
87 paa('--autoindent',
62 88 action='store_true', dest='InteractiveShell.autoindent',
63 89 help='Turn on autoindenting.')
64 ),
65 (('--no-autoindent',), dict(
90 paa('--no-autoindent',
66 91 action='store_false', dest='InteractiveShell.autoindent',
67 92 help='Turn off autoindenting.')
68 ),
69 (('--automagic',), dict(
93 paa('--automagic',
70 94 action='store_true', dest='InteractiveShell.automagic',
71 help='Turn on the auto calling of magic commands.'
72 'Type %%magic at the IPython prompt for more information.')
73 ),
74 (('--no-automagic',), dict(
95 help=
96 """Turn on the auto calling of magic commands. Type %%magic at the
97 IPython prompt for more information.""")
98 paa('--no-automagic',
75 99 action='store_false', dest='InteractiveShell.automagic',
76 100 help='Turn off the auto calling of magic commands.')
77 ),
78 (('--autoedit-syntax',), dict(
101 paa('--autoedit-syntax',
79 102 action='store_true', dest='InteractiveShell.autoedit_syntax',
80 103 help='Turn on auto editing of files with syntax errors.')
81 ),
82 (('--no-autoedit-syntax',), dict(
104 paa('--no-autoedit-syntax',
83 105 action='store_false', dest='InteractiveShell.autoedit_syntax',
84 106 help='Turn off auto editing of files with syntax errors.')
85 ),
86 (('--banner',), dict(
107 paa('--banner',
87 108 action='store_true', dest='Global.display_banner',
88 109 help='Display a banner upon starting IPython.')
89 ),
90 (('--no-banner',), dict(
110 paa('--no-banner',
91 111 action='store_false', dest='Global.display_banner',
92 112 help="Don't display a banner upon starting IPython.")
93 ),
94 (('--cache-size',), dict(
113 paa('--cache-size',
95 114 type=int, dest='InteractiveShell.cache_size',
96 115 help=
97 116 """Set the size of the output cache. The default is 1000, you can
@@ -99,20 +118,16 b' cl_args = ('
99 118 disables the caching system, and the minimum value accepted is 20 (if
100 119 you provide a value less than 20, it is reset to 0 and a warning is
101 120 issued). This limit is defined because otherwise you'll spend more
102 time re-flushing a too small cache than working.
103 """,
121 time re-flushing a too small cache than working""",
104 122 metavar='InteractiveShell.cache_size')
105 ),
106 (('--classic',), dict(
123 paa('--classic',
107 124 action='store_true', dest='Global.classic',
108 125 help="Gives IPython a similar feel to the classic Python prompt.")
109 ),
110 (('--colors',), dict(
126 paa('--colors',
111 127 type=str, dest='InteractiveShell.colors',
112 128 help="Set the color scheme (NoColor, Linux, and LightBG).",
113 129 metavar='InteractiveShell.colors')
114 ),
115 (('--color-info',), dict(
130 paa('--color-info',
116 131 action='store_true', dest='InteractiveShell.color_info',
117 132 help=
118 133 """IPython can display information about objects via a set of func-
@@ -123,27 +138,20 b' cl_args = ('
123 138 it and turn it on permanently in your ipython_config.py file if it
124 139 works for you. Test it and turn it on permanently if it works with
125 140 your system. The magic function %%color_info allows you to toggle this
126 inter- actively for testing."""
127 )
128 ),
129 (('--no-color-info',), dict(
141 inter- actively for testing.""")
142 paa('--no-color-info',
130 143 action='store_false', dest='InteractiveShell.color_info',
131 144 help="Disable using colors for info related things.")
132 ),
133 (('--confirm-exit',), dict(
145 paa('--confirm-exit',
134 146 action='store_true', dest='InteractiveShell.confirm_exit',
135 147 help=
136 148 """Set to confirm when you try to exit IPython with an EOF (Control-D
137 149 in Unix, Control-Z/Enter in Windows). By typing 'exit', 'quit' or
138 '%%Exit', you can force a direct exit without any confirmation.
139 """
140 )
141 ),
142 (('--no-confirm-exit',), dict(
150 '%%Exit', you can force a direct exit without any confirmation.""")
151 paa('--no-confirm-exit',
143 152 action='store_false', dest='InteractiveShell.confirm_exit',
144 153 help="Don't prompt the user when exiting.")
145 ),
146 (('--deep-reload',), dict(
154 paa('--deep-reload',
147 155 action='store_true', dest='InteractiveShell.deep_reload',
148 156 help=
149 157 """Enable deep (recursive) reloading by default. IPython can use the
@@ -155,47 +163,37 b' cl_args = ('
155 163 deep_reload will still be available as dreload(). This fea- ture is off
156 164 by default [which means that you have both normal reload() and
157 165 dreload()].""")
158 ),
159 (('--no-deep-reload',), dict(
166 paa('--no-deep-reload',
160 167 action='store_false', dest='InteractiveShell.deep_reload',
161 168 help="Disable deep (recursive) reloading by default.")
162 ),
163 (('--editor',), dict(
169 paa('--editor',
164 170 type=str, dest='InteractiveShell.editor',
165 171 help="Set the editor used by IPython (default to $EDITOR/vi/notepad).",
166 172 metavar='InteractiveShell.editor')
167 ),
168 (('--log','-l'), dict(
173 paa('--log','-l',
169 174 action='store_true', dest='InteractiveShell.logstart',
170 175 help="Start logging to the default log file (./ipython_log.py).")
171 ),
172 (('--logfile','-lf'), dict(
176 paa('--logfile','-lf',
173 177 type=unicode, dest='InteractiveShell.logfile',
174 178 help="Start logging to logfile with this name.",
175 179 metavar='InteractiveShell.logfile')
176 ),
177 (('--log-append','-la'), dict(
180 paa('--log-append','-la',
178 181 type=unicode, dest='InteractiveShell.logappend',
179 182 help="Start logging to the given file in append mode.",
180 183 metavar='InteractiveShell.logfile')
181 ),
182 (('--pdb',), dict(
184 paa('--pdb',
183 185 action='store_true', dest='InteractiveShell.pdb',
184 186 help="Enable auto calling the pdb debugger after every exception.")
185 ),
186 (('--no-pdb',), dict(
187 paa('--no-pdb',
187 188 action='store_false', dest='InteractiveShell.pdb',
188 189 help="Disable auto calling the pdb debugger after every exception.")
189 ),
190 (('--pprint',), dict(
190 paa('--pprint',
191 191 action='store_true', dest='InteractiveShell.pprint',
192 192 help="Enable auto pretty printing of results.")
193 ),
194 (('--no-pprint',), dict(
193 paa('--no-pprint',
195 194 action='store_false', dest='InteractiveShell.pprint',
196 195 help="Disable auto auto pretty printing of results.")
197 ),
198 (('--prompt-in1','-pi1'), dict(
196 paa('--prompt-in1','-pi1',
199 197 type=str, dest='InteractiveShell.prompt_in1',
200 198 help=
201 199 """Set the main input prompt ('In [\#]: '). Note that if you are using
@@ -206,8 +204,7 b' cl_args = ('
206 204 prompt escapes are described in detail in the Customization section of
207 205 the IPython manual.""",
208 206 metavar='InteractiveShell.prompt_in1')
209 ),
210 (('--prompt-in2','-pi2'), dict(
207 paa('--prompt-in2','-pi2',
211 208 type=str, dest='InteractiveShell.prompt_in2',
212 209 help=
213 210 """Set the secondary input prompt (' .\D.: '). Similar to the previous
@@ -217,25 +214,20 b' cl_args = ('
217 214 Default: ' .\D.: ' (note three spaces at the start for alignment with
218 215 'In [\#]')""",
219 216 metavar='InteractiveShell.prompt_in2')
220 ),
221 (('--prompt-out','-po'), dict(
217 paa('--prompt-out','-po',
222 218 type=str, dest='InteractiveShell.prompt_out',
223 219 help="Set the output prompt ('Out[\#]:')",
224 220 metavar='InteractiveShell.prompt_out')
225 ),
226 (('--quick',), dict(
221 paa('--quick',
227 222 action='store_true', dest='Global.quick',
228 223 help="Enable quick startup with no config files.")
229 ),
230 (('--readline',), dict(
224 paa('--readline',
231 225 action='store_true', dest='InteractiveShell.readline_use',
232 226 help="Enable readline for command line usage.")
233 ),
234 (('--no-readline',), dict(
227 paa('--no-readline',
235 228 action='store_false', dest='InteractiveShell.readline_use',
236 229 help="Disable readline for command line usage.")
237 ),
238 (('--screen-length','-sl'), dict(
230 paa('--screen-length','-sl',
239 231 type=int, dest='InteractiveShell.screen_length',
240 232 help=
241 233 """Number of lines of your screen, used to control printing of very
@@ -248,35 +240,28 b' cl_args = ('
248 240 curses support), specify it yourself. Otherwise don't change the
249 241 default.""",
250 242 metavar='InteractiveShell.screen_length')
251 ),
252 (('--separate-in','-si'), dict(
243 paa('--separate-in','-si',
253 244 type=str, dest='InteractiveShell.separate_in',
254 245 help="Separator before input prompts. Default '\\n'.",
255 246 metavar='InteractiveShell.separate_in')
256 ),
257 (('--separate-out','-so'), dict(
247 paa('--separate-out','-so',
258 248 type=str, dest='InteractiveShell.separate_out',
259 249 help="Separator before output prompts. Default 0 (nothing).",
260 250 metavar='InteractiveShell.separate_out')
261 ),
262 (('--separate-out2','-so2'), dict(
251 paa('--separate-out2','-so2',
263 252 type=str, dest='InteractiveShell.separate_out2',
264 253 help="Separator after output prompts. Default 0 (nonight).",
265 254 metavar='InteractiveShell.separate_out2')
266 ),
267 (('-no-sep',), dict(
255 paa('--no-sep',
268 256 action='store_true', dest='Global.nosep',
269 257 help="Eliminate all spacing between prompts.")
270 ),
271 (('--term-title',), dict(
258 paa('--term-title',
272 259 action='store_true', dest='InteractiveShell.term_title',
273 260 help="Enable auto setting the terminal title.")
274 ),
275 (('--no-term-title',), dict(
261 paa('--no-term-title',
276 262 action='store_false', dest='InteractiveShell.term_title',
277 263 help="Disable auto setting the terminal title.")
278 ),
279 (('--xmode',), dict(
264 paa('--xmode',
280 265 type=str, dest='InteractiveShell.xmode',
281 266 help=
282 267 """Exception reporting mode ('Plain','Context','Verbose'). Plain:
@@ -291,104 +276,121 b' cl_args = ('
291 276 it more than once).
292 277 """,
293 278 metavar='InteractiveShell.xmode')
294 ),
295 (('--ext',), dict(
279 paa('--ext',
296 280 type=str, dest='Global.extra_extension',
297 281 help="The dotted module name of an IPython extension to load.",
298 282 metavar='Global.extra_extension')
299 ),
300 (('-c',), dict(
283 paa('-c',
301 284 type=str, dest='Global.code_to_run',
302 285 help="Execute the given command string.",
303 286 metavar='Global.code_to_run')
304 ),
305 (('-i',), dict(
287 paa('-i',
306 288 action='store_true', dest='Global.force_interact',
307 289 help=
308 "If running code from the command line, become interactive afterwards."
309 )
310 ),
290 "If running code from the command line, become interactive afterwards.")
311 291
312 292 # Options to start with GUI control enabled from the beginning
313 (('--gui',), dict(
293 paa('--gui',
314 294 type=str, dest='Global.gui',
315 295 help="Enable GUI event loop integration ('qt', 'wx', 'gtk').",
316 296 metavar='gui-mode')
317 ),
318
319 (('--pylab','-pylab'), dict(
297 paa('--pylab','-pylab',
320 298 type=str, dest='Global.pylab',
321 299 nargs='?', const='auto', metavar='gui-mode',
322 300 help="Pre-load matplotlib and numpy for interactive use. "+
323 301 "If no value is given, the gui backend is matplotlib's, else use "+
324 302 "one of: ['tk', 'qt', 'wx', 'gtk'].")
325 ),
326 303
327 304 # Legacy GUI options. Leave them in for backwards compatibility, but the
328 305 # 'thread' names are really a misnomer now.
329 (('--wthread','-wthread'), dict(
306 paa('--wthread', '-wthread',
330 307 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(
308 help=
309 """Enable wxPython event loop integration. (DEPRECATED, use --gui wx)""")
310 paa('--q4thread', '--qthread', '-q4thread', '-qthread',
335 311 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(
312 help=
313 """Enable Qt4 event loop integration. Qt3 is no longer supported.
314 (DEPRECATED, use --gui qt)""")
315 paa('--gthread', '-gthread',
340 316 action='store_true', dest='Global.gthread',
341 help="Enable GTK event loop integration. "+
342 "(DEPRECATED, use --gui gtk)")
343 ),
344 )
317 help=
318 """Enable GTK event loop integration. (DEPRECATED, use --gui gtk)""")
319
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...
358 328
359 cl_arguments = Application.cl_arguments + cl_args
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.
360 333
361 # Private and configuration attributes
362 _CrashHandler = crashhandler.IPythonCrashHandler
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.
363 338
364 def __init__(self, argv=None,
365 constructor_config=None, override_config=None,
366 **shell_params):
367 """Create a new IPythonApp.
339 You can mail it to: $self.contact_name at $self.contact_email
340 with the subject '$self.app_name Crash Report'.
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
372 ----------
373 argv : optional, list
374 If given, used as the command-line argv environment to read arguments
375 from.
345 To ensure accurate tracking of this issue, please file a report about it at:
346 $self.bug_tracker
347 """
376 348
377 constructor_config : optional, Config
378 If given, additional config that is merged last, after internal
379 defaults, command-line and file-based configs.
349 class IPAppCrashHandler(CrashHandler):
350 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
380 351
381 override_config : optional, Config
382 If given, config that overrides all others unconditionally (except
383 for internal defaults, which ensure that all parameters exist).
352 message_template = _message_template
353
354 def __init__(self, app):
355 contact_name = release.authors['Fernando'][0]
356 contact_email = release.authors['Fernando'][1]
357 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
358 super(IPAppCrashHandler,self).__init__(
359 app, contact_name, contact_email, bug_tracker
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)
384 380
385 shell_params : optional, dict
386 All other keywords are passed to the :class:`iplib.InteractiveShell`
387 constructor.
388 """
389 super(IPythonApp, self).__init__(argv, constructor_config,
390 override_config)
391 self.shell_params = shell_params
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 395 def create_default_config(self):
394 396 super(IPythonApp, self).create_default_config()
@@ -474,8 +476,7 b' class IPythonApp(Application):'
474 476 sys.path.insert(0, '')
475 477
476 478 # Create an InteractiveShell instance
477 self.shell = InteractiveShell(None, self.master_config,
478 **self.shell_params )
479 self.shell = InteractiveShell(None, self.master_config)
479 480
480 481 def post_construct(self):
481 482 """Do actions after construct, but before starting the app."""
@@ -485,7 +486,6 b' class IPythonApp(Application):'
485 486 # based app, because we call shell.show_banner() by hand below
486 487 # so the banner shows *before* all extension loading stuff.
487 488 self.shell.display_banner = False
488
489 489 if config.Global.display_banner and \
490 490 config.Global.interact:
491 491 self.shell.show_banner()
@@ -499,7 +499,6 b' class IPythonApp(Application):'
499 499 self._run_exec_lines()
500 500 self._run_exec_files()
501 501 self._run_cmd_line_code()
502 self._configure_xmode()
503 502
504 503 def _enable_gui_pylab(self):
505 504 """Enable GUI event loop integration, taking pylab into account."""
@@ -624,11 +623,6 b' class IPythonApp(Application):'
624 623 self.log.warn("Error in executing file in user namespace: %s" % fname)
625 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 626 def start_app(self):
633 627 if self.master_config.Global.interact:
634 628 self.log.debug("Starting IPython's mainloop...")
@@ -653,3 +647,7 b' def launch_new_instance():'
653 647 """Create and run a full blown IPython instance"""
654 648 app = IPythonApp()
655 649 app.start()
650
651
652 if __name__ == '__main__':
653 launch_new_instance()
@@ -20,7 +20,6 b' from __future__ import with_statement'
20 20 from __future__ import absolute_import
21 21
22 22 import __builtin__
23 import StringIO
24 23 import bdb
25 24 import codeop
26 25 import exceptions
@@ -47,29 +46,35 b' from IPython.core.logger import Logger'
47 46 from IPython.core.magic import Magic
48 47 from IPython.core.prefilter import PrefilterManager
49 48 from IPython.core.prompts import CachedOutput
50 from IPython.core.pylabtools import pylab_activate
51 49 from IPython.core.usage import interactive_usage, default_banner
50 import IPython.core.hooks
52 51 from IPython.external.Itpl import ItplNS
53 52 from IPython.lib.inputhook import enable_gui
54 53 from IPython.lib.backgroundjobs import BackgroundJobManager
54 from IPython.lib.pylabtools import pylab_activate
55 55 from IPython.utils import PyColorize
56 56 from IPython.utils import pickleshare
57 from IPython.utils.genutils import get_ipython_dir
57 from IPython.utils.doctestreload import doctest_reload
58 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 67 from IPython.utils.strdispatch import StrDispatch
61 68 from IPython.utils.syspathcontext import prepended_to_syspath
62
63 # XXX - need to clean up this import * line
64 from IPython.utils.genutils import *
65
66 # from IPython.utils import growl
67 # growl.start("IPython")
68
69 from IPython.utils.terminal import toggle_set_term_title, set_term_title
70 from IPython.utils.warn import warn, error, fatal
69 71 from IPython.utils.traitlets import (
70 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 79 # Globals
75 80 #-----------------------------------------------------------------------------
@@ -186,64 +191,6 b' class SeparateStr(Str):'
186 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 195 # Main IPython class
249 196 #-----------------------------------------------------------------------------
@@ -658,7 +605,6 b' class InteractiveShell(Component, Magic):'
658 605 self.strdispatchers = {}
659 606
660 607 # Set all default hooks, defined in the IPython.hooks module.
661 import IPython.core.hooks
662 608 hooks = IPython.core.hooks
663 609 for hook_name in hooks.__all__:
664 610 # default hooks have priority 100, i.e. low; user hooks should have
@@ -876,7 +822,7 b' class InteractiveShell(Component, Magic):'
876 822 # These routines return properly built dicts as needed by the rest of
877 823 # the code, and can also be used by extension writers to generate
878 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 827 # Assign namespaces
882 828 # This is the namespace where all normal user variables live
@@ -887,7 +833,7 b' class InteractiveShell(Component, Magic):'
887 833 # loaded at startup, so we can list later only variables defined in
888 834 # actual interactive use. Since it is always a subset of user_ns, it
889 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 838 # A namespace to keep track of internal data structures to prevent
893 839 # them from cluttering user-visible stuff. Will be updated later
@@ -933,9 +879,67 b' class InteractiveShell(Component, Magic):'
933 879 # Similarly, track all namespaces where references can be held and that
934 880 # we can safely clear (so it can NOT include builtin). This one can be
935 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 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 943 def init_sys_modules(self):
940 944 # We need to insert into sys.modules something that looks like a
941 945 # module but which accesses the IPython namespace, for shelve and
@@ -974,7 +978,7 b' class InteractiveShell(Component, Magic):'
974 978 therm.
975 979 """
976 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 982 # initial variables aren't shown by %who. After the sync, we add the
979 983 # rest of what we *do* want the user to see with %who even on a new
980 984 # session (probably nothing, so theye really only see their own stuff)
@@ -1014,9 +1018,9 b' class InteractiveShell(Component, Magic):'
1014 1018 # Store myself as the public api!!!
1015 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 1022 # by %who
1019 self.user_config_ns.update(ns)
1023 self.user_ns_hidden.update(ns)
1020 1024
1021 1025 # Anything put into ns now would show up in %who. Think twice before
1022 1026 # putting anything here, as we really want %who to show the user their
@@ -1089,7 +1093,7 b' class InteractiveShell(Component, Magic):'
1089 1093 self.user_ns.update(vdict)
1090 1094
1091 1095 # And configure interactive visibility
1092 config_ns = self.user_config_ns
1096 config_ns = self.user_ns_hidden
1093 1097 if interactive:
1094 1098 for name, val in vdict.iteritems():
1095 1099 config_ns.pop(name, None)
@@ -1164,7 +1168,9 b' class InteractiveShell(Component, Magic):'
1164 1168 Convert func into callable that saves & restores
1165 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 1174 return func
1169 1175
1170 1176 def wrapper():
@@ -1198,6 +1204,9 b' class InteractiveShell(Component, Magic):'
1198 1204 # and add any custom exception handlers the user may have specified
1199 1205 self.set_custom_exc(*custom_exceptions)
1200 1206
1207 # Set the exception mode
1208 self.InteractiveTB.set_mode(mode=self.xmode)
1209
1201 1210 def set_custom_exc(self,exc_tuple,handler):
1202 1211 """set_custom_exc(exc_tuple,handler)
1203 1212
@@ -2351,6 +2360,9 b' class InteractiveShell(Component, Magic):'
2351 2360 to make it easy to write extensions, you can also put your extensions
2352 2361 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
2353 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 2367 from IPython.utils.syspathcontext import prepended_to_syspath
2356 2368
@@ -2495,11 +2507,11 b' class InteractiveShell(Component, Magic):'
2495 2507 # We want to prevent the loading of pylab to pollute the user's
2496 2508 # namespace as shown by the %who* magics, so we execute the activation
2497 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 2511 ns = {}
2500 2512 gui = pylab_activate(ns, gui)
2501 2513 self.user_ns.update(ns)
2502 self.user_config_ns.update(ns)
2514 self.user_ns_hidden.update(ns)
2503 2515 # Now we must activate the gui pylab wants to use, and fix %run to take
2504 2516 # plot updates into account
2505 2517 enable_gui(gui)
@@ -7,7 +7,7 b''
7 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 11 from IPython.core.autocall import IPyAutocall
12 12
13 13 class Macro(IPyAutocall):
@@ -1,35 +1,33 b''
1 # -*- coding: utf-8 -*-
1 # encoding: utf-8
2 2 """Magic functions for InteractiveShell.
3 3 """
4 4
5 #*****************************************************************************
5 #-----------------------------------------------------------------------------
6 6 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
7 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
8 #
7 # Copyright (C) 2001-2007 Fernando Perez <fperez@colorado.edu>
8 # Copyright (C) 2008-2009 The IPython Development Team
9
9 10 # Distributed under the terms of the BSD License. The full license is in
10 11 # the file COPYING, distributed as part of this software.
11 #*****************************************************************************
12 #-----------------------------------------------------------------------------
12 13
13 #****************************************************************************
14 # Modules and globals
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
15 17
16 # Python standard modules
17 18 import __builtin__
18 19 import bdb
19 20 import inspect
20 21 import os
21 import pdb
22 import pydoc
23 22 import sys
24 23 import shutil
25 24 import re
26 import tempfile
27 25 import time
28 import cPickle as pickle
29 26 import textwrap
27 import types
30 28 from cStringIO import StringIO
31 29 from getopt import getopt,GetoptError
32 from pprint import pprint, pformat
30 from pprint import pformat
33 31
34 32 # cProfile was added in Python2.5
35 33 try:
@@ -42,10 +40,7 b' except ImportError:'
42 40 except ImportError:
43 41 profile = pstats = None
44 42
45 # Homebrewed
46 43 import IPython
47 import IPython.utils.generics
48
49 44 from IPython.core import debugger, oinspect
50 45 from IPython.core.error import TryNext
51 46 from IPython.core.error import UsageError
@@ -53,20 +48,24 b' from IPython.core.fakemodule import FakeModule'
53 48 from IPython.core.macro import Macro
54 49 from IPython.core.page import page
55 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 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 54 from IPython.testing import decorators as testdec
60 from IPython.utils import platutils
61 from IPython.utils import wildcard
62 from IPython.utils.PyColorize import Parser
55 from IPython.utils.io import Term, file_read, nlprint
56 from IPython.utils.path import get_py_filename
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 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
66 from IPython.utils.genutils import *
67
68 #***************************************************************************
65 #-----------------------------------------------------------------------------
69 66 # Utility functions
67 #-----------------------------------------------------------------------------
68
70 69 def on_off(tag):
71 70 """Return an ON/OFF string for a 1/0 input. Simple utility function."""
72 71 return ['OFF','ON'][tag]
@@ -94,6 +93,9 b' def compress_dhist(dh):'
94 93 # on construction of the main InteractiveShell object. Something odd is going
95 94 # on with super() calls, Component and the MRO... For now leave it as-is, but
96 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 100 class Magic:
99 101 """Magic functions for InteractiveShell.
@@ -277,7 +279,7 b' python-profiler package from non-free.""")'
277 279 def arg_err(self,func):
278 280 """Print docstring if incorrect arguments were passed"""
279 281 print 'Error in arguments:'
280 print OInspect.getdoc(func)
282 print oinspect.getdoc(func)
281 283
282 284 def format_latex(self,strng):
283 285 """Format a string for latex inclusion."""
@@ -884,10 +886,10 b' Currently the magic system has the following functions:\\n"""'
884 886
885 887 user_ns = self.shell.user_ns
886 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 890 out = [ i for i in user_ns
889 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 894 typelist = parameter_s.split()
893 895 if typelist:
@@ -1170,7 +1172,7 b' Currently the magic system has the following functions:\\n"""'
1170 1172 started = logger.logstart(logfname,loghead,logmode,
1171 1173 log_output,timestamp,log_raw_input)
1172 1174 except:
1173 rc.opts.logfile = old_logfile
1175 self.shell.logfile = old_logfile
1174 1176 warn("Couldn't start log: %s" % sys.exc_info()[1])
1175 1177 else:
1176 1178 # log input history up to this point, optionally interleaving
@@ -2811,7 +2813,7 b' Defaulting color scheme to \'NoColor\'"""'
2811 2813 try:
2812 2814 os.chdir(os.path.expanduser(ps))
2813 2815 if self.shell.term_title:
2814 platutils.set_term_title('IPython: ' + abbrev_cwd())
2816 set_term_title('IPython: ' + abbrev_cwd())
2815 2817 except OSError:
2816 2818 print sys.exc_info()[1]
2817 2819 else:
@@ -2824,7 +2826,7 b' Defaulting color scheme to \'NoColor\'"""'
2824 2826 else:
2825 2827 os.chdir(self.shell.home_dir)
2826 2828 if self.shell.term_title:
2827 platutils.set_term_title('IPython: ' + '~')
2829 set_term_title('IPython: ' + '~')
2828 2830 cwd = os.getcwd()
2829 2831 dhist = self.shell.user_ns['_dh']
2830 2832
@@ -27,10 +27,11 b' import sys'
27 27 import types
28 28
29 29 # IPython's own
30 from IPython.utils import PyColorize
31 from IPython.utils.genutils import indent, Term
32 30 from IPython.core.page import page
33 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 35 from IPython.utils.wildcard import list_namespace
35 36 from IPython.utils.coloransi import *
36 37
@@ -30,15 +30,15 b' rid of that dependency, we could move it there.'
30 30 import os
31 31 import re
32 32 import sys
33 import tempfile
33 34
34 35 from IPython.core import ipapi
35 36 from IPython.core.error import TryNext
36 from IPython.utils.genutils import (
37 chop, Term, USE_CURSES
38 )
39
40 if os.name == "nt":
41 from IPython.utils.winconsole import get_console_size
37 from IPython.utils.cursesimport import use_curses
38 from IPython.utils.data import chop
39 from IPython.utils.io import Term
40 from IPython.utils.process import xsys
41 from IPython.utils.terminal import get_terminal_size
42 42
43 43
44 44 #-----------------------------------------------------------------------------
@@ -69,7 +69,7 b' def page_dumb(strng,start=0,screen_lines=25):'
69 69 last_escape = esc_list[-1]
70 70 print >>Term.cout, last_escape + os.linesep.join(screens[-1])
71 71
72 #----------------------------------------------------------------------------
72
73 73 def page(strng,start=0,screen_lines=0,pager_cmd = None):
74 74 """Print a string, piping through a pager after a certain length.
75 75
@@ -120,19 +120,16 b' def page(strng,start=0,screen_lines=0,pager_cmd = None):'
120 120 # terminals. If someone later feels like refining it, it's not hard.
121 121 numlines = max(num_newlines,int(len_str/80)+1)
122 122
123 if os.name == "nt":
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
123 screen_lines_def = get_terminal_size()[1]
127 124
128 125 # auto-determine screen size
129 126 if screen_lines <= 0:
130 127 if TERM=='xterm' or TERM=='xterm-color':
131 use_curses = USE_CURSES
128 local_use_curses = use_curses
132 129 else:
133 130 # curses causes problems on many terminals other than xterm.
134 use_curses = False
135 if use_curses:
131 local_use_curses = False
132 if local_use_curses:
136 133 import termios
137 134 import curses
138 135 # There is a bug in curses, where *sometimes* it fails to properly
@@ -201,7 +198,7 b' def page(strng,start=0,screen_lines=0,pager_cmd = None):'
201 198 if retval is not None:
202 199 page_dumb(strng,screen_lines=screen_lines)
203 200
204 #----------------------------------------------------------------------------
201
205 202 def page_file(fname,start = 0, pager_cmd = None):
206 203 """Page a file, using an optional pager command and starting line.
207 204 """
@@ -221,12 +218,12 b' def page_file(fname,start = 0, pager_cmd = None):'
221 218 except:
222 219 print 'Unable to show file',`fname`
223 220
224 #----------------------------------------------------------------------------
221
225 222 def get_pager_cmd(pager_cmd = None):
226 223 """Return a pager command.
227 224
228 Makes some attempts at finding an OS-correct one."""
229
225 Makes some attempts at finding an OS-correct one.
226 """
230 227 if os.name == 'posix':
231 228 default_pager_cmd = 'less -r' # -r for color control sequences
232 229 elif os.name in ['nt','dos']:
@@ -239,7 +236,7 b' def get_pager_cmd(pager_cmd = None):'
239 236 pager_cmd = default_pager_cmd
240 237 return pager_cmd
241 238
242 #-----------------------------------------------------------------------------
239
243 240 def get_pager_start(pager,start):
244 241 """Return the string for paging files with an offset.
245 242
@@ -255,8 +252,8 b' def get_pager_start(pager,start):'
255 252 start_string = ''
256 253 return start_string
257 254
258 #----------------------------------------------------------------------------
259 # (X)emacs on W32 doesn't like to be bypassed with msvcrt.getch()
255
256 # (X)emacs on win32 doesn't like to be bypassed with msvcrt.getch()
260 257 if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
261 258 import msvcrt
262 259 def page_more():
@@ -280,7 +277,7 b' else:'
280 277 else:
281 278 return True
282 279
283 #----------------------------------------------------------------------------
280
284 281 def snip_print(str,width = 75,print_full = 0,header = ''):
285 282 """Print a string snipping the midsection to fit in width.
286 283
@@ -306,3 +303,4 b" def snip_print(str,width = 75,print_full = 0,header = ''):"
306 303 if raw_input(header+' Snipped. View (y/n)? [N]').lower() == 'y':
307 304 page(str)
308 305 return snip
306
@@ -27,10 +27,7 b' Authors:'
27 27
28 28 import __builtin__
29 29 import codeop
30 import keyword
31 import os
32 30 import re
33 import sys
34 31
35 32 from IPython.core.alias import AliasManager
36 33 from IPython.core.autocall import IPyAutocall
@@ -39,7 +36,8 b' from IPython.core.splitinput import split_user_input'
39 36 from IPython.core.page import page
40 37
41 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 41 from IPython.utils.autoattr import auto_attr
44 42
45 43 #-----------------------------------------------------------------------------
@@ -158,6 +156,7 b' class LineInfo(object):'
158 156 without worrying about *further* damaging state.
159 157 """
160 158 if not self._oinfo:
159 # ip.shell._ofind is actually on the Magic class!
161 160 self._oinfo = ip.shell._ofind(self.ifun)
162 161 return self._oinfo
163 162
@@ -12,23 +12,20 b' Classes for handling input/output prompts.'
12 12 #*****************************************************************************
13 13
14 14 #****************************************************************************
15 # Required modules
15
16 16 import __builtin__
17 17 import os
18 import re
18 19 import socket
19 20 import sys
20 import time
21 21
22 # IPython's own
23 from IPython.utils import coloransi
24 22 from IPython.core import release
25 23 from IPython.external.Itpl import ItplNS
26 24 from IPython.core.error import TryNext
27 from IPython.utils.ipstruct import Struct
28 from IPython.core.macro import Macro
25 from IPython.utils import coloransi
29 26 import IPython.utils.generics
30
31 from IPython.utils.genutils import *
27 from IPython.utils.warn import warn
28 from IPython.utils.io import Term
32 29
33 30 #****************************************************************************
34 31 #Color schemes for Prompts.
@@ -19,7 +19,11 b' Authors'
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 import sys
22
23 #-----------------------------------------------------------------------------
24 # Code
25 #-----------------------------------------------------------------------------
26
23 27
24 28 class Quitter(object):
25 29 """Simple class to handle exit, similar to Python 2.5's.
@@ -40,4 +44,4 b' class Quitter(object):'
40 44 # Repr MUST return a string, else display like pprint hooks get confused
41 45 def __repr__(self):
42 46 self.shell.ask_exit()
43 return 'Bye.'
47 return ''
@@ -21,9 +21,9 b" name = 'ipython'"
21 21 # bdist_deb does not accept underscores (a Debian convention).
22 22
23 23 development = True # change this to False to do a release
24 version_base = '0.11'
24 version_base = '0.11.alpha1'
25 25 branch = 'ipython'
26 revision = '1346'
26 revision = '1223'
27 27
28 28 if development:
29 29 if branch == 'ipython':
@@ -30,9 +30,9 b' ip = get_ipython()'
30 30 @dec.parametric
31 31 def test_reset():
32 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 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 37 # Check that reset runs without error
38 38 ip.reset()
@@ -51,7 +51,7 b' def test_reset():'
51 51 for ns in ip.ns_refs_table:
52 52 if ns is ip.user_ns:
53 53 nvars_expected = nvars_user_ns
54 elif ns is ip.user_config_ns:
54 elif ns is ip.user_ns_hidden:
55 55 nvars_expected = nvars_config_ns
56 56 else:
57 57 nvars_expected = 0
@@ -8,19 +8,15 b' from __future__ import absolute_import'
8 8 # Imports
9 9 #-----------------------------------------------------------------------------
10 10
11 # stdlib
12 11 import os
13 12 import sys
14 13 import tempfile
15 14 import types
16 15 from cStringIO import StringIO
17 16
18 # third-party
19 17 import nose.tools as nt
20 18
21 # our own
22 from IPython.utils import genutils
23 from IPython.utils.platutils import find_cmd, get_long_path_name
19 from IPython.utils.path import get_long_path_name
24 20 from IPython.testing import decorators as dec
25 21 from IPython.testing import tools as tt
26 22
@@ -12,17 +12,12 b' from __future__ import absolute_import'
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 # stdlib
16 15 import os
17 16 import sys
18 17 import tempfile
19 18
20 # third-party
21 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 21 from IPython.testing import decorators as dec
27 22 from IPython.testing import tools as tt
28 23
@@ -142,10 +137,10 b' class TestMagicRunSimple(tt.TempFileMixin):'
142 137 _ip.runlines('t = isinstance(f(), foo)')
143 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 141 # due to the fact that subprocess does not support close_fds when
147 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 144 @dec.skip_win32
150 145 def test_obj_del(self):
151 146 """Test that object's __del__ methods are called on exit."""
@@ -93,9 +93,10 b' from inspect import getsourcefile, getfile, getmodule,\\'
93 93 from IPython.utils import PyColorize
94 94 from IPython.core import debugger, ipapi
95 95 from IPython.core.display_trap import DisplayTrap
96 from IPython.utils.ipstruct import Struct
97 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 101 # Globals
101 102 # amount of space to put line numbers before verbose tracebacks
@@ -384,7 +385,8 b' class ListTB(TBTools):'
384 385
385 386 def __call__(self, etype, value, elist):
386 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 391 def text(self, etype, value, elist, context=5):
390 392 """Return a color formatted string with the traceback info.
@@ -909,7 +911,8 b' class VerboseTB(TBTools):'
909 911 (etype, evalue, etb) = info or sys.exc_info()
910 912 self.tb = etb
911 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 917 # Changed so an instance can just be called as VerboseTB_inst() and print
915 918 # out the right info on its own.
@@ -53,7 +53,7 b" __all__ = ['Gnuplot','gp','gp_new','Data','File','Func','GridData',"
53 53 'pm3d_config','eps_fix_bbox']
54 54
55 55 import os,tempfile,sys
56 from IPython.utils.genutils import getoutput
56 from IPython.utils.process import getoutput
57 57
58 58 #---------------------------------------------------------------------------
59 59 # Notes on mouse support for Gnuplot.py
@@ -133,10 +133,10 b' from IPython.external import simplegeneric'
133 133 from IPython.external import path
134 134
135 135 try:
136 from IPython.utils import genutils
136 from IPython.utils.io import Term
137 137 from IPython.utils import generics
138 138 except ImportError:
139 genutils = None
139 Term = None
140 140 generics = None
141 141
142 142 from IPython.core import ipapi
@@ -2168,7 +2168,7 b' class idump(Display):'
2168 2168 self.datasepchar = "|"
2169 2169
2170 2170 def display(self):
2171 stream = genutils.Term.cout
2171 stream = Term.cout
2172 2172 allattrs = []
2173 2173 attrset = set()
2174 2174 colwidths = {}
@@ -54,7 +54,7 b' from enthought.traits import api as T'
54 54 # IPython imports
55 55 from IPython.core.error import TryNext
56 56 from IPython.core.ipapi import get as ipget
57 from IPython.utils.genutils import dir2
57 from IPython.utils.dir2 import dir2
58 58 try:
59 59 set
60 60 except:
@@ -14,7 +14,9 b' from IPython.core.iplib import InteractiveShell'
14 14 from IPython.utils.ipstruct import Struct
15 15 import Queue,thread,threading,signal
16 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 20 from IPython.core import shellglobals
19 21
20 22 def install_gtk2():
@@ -39,7 +39,7 b' from IPython.core.error import TryNext'
39 39 from IPython.external import pretty
40 40 from IPython.core.component import Component
41 41 from IPython.utils.traitlets import Bool, List
42 from IPython.utils.genutils import Term
42 from IPython.utils.io import Term
43 43 from IPython.utils.autoattr import auto_attr
44 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 132 """Load the extension in IPython as a hook."""
133 if ip is None: ip = get_ipython()
134 133 global _loaded
135 134 if not _loaded:
136 135 prd = PrettyResultDisplay(ip, name='pretty_result_display')
@@ -213,7 +213,7 b' def main():'
213 213 print "\n".join(expand(sys.argv[1:])),
214 214
215 215 def mglob_f(self, arg):
216 from IPython.utils.genutils import SList
216 from IPython.utils.text import SList
217 217 if arg.strip():
218 218 return SList(expand(arg))
219 219 print "Please specify pattern!"
@@ -39,9 +39,10 b' def common_prefix(strings):'
39 39
40 40 return prefix
41 41
42 #-------------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 43 # Base class for the line-oriented front ends
44 #-------------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45 46 class LineFrontEndBase(FrontEndBase):
46 47 """ Concrete implementation of the FrontEndBase class. This is meant
47 48 to be the base class behind all the frontend that are line-oriented,
@@ -26,12 +26,12 b' import os'
26 26 import re
27 27 import __builtin__
28 28
29 from IPython.core.ipapp import IPythonApp
29 from IPython.core.iplib import InteractiveShell
30 30 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
31 31
32 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 36 from linefrontendbase import LineFrontEndBase, common_prefix
37 37
@@ -50,9 +50,10 b' def mk_system_call(system_call_function, command):'
50 50 my_system_call.__doc__ = "Calls %s" % command
51 51 return my_system_call
52 52
53 #-------------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 54 # Frontend class using ipython0 to do the prefiltering.
55 #-------------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56 57 class PrefilterFrontEnd(LineFrontEndBase):
57 58 """ Class that uses ipython0 to do prefilter the input, do the
58 59 completion and the magics.
@@ -65,19 +66,13 b' class PrefilterFrontEnd(LineFrontEndBase):'
65 66
66 67 debug = False
67 68
68 def __init__(self, ipython0=None, argv=None, *args, **kwargs):
69 def __init__(self, ipython0=None, *args, **kwargs):
69 70 """ Parameters
70 71 ----------
71 72
72 73 ipython0: an optional ipython0 instance to use for command
73 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 76 LineFrontEndBase.__init__(self, *args, **kwargs)
82 77 self.shell.output_trap = RedirectorOutputTrap(
83 78 out_callback=self.write,
@@ -90,22 +85,19 b' class PrefilterFrontEnd(LineFrontEndBase):'
90 85 # Start the ipython0 instance:
91 86 self.save_output_hooks()
92 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 89 # prefiltering.
95 90 # Suppress all key input, to avoid waiting
96 91 def my_rawinput(x=None):
97 92 return '\n'
98 93 old_rawinput = __builtin__.raw_input
99 94 __builtin__.raw_input = my_rawinput
100 ipython0 = IPythonApp(argv=argv,
101 user_ns=self.shell.user_ns,
102 user_global_ns=self.shell.user_global_ns)
103 ipython0.initialize()
95 ipython0 = InteractiveShell(
96 parent=None, user_ns=self.shell.user_ns,
97 user_global_ns=self.shell.user_global_ns
98 )
104 99 __builtin__.raw_input = old_rawinput
105 # XXX This will need to be updated as we refactor things, but for now,
106 # the .shell attribute of the ipythonapp instance conforms to the old
107 # api.
108 self.ipython0 = ipython0.shell
100 self.ipython0 = ipython0
109 101 # Set the pager:
110 102 self.ipython0.set_hook('show_in_pager',
111 103 lambda s, string: self.write("\n" + string))
@@ -1,14 +1,8 b''
1 1 # encoding: utf-8
2
3 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 7 # Distributed under the terms of the BSD License. The full license is in
14 8 # the file COPYING, distributed as part of this software.
@@ -21,7 +21,6 b' from nose.tools import assert_equal'
21 21
22 22 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
23 23 from IPython.testing.globalipapp import get_ipython
24 from IPython.testing.tools import default_argv
25 24
26 25 #-----------------------------------------------------------------------------
27 26 # Support utilities
@@ -35,7 +34,7 b' class TestPrefilterFrontEnd(PrefilterFrontEnd):'
35 34
36 35 def __init__(self):
37 36 self.out = StringIO()
38 PrefilterFrontEnd.__init__(self,argv=default_argv())
37 PrefilterFrontEnd.__init__(self)
39 38 # Some more code for isolation (yeah, crazy)
40 39 self._on_enter()
41 40 self.out.flush()
@@ -47,6 +47,7 b' def test_io():'
47 47 assert result == test_string
48 48
49 49
50 @testdec.skip_win32
50 51 def test_kill():
51 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 139 # The console widget class
140 #-------------------------------------------------------------------------------
140 #-----------------------------------------------------------------------------
141
141 142 class ConsoleWidget(editwindow.EditWindow):
142 143 """ Specialized styled text control view for console-like workflow.
143 144
@@ -47,7 +47,7 b' class IPythonXController(WxController):'
47 47 self._input_state = 'subprocess'
48 48 self.write('\n', refresh=False)
49 49 self.capture_output()
50 self.ipython0.shell.exit()
50 self.ipython0.exit()
51 51 self.release_output()
52 52 if not self.ipython0.exit_now:
53 53 wx.CallAfter(self.new_prompt,
@@ -23,9 +23,8 b' import os'
23 23 import locale
24 24 from thread_ex import ThreadEx
25 25
26 import IPython
27 from IPython.core import iplib, ipapp
28 from IPython.utils import genutils
26 from IPython.core import iplib
27 from IPython.utils.io import Term
29 28
30 29 ##############################################################################
31 30 class _Helper(object):
@@ -88,12 +87,10 b' class NonBlockingIPShell(object):'
88 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 91 cin=None, cout=None, cerr=None,
93 92 ask_exit_handler=None):
94 93 '''
95 @param argv: Command line options for IPython
96 @type argv: list
97 94 @param user_ns: User namespace.
98 95 @type user_ns: dictionary
99 96 @param user_global_ns: User global namespace.
@@ -111,7 +108,7 b' class NonBlockingIPShell(object):'
111 108 '''
112 109 #ipython0 initialisation
113 110 self._IP = None
114 self.init_ipython0(argv, user_ns, user_global_ns,
111 self.init_ipython0(user_ns, user_global_ns,
115 112 cin, cout, cerr,
116 113 ask_exit_handler)
117 114
@@ -131,7 +128,7 b' class NonBlockingIPShell(object):'
131 128 self._help_text = None
132 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 132 cin=None, cout=None, cerr=None,
136 133 ask_exit_handler=None):
137 134 ''' Initialize an ipython0 instance '''
@@ -141,27 +138,22 b' class NonBlockingIPShell(object):'
141 138 #only one instance can be instanciated else tehre will be
142 139 #cin/cout/cerr clash...
143 140 if cin:
144 genutils.Term.cin = cin
141 Term.cin = cin
145 142 if cout:
146 genutils.Term.cout = cout
143 Term.cout = cout
147 144 if cerr:
148 genutils.Term.cerr = cerr
145 Term.cerr = cerr
149 146
150 147 excepthook = sys.excepthook
151 148
152 149 #Hack to save sys.displayhook, because ipython seems to overwrite it...
153 150 self.sys_displayhook_ori = sys.displayhook
154
155 ipython0 = ipapp.IPythonApp(argv,user_ns=user_ns,
156 user_global_ns=user_global_ns)
157 ipython0.initialize()
158 self._IP = ipython0.shell
159
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)
151 ipython0 = iplib.InteractiveShell(
152 parent=None, config=None,
153 user_ns=user_ns,
154 user_global_ns=user_global_ns
155 )
156 self._IP = ipython0
165 157
166 158 #we save ipython0 displayhook and we restore sys.displayhook
167 159 self.displayhook = sys.displayhook
@@ -185,12 +177,10 b' class NonBlockingIPShell(object):'
185 177 #we replace the help command
186 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.
189 from IPython.core import ipapi
190 ip = ipapi.get()
180 #we disable cpaste magic... until we found a way to use it properly.
191 181 def bypass_magic(self, arg):
192 182 print '%this magic is currently disabled.'
193 ip.define_magic('cpaste', bypass_magic)
183 ipython0.define_magic('cpaste', bypass_magic)
194 184
195 185 import __builtin__
196 186 __builtin__.raw_input = self._raw_input
@@ -471,7 +461,7 b' class NonBlockingIPShell(object):'
471 461 '''
472 462
473 463 orig_stdout = sys.stdout
474 sys.stdout = genutils.Term.cout
464 sys.stdout = Term.cout
475 465 #self.sys_displayhook_ori = sys.displayhook
476 466 #sys.displayhook = self.displayhook
477 467
@@ -11,6 +11,7 b' __author__ = "Laurent Dufrechou"'
11 11 __email__ = "laurent.dufrechou _at_ gmail.com"
12 12 __license__ = "BSD"
13 13 #-----------------------------------------
14
14 15 class IPythonHistoryPanel(wx.Panel):
15 16
16 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 30 import warnings
33 31
34 # from IPython.utils import growl
35 # growl.start("IPython1 Client")
32 # Twisted generates annoying warnings with Python 2.6, as will do other code
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 47 from twisted.internet import reactor
39 48 from twisted.internet.error import PotentialZombieWarning
@@ -74,8 +83,6 b' rit.setDaemon(True)'
74 83 rit.start()
75 84
76 85
77
78
79 86 __all__ = [
80 87 'MapTask',
81 88 'StringTask',
@@ -1,6 +1,4 b''
1 #!/usr/bin/env python
2 1 # encoding: utf-8
3
4 2 """Facilities for handling client connections to the controller."""
5 3
6 4 #-----------------------------------------------------------------------------
@@ -20,6 +18,8 b' import os'
20 18 from IPython.kernel.fcutil import (
21 19 Tub,
22 20 find_furl,
21 is_valid_furl,
22 is_valid_furl_file,
23 23 is_valid_furl_or_file,
24 24 validate_furl_or_file,
25 25 FURLError
@@ -33,7 +33,7 b' from IPython.kernel.twistedutil import ('
33 33 sleep_deferred
34 34 )
35 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 38 from twisted.internet import defer
39 39 from twisted.internet.defer import inlineCallbacks, returnValue
@@ -66,18 +66,30 b' class AsyncClientConnector(object):'
66 66 def _find_furl(self, profile='default', cluster_dir=None,
67 67 furl_or_file=None, furl_file_name=None,
68 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 76 This raises an :exc:`~IPython.kernel.fcutil.FURLError` exception
72 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 86 # Try by furl_or_file
75 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 89 return furl_or_file
78 90
79 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 94 # Try by cluster_dir
83 95 if cluster_dir is not None:
@@ -151,7 +163,7 b' class AsyncClientConnector(object):'
151 163 The full path to a cluster directory. This is useful if profiles
152 164 are not being used.
153 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 167 simply know the location of the FURL file.
156 168 ipython_dir : str
157 169 The location of the ipython_dir if different from the default.
@@ -193,7 +205,7 b' class AsyncClientConnector(object):'
193 205 The full path to a cluster directory. This is useful if profiles
194 206 are not being used.
195 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 209 simply know the location of the FURL file.
198 210 ipython_dir : str
199 211 The location of the ipython_dir if different from the default.
@@ -259,6 +271,9 b' class AsyncClientConnector(object):'
259 271 profile, cluster_dir, furl_or_file,
260 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 277 except FURLError:
263 278 return defer.fail(failure.Failure())
264 279
@@ -349,7 +364,7 b' class ClientConnector(object):'
349 364 The full path to a cluster directory. This is useful if profiles
350 365 are not being used.
351 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 368 simply know the location of the FURL file.
354 369 ipython_dir : str
355 370 The location of the ipython_dir if different from the default.
@@ -390,7 +405,7 b' class ClientConnector(object):'
390 405 The full path to a cluster directory. This is useful if profiles
391 406 are not being used.
392 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 409 simply know the location of the FURL file.
395 410 ipython_dir : str
396 411 The location of the ipython_dir if different from the default.
@@ -15,7 +15,7 b' __docformat__ = "restructuredtext en"'
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 from zope.interface import Interface, implements
18 from zope.interface import Interface
19 19
20 20 class IFCClientInterfaceProvider(Interface):
21 21
@@ -24,13 +24,16 b' import warnings'
24 24
25 25 from twisted.python import log
26 26
27 from IPython.core import release
28 27 from IPython.config.loader import PyFileConfigLoader
29 from IPython.core.application import Application
28 from IPython.core.application import Application, BaseAppConfigLoader
30 29 from IPython.core.component import Component
31 from IPython.utils.genutils import get_ipython_dir, get_ipython_package_dir
32 from IPython.utils.traitlets import Unicode, Bool
33 from IPython.utils import genutils
30 from IPython.core.crashhandler import CrashHandler
31 from IPython.core import release
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 39 # Warnings control
@@ -45,7 +48,7 b" warnings.filterwarnings('ignore', 'the sha module is deprecated',"
45 48 DeprecationWarning)
46 49
47 50 #-----------------------------------------------------------------------------
48 # Classes and functions
51 # Module errors
49 52 #-----------------------------------------------------------------------------
50 53
51 54 class ClusterDirError(Exception):
@@ -56,6 +59,10 b' class PIDFileError(Exception):'
56 59 pass
57 60
58 61
62 #-----------------------------------------------------------------------------
63 # Class for managing cluster directories
64 #-----------------------------------------------------------------------------
65
59 66 class ClusterDir(Component):
60 67 """An object to manage the cluster directory and its resources.
61 68
@@ -225,44 +232,110 b' class ClusterDir(Component):'
225 232 The path of the cluster directory. This is expanded using
226 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 236 if not os.path.isdir(cluster_dir):
230 237 raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
231 238 return ClusterDir(cluster_dir)
232 239
233 240
234 # Default command line options for IPython cluster applications.
235 cl_args = (
236 (('--ipython-dir',), dict(
237 dest='Global.ipython_dir',type=unicode,
238 help='Set to override default location of Global.ipython_dir.',
239 metavar='Global.ipython_dir') ),
240 (('-p', '--profile',), dict(
241 #-----------------------------------------------------------------------------
242 # Command line options
243 #-----------------------------------------------------------------------------
244
245 class ClusterDirConfigLoader(BaseAppConfigLoader):
246
247 def _add_cluster_profile(self, parser):
248 paa = parser.add_argument
249 paa('-p', '--profile',
241 250 dest='Global.profile',type=unicode,
242 251 help=
243 252 """The string name of the profile to be used. This determines the name
244 253 of the cluster dir as: cluster_<profile>. The default profile is named
245 254 'default'. The cluster directory is resolve this way if the
246 255 --cluster-dir option is not used.""",
247 metavar='Global.profile') ),
248 (('--cluster-dir',), dict(
256 metavar='Global.profile')
257
258 def _add_cluster_dir(self, parser):
259 paa = parser.add_argument
260 paa('--cluster-dir',
249 261 dest='Global.cluster_dir',type=unicode,
250 262 help="""Set the cluster dir. This overrides the logic used by the
251 263 --profile option.""",
252 metavar='Global.cluster_dir') ),
253 (('--work-dir',), dict(
264 metavar='Global.cluster_dir')
265
266 def _add_work_dir(self, parser):
267 paa = parser.add_argument
268 paa('--work-dir',
254 269 dest='Global.work_dir',type=unicode,
255 270 help='Set the working dir for the process.',
256 metavar='Global.work_dir') ),
257 (('--clean-logs',), dict(
271 metavar='Global.work_dir')
272
273 def _add_clean_logs(self, parser):
274 paa = parser.add_argument
275 paa('--clean-logs',
258 276 dest='Global.clean_logs', action='store_true',
259 help='Delete old log flies before starting.') ),
260 (('--no-clean-logs',), dict(
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',
261 282 dest='Global.clean_logs', action='store_false',
262 help="Don't Delete old log flies before starting.") ),
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.
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
263 332 )
264 333
265 334
335 #-----------------------------------------------------------------------------
336 # Main application
337 #-----------------------------------------------------------------------------
338
266 339 class ApplicationWithClusterDir(Application):
267 340 """An application that puts everything into a cluster directory.
268 341
@@ -282,10 +355,10 b' class ApplicationWithClusterDir(Application):'
282 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 360 auto_create_cluster_dir = True
286 361
287 cl_arguments = Application.cl_arguments + cl_args
288
289 362 def create_default_config(self):
290 363 super(ApplicationWithClusterDir, self).create_default_config()
291 364 self.default_config.Global.profile = u'default'
@@ -316,7 +389,7 b' class ApplicationWithClusterDir(Application):'
316 389 cluster_dir = self.command_line_config.Global.cluster_dir
317 390 except AttributeError:
318 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 393 try:
321 394 self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
322 395 except ClusterDirError:
@@ -368,15 +441,17 b' class ApplicationWithClusterDir(Application):'
368 441 def find_config_file_name(self):
369 442 """Find the config file name for this application."""
370 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 445 self.log.critical("No config filename found")
446 else:
447 self.config_file_name = self.default_config_file_name
373 448
374 449 def find_config_file_paths(self):
375 # Include our own config directory last, so that users can still find
376 # our shipped copies of builtin config files even if they don't have
377 # them in their ipython cluster directory.
450 # Set the search path to to the cluster directory. We should NOT
451 # include IPython.config.default here as the default config files
452 # are ALWAYS automatically moved to the cluster directory.
378 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 456 def pre_construct(self):
382 457 # The log and security dirs were set earlier, but here we put them
@@ -389,7 +464,7 b' class ApplicationWithClusterDir(Application):'
389 464 pdir = self.cluster_dir_obj.pid_dir
390 465 self.pid_dir = config.Global.pid_dir = pdir
391 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 468 # Change to the working directory. We do this just before construct
394 469 # is called so all the components there have the right working dir.
395 470 self.to_work_dir()
@@ -461,3 +536,4 b' class ApplicationWithClusterDir(Application):'
461 536 return pid
462 537 else:
463 538 raise PIDFileError('pid file not found: %s' % pid_file)
539
@@ -37,20 +37,18 b' __docformat__ = "restructuredtext en"'
37 37 # Imports
38 38 #-------------------------------------------------------------------------------
39 39
40 import os, sys
40 import os
41 41
42 42 from twisted.application import service
43 from twisted.internet import defer, reactor
44 from twisted.python import log, components
43 from twisted.python import log
45 44 from zope.interface import Interface, implements, Attribute
46 import zope.interface as zi
47 45
48 46 from IPython.kernel.engineservice import \
49 47 IEngineCore, \
50 48 IEngineSerialized, \
51 49 IEngineQueued
52 50
53 from IPython.utils.genutils import get_ipython_dir
51 from IPython.utils.path import get_ipython_dir
54 52 from IPython.kernel import codeutil
55 53
56 54 #-------------------------------------------------------------------------------
@@ -21,6 +21,8 b' __docformat__ = "restructuredtext en"'
21 21
22 22 # Required modules
23 23 import __builtin__
24 import os
25 import re
24 26 import socket
25 27 import sys
26 28
@@ -30,7 +32,8 b' from IPython.external.Itpl import ItplNS'
30 32 from IPython.utils import coloransi
31 33 from IPython.core import release
32 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 37 import IPython.utils.generics
35 38
36 39 #****************************************************************************
@@ -22,15 +22,15 b' import sys'
22 22
23 23 from twisted.trial import unittest
24 24
25 from IPython.testing import decorators_trial as dec
26
25 27 #-----------------------------------------------------------------------------
26 28 # Tests
27 29 #-----------------------------------------------------------------------------
28 30
29 31 class TestRedirector(unittest.TestCase):
30 32
31 if sys.platform == 'win32':
32 skip = True
33
33 @dec.skip_win32
34 34 def test_redirector(self):
35 35 """Checks that the redirector can be used to do synchronous capture.
36 36 """
@@ -51,6 +51,7 b' class TestRedirector(unittest.TestCase):'
51 51 result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10))
52 52 self.assertEquals(result1, result2)
53 53
54 @dec.skip_win32
54 55 def test_redirector_output_trap(self):
55 56 """Check the greedy trapping behavior of the traps.
56 57
@@ -17,8 +17,7 b''
17 17 import os
18 18 import cPickle as pickle
19 19
20 from twisted.python import log, failure
21 from twisted.internet import defer
20 from twisted.python import log
22 21 from twisted.internet.defer import inlineCallbacks, returnValue
23 22
24 23 from IPython.kernel.fcutil import find_furl, validate_furl_or_file
@@ -19,40 +19,36 b' __docformat__ = "restructuredtext en"'
19 19 # Imports
20 20 #-------------------------------------------------------------------------------
21 21
22 import os, time
23 22 import cPickle as pickle
24 23
25 24 from twisted.python import components, log, failure
26 from twisted.python.failure import Failure
27 from twisted.internet import defer, reactor, threads
28 from twisted.internet.interfaces import IProtocolFactory
29 from zope.interface import Interface, implements, Attribute
25 from twisted.internet import defer, threads
26 from zope.interface import Interface, implements
30 27
31 28 from twisted.internet.base import DelayedCall
32 29 DelayedCall.debug = True
33 30
31 try:
32 from foolscap.api import Referenceable, DeadReferenceError
33 except ImportError:
34 34 from foolscap import Referenceable, DeadReferenceError
35 35 from foolscap.referenceable import RemoteReference
36 36
37 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 38 from IPython.kernel.controllerservice import IControllerBase
44 from IPython.kernel.engineservice import \
45 IEngineBase, \
46 IEngineQueued, \
47 EngineService, \
39 from IPython.kernel.engineservice import (
40 IEngineBase,
41 IEngineQueued,
48 42 StrictDict
49 from IPython.kernel.pickleutil import \
50 can, \
51 canDict, \
52 canSequence, \
53 uncan, \
54 uncanDict, \
43 )
44 from IPython.kernel.pickleutil import (
45 can,
46 canDict,
47 canSequence,
48 uncan,
49 uncanDict,
55 50 uncanSequence
51 )
56 52
57 53
58 54 #-------------------------------------------------------------------------------
@@ -17,6 +17,9 b' __test__ = {}'
17 17 #-------------------------------------------------------------------------------
18 18 # Imports
19 19 #-------------------------------------------------------------------------------
20
21 from twisted.python import failure
22
20 23 from IPython.kernel.core import error
21 24
22 25 #-------------------------------------------------------------------------------
@@ -26,6 +29,7 b' from IPython.kernel.core import error'
26 29 class KernelError(error.IPythonError):
27 30 pass
28 31
32
29 33 class NotDefined(KernelError):
30 34 def __init__(self, name):
31 35 self.name = name
@@ -36,78 +40,102 b' class NotDefined(KernelError):'
36 40
37 41 __str__ = __repr__
38 42
43
39 44 class QueueCleared(KernelError):
40 45 pass
41 46
47
42 48 class IdInUse(KernelError):
43 49 pass
44 50
51
45 52 class ProtocolError(KernelError):
46 53 pass
47 54
55
48 56 class ConnectionError(KernelError):
49 57 pass
50 58
59
51 60 class InvalidEngineID(KernelError):
52 61 pass
53 62
63
54 64 class NoEnginesRegistered(KernelError):
55 65 pass
56 66
67
57 68 class InvalidClientID(KernelError):
58 69 pass
59 70
71
60 72 class InvalidDeferredID(KernelError):
61 73 pass
62 74
75
63 76 class SerializationError(KernelError):
64 77 pass
65 78
79
66 80 class MessageSizeError(KernelError):
67 81 pass
68 82
83
69 84 class PBMessageSizeError(MessageSizeError):
70 85 pass
71 86
87
72 88 class ResultNotCompleted(KernelError):
73 89 pass
74 90
91
75 92 class ResultAlreadyRetrieved(KernelError):
76 93 pass
77 94
78 95 class ClientError(KernelError):
79 96 pass
80 97
98
81 99 class TaskAborted(KernelError):
82 100 pass
83 101
102
84 103 class TaskTimeout(KernelError):
85 104 pass
86 105
106
87 107 class NotAPendingResult(KernelError):
88 108 pass
89 109
110
90 111 class UnpickleableException(KernelError):
91 112 pass
92 113
114
93 115 class AbortedPendingDeferredError(KernelError):
94 116 pass
95 117
118
96 119 class InvalidProperty(KernelError):
97 120 pass
98 121
122
99 123 class MissingBlockArgument(KernelError):
100 124 pass
101 125
126
102 127 class StopLocalExecution(KernelError):
103 128 pass
104 129
130
105 131 class SecurityError(KernelError):
106 132 pass
107 133
134
108 135 class FileTimeoutError(KernelError):
109 136 pass
110 137
138
111 139 class TaskRejectError(KernelError):
112 140 """Exception to raise when a task should be rejected by an engine.
113 141
@@ -122,6 +150,7 b' class TaskRejectError(KernelError):'
122 150 properties don't have to be managed or tested by the controller.
123 151 """
124 152
153
125 154 class CompositeError(KernelError):
126 155 def __init__(self, message, elist):
127 156 Exception.__init__(self, *(message, elist))
@@ -176,9 +205,8 b' class CompositeError(KernelError):'
176 205 else:
177 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 210 elist = []
183 211 for r in rlist:
184 212 if isinstance(r, failure.Failure):
@@ -204,4 +232,3 b' def collect_exceptions(rlist, method):'
204 232 except CompositeError, e:
205 233 raise e
206 234
207
@@ -23,16 +23,19 b' import tempfile'
23 23 from twisted.internet import reactor, defer
24 24 from twisted.python import log
25 25
26 import foolscap
27 try:
28 from foolscap.api import Tub, UnauthenticatedTub
29 except ImportError:
26 30 from foolscap import Tub, UnauthenticatedTub
27 31
28 32 from IPython.config.loader import Config
29
30 33 from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory
31
32 34 from IPython.kernel.error import SecurityError
33 35
34 from IPython.utils.traitlets import Int, Str, Bool, Instance
35 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 41 # Code
@@ -57,17 +60,17 b' class FURLError(Exception):'
57 60
58 61 def check_furl_file_security(furl_file, secure):
59 62 """Remove the old furl_file if changing security modes."""
63 furl_file = expand_path(furl_file)
60 64 if os.path.isfile(furl_file):
61 f = open(furl_file, 'r')
65 with open(furl_file, 'r') as f:
62 66 oldfurl = f.read().strip()
63 f.close()
64 67 if (oldfurl.startswith('pb://') and not secure) or (oldfurl.startswith('pbu://') and secure):
65 68 os.remove(furl_file)
66 69
67 70
68 71 def is_secure(furl):
69 72 """Is the given FURL secure or not."""
70 if is_valid(furl):
73 if is_valid_furl(furl):
71 74 if furl.startswith("pb://"):
72 75 return True
73 76 elif furl.startswith("pbu://"):
@@ -76,26 +79,45 b' def is_secure(furl):'
76 79 raise FURLError("invalid FURL: %s" % furl)
77 80
78 81
79 def is_valid(furl):
82 def is_valid_furl(furl):
80 83 """Is the str a valid FURL or not."""
81 84 if isinstance(furl, str):
82 85 if furl.startswith("pb://") or furl.startswith("pbu://"):
83 86 return True
84 87 else:
85 88 return False
89 else:
90 return False
91
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
86 104
87 105
88 106 def find_furl(furl_or_file):
89 """Find, validate and return a FURL in a string or file."""
90 if isinstance(furl_or_file, str):
91 if is_valid(furl_or_file):
107 """Find, validate and return a FURL in a string or file.
108
109 This calls :func:`IPython.utils.path.expand_path` on the argument to
110 properly handle ``~`` and ``$`` variables in the path.
111 """
112 if is_valid_furl(furl_or_file):
92 113 return furl_or_file
93 if os.path.isfile(furl_or_file):
114 furl_or_file = expand_path(furl_or_file)
115 if is_valid_furl_file(furl_or_file):
94 116 with open(furl_or_file, 'r') as f:
95 117 furl = f.read().strip()
96 if is_valid(furl):
118 if is_valid_furl(furl):
97 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 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 128 if the FURL file exists or to read its contents. This is useful for
107 129 cases where auto re-connection is being used.
108 130 """
109 if isinstance(furl_or_file, str):
110 if is_valid(furl_or_file):
111 return True
112 if isinstance(furl_or_file, (str, unicode)):
113 path, furl_filename = os.path.split(furl_or_file)
114 if os.path.isdir(path) and furl_filename.endswith('.furl'):
131 if is_valid_furl(furl_or_file) or is_valid_furl_file(furl_or_file):
115 132 return True
133 else:
116 134 return False
117 135
118 136
119 137 def validate_furl_or_file(furl_or_file):
138 """Like :func:`is_valid_furl_or_file`, but raises an error."""
120 139 if not is_valid_furl_or_file(furl_or_file):
121 140 raise FURLError('Not a valid FURL or FURL file: %r' % furl_or_file)
122 141
@@ -265,8 +284,12 b' class FCServiceFactory(AdaptedConfiguredObjectFactory):'
265 284 """Register the reference with the FURL file.
266 285
267 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 """
290 if self.reuse_furls:
291 self.tub.registerReference(ref, furlFile=furl_file)
292 else:
270 293 temp_furl_file = get_temp_furlfile(furl_file)
271 294 self.tub.registerReference(ref, furlFile=temp_furl_file)
272 295 os.rename(temp_furl_file, furl_file)
@@ -22,161 +22,203 b' import signal'
22 22 if os.name=='posix':
23 23 from twisted.scripts._twistd_unix import daemonize
24 24
25 from IPython.core import release
26 from IPython.external.argparse import ArgumentParser
27 from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
28 from IPython.utils.importstring import import_item
25 from twisted.internet import reactor, defer
26 from twisted.python import log, failure
27
29 28
29 from IPython.external.argparse import ArgumentParser, SUPPRESS
30 from IPython.utils.importstring import import_item
30 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 58 # Exit codes for ipcluster
44 59
45 60 # This will be the exit code if the ipcluster appears to be running because
46 61 # a .pid file exists
47 62 ALREADY_STARTED = 10
48 63
64
49 65 # This will be the exit code if ipcluster stop is run, but there is not .pid
50 66 # file to be found.
51 67 ALREADY_STOPPED = 11
52 68
53 69
54 class IPClusterCLLoader(ArgParseConfigLoader):
70 #-----------------------------------------------------------------------------
71 # Command line options
72 #-----------------------------------------------------------------------------
73
74
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.
55 81
56 def _add_other_arguments(self):
57 82 # This has all the common options that all subcommands use
58 parent_parser1 = ArgumentParser(add_help=False,
59 argument_default=NoConfigDefault)
60 parent_parser1.add_argument('--ipython-dir',
61 dest='Global.ipython_dir',type=unicode,
62 help='Set to override default location of Global.ipython_dir.',
63 metavar='Global.ipython_dir')
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')
83 parent_parser1 = ArgumentParser(
84 add_help=False,
85 argument_default=SUPPRESS
86 )
87 self._add_ipython_dir(parent_parser1)
88 self._add_log_level(parent_parser1)
68 89
69 90 # This has all the common options that other subcommands use
70 parent_parser2 = ArgumentParser(add_help=False,
71 argument_default=NoConfigDefault)
72 parent_parser2.add_argument('-p','--profile',
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 parent_parser2 = ArgumentParser(
92 add_help=False,
93 argument_default=SUPPRESS
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 104 subparsers = self.parser.add_subparsers(
94 105 dest='Global.subcommand',
95 106 title='ipcluster subcommands',
96 description='ipcluster has a variety of subcommands. '
97 'The general way of running ipcluster is "ipcluster <cmd> '
98 ' [options]""',
99 help='For more help, type "ipcluster <cmd> -h"')
107 description=
108 """ipcluster has a variety of subcommands. The general way of
109 running ipcluster is 'ipcluster <cmd> [options]'. To get help
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 115 parser_list = subparsers.add_parser(
102 116 'list',
103 help='List all clusters in cwd and ipython_dir.',
104 parents=[parent_parser1]
117 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 128 parser_create = subparsers.add_parser(
108 129 'create',
109 help='Create a new cluster directory.',
110 parents=[parent_parser1, parent_parser2]
130 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(
113 '--reset-config',
146 paa = parser_create.add_argument
147 paa('--reset-config',
114 148 dest='Global.reset_config', action='store_true',
115 default=NoConfigDefault,
116 help='Recopy the default config files to the cluster directory. '
117 'You will loose any modifications you have made to these files.'
118 )
149 help=
150 """Recopy the default config files to the cluster directory.
151 You will loose any modifications you have made to these files.""")
119 152
153 # The "start" subcommand parser
120 154 parser_start = subparsers.add_parser(
121 155 'start',
122 help='Start a cluster.',
123 parents=[parent_parser1, parent_parser2]
156 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(
126 '-n', '--number',
170 paa = parser_start.add_argument
171 paa('-n', '--number',
127 172 type=int, dest='Global.n',
128 173 help='The number of engines to start.',
129 metavar='Global.n'
130 )
131 parser_start.add_argument('--clean-logs',
174 metavar='Global.n')
175 paa('--clean-logs',
132 176 dest='Global.clean_logs', action='store_true',
133 help='Delete old log flies before starting.',
134 )
135 parser_start.add_argument('--no-clean-logs',
177 help='Delete old log flies before starting.')
178 paa('--no-clean-logs',
136 179 dest='Global.clean_logs', action='store_false',
137 help="Don't delete old log flies before starting.",
138 )
139 parser_start.add_argument('--daemon',
180 help="Don't delete old log flies before starting.")
181 paa('--daemon',
140 182 dest='Global.daemonize', action='store_true',
141 help='Daemonize the ipcluster program. This implies --log-to-file',
142 )
143 parser_start.add_argument('--no-daemon',
183 help='Daemonize the ipcluster program. This implies --log-to-file')
184 paa('--no-daemon',
144 185 dest='Global.daemonize', action='store_false',
145 help="Dont't daemonize the ipcluster program.",
146 )
186 help="Dont't daemonize the ipcluster program.")
147 187
148 parser_start = subparsers.add_parser(
188 # The "stop" subcommand parser
189 parser_stop = subparsers.add_parser(
149 190 'stop',
150 help='Stop a cluster.',
151 parents=[parent_parser1, parent_parser2]
191 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 205 dest='Global.signal', type=int,
155 206 help="The signal number to use in stopping the cluster (default=2).",
156 metavar="Global.signal",
157 )
207 metavar="Global.signal")
158 208
159 209
160 default_config_file_name = u'ipcluster_config.py'
161
162
163 _description = """Start an IPython cluster for parallel computing.\n\n
164
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 """
210 #-----------------------------------------------------------------------------
211 # Main application
212 #-----------------------------------------------------------------------------
173 213
174 214
175 215 class IPClusterApp(ApplicationWithClusterDir):
176 216
177 217 name = u'ipcluster'
178 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 222 default_log_level = logging.INFO
181 223 auto_create_cluster_dir = False
182 224
@@ -192,13 +234,6 b' class IPClusterApp(ApplicationWithClusterDir):'
192 234 self.default_config.Global.signal = 2
193 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 237 def find_resources(self):
203 238 subcommand = self.command_line_config.Global.subcommand
204 239 if subcommand=='list':
@@ -361,8 +396,11 b' class IPClusterApp(ApplicationWithClusterDir):'
361 396 log.msg('Unexpected error in ipcluster:')
362 397 log.msg(r.getTraceback())
363 398 log.msg("IPython cluster: stopping")
364 self.stop_engines()
365 self.stop_controller()
399 # These return deferreds. We are not doing anything with them
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 404 # Wait a few seconds to let things shut down.
367 405 reactor.callLater(4.0, reactor.stop)
368 406
@@ -449,6 +487,7 b' class IPClusterApp(ApplicationWithClusterDir):'
449 487 # old .pid files.
450 488 self.remove_pid_file()
451 489
490
452 491 def launch_new_instance():
453 492 """Create and run the IPython cluster."""
454 493 app = IPClusterApp()
@@ -18,20 +18,40 b' The IPython controller application.'
18 18 from __future__ import with_statement
19 19
20 20 import copy
21 import os
22 21 import sys
23 22
24 23 from twisted.application import service
25 24 from twisted.internet import reactor
26 25 from twisted.python import log
27 26
28 from IPython.config.loader import Config, NoConfigDefault
29 from IPython.core import release
30 from IPython.core.application import Application
27 from IPython.config.loader import Config
31 28 from IPython.kernel import controllerservice
32 from IPython.kernel.clusterdir import ApplicationWithClusterDir
33 from IPython.kernel.fcutil import FCServiceFactory
34 from IPython.utils.traitlets import Str, Instance, Unicode
29 from IPython.kernel.clusterdir import (
30 ApplicationWithClusterDir,
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 57 # Default interfaces
@@ -92,25 +112,27 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):
120
121 def _add_arguments(self):
122 super(IPControllerAppConfigLoader, self)._add_arguments()
123 paa = self.parser.add_argument
100 124 # Client config
101 (('--client-ip',), dict(
125 paa('--client-ip',
102 126 type=str, dest='FCClientServiceFactory.ip',
103 127 help='The IP address or hostname the controller will listen on for '
104 128 'client connections.',
105 129 metavar='FCClientServiceFactory.ip')
106 ),
107 (('--client-port',), dict(
130 paa('--client-port',
108 131 type=int, dest='FCClientServiceFactory.port',
109 132 help='The port the controller will listen on for client connections. '
110 133 'The default is to use 0, which will autoselect an open port.',
111 134 metavar='FCClientServiceFactory.port')
112 ),
113 (('--client-location',), dict(
135 paa('--client-location',), dict(
114 136 type=str, dest='FCClientServiceFactory.location',
115 137 help='The hostname or IP that clients should connect to. This does '
116 138 'not control which interface the controller listens on. Instead, this '
@@ -118,21 +140,18 b' cl_args = ('
118 140 'clients know where to connect. Useful if the controller is listening '
119 141 'on multiple interfaces.',
120 142 metavar='FCClientServiceFactory.location')
121 ),
122 143 # Engine config
123 (('--engine-ip',), dict(
144 paa('--engine-ip',
124 145 type=str, dest='FCEngineServiceFactory.ip',
125 146 help='The IP address or hostname the controller will listen on for '
126 147 'engine connections.',
127 148 metavar='FCEngineServiceFactory.ip')
128 ),
129 (('--engine-port',), dict(
149 paa('--engine-port',
130 150 type=int, dest='FCEngineServiceFactory.port',
131 151 help='The port the controller will listen on for engine connections. '
132 152 'The default is to use 0, which will autoselect an open port.',
133 153 metavar='FCEngineServiceFactory.port')
134 ),
135 (('--engine-location',), dict(
154 paa('--engine-location',
136 155 type=str, dest='FCEngineServiceFactory.location',
137 156 help='The hostname or IP that engines should connect to. This does '
138 157 'not control which interface the controller listens on. Instead, this '
@@ -140,60 +159,49 b' cl_args = ('
140 159 'engines know where to connect. Useful if the controller is listening '
141 160 'on multiple interfaces.',
142 161 metavar='FCEngineServiceFactory.location')
143 ),
144 162 # Global config
145 (('--log-to-file',), dict(
163 paa('--log-to-file',
146 164 action='store_true', dest='Global.log_to_file',
147 165 help='Log to a file in the log directory (default is stdout)')
148 ),
149 (('-r','--reuse-furls'), dict(
166 paa('-r','--reuse-furls',
150 167 action='store_true', dest='Global.reuse_furls',
151 168 help='Try to reuse all FURL files. If this is not set all FURL files '
152 169 'are deleted before the controller starts. This must be set if '
153 170 'specific ports are specified by --engine-port or --client-port.')
154 ),
155 (('--no-secure',), dict(
171 paa('--no-secure',
156 172 action='store_false', dest='Global.secure',
157 173 help='Turn off SSL encryption for all connections.')
158 ),
159 (('--secure',), dict(
174 paa('--secure',
160 175 action='store_true', dest='Global.secure',
161 176 help='Turn off SSL encryption for all connections.')
162 )
163 )
164 177
165 178
166 _description = """Start the IPython controller for parallel computing.
167
168 The IPython controller provides a gateway between the IPython engines and
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'
179 #-----------------------------------------------------------------------------
180 # The main application
181 #-----------------------------------------------------------------------------
177 182
178 183
179 184 class IPControllerApp(ApplicationWithClusterDir):
180 185
181 186 name = u'ipcontroller'
182 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 190 auto_create_cluster_dir = True
185 cl_arguments = Application.cl_arguments + cl_args
186 191
187 192 def create_default_config(self):
188 193 super(IPControllerApp, self).create_default_config()
189 self.default_config.Global.reuse_furls = False
190 self.default_config.Global.secure = True
194 # Don't set defaults for Global.secure or Global.reuse_furls
195 # as those are set in a component.
191 196 self.default_config.Global.import_statements = []
192 197 self.default_config.Global.clean_logs = True
193 198
194 def post_load_command_line_config(self):
195 # Now setup reuse_furls
196 c = self.command_line_config
199 def pre_construct(self):
200 super(IPControllerApp, self).pre_construct()
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 205 if hasattr(c.Global, 'reuse_furls'):
198 206 c.FCClientServiceFactory.reuse_furls = c.Global.reuse_furls
199 207 c.FCEngineServiceFactory.reuse_furls = c.Global.reuse_furls
@@ -216,11 +224,19 b' class IPControllerApp(ApplicationWithClusterDir):'
216 224 controller_service = controllerservice.ControllerService()
217 225 controller_service.setServiceParent(self.main_service)
218 226 # The client tub and all its refereceables
227 try:
219 228 csfactory = FCClientServiceFactory(self.master_config, controller_service)
229 except FURLError, e:
230 log.err(e)
231 self.exit(0)
220 232 client_service = csfactory.create()
221 233 client_service.setServiceParent(self.main_service)
222 234 # The engine tub
235 try:
223 236 esfactory = FCEngineServiceFactory(self.master_config, controller_service)
237 except FURLError, e:
238 log.err(e)
239 self.exit(0)
224 240 engine_service = esfactory.create()
225 241 engine_service.setServiceParent(self.main_service)
226 242
@@ -22,39 +22,21 b' from twisted.application import service'
22 22 from twisted.internet import reactor
23 23 from twisted.python import log
24 24
25 from IPython.core.application import Application
26 from IPython.kernel.clusterdir import ApplicationWithClusterDir
25 from IPython.kernel.clusterdir import (
26 ApplicationWithClusterDir,
27 ClusterDirConfigLoader
28 )
27 29 from IPython.kernel.engineconnector import EngineConnector
28 30 from IPython.kernel.engineservice import EngineService
29 31 from IPython.kernel.fcutil import Tub
30 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 = (
37 # Controller config
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 )
38 #: The default config file name for this application
39 default_config_file_name = u'ipengine_config.py'
58 40
59 41
60 42 mpi4py_init = """from mpi4py import MPI as mpi
@@ -62,6 +44,7 b' mpi.size = mpi.COMM_WORLD.Get_size()'
62 44 mpi.rank = mpi.COMM_WORLD.Get_rank()
63 45 """
64 46
47
65 48 pytrilinos_init = """from PyTrilinos import Epetra
66 49 class SimpleStruct:
67 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 57 _description = """Start an IPython engine for parallel computing.\n\n
78 58
79 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 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 101 class IPEngineApp(ApplicationWithClusterDir):
89 102
90 103 name = u'ipengine'
91 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 107 auto_create_cluster_dir = True
94 cl_arguments = Application.cl_arguments + cl_args
95 108
96 109 def create_default_config(self):
97 110 super(IPEngineApp, self).create_default_config()
@@ -21,11 +21,15 b' import sys'
21 21
22 22 from IPython.core.component import Component
23 23 from IPython.external import Itpl
24 from IPython.utils.traitlets import Str, Int, List, Unicode, Enum
25 from IPython.utils.platutils import find_cmd
26 from IPython.kernel.twistedutil import gatherBoth, make_deferred, sleep_deferred
24 from IPython.utils.traitlets import Str, Int, List, Unicode
25 from IPython.utils.path import get_ipython_module_path
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 32 from IPython.kernel.winhpcjob import (
28 WinHPCJob, WinHPCTask,
29 33 IPControllerTask, IPEngineTask,
30 34 IPControllerJob, IPEngineSetJob
31 35 )
@@ -38,46 +42,23 b' from twisted.internet.error import ProcessDone, ProcessTerminated'
38 42 from twisted.python import log
39 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():
47 """Find the command line ipcontroller program in a cross platform way."""
48 if sys.platform == 'win32':
49 # This logic is needed because the ipcontroller script doesn't
50 # always get installed in the same way or in the same location.
51 from IPython.kernel import ipcontrollerapp
52 script_location = ipcontrollerapp.__file__.replace('.pyc', '.py')
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
51 ipcluster_cmd_argv = pycmd2argv(get_ipython_module_path(
52 'IPython.kernel.ipclusterapp'
53 ))
54
55 ipengine_cmd_argv = pycmd2argv(get_ipython_module_path(
56 'IPython.kernel.ipengineapp'
57 ))
80 58
59 ipcontroller_cmd_argv = pycmd2argv(get_ipython_module_path(
60 'IPython.kernel.ipcontrollerapp'
61 ))
81 62
82 63 #-----------------------------------------------------------------------------
83 64 # Base launchers and errors
@@ -333,7 +314,7 b' class LocalProcessLauncher(BaseLauncher):'
333 314 class LocalControllerLauncher(LocalProcessLauncher):
334 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 318 # Command line arguments to ipcontroller.
338 319 controller_args = List(['--log-to-file','--log-level', '40'], config=True)
339 320
@@ -351,7 +332,7 b' class LocalControllerLauncher(LocalProcessLauncher):'
351 332 class LocalEngineLauncher(LocalProcessLauncher):
352 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 336 # Command line arguments for ipengine.
356 337 engine_args = List(
357 338 ['--log-to-file','--log-level', '40'], config=True
@@ -462,7 +443,7 b' class MPIExecLauncher(LocalProcessLauncher):'
462 443 class MPIExecControllerLauncher(MPIExecLauncher):
463 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 447 # Command line arguments to ipcontroller.
467 448 controller_args = List(['--log-to-file','--log-level', '40'], config=True)
468 449 n = Int(1, config=False)
@@ -481,7 +462,7 b' class MPIExecControllerLauncher(MPIExecLauncher):'
481 462
482 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 466 # Command line arguments for ipengine.
486 467 engine_args = List(
487 468 ['--log-to-file','--log-level', '40'], config=True
@@ -557,7 +538,10 b' class SSHEngineSetLauncher(BaseLauncher):'
557 538 # This is only used on Windows.
558 539 def find_job_cmd():
559 540 if os.name=='nt':
541 try:
560 542 return find_cmd('job')
543 except FindCmdError:
544 return 'job'
561 545 else:
562 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 818 class IPClusterLauncher(LocalProcessLauncher):
853 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 822 # Command line arguments to pass to ipcluster.
857 823 ipcluster_args = List(
858 824 ['--clean-logs', '--log-to-file', '--log-level', '40'], config=True)
@@ -21,7 +21,7 b' __docformat__ = "restructuredtext en"'
21 21
22 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 27 # Figure out which array packages are present and their array types
@@ -87,7 +87,7 b' class Map:'
87 87 return m['module'].concatenate(listOfPartitions)
88 88 # Next try for Python sequence types
89 89 if isinstance(testObject, (types.ListType, types.TupleType)):
90 return genutil_flatten(listOfPartitions)
90 return utils_flatten(listOfPartitions)
91 91 # If we have scalars, just return listOfPartitions
92 92 return listOfPartitions
93 93
@@ -18,8 +18,7 b' __docformat__ = "restructuredtext en"'
18 18 from types import FunctionType
19 19 from zope.interface import Interface, implements
20 20 from IPython.kernel.task import MapTask
21 from IPython.kernel.twistedutil import DeferredList, gatherBoth
22 from IPython.kernel.util import printer
21 from IPython.kernel.twistedutil import gatherBoth
23 22 from IPython.kernel.error import collect_exceptions
24 23
25 24 #----------------------------------------------------------------------------
@@ -27,24 +27,17 b' __docformat__ = "restructuredtext en"'
27 27 # Imports
28 28 #-------------------------------------------------------------------------------
29 29
30 from new import instancemethod
31 from types import FunctionType
32
33 from twisted.application import service
34 30 from twisted.internet import defer, reactor
35 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 34 from IPython.kernel.twistedutil import gatherBoth
41 from IPython.kernel import map as Map
42 35 from IPython.kernel import error
43 36 from IPython.kernel.pendingdeferred import PendingDeferredManager, two_phase
44 from IPython.kernel.controllerservice import \
45 ControllerAdapterBase, \
46 ControllerService, \
37 from IPython.kernel.controllerservice import (
38 ControllerAdapterBase,
47 39 IControllerBase
40 )
48 41
49 42
50 43 #-------------------------------------------------------------------------------
@@ -17,13 +17,17 b' __docformat__ = "restructuredtext en"'
17 17 #-------------------------------------------------------------------------------
18 18
19 19 import sys
20 import linecache
21 20 import warnings
22 21
23 22 from twisted.python import components
24 23 from twisted.python.failure import Failure
25 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 31 from IPython.utils.coloransi import TermColors
28 32
29 33 from IPython.kernel.twistedutil import blockingCallFromThread
@@ -307,85 +311,6 b' class InteractiveMultiEngineClient(object):'
307 311 """Return the number of available engines."""
308 312 return len(self.get_ids())
309 313
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
389 314
390 315 #-------------------------------------------------------------------------------
391 316 # The top-level MultiEngine client adaptor
@@ -445,17 +370,30 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
445 370 def _findTargetsAndBlock(self, targets=None, block=None):
446 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 386 def _blockFromThread(self, function, *args, **kwargs):
449 387 block = kwargs.get('block', None)
450 388 if block is None:
451 389 raise error.MissingBlockArgument("'block' keyword argument is missing")
452 result = blockingCallFromThread(function, *args, **kwargs)
390 result = self._bcft(function, *args, **kwargs)
453 391 if not block:
454 392 result = PendingResult(self, result)
455 393 return result
456 394
457 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 398 def barrier(self, pendingResults):
461 399 """Synchronize a set of `PendingResults`.
@@ -505,7 +443,7 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
505 443 controller. This method allows the user to clear out all un-retrieved
506 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 447 return r
510 448
511 449 clear_pending_results = flush
@@ -529,7 +467,7 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
529 467 at a later time.
530 468 """
531 469 targets, block = self._findTargetsAndBlock(targets, block)
532 result = blockingCallFromThread(self.smultiengine.execute, lines,
470 result = self._bcft(self.smultiengine.execute, lines,
533 471 targets=targets, block=block)
534 472 if block:
535 473 result = ResultList(result)
@@ -647,7 +585,7 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
647 585 at a later time.
648 586 """
649 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 589 if block:
652 590 result = ResultList(result)
653 591 else:
@@ -773,7 +711,7 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
773 711 """
774 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 715 return result
778 716
779 717 #---------------------------------------------------------------------------
@@ -22,12 +22,14 b' from types import FunctionType'
22 22
23 23 from zope.interface import Interface, implements
24 24 from twisted.internet import defer
25 from twisted.python import components, failure, log
25 from twisted.python import components, failure
26 26
27 try:
28 from foolscap.api import Referenceable
29 except ImportError:
27 30 from foolscap import Referenceable
28 31
29 32 from IPython.kernel import error
30 from IPython.kernel.util import printer
31 33 from IPython.kernel import map as Map
32 34 from IPython.kernel.parallelfunction import ParallelFunction
33 35 from IPython.kernel.mapper import (
@@ -36,14 +38,15 b' from IPython.kernel.mapper import ('
36 38 IMapper
37 39 )
38 40 from IPython.kernel.twistedutil import gatherBoth
39 from IPython.kernel.multiengine import (MultiEngine,
41 from IPython.kernel.multiengine import (
40 42 IMultiEngine,
41 43 IFullSynchronousMultiEngine,
42 44 ISynchronousMultiEngine)
43 from IPython.kernel.multiengineclient import wrapResultList
44 45 from IPython.kernel.pendingdeferred import PendingDeferredManager
45 from IPython.kernel.pickleutil import (can, canDict,
46 canSequence, uncan, uncanDict, uncanSequence)
46 from IPython.kernel.pickleutil import (
47 canDict,
48 canSequence, uncanDict, uncanSequence
49 )
47 50
48 51 from IPython.kernel.clientinterfaces import (
49 52 IFCClientInterfaceProvider,
@@ -19,7 +19,6 b' import cPickle as pickle'
19 19
20 20 from twisted.python.failure import Failure
21 21 from twisted.python import failure
22 import threading, sys
23 22
24 23 from IPython.kernel import pbconfig
25 24 from IPython.kernel.error import PBMessageSizeError, UnpickleableException
@@ -58,7 +57,7 b' def unpackageFailure(r):'
58 57 result = pickle.loads(r[8:])
59 58 except pickle.PickleError:
60 59 return failure.Failure( \
61 FailureUnpickleable("Could not unpickle failure."))
60 UnpickleableException("Could not unpickle failure."))
62 61 else:
63 62 return result
64 63 return r
@@ -22,15 +22,11 b' __docformat__ = "restructuredtext en"'
22 22 # Imports
23 23 #-------------------------------------------------------------------------------
24 24
25 from twisted.application import service
26 from twisted.internet import defer, reactor
27 from twisted.python import log, components, failure
28 from zope.interface import Interface, implements, Attribute
25 from twisted.internet import defer
26 from twisted.python import failure
29 27
30 from IPython.kernel.twistedutil import gatherBoth
31 28 from IPython.kernel import error
32 29 from IPython.external import guid
33 from IPython.utils import growl
34 30
35 31 class PendingDeferredManager(object):
36 32 """A class to track pending deferreds.
@@ -16,7 +16,6 b' __docformat__ = "restructuredtext en"'
16 16 #-------------------------------------------------------------------------------
17 17
18 18 from types import FunctionType
19 from twisted.python import log
20 19
21 20 class CannedObject(object):
22 21 pass
@@ -19,19 +19,18 b' __docformat__ = "restructuredtext en"'
19 19 # Tell nose to skip the testing of this module
20 20 __test__ = {}
21 21
22 import copy, time
22 import time
23 23 from types import FunctionType
24 24
25 import zope.interface as zi, string
25 import zope.interface as zi
26 26 from twisted.internet import defer, reactor
27 27 from twisted.python import components, log, failure
28 28
29 from IPython.kernel.util import printer
30 29 from IPython.kernel import engineservice as es, error
31 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 36 # Definition of the Task objects
@@ -19,7 +19,12 b' __docformat__ = "restructuredtext en"'
19 19 #-------------------------------------------------------------------------------
20 20
21 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 29 from IPython.kernel.twistedutil import blockingCallFromThread
25 30 from IPython.kernel import task, error
@@ -59,6 +64,19 b' class BlockingTaskClient(object):'
59 64 self.task_controller = task_controller
60 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 80 def run(self, task, block=False):
63 81 """Run a task on the `TaskController`.
64 82
@@ -71,7 +89,7 b' class BlockingTaskClient(object):'
71 89 :Returns: The int taskid of the submitted task. Pass this to
72 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 93 if block:
76 94 return self.get_task_result(tid, block=True)
77 95 else:
@@ -89,7 +107,7 b' class BlockingTaskClient(object):'
89 107
90 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 111 taskid, block)
94 112
95 113 def abort(self, taskid):
@@ -100,7 +118,7 b' class BlockingTaskClient(object):'
100 118 taskid : int
101 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 123 def barrier(self, taskids):
106 124 """Block until a set of tasks are completed.
@@ -109,7 +127,7 b' class BlockingTaskClient(object):'
109 127 taskids : list, tuple
110 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 132 def spin(self):
115 133 """
@@ -118,7 +136,7 b' class BlockingTaskClient(object):'
118 136 This method only needs to be called in unusual situations where the
119 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 141 def queue_status(self, verbose=False):
124 142 """
@@ -132,7 +150,7 b' class BlockingTaskClient(object):'
132 150 :Returns:
133 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 155 def clear(self):
138 156 """
@@ -143,7 +161,7 b' class BlockingTaskClient(object):'
143 161 tasks. Users should call this periodically to clean out these
144 162 cached task results.
145 163 """
146 return blockingCallFromThread(self.task_controller.clear)
164 return self._bcft(self.task_controller.clear)
147 165
148 166 def map(self, func, *sequences):
149 167 """
@@ -19,17 +19,17 b' __docformat__ = "restructuredtext en"'
19 19 #-------------------------------------------------------------------------------
20 20
21 21 import cPickle as pickle
22 import xmlrpclib, copy
23 22
24 23 from zope.interface import Interface, implements
25 24 from twisted.internet import defer
26 from twisted.python import components, failure
25 from twisted.python import components
27 26
27 try:
28 from foolscap.api import Referenceable
29 except ImportError:
28 30 from foolscap import Referenceable
29 31
30 from IPython.kernel.twistedutil import blockingCallFromThread
31 from IPython.kernel import error, task as taskmodule, taskclient
32 from IPython.kernel.pickleutil import can, uncan
32 from IPython.kernel import task as taskmodule
33 33 from IPython.kernel.clientinterfaces import (
34 34 IFCClientInterfaceProvider,
35 35 IBlockingClientAdaptor
@@ -53,8 +53,8 b' class EngineFCTest(DeferredTestCase,'
53 53 # Start a server and append to self.servers
54 54 self.controller_reference = FCRemoteEngineRefFromService(self)
55 55 self.controller_tub = Tub()
56 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
57 self.controller_tub.setLocation('127.0.0.1:10105')
56 self.controller_tub.listenOn('tcp:10111:interface=127.0.0.1')
57 self.controller_tub.setLocation('127.0.0.1:10111')
58 58
59 59 furl = self.controller_tub.registerReference(self.controller_reference)
60 60 self.controller_tub.startService()
@@ -27,7 +27,7 b' from IPython.kernel.multiengine import IMultiEngine'
27 27 from IPython.kernel.tests.multienginetest import IFullSynchronousMultiEngineTestCase
28 28 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
29 29 from IPython.kernel import multiengine as me
30 from IPython.kernel.clientconnector import ClientConnector
30 from IPython.kernel.clientconnector import AsyncClientConnector
31 31 from IPython.kernel.parallelfunction import ParallelFunction
32 32 from IPython.kernel.error import CompositeError
33 33 from IPython.kernel.util import printer
@@ -40,15 +40,8 b' def _raise_it(f):'
40 40 e.raise_exception()
41 41
42 42
43 class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMultiEngineTestCase):
44
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
43 class FullSynchronousMultiEngineTestCase(
44 DeferredTestCase, IFullSynchronousMultiEngineTestCase):
52 45
53 46 def setUp(self):
54 47
@@ -60,14 +53,14 b' class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMulti'
60 53 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
61 54
62 55 self.controller_tub = Tub()
63 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
64 self.controller_tub.setLocation('127.0.0.1:10105')
56 self.controller_tub.listenOn('tcp:10111:interface=127.0.0.1')
57 self.controller_tub.setLocation('127.0.0.1:10111')
65 58
66 59 furl = self.controller_tub.registerReference(self.mec_referenceable)
67 60 self.controller_tub.startService()
68 61
69 self.client_tub = ClientConnector()
70 d = self.client_tub.get_multiengine_client(furl)
62 self.client_tub = AsyncClientConnector()
63 d = self.client_tub.get_multiengine_client(furl_or_file=furl)
71 64 d.addCallback(self.handle_got_client)
72 65 return d
73 66
@@ -150,3 +143,4 b' class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMulti'
150 143 d = f(range(10))
151 144 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
152 145 return d
146
@@ -31,7 +31,7 b' from IPython.kernel.multienginefc import IFCSynchronousMultiEngine'
31 31 from IPython.kernel.taskfc import IFCTaskController
32 32 from IPython.kernel.util import printer
33 33 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
34 from IPython.kernel.clientconnector import ClientConnector
34 from IPython.kernel.clientconnector import AsyncClientConnector
35 35 from IPython.kernel.error import CompositeError
36 36 from IPython.kernel.parallelfunction import ParallelFunction
37 37
@@ -48,14 +48,6 b' def _raise_it(f):'
48 48
49 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 51 def setUp(self):
60 52
61 53 self.engines = []
@@ -70,17 +62,17 b' class TaskTest(DeferredTestCase, ITaskControllerTestCase):'
70 62 self.tc_referenceable = IFCTaskController(self.itc)
71 63
72 64 self.controller_tub = Tub()
73 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
74 self.controller_tub.setLocation('127.0.0.1:10105')
65 self.controller_tub.listenOn('tcp:10111:interface=127.0.0.1')
66 self.controller_tub.setLocation('127.0.0.1:10111')
75 67
76 68 mec_furl = self.controller_tub.registerReference(self.mec_referenceable)
77 69 tc_furl = self.controller_tub.registerReference(self.tc_referenceable)
78 70 self.controller_tub.startService()
79 71
80 self.client_tub = ClientConnector()
81 d = self.client_tub.get_multiengine_client(mec_furl)
72 self.client_tub = AsyncClientConnector()
73 d = self.client_tub.get_multiengine_client(furl_or_file=mec_furl)
82 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 76 d.addCallback(self.handle_tc_client)
85 77 return d
86 78
@@ -167,3 +159,4 b' class TaskTest(DeferredTestCase, ITaskControllerTestCase):'
167 159 d = f(range(10))
168 160 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
169 161 return d
162
@@ -15,7 +15,7 b''
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import os, sys
18 import threading, Queue, atexit
18 import threading, Queue
19 19
20 20 import twisted
21 21 from twisted.internet import defer, reactor
@@ -23,12 +23,10 b' import re'
23 23 import uuid
24 24
25 25 from xml.etree import ElementTree as ET
26 from xml.dom import minidom
27 26
28 27 from IPython.core.component import Component
29 from IPython.external import Itpl
30 28 from IPython.utils.traitlets import (
31 Str, Int, List, Unicode, Instance,
29 Str, Int, List, Instance,
32 30 Enum, Bool, CStr
33 31 )
34 32
@@ -31,7 +31,7 b' import sys'
31 31 import threading
32 32
33 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 36 class BackgroundJobManager:
37 37 """Class to manage a pool of backgrounded threaded jobs.
@@ -1,15 +1,18 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 A module to change reload() so that it acts recursively.
4 To enable it type:
5 >>> import __builtin__, deepreload
6 >>> __builtin__.reload = deepreload.reload
4 To enable it type::
7 5
8 You can then disable it with:
9 >>> __builtin__.reload = deepreload.original_reload
6 import __builtin__, deepreload
7 __builtin__.reload = deepreload.reload
10 8
11 Alternatively, you can add a dreload builtin alongside normal reload with:
12 >>> __builtin__.dreload = deepreload.reload
9 You can then disable it with::
10
11 __builtin__.reload = deepreload.original_reload
12
13 Alternatively, you can add a dreload builtin alongside normal reload with::
14
15 __builtin__.dreload = deepreload.reload
13 16
14 17 This code is almost entirely based on knee.py from the standard library.
15 18 """
@@ -176,7 +176,8 b' import shlex'
176 176 import sys
177 177
178 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 182 __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError']
182 183
@@ -543,7 +544,7 b' class ClearMixin(object):'
543 544 """Method called before executing each block.
544 545
545 546 This one simply clears the screen."""
546 from IPython.utils.platutils import term_clear
547 from IPython.utils.terminal import term_clear
547 548 term_clear()
548 549
549 550 class ClearDemo(ClearMixin,Demo):
@@ -12,10 +12,12 b' Fernando Perez.'
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15
15 16 #-----------------------------------------------------------------------------
16 17 # 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 23 # Main classes and functions
@@ -50,7 +50,7 b' del InteractiveShell,prefilter_shell'
50 50
51 51 # Provide pysh and further shell-oriented services
52 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 55 # Short aliases for getting shell output as a string and a list
56 56 sout = getoutput
@@ -10,6 +10,7 b' var = !ls'
10 10
11 11 from IPython.core import ipapi
12 12 from IPython.core.error import TryNext
13 from IPython.utils.text import make_quoted_expr
13 14 from IPython.utils.genutils import *
14 15
15 16 ip = ipapi.get()
@@ -12,7 +12,7 b' do the same in default completer.'
12 12 from IPython.core import ipapi
13 13 from IPython.core.error import TryNext
14 14 from IPython.utils import generics
15 from IPython.utils.genutils import dir2
15 from IPython.utils.dir2 import dir2
16 16
17 17 def attr_matches(self, text):
18 18 """Compute matches when text contains a dot.
@@ -16,6 +16,7 b' import pickleshare'
16 16 import inspect,pickle,os,sys,textwrap
17 17 from IPython.core.fakemodule import FakeModule
18 18 from IPython.utils.ipstruct import Struct
19 from IPython.utils.warn import error
19 20
20 21
21 22 def refresh_variables(ip, key=None):
@@ -1,6 +1,6 b''
1 1 import inspect
2 2 from IPython.core import ipapi
3 from IPython.utils.genutils import arg_split
3 from IPython.utils.process import arg_split
4 4 ip = ipapi.get()
5 5
6 6 from IPython.core import debugger
@@ -45,10 +45,9 b' from subprocess import *'
45 45 import os,shlex,sys,time
46 46 import threading,Queue
47 47
48 from IPython.utils import genutils
49
50 48 from IPython.core import ipapi
51 49 from IPython.core.error import TryNext
50 from IPython.utils.text import make_quoted_expr
52 51
53 52 if os.name == 'nt':
54 53 def kill_process(pid):
@@ -126,8 +125,8 b' def jobctrl_prefilter_f(self,line):'
126 125
127 126 line = ip.expand_aliases(fn,rest)
128 127 if not _jobq:
129 return 'get_ipython().startjob(%s)' % genutils.make_quoted_expr(line)
130 return 'get_ipython().jobq(%s)' % genutils.make_quoted_expr(line)
128 return 'get_ipython().startjob(%s)' % make_quoted_expr(line)
129 return 'get_ipython().jobq(%s)' % make_quoted_expr(line)
131 130
132 131 raise TryNext
133 132
@@ -1,6 +1,17 b''
1 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 15 # User-level entry point for testing
5 16 def test():
6 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 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 18 from keyword import iskeyword as _iskeyword
8 19 from operator import itemgetter as _itemgetter
9 20 import sys as _sys
@@ -1,10 +1,17 b''
1 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 12 # Imports
5 13 #-----------------------------------------------------------------------------
6 14
7 # Stdlib
8 15 import unittest
9 16 from compiler.consts import CO_GENERATOR
10 17
@@ -3,11 +3,18 b''
3 3 Thanks for the py3 version to Robert Collins, from the Testing in Python
4 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 15 # Imports
8 16 #-----------------------------------------------------------------------------
9 17
10 # Stdlib
11 18 import unittest
12 19 from unittest import TestSuite
13 20
@@ -9,18 +9,22 b' done.'
9 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 22 import __builtin__
17 23 import commands
18 import new
19 24 import os
20 25 import sys
21 26
22 27 from . import tools
23 from IPython.utils.genutils import Term
24 28
25 29 #-----------------------------------------------------------------------------
26 30 # Functions
@@ -35,7 +39,7 b' class py_file_finder(object):'
35 39 self.test_filename = test_filename
36 40
37 41 def __call__(self,name):
38 from IPython.utils.genutils import get_py_filename
42 from IPython.utils.path import get_py_filename
39 43 try:
40 44 return get_py_filename(name)
41 45 except IOError:
@@ -99,6 +103,7 b' def get_ipython():'
99 103 # This will get replaced by the real thing once we start IPython below
100 104 return start_ipython()
101 105
106
102 107 def start_ipython():
103 108 """Start a global IPython shell, which we need for IPython-specific syntax.
104 109 """
@@ -110,7 +115,7 b' def start_ipython():'
110 115 start_ipython.already_called = True
111 116
112 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 120 def xsys(cmd):
116 121 """Execute a command and print its output.
@@ -128,26 +133,27 b' def start_ipython():'
128 133 _main = sys.modules.get('__main__')
129 134
130 135 # Create custom argv and namespaces for our IPython to be test-friendly
131 argv = tools.default_argv()
132 user_ns, global_ns = iplib.make_user_namespaces(ipnsdict(), {})
136 config = tools.default_config()
133 137
134 138 # Create and initialize our test-friendly IPython instance.
135 ip = ipapp.IPythonApp(argv, user_ns=user_ns, user_global_ns=global_ns)
136 ip.initialize()
139 shell = iplib.InteractiveShell(
140 parent=None, config=config,
141 user_ns=ipnsdict(), user_global_ns={}
142 )
137 143
138 144 # A few more tweaks needed for playing nicely with doctests...
139 145
140 146 # These traps are normally only active for interactive use, set them
141 147 # permanently since we'll be mocking interactive sessions.
142 ip.shell.builtin_trap.set()
148 shell.builtin_trap.set()
143 149
144 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 153 # Modify the IPython system call with one that uses getoutput, so that we
148 154 # can capture subcommands and print them to Python's stdout, otherwise the
149 155 # doctest machinery would miss them.
150 ip.shell.system = xsys
156 shell.system = xsys
151 157
152 158 # IPython is ready, now clean up some global state...
153 159
@@ -160,7 +166,7 b' def start_ipython():'
160 166 # So that ipython magics and aliases can be doctested (they work by making
161 167 # a call into a global _ip object). Also make the top-level get_ipython
162 168 # now return this without recursively calling here again.
163 _ip = ip.shell
169 _ip = shell
164 170 get_ipython = _ip.get_ipython
165 171 __builtin__._ip = _ip
166 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 30 # Stdlib
24 31 import os
25 32 import os.path as path
26 import platform
27 33 import signal
28 34 import sys
29 35 import subprocess
@@ -31,15 +37,6 b' import tempfile'
31 37 import time
32 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 40 # Note: monkeypatch!
44 41 # We need to monkeypatch a small problem in nose itself first, before importing
45 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 47 from nose.core import TestProgram
51 48
52 49 # Our own imports
53 from IPython.core import release
54 from IPython.utils import genutils
55 from IPython.utils.platutils import find_cmd, FindCmdError
50 from IPython.utils.path import get_ipython_module_path
51 from IPython.utils.process import find_cmd, pycmd2argv
52 from IPython.utils.sysinfo import sys_info
53
56 54 from IPython.testing import globalipapp
57 from IPython.testing import tools
58 55 from IPython.testing.plugin.ipdoctest import IPythonDoctest
59 56
60 57 pjoin = path.join
@@ -64,16 +61,11 b' pjoin = path.join'
64 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 66 # Warnings control
76 67 #-----------------------------------------------------------------------------
68
77 69 # Twisted generates annoying warnings with Python 2.6, as will do other code
78 70 # that imports 'sets' as of today
79 71 warnings.filterwarnings('ignore', 'the sets module is deprecated',
@@ -124,9 +116,7 b" have['gobject'] = test_for('gobject')"
124 116 def report():
125 117 """Return a string with a summary report of test-related variables."""
126 118
127 out = [ genutils.sys_info() ]
128
129 out.append('\nRunning from an installed IPython: %s\n' % INSTALLED)
119 out = [ sys_info() ]
130 120
131 121 avail = []
132 122 not_avail = []
@@ -155,8 +145,8 b' def make_exclude():'
155 145
156 146 For the IPythonDoctest plugin, we need to exclude certain patterns that
157 147 cause testing problems. We should strive to minimize the number of
158 skipped modules, since this means untested code. As the testing
159 machinery solidifies, this list should eventually become empty.
148 skipped modules, since this means untested code.
149
160 150 These modules and packages will NOT get scanned by nose at all for tests.
161 151 """
162 152 # Simple utility to make IPython paths more readably, we need a lot of
@@ -198,18 +188,12 b' def make_exclude():'
198 188 if not have['objc']:
199 189 exclusions.append(ipjoin('frontend', 'cocoa'))
200 190
201 if not sys.platform == 'win32':
202 exclusions.append(ipjoin('utils', 'platutils_win32'))
203
204 191 # These have to be skipped on win32 because the use echo, rm, cd, etc.
205 192 # See ticket https://bugs.launchpad.net/bugs/366982
206 193 if sys.platform == 'win32':
207 194 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
208 195 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
209 196
210 if not os.name == 'posix':
211 exclusions.append(ipjoin('utils', 'platutils_posix'))
212
213 197 if not have['pexpect']:
214 198 exclusions.extend([ipjoin('scripts', 'irunner'),
215 199 ipjoin('lib', 'irunner')])
@@ -255,20 +239,13 b' class IPTester(object):'
255 239 """Create new test runner."""
256 240 p = os.path
257 241 if runner == 'iptest':
258 if INSTALLED:
259 self.runner = tools.cmd2argv(
260 p.abspath(find_cmd('iptest'))) + sys.argv[1:]
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:
242 iptest_app = get_ipython_module_path('IPython.testing.iptest')
243 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
244 elif runner == 'trial':
270 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 249 if params is None:
273 250 params = []
274 251 if isinstance(params, str):
@@ -290,6 +267,8 b' class IPTester(object):'
290 267 # fashioned' way to do it, but it works just fine. If someone
291 268 # later can clean this up that's fine, as long as the tests run
292 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 272 return os.system(' '.join(self.call_args))
294 273 else:
295 274 def _run_cmd(self):
@@ -343,9 +322,9 b' def make_runners():'
343 322
344 323 # And add twisted ones if conditions are met
345 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
347 # twisted-based, because nose picks up doctests that twisted doesn't.
348 nose_pkg_names.append('kernel')
325 # We only list IPython.kernel for testing using twisted.trial as
326 # nose and twisted.trial have conflicts that make the testing system
327 # unstable.
349 328 trial_pkg_names.append('kernel')
350 329
351 330 # For debugging this code, only load quick stuff
@@ -31,7 +31,6 b' from __future__ import absolute_import'
31 31 # the file COPYING, distributed as part of this software.
32 32 #-----------------------------------------------------------------------------
33 33
34
35 34 #-----------------------------------------------------------------------------
36 35 # Imports
37 36 #-----------------------------------------------------------------------------
@@ -38,7 +38,7 b' import tempfile'
38 38
39 39 # IPython-specific libraries
40 40 from IPython.lib import irunner
41 from IPython.utils.genutils import fatal
41 from IPython.utils.warn import fatal
42 42
43 43 class IndentOut(object):
44 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 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 19 import unittest
9 20 import nose.loader
10 21 from inspect import ismethod, isfunction
11 22
23 #-----------------------------------------------------------------------------
24 # Classes and functions
25 #-----------------------------------------------------------------------------
26
12 27 def getTestCaseNames(self, testCaseClass):
13 28 """Override to select with selector, unless
14 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 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 20 from twisted.trial.unittest import TestCase
11 21
22 #-----------------------------------------------------------------------------
23 # Classes and functions
24 #-----------------------------------------------------------------------------
25
26 __all__ = ['parametric','Parametric']
27
28
12 29 def partial(f, *partial_args, **partial_kwargs):
13 30 """Generate a partial class method.
14 31
@@ -20,6 +37,7 b' def partial(f, *partial_args, **partial_kwargs):'
20 37
21 38 return partial_func
22 39
40
23 41 def parametric(f):
24 42 """Mark f as a parametric test.
25 43
@@ -27,6 +45,7 b' def parametric(f):'
27 45 f._parametric = True
28 46 return classmethod(f)
29 47
48
30 49 def Parametric(cls):
31 50 """Register parametric tests with a class.
32 51
@@ -56,3 +75,4 b' def Parametric(cls):'
56 75
57 76 # rename test generator so it isn't called again by nose
58 77 test_gen.im_func.func_name = '__done_' + test_name
78
@@ -1,6 +1,6 b''
1 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 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 18 __test__ = {}
19 19
20 20 import os
21 21 import sys
22 22
23 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 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 30 class TestDecoratorsTrial(unittest.TestCase):
36 31
37 32 @dec.skip()
@@ -15,22 +15,22 b' Authors'
15 15 - Fernando Perez <Fernando.Perez@berkeley.edu>
16 16 """
17 17
18 #*****************************************************************************
18 from __future__ import absolute_import
19
20 #-----------------------------------------------------------------------------
19 21 # Copyright (C) 2009 The IPython Development Team
20 22 #
21 23 # Distributed under the terms of the BSD License. The full license is in
22 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 31 import os
31 32 import re
32 33 import sys
33 import tempfile
34 34
35 35 try:
36 36 # These tools are used by parts of the runtime, so we make the nose
@@ -41,7 +41,10 b' try:'
41 41 except ImportError:
42 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 49 from . import decorators as dec
47 50
@@ -49,13 +52,6 b' from . import decorators as dec'
49 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 55 # Make a bunch of nose.tools assert wrappers that can be used in test
60 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 103 ['/a.txt']
108 104 """
109 105
110 files = genutils.list_strings(files)
106 files = list_strings(files)
111 107 base = os.path.split(startPath)[0]
112 108 return [ os.path.join(base,f) for f in files ]
113 109
@@ -156,63 +152,6 b' def parse_test_output(txt):'
156 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 155 def default_argv():
217 156 """Return a valid default argv for creating testing instances of ipython"""
218 157
@@ -222,6 +161,15 b' def default_argv():'
222 161 '--autocall=0']
223 162
224 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
172
225 173 def ipexec(fname, options=None):
226 174 """Utility to call 'ipython filename'.
227 175
@@ -252,20 +200,12 b' def ipexec(fname, options=None):'
252 200 _ip = get_ipython()
253 201 test_dir = os.path.dirname(__file__)
254 202
255 # Find the ipython script from the package we're using, so that the test
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
203 ipython_cmd = find_cmd('ipython')
264 204 # Absolute path for filename
265 full_fname = p.join(test_dir, fname)
205 full_fname = os.path.join(test_dir, fname)
266 206 full_cmd = '%s %s %s' % (ipython_cmd, cmdargs, full_fname)
267 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 211 def ipexec_validate(fname, expected_out, expected_err='',
@@ -1,23 +1,24 b''
1 1 # encoding: utf-8
2 2 """This file contains utility classes for performing tests with Deferreds.
3 3 """
4 __docformat__ = "restructuredtext en"
5 #-------------------------------------------------------------------------------
6 # Copyright (C) 2005 Fernando Perez <fperez@colorado.edu>
7 # Brian E Granger <ellisonbg@gmail.com>
8 # Benjamin Ragan-Kelley <benjaminrk@gmail.com>
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2009 The IPython Development Team
9 6 #
10 7 # Distributed under the terms of the BSD License. The full license is in
11 8 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
13 10
14 #-------------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
15 12 # Imports
16 #-------------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
17 14
18 15 from twisted.trial import unittest
19 16 from twisted.internet import defer
20 17
18 #-----------------------------------------------------------------------------
19 # Classes and functions
20 #-----------------------------------------------------------------------------
21
21 22 class DeferredTestCase(unittest.TestCase):
22 23
23 24 def assertDeferredEquals(self, deferred, expectedResult,
@@ -1,8 +1,9 b''
1 # encoding: utf-8
1 2 """Generic functions for extending IPython.
2 3
3 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 8 def print_lsstring(arg):
8 9 "Prettier (non-repr-like) and more informative printer for LSString"
@@ -12,19 +13,37 b' Here is an example from genutils.py::'
12 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 27 from IPython.core.error import TryNext
16 28 from IPython.external.simplegeneric import generic
17 29
30 #-----------------------------------------------------------------------------
31 # Imports
32 #-----------------------------------------------------------------------------
33
34
18 35 @generic
19 36 def result_display(result):
20 37 """Print the result of computation."""
21 38 raise TryNext
22 39
40
23 41 @generic
24 42 def inspect_object(obj):
25 43 """Called when you do obj?"""
26 44 raise TryNext
27 45
46
28 47 @generic
29 48 def complete_object(obj, prev_completions):
30 49 """Custom completer dispatching for python objects.
@@ -41,3 +60,5 b' def complete_object(obj, prev_completions):'
41 60 own_attrs + prev_completions.
42 61 """
43 62 raise TryNext
63
64
@@ -1,6 +1,22 b''
1 #!/usr/bin/env python
2 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 21 class IPythonGrowlError(Exception):
6 22 pass
@@ -1,4 +1,3 b''
1 #!/usr/bin/env python
2 1 # encoding: utf-8
3 2 """
4 3 A simple utility to import something by its string name.
@@ -19,9 +19,7 b' Authors:'
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 import pprint
23
24 from IPython.utils.genutils import list2dict2
22 from IPython.utils.data import list2dict2
25 23
26 24 __all__ = ['Struct']
27 25
@@ -37,7 +37,6 b' from IPython.external.path import path as Path'
37 37 import os,stat,time
38 38 import cPickle as pickle
39 39 import UserDict
40 import warnings
41 40 import glob
42 41
43 42 def gethashfile(key):
@@ -1,10 +1,12 b''
1 1 # -*- coding: utf-8 -*-
2 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 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 12 import sys
@@ -19,9 +19,12 b' Authors:'
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22
23 22 import sys
24 23
24 #-----------------------------------------------------------------------------
25 # Code
26 #-----------------------------------------------------------------------------
27
25 28 class appended_to_syspath(object):
26 29 """A context for appending a directory to sys.path for a second."""
27 30
@@ -3,21 +3,12 b''
3 3 def test_import_coloransi():
4 4 from IPython.utils import coloransi
5 5
6 def test_import_DPyGetOpt():
7 from IPython.utils import DPyGetOpt
8
9 6 def test_import_generics():
10 7 from IPython.utils import generics
11 8
12 def test_import_genutils():
13 from IPython.utils import genutils
14
15 9 def test_import_ipstruct():
16 10 from IPython.utils import ipstruct
17 11
18 def test_import_platutils():
19 from IPython.utils import platutils
20
21 12 def test_import_PyColorize():
22 13 from IPython.utils import PyColorize
23 14
@@ -33,5 +24,3 b' def test_import_upgradedir():'
33 24 def test_import_wildcard():
34 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 16 import unittest
17 17
18 from IPython.utils.notification import (
19 NotificationCenter,
20 NotificationError,
21 shared_center
22 )
18 from IPython.utils.notification import shared_center
23 19
24 20 #-----------------------------------------------------------------------------
25 21 # Support Classes
@@ -1,8 +1,5 b''
1 1 # encoding: utf-8
2
3 """Tests for genutils.py"""
4
5 __docformat__ = "restructuredtext en"
2 """Tests for IPython.utils.path.py"""
6 3
7 4 #-----------------------------------------------------------------------------
8 5 # Copyright (C) 2008 The IPython Development Team
@@ -15,27 +12,21 b' __docformat__ = "restructuredtext en"'
15 12 # Imports
16 13 #-----------------------------------------------------------------------------
17 14
18 # stdlib
19 15 import os
20 16 import shutil
21 17 import sys
22 18 import tempfile
23 import unittest
24 19
25 from cStringIO import StringIO
26 20 from os.path import join, abspath, split
27 21
28 # third-party
29 22 import nose.tools as nt
30 23
31 24 from nose import with_setup
32 from nose.tools import raises
33 25
34 # Our own
35 26 import IPython
36 from IPython.utils import genutils
37 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 31 # Platform-dependent imports
41 32 try:
@@ -69,6 +60,7 b' def setup():'
69 60 # problem because that exception is only defined on Windows...
70 61 os.makedirs(IP_TEST_DIR)
71 62
63
72 64 def teardown():
73 65 """Teardown testenvironment for the module:
74 66
@@ -88,7 +80,7 b' def setup_environment():'
88 80 each testfunction needs a pristine environment.
89 81 """
90 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 85 if os.name == 'nt':
94 86 platformstuff = (wreg.OpenKey, wreg.QueryValueEx,)
@@ -97,7 +89,7 b' def setup_environment():'
97 89 def teardown_environment():
98 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 94 for key in env.keys():
103 95 if key not in oldenv:
@@ -112,10 +104,6 b' def teardown_environment():'
112 104 with_environment = with_setup(setup_environment, teardown_environment)
113 105
114 106
115 #
116 # Tests for get_home_dir
117 #
118
119 107 @skip_if_not_win32
120 108 @with_environment
121 109 def test_get_home_dir_1():
@@ -126,9 +114,10 b' def test_get_home_dir_1():'
126 114 #fake filename for IPython.__init__
127 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 118 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
131 119
120
132 121 @skip_if_not_win32
133 122 @with_environment
134 123 def test_get_home_dir_2():
@@ -138,61 +127,78 b' def test_get_home_dir_2():'
138 127 #fake filename for IPython.__init__
139 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 131 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower())
143 132
133
144 134 @with_environment
135 @skip_win32
145 136 def test_get_home_dir_3():
146 137 """Testcase $HOME is set, then use its value as home directory."""
147 138 env["HOME"] = HOME_TEST_DIR
148 home_dir = genutils.get_home_dir()
139 home_dir = path.get_home_dir()
149 140 nt.assert_equal(home_dir, env["HOME"])
150 141
142
151 143 @with_environment
152 144 def test_get_home_dir_4():
153 """Testcase $HOME is not set, os=='poix'.
145 """Testcase $HOME is not set, os=='posix'.
154 146 This should fail with HomeDirError"""
155 147
156 148 os.name = 'posix'
157 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)
151
159 152
160 153 @skip_if_not_win32
161 154 @with_environment
162 155 def test_get_home_dir_5():
163 """Testcase $HOME is not set, os=='nt'
164 env['HOMEDRIVE'],env['HOMEPATH'] points to path."""
156 """Using HOMEDRIVE + HOMEPATH, os=='nt'.
157
158 HOMESHARE is missing.
159 """
165 160
166 161 os.name = 'nt'
167 if 'HOME' in env: del env['HOME']
162 env.pop('HOMESHARE', None)
168 163 env['HOMEDRIVE'], env['HOMEPATH'] = os.path.splitdrive(HOME_TEST_DIR)
169
170 home_dir = genutils.get_home_dir()
164 home_dir = path.get_home_dir()
171 165 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
172 166
167
173 168 @skip_if_not_win32
174 169 @with_environment
175 170 def test_get_home_dir_6():
176 """Testcase $HOME is not set, os=='nt'
177 env['HOMEDRIVE'],env['HOMEPATH'] do not point to path.
178 env['USERPROFILE'] points to path
171 """Using USERPROFILE, os=='nt'.
172
173 HOMESHARE, HOMEDRIVE, HOMEPATH are missing.
179 174 """
180 175
181 176 os.name = 'nt'
182 if 'HOME' in env: del env['HOME']
183 env['HOMEDRIVE'], env['HOMEPATH'] = os.path.abspath(TEST_FILE_PATH), "DOES NOT EXIST"
177 env.pop('HOMESHARE', None)
178 env.pop('HOMEDRIVE', None)
179 env.pop('HOMEPATH', None)
184 180 env["USERPROFILE"] = abspath(HOME_TEST_DIR)
181 home_dir = path.get_home_dir()
182 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
185 183
186 home_dir = genutils.get_home_dir()
184
185 @skip_if_not_win32
186 @with_environment
187 def test_get_home_dir_7():
188 """Using HOMESHARE, os=='nt'."""
189
190 os.name = 'nt'
191 env["HOMESHARE"] = abspath(HOME_TEST_DIR)
192 home_dir = path.get_home_dir()
187 193 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
188 194
189 195 # Should we stub wreg fully so we can run the test on all platforms?
190 196 @skip_if_not_win32
191 197 @with_environment
192 def test_get_home_dir_7():
193 """Testcase $HOME is not set, os=='nt'
198 def test_get_home_dir_8():
199 """Using registry hack for 'My Documents', os=='nt'
194 200
195 env['HOMEDRIVE'],env['HOMEPATH'], env['USERPROFILE'] and others missing
201 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
196 202 """
197 203 os.name = 'nt'
198 204 # Remove from stub environment all keys that may be set
@@ -211,116 +217,56 b' def test_get_home_dir_7():'
211 217 wreg.OpenKey = OpenKey
212 218 wreg.QueryValueEx = QueryValueEx
213 219
214 home_dir = genutils.get_home_dir()
220 home_dir = path.get_home_dir()
215 221 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
216 222
217 #
218 # Tests for get_ipython_dir
219 #
220 223
221 224 @with_environment
222 225 def test_get_ipython_dir_1():
223 226 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
224 227 env['IPYTHON_DIR'] = "someplace/.ipython"
225 ipdir = genutils.get_ipython_dir()
228 ipdir = path.get_ipython_dir()
226 229 nt.assert_equal(ipdir, "someplace/.ipython")
227 230
228 231
229 232 @with_environment
230 233 def test_get_ipython_dir_2():
231 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 236 os.name = "posix"
234 237 env.pop('IPYTHON_DIR', None)
235 238 env.pop('IPYTHONDIR', None)
236 ipdir = genutils.get_ipython_dir()
239 ipdir = path.get_ipython_dir()
237 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 243 def test_filefind():
281 244 """Various tests for filefind"""
282 245 f = tempfile.NamedTemporaryFile()
283 print 'fname:',f.name
284 alt_dirs = genutils.get_ipython_dir()
285 t = genutils.filefind(f.name,alt_dirs)
286 print 'found:',t
246 # print 'fname:',f.name
247 alt_dirs = path.get_ipython_dir()
248 t = path.filefind(f.name, alt_dirs)
249 # print 'found:',t
287 250
288 251
289 252 def test_get_ipython_package_dir():
290 ipdir = genutils.get_ipython_package_dir()
253 ipdir = path.get_ipython_package_dir()
291 254 nt.assert_true(os.path.isdir(ipdir))
292 255
293 256
294 def test_tee_simple():
295 "Very simple check with stdout only"
296 chan = StringIO()
297 text = 'Hello'
298 tee = genutils.Tee(chan, channel='stdout')
299 print >> chan, text,
300 nt.assert_equal(chan.getvalue(), text)
301
257 def test_get_ipython_module_path():
258 ipapp_path = path.get_ipython_module_path('IPython.core.ipapp')
259 nt.assert_true(os.path.isfile(ipapp_path))
302 260
303 class TeeTestCase(dec.ParametricTestCase):
304 261
305 def tchan(self, channel, check='close'):
306 trap = StringIO()
307 chan = StringIO()
308 text = 'Hello'
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')
309 266
310 std_ori = getattr(sys, channel)
311 setattr(sys, channel, trap)
312 267
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
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')
322 272
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 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 import os
18 17 import sys
19 18
20 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 22 from IPython.testing import decorators as dec
24 23
25 24 #-----------------------------------------------------------------------------
@@ -60,13 +59,4 b' def test_find_cmd_fail():'
60 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 22 # Imports
23 23 #-----------------------------------------------------------------------------
24 24
25 import sys
26 import os
27
28
29 25 from unittest import TestCase
30 26
31 27 from IPython.utils.traitlets import (
32 28 HasTraitlets, MetaHasTraitlets, TraitletType, Any,
33 Int, Long, Float, Complex, Str, Unicode, Bool, TraitletError,
29 Int, Long, Float, Complex, Str, Unicode, TraitletError,
34 30 Undefined, Type, This, Instance
35 31 )
36 32
1 NO CONTENT: modified file
@@ -14,13 +14,10 b' Authors'
14 14 #*****************************************************************************
15 15
16 16 import __builtin__
17 import exceptions
18 import pdb
19 import pprint
20 17 import re
21 18 import types
22 19
23 from IPython.utils.genutils import dir2
20 from IPython.utils.dir2 import dir2
24 21
25 22 def create_typestr2type_dicts(dont_include_in_type2type2str=["lambda"]):
26 23 """Return dictionaries mapping lower case typename to type objects, from
@@ -1,5 +1,4 b''
1 1 include ipython.py
2 include iptest.py
3 2 include setupbase.py
4 3 include setupegg.py
5 4
@@ -9,7 +8,7 b' graft scripts'
9 8 graft IPython/kernel
10 9 graft IPython/config
11 10 graft IPython/core
12 graft IPython/deathrow
11 # graft IPython/deathrow
13 12 graft IPython/external
14 13 graft IPython/frontend
15 14 graft IPython/gui
@@ -1,71 +1,148 b''
1 1 #!/usr/bin/env python
2 2 """Run a Monte-Carlo options pricer in parallel."""
3 3
4 #-----------------------------------------------------------------------------
5 # Imports
6 #-----------------------------------------------------------------------------
7
8 import sys
9 import time
4 10 from IPython.kernel import client
5 11 import numpy as np
6 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 46 # The MultiEngineClient is used to setup the calculation and works with all
9 47 # engine.
10 mec = client.MultiEngineClient(profile='mycluster')
48 mec = client.MultiEngineClient(profile=cluster_profile)
11 49
12 50 # The TaskClient is an interface to the engines that provides dynamic load
13 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 54 # Initialize the common code on the engines. This Python module has the
17 55 # price_options function that prices the options.
18 56 mec.run('mcpricer.py')
19 57
20 # Define the function that will make up our tasks. We basically want to
21 # call the price_options function with all but two arguments (K, sigma)
22 # fixed.
23 def my_prices(K, sigma):
24 S = 100.0
25 r = 0.05
26 days = 260
27 paths = 100000
28 return price_options(S, K, sigma, r, days, paths)
29
30 # Create arrays of strike prices and volatilities
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.
58 #-----------------------------------------------------------------------------
59 # Perform parallel calculation
60 #-----------------------------------------------------------------------------
61
62 print "Running parallel calculation over strike prices and volatilities..."
63 print "Strike prices: ", strike_vals
64 print "Volatilities: ", sigma_vals
65 sys.stdout.flush()
66
67 # Submit tasks to the TaskClient for each (strike, sigma) pair as a MapTask.
68 t1 = time.time()
39 69 taskids = []
40 for K in K_vals:
70 for strike in strike_vals:
41 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 76 taskids.append(tc.run(t))
44 77
45 78 print "Submitted tasks: ", len(taskids)
79 sys.stdout.flush()
46 80
47 81 # Block until all tasks are completed.
48 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 89 # Get the results using TaskClient.get_task_result.
51 90 results = [tc.get_task_result(tid) for tid in taskids]
52 91
53 92 # Assemble the result into a structured NumPy array.
54 prices = np.empty(nK*nsigma,
93 prices = np.empty(n_strikes*n_sigmas,
55 94 dtype=[('ecall',float),('eput',float),('acall',float),('aput',float)]
56 95 )
96
57 97 for i, price_tuple in enumerate(results):
58 98 prices[i] = price_tuple
59 prices.shape = (nK, nsigma)
60 K_vals, sigma_vals = np.meshgrid(K_vals, sigma_vals)
61 99
62 def plot_options(sigma_vals, K_vals, prices):
100 prices.shape = (n_strikes, n_sigmas)
101 strike_mesh, sigma_mesh = np.meshgrid(strike_vals, sigma_vals)
102
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
67 plt.contourf(sigma_vals, K_vals, prices)
114 plt.figure(1)
115
116 plt.subplot(221)
117 plt.contourf(sigma_mesh, strike_mesh, prices['ecall'])
118 plt.axis('tight')
119 plt.colorbar()
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')
68 126 plt.colorbar()
69 plt.title("Option Price")
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 134 plt.xlabel("Volatility")
71 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 18 import random, sys
19 19 from optparse import OptionParser
20 20
21 from IPython.genutils import time
21 from IPython.utils.timing import time
22 22 from IPython.kernel import client
23 23
24 24 def main():
@@ -51,7 +51,7 b' def main():'
51 51 print tc.task_controller
52 52 rc.block=True
53 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 56 # the jobs should take a random time within a range
57 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 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 249 is used. This function will first look at the :envvar:`IPYTHON_DIR`
250 250 environment variable and then default to the directory
251 251 :file:`$HOME/.ipython`.
@@ -41,8 +41,8 b' A bit of Python code::'
41 41
42 42 An interactive Python session::
43 43
44 >>> from IPython import genutils
45 >>> genutils.get_ipython_dir()
44 >>> from IPython.utils.path import get_ipython_dir
45 >>> get_ipython_dir()
46 46 '/home/fperez/.ipython'
47 47
48 48 An IPython session:
@@ -57,3 +57,5 b' methods in :class:`InteractiveShell` that manage code execution::'
57 57 nx.draw_spectral(g, node_size=100, alpha=0.6, node_color='r',
58 58 font_size=10, node_shape='o')
59 59 plt.show()
60
61
@@ -244,7 +244,7 b' and an example of ``# all-random``::'
244 244
245 245 When writing docstrings, you can use the ``@skip_doctest`` decorator to
246 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 248 executes the example but ignores output, while the latter doesn't execute any
249 249 code. ``@skip_doctest`` should be used for docstrings whose examples are
250 250 purely informational.
@@ -166,7 +166,7 b' ipy_user_conf.py.'
166 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 170 from system commands. They are produced by ``var = !cmd`` syntax.
171 171
172 172 First, we acquire the output of 'ls -l'::
@@ -8,7 +8,7 b' import re'
8 8 import pydoc
9 9 from StringIO import StringIO
10 10 from warnings import warn
11 4
11
12 12 class Reader(object):
13 13 """A line-based string reader.
14 14
@@ -59,12 +59,9 b' Authors'
59 59
60 60 # Stdlib
61 61 import cStringIO
62 import imp
63 62 import os
64 63 import re
65 import shutil
66 64 import sys
67 import warnings
68 65
69 66 # To keep compatibility with various python versions
70 67 try:
@@ -80,8 +77,8 b' from docutils.parsers.rst import directives'
80 77 matplotlib.use('Agg')
81 78
82 79 # Our own
83 from IPython import Config, IPythonApp
84 from IPython.utils.genutils import Term, Tee
80 from IPython import Config, InteractiveShell
81 from IPython.utils.io import Term
85 82
86 83 #-----------------------------------------------------------------------------
87 84 # Globals
@@ -208,8 +205,9 b' class EmbeddedSphinxShell(object):'
208 205 Term.cerr = self.cout
209 206
210 207 # For debugging, so we can see normal output, use this:
211 #Term.cout = genutils.Tee(self.cout, channel='stdout') # dbg
212 #Term.cerr = genutils.Tee(self.cout, channel='stderr') # dbg
208 # from IPython.utils.io import Tee
209 #Term.cout = Tee(self.cout, channel='stdout') # dbg
210 #Term.cerr = Tee(self.cout, channel='stderr') # dbg
213 211
214 212 # Create config object for IPython
215 213 config = Config()
@@ -221,15 +219,11 b' class EmbeddedSphinxShell(object):'
221 219 config.InteractiveShell.autoindent = False
222 220 config.InteractiveShell.colors = 'NoColor'
223 221
224 # Merge global config which can be used to override.
225 config._merge(CONFIG)
226
227 222 # Create and initialize ipython, but don't start its mainloop
228 IP = IPythonApp(override_config=config)
229 IP.initialize()
223 IP = InteractiveShell(parent=None, config=config)
230 224
231 225 # Store a few parts of IPython we'll need.
232 self.IP = IP.shell
226 self.IP = IP
233 227 self.user_ns = self.IP.user_ns
234 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 47 from distutils.core import setup
48 48
49 49 # Our own imports
50 from IPython.utils.genutils import target_update
50 from IPython.utils.path import target_update
51 51
52 52 from setupbase import (
53 53 setup_args,
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (690 lines changed) Show them Hide them
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (1894 lines changed) Show them Hide them
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now