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 | ||
|
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 | ||
|
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. |
|
|
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 |
|
|
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, |
|
|
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 |
|
|
|
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. |
|
|
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 |
|
|
339 |
from. |
|
|
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 |
|
|
377 | 368 |
|
|
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 |
|
|
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. |
|
|
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 |
|
|
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 |
|
|
89 |
objects, which are loaded and ultimately merged into a single one used |
|
|
90 |
that point on by the app. |
|
|
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 |
#: |
|
|
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 |
|
|
|
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 |
#: |
|
|
110 | #: Internal defaults, implemented in code. | |
|
124 | 111 | default_config = None |
|
125 |
#: |
|
|
112 | #: Read from the filesystem. | |
|
126 | 113 | file_config = None |
|
127 |
#: |
|
|
114 | #: Read from the system's command line flags. | |
|
128 | 115 | command_line_config = None |
|
129 | #: passed parametrically to the constructor. | |
|
130 |
|
|
|
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 |
|
|
199 | 176 |
|
|
200 | 177 |
|
@@ -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 |
|
|
214 | 190 |
|
|
215 | 191 |
|
@@ -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. |
|
|
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 |
|
|
275 | 252 |
|
|
276 |
|
|
|
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 |
|
|
423 | 403 |
|
|
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 |
|
|
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 |
|
|
484 | 451 |
|
|
485 | self.abort() | |
|
486 | raise | |
|
487 | elif action == 'exit': | |
|
488 | 452 |
|
|
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. |
|
|
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 |
|
|
30 | HasTraitlets, MetaHasTraitlets, Instance, This | |
|
31 | 31 | ) |
|
32 | 32 | |
|
33 | 33 |
@@ -1,74 +1,94 b'' | |||
|
1 |
# |
|
|
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 |
# |
|
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. |
|
77 | 97 | source for further details. |
|
78 | 98 | """ |
|
79 | ||
|
80 | # apply args into instance | |
|
81 | 99 | self.app = app |
|
82 |
self.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 = |
|
|
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( |
|
|
129 | TBhandler = ultratb.VerboseTB( | |
|
130 | color_scheme=color_scheme, | |
|
141 | 131 |
|
|
142 | 132 |
|
|
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. |
|
|
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. |
|
|
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. |
|
|
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. |
|
|
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 |
|
|
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. |
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
306 | paa('--wthread', '-wthread', | |
|
330 | 307 | action='store_true', dest='Global.wthread', |
|
331 | help="Enable wxPython event loop integration "+ | |
|
332 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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. |
|
|
57 | from IPython.utils.doctestreload import doctest_reload | |
|
58 | 58 | from IPython.utils.ipstruct import Struct |
|
59 |
from IPython.utils. |
|
|
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_ |
|
|
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_ |
|
|
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_ |
|
|
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_ |
|
|
1021 | # Sync what we've added so far to user_ns_hidden so these aren't seen | |
|
1018 | 1022 | # by %who |
|
1019 |
self.user_ |
|
|
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_ |
|
|
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 |
|
|
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_ |
|
|
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_ |
|
|
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. |
|
|
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 |
# |
|
|
1 | # encoding: utf-8 | |
|
2 | 2 | """Magic functions for InteractiveShell. |
|
3 | 3 | """ |
|
4 | 4 | |
|
5 | #***************************************************************************** | |
|
5 | #----------------------------------------------------------------------------- | |
|
6 | 6 |
# |
|
7 |
# |
|
|
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 |
|
|
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. |
|
|
51 | from IPython.lib.pylabtools import mpl_runner | |
|
57 | 52 | from IPython.lib.inputhook import enable_gui |
|
58 |
from IPython.external.Itpl import |
|
|
53 | from IPython.external.Itpl import itpl, printpl | |
|
59 | 54 | from IPython.testing import decorators as testdec |
|
60 |
from IPython.utils import |
|
|
61 |
from IPython.utils import |
|
|
62 |
from IPython.utils. |
|
|
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 |
|
|
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_ |
|
|
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_ |
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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. |
|
|
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 |
|
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 = |
|
|
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 |
|
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 |
|
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 |
|
|
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. |
|
|
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 |
|
|
28 | from IPython.core.macro import Macro | |
|
25 | from IPython.utils import coloransi | |
|
29 | 26 | import IPython.utils.generics |
|
30 | ||
|
31 |
from IPython.utils. |
|
|
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 ' |
|
|
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 = '13 |
|
|
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_ |
|
|
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_ |
|
|
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_ |
|
|
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 ge |
|
|
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 ge |
|
|
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. |
|
|
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.write |
|
|
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.write |
|
|
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. |
|
|
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 |
|
|
136 | from IPython.utils.io import Term | |
|
137 | 137 | from IPython.utils import generics |
|
138 | 138 | except ImportError: |
|
139 |
|
|
|
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 = |
|
|
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. |
|
|
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. |
|
|
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. |
|
|
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 |
|
|
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. |
|
|
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.ip |
|
|
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. |
|
|
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, |
|
|
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 |
|
|
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 = I |
|
|
101 |
|
|
|
102 |
|
|
|
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 |
|
|
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. |
|
|
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. |
|
|
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 |
|
|
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( |
|
|
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 |
|
|
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 |
|
|
|
141 | Term.cin = cin | |
|
145 | 142 | if cout: |
|
146 |
|
|
|
143 | Term.cout = cout | |
|
147 | 144 | if cerr: |
|
148 |
|
|
|
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 = |
|
|
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 |
|
|
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. |
|
|
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 |
valid |
|
|
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 FURL |
|
|
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 FURL |
|
|
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 FURL |
|
|
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 FURL |
|
|
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 |
|
|
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. |
|
|
33 |
from IPython.utils import |
|
|
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 = |
|
|
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 = |
|
|
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 |
# |
|
|
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, |
|
|
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( |
|
|
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 |
|
|
40 | import os | |
|
41 | 41 | |
|
42 | 42 | from twisted.application import service |
|
43 |
from twisted. |
|
|
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. |
|
|
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. |
|
|
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 |
|
|
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. |
|
|
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 |
can |
|
|
52 |
can |
|
|
53 | uncan, \ | |
|
54 |
uncan |
|
|
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 |
|
|
|
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 |
|
|
93 |
|
|
|
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: % |
|
|
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 |
|
|
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, ClusterDir |
|
|
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( |
|
|
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( |
|
|
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 |
|
|
|
146 | paa = parser_create.add_argument | |
|
147 | paa('--reset-config', | |
|
114 | 148 | dest='Global.reset_config', action='store_true', |
|
115 | default=NoConfigDefault, | |
|
116 |
|
|
|
117 |
|
|
|
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 |
|
|
|
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_st |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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. |
|
|
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 |
|
|
|
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 |
|
|
25 |
from IPython.utils.p |
|
|
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 |
|
|
|
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( |
|
|
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( |
|
|
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( |
|
|
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( |
|
|
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( |
|
|
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. |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 = |
|
|
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 |
|
|
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 = |
|
|
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 = |
|
|
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 = |
|
|
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 = |
|
|
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 |
|
|
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 ( |
|
|
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 ( |
|
|
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 |
|
|
|
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. |
|
|
26 |
from twisted. |
|
|
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 |
|
|
22 | import time | |
|
23 | 23 | from types import FunctionType |
|
24 | 24 | |
|
25 |
import zope.interface as zi |
|
|
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 |
|
|
31 | from IPython.kernel.twistedutil import DeferredList | |
|
33 | 32 | |
|
34 |
from IPython.kernel.pickleutil import can, uncan |
|
|
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 |
|
|
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 = |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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:101 |
|
|
57 |
self.controller_tub.setLocation('127.0.0.1:101 |
|
|
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:101 |
|
|
64 |
self.controller_tub.setLocation('127.0.0.1:101 |
|
|
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:101 |
|
|
74 |
self.controller_tub.setLocation('127.0.0.1:101 |
|
|
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 |
|
|
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, |
|
|
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. |
|
|
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 |
|
|
|
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. |
|
|
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. |
|
|
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. |
|
|
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. |
|
|
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. |
|
|
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)' % |
|
|
130 |
return 'get_ipython().jobq(%s)' % |
|
|
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. |
|
|
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 |
|
|
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 |
|
|
|
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 |
|
|
|
148 | shell.builtin_trap.set() | |
|
143 | 149 | |
|
144 | 150 | # Set error printing to stdout so nose can doctest exceptions |
|
145 |
|
|
|
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 |
|
|
|
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 = |
|
|
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 |
|
|
55 |
from IPython.utils. |
|
|
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 = [ |
|
|
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. |
|
|
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 |
|
|
|
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 = |
|
|
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. |
|
|
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 |
|
|
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 |
# |
|
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 = |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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_ |
|
|
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, |
|
|
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, |
|
|
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 = |
|
|
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 = |
|
|
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 = |
|
|
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( |
|
|
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_ |
|
|
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 |
|
|
|
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 = |
|
|
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 = |
|
|
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 |
|
|
|
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 = |
|
|
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 = |
|
|
285 |
t = |
|
|
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 = |
|
|
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.p |
|
|
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, |
|
|
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. |
|
|
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= |
|
|
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= |
|
|
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 |
|
|
70 | for strike in strike_vals: | |
|
41 | 71 | for sigma in sigma_vals: |
|
42 |
t = client.MapTask( |
|
|
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(n |
|
|
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, |
|
|
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(" |
|
|
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. |
|
|
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. |
|
|
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. |
|
|
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 ge |
|
|
45 |
>>> ge |
|
|
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. |
|
|
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, I |
|
|
84 |
from IPython.utils. |
|
|
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.c |
|
|
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 = I |
|
|
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 |
|
|
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. |
|
|
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