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 | # Make it easy to import extensions - they are always directly on pythonpath. |
|
32 | # Make it easy to import extensions - they are always directly on pythonpath. | |
33 | # Therefore, non-IPython modules can be added to extensions directory |
|
33 | # Therefore, non-IPython modules can be added to extensions directory. | |
|
34 | # This should probably be in ipapp.py. | |||
34 | sys.path.append(os.path.join(os.path.dirname(__file__), "extensions")) |
|
35 | sys.path.append(os.path.join(os.path.dirname(__file__), "extensions")) | |
35 |
|
36 | |||
36 | #----------------------------------------------------------------------------- |
|
37 | #----------------------------------------------------------------------------- | |
37 | # Setup the top level names |
|
38 | # Setup the top level names | |
38 | #----------------------------------------------------------------------------- |
|
39 | #----------------------------------------------------------------------------- | |
39 |
|
40 | |||
40 | # In some cases, these are causing circular imports. |
|
|||
41 | from .config.loader import Config |
|
41 | from .config.loader import Config | |
42 | from .core import release |
|
42 | from .core import release | |
43 | from .core.application import Application |
|
43 | from .core.application import Application |
@@ -23,7 +23,7 b' import os' | |||||
23 | import sys |
|
23 | import sys | |
24 |
|
24 | |||
25 | from IPython.external import argparse |
|
25 | from IPython.external import argparse | |
26 |
from IPython.utils. |
|
26 | from IPython.utils.path import filefind | |
27 |
|
27 | |||
28 | #----------------------------------------------------------------------------- |
|
28 | #----------------------------------------------------------------------------- | |
29 | # Exceptions |
|
29 | # Exceptions | |
@@ -40,6 +40,7 b' class ConfigLoaderError(ConfigError):' | |||||
40 | #----------------------------------------------------------------------------- |
|
40 | #----------------------------------------------------------------------------- | |
41 | # Argparse fix |
|
41 | # Argparse fix | |
42 | #----------------------------------------------------------------------------- |
|
42 | #----------------------------------------------------------------------------- | |
|
43 | ||||
43 | # Unfortunately argparse by default prints help messages to stderr instead of |
|
44 | # Unfortunately argparse by default prints help messages to stderr instead of | |
44 | # stdout. This makes it annoying to capture long help screens at the command |
|
45 | # stdout. This makes it annoying to capture long help screens at the command | |
45 | # line, since one must know how to pipe stderr, which many users don't know how |
|
46 | # line, since one must know how to pipe stderr, which many users don't know how | |
@@ -200,10 +201,13 b' class ConfigLoader(object):' | |||||
200 | self.config = Config() |
|
201 | self.config = Config() | |
201 |
|
202 | |||
202 | def load_config(self): |
|
203 | def load_config(self): | |
203 |
"""Load a config from somewhere, return a |
|
204 | """Load a config from somewhere, return a :class:`Config` instance. | |
204 |
|
205 | |||
205 | Usually, this will cause self.config to be set and then returned. |
|
206 | Usually, this will cause self.config to be set and then returned. | |
|
207 | However, in most cases, :meth:`ConfigLoader.clear` should be called | |||
|
208 | to erase any previous state. | |||
206 | """ |
|
209 | """ | |
|
210 | self.clear() | |||
207 | return self.config |
|
211 | return self.config | |
208 |
|
212 | |||
209 |
|
213 | |||
@@ -242,6 +246,7 b' class PyFileConfigLoader(FileConfigLoader):' | |||||
242 |
|
246 | |||
243 | def load_config(self): |
|
247 | def load_config(self): | |
244 | """Load the config from a file and return it as a Struct.""" |
|
248 | """Load the config from a file and return it as a Struct.""" | |
|
249 | self.clear() | |||
245 | self._find_file() |
|
250 | self._find_file() | |
246 | self._read_file_as_dict() |
|
251 | self._read_file_as_dict() | |
247 | self._convert_to_config() |
|
252 | self._convert_to_config() | |
@@ -292,21 +297,11 b' class CommandLineConfigLoader(ConfigLoader):' | |||||
292 | """ |
|
297 | """ | |
293 |
|
298 | |||
294 |
|
299 | |||
295 | class __NoConfigDefault(object): pass |
|
|||
296 | NoConfigDefault = __NoConfigDefault() |
|
|||
297 |
|
||||
298 |
|
||||
299 | class ArgParseConfigLoader(CommandLineConfigLoader): |
|
300 | class ArgParseConfigLoader(CommandLineConfigLoader): | |
300 | #: Global default for arguments (see argparse docs for details) |
|
|||
301 | argument_default = NoConfigDefault |
|
|||
302 |
|
301 | |||
303 |
def __init__(self, argv=None, |
|
302 | def __init__(self, argv=None, *parser_args, **parser_kw): | |
304 | """Create a config loader for use with argparse. |
|
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 | Parameters |
|
305 | Parameters | |
311 | ---------- |
|
306 | ---------- | |
312 |
|
307 | |||
@@ -314,19 +309,22 b' class ArgParseConfigLoader(CommandLineConfigLoader):' | |||||
314 | If given, used to read command-line arguments from, otherwise |
|
309 | If given, used to read command-line arguments from, otherwise | |
315 | sys.argv[1:] is used. |
|
310 | sys.argv[1:] is used. | |
316 |
|
311 | |||
317 |
|
|
312 | parser_args : tuple | |
318 | Description of valid command-line arguments, to be called in sequence |
|
313 | A tuple of positional arguments that will be passed to the | |
319 | with parser.add_argument() to configure the parser. |
|
314 | constructor of :class:`argparse.ArgumentParser`. | |
|
315 | ||||
|
316 | parser_kw : dict | |||
|
317 | A tuple of keyword arguments that will be passed to the | |||
|
318 | constructor of :class:`argparse.ArgumentParser`. | |||
320 | """ |
|
319 | """ | |
321 | super(CommandLineConfigLoader, self).__init__() |
|
320 | super(CommandLineConfigLoader, self).__init__() | |
322 | if argv == None: |
|
321 | if argv == None: | |
323 | argv = sys.argv[1:] |
|
322 | argv = sys.argv[1:] | |
324 | self.argv = argv |
|
323 | self.argv = argv | |
325 |
self. |
|
324 | self.parser_args = parser_args | |
326 | self.args = args |
|
325 | kwargs = dict(argument_default=argparse.SUPPRESS) | |
327 | kwargs = dict(argument_default=self.argument_default) |
|
326 | kwargs.update(parser_kw) | |
328 | kwargs.update(kw) |
|
327 | self.parser_kw = kwargs | |
329 | self.kw = kwargs |
|
|||
330 |
|
328 | |||
331 | def load_config(self, args=None): |
|
329 | def load_config(self, args=None): | |
332 | """Parse command line arguments and return as a Struct. |
|
330 | """Parse command line arguments and return as a Struct. | |
@@ -335,10 +333,10 b' class ArgParseConfigLoader(CommandLineConfigLoader):' | |||||
335 | ---------- |
|
333 | ---------- | |
336 |
|
334 | |||
337 | args : optional, list |
|
335 | args : optional, list | |
338 |
If given, a list with the structure of sys.argv[1:] to parse |
|
336 | If given, a list with the structure of sys.argv[1:] to parse | |
339 |
from. |
|
337 | arguments from. If not given, the instance's self.argv attribute | |
340 | construction time) is used.""" |
|
338 | (given at construction time) is used.""" | |
341 |
|
339 | self.clear() | ||
342 | if args is None: |
|
340 | if args is None: | |
343 | args = self.argv |
|
341 | args = self.argv | |
344 | self._create_parser() |
|
342 | self._create_parser() | |
@@ -353,17 +351,11 b' class ArgParseConfigLoader(CommandLineConfigLoader):' | |||||
353 | return [] |
|
351 | return [] | |
354 |
|
352 | |||
355 | def _create_parser(self): |
|
353 | def _create_parser(self): | |
356 | self.parser = ArgumentParser(*self.args, **self.kw) |
|
354 | self.parser = ArgumentParser(*self.parser_args, **self.parser_kw) | |
357 | self._add_arguments() |
|
355 | self._add_arguments() | |
358 | self._add_other_arguments() |
|
|||
359 |
|
356 | |||
360 | def _add_arguments(self): |
|
357 | def _add_arguments(self): | |
361 | for argument in self.arguments: |
|
358 | raise NotImplementedError("subclasses must implement _add_arguments") | |
362 | self.parser.add_argument(*argument[0],**argument[1]) |
|
|||
363 |
|
||||
364 | def _add_other_arguments(self): |
|
|||
365 | """Meant for subclasses to add their own arguments.""" |
|
|||
366 | pass |
|
|||
367 |
|
359 | |||
368 | def _parse_args(self, args): |
|
360 | def _parse_args(self, args): | |
369 | """self.parser->self.parsed_data""" |
|
361 | """self.parser->self.parsed_data""" | |
@@ -372,6 +364,7 b' class ArgParseConfigLoader(CommandLineConfigLoader):' | |||||
372 | def _convert_to_config(self): |
|
364 | def _convert_to_config(self): | |
373 | """self.parsed_data->self.config""" |
|
365 | """self.parsed_data->self.config""" | |
374 | for k, v in vars(self.parsed_data).items(): |
|
366 | for k, v in vars(self.parsed_data).items(): | |
375 | if v is not NoConfigDefault: |
|
|||
376 |
|
|
367 | exec_str = 'self.config.' + k + '= v' | |
377 |
|
|
368 | exec exec_str in locals(), globals() | |
|
369 | ||||
|
370 |
@@ -61,35 +61,38 b' class TestPyFileCL(TestCase):' | |||||
61 | self.assertEquals(config.Foo.Bam.value, range(10)) |
|
61 | self.assertEquals(config.Foo.Bam.value, range(10)) | |
62 | self.assertEquals(config.D.C.value, 'hi there') |
|
62 | self.assertEquals(config.D.C.value, 'hi there') | |
63 |
|
63 | |||
|
64 | class MyLoader1(ArgParseConfigLoader): | |||
|
65 | def _add_arguments(self): | |||
|
66 | p = self.parser | |||
|
67 | p.add_argument('-f', '--foo', dest='Global.foo', type=str) | |||
|
68 | p.add_argument('-b', dest='MyClass.bar', type=int) | |||
|
69 | p.add_argument('-n', dest='n', action='store_true') | |||
|
70 | p.add_argument('Global.bam', type=str) | |||
|
71 | ||||
|
72 | class MyLoader2(ArgParseConfigLoader): | |||
|
73 | def _add_arguments(self): | |||
|
74 | subparsers = self.parser.add_subparsers(dest='subparser_name') | |||
|
75 | subparser1 = subparsers.add_parser('1') | |||
|
76 | subparser1.add_argument('-x',dest='Global.x') | |||
|
77 | subparser2 = subparsers.add_parser('2') | |||
|
78 | subparser2.add_argument('y') | |||
64 |
|
79 | |||
65 | class TestArgParseCL(TestCase): |
|
80 | class TestArgParseCL(TestCase): | |
66 |
|
81 | |||
67 | def test_basic(self): |
|
82 | def test_basic(self): | |
68 |
|
83 | cl = MyLoader1() | ||
69 | arguments = ( |
|
|||
70 | (('-f','--foo'), dict(dest='Global.foo', type=str)), |
|
|||
71 | (('-b',), dict(dest='MyClass.bar', type=int)), |
|
|||
72 | (('-n',), dict(dest='n', action='store_true')), |
|
|||
73 | (('Global.bam',), dict(type=str)) |
|
|||
74 | ) |
|
|||
75 | cl = ArgParseConfigLoader(arguments=arguments) |
|
|||
76 | config = cl.load_config('-f hi -b 10 -n wow'.split()) |
|
84 | config = cl.load_config('-f hi -b 10 -n wow'.split()) | |
77 | self.assertEquals(config.Global.foo, 'hi') |
|
85 | self.assertEquals(config.Global.foo, 'hi') | |
78 | self.assertEquals(config.MyClass.bar, 10) |
|
86 | self.assertEquals(config.MyClass.bar, 10) | |
79 | self.assertEquals(config.n, True) |
|
87 | self.assertEquals(config.n, True) | |
80 | self.assertEquals(config.Global.bam, 'wow') |
|
88 | self.assertEquals(config.Global.bam, 'wow') | |
|
89 | config = cl.load_config(['wow']) | |||
|
90 | self.assertEquals(config.keys(), ['Global']) | |||
|
91 | self.assertEquals(config.Global.keys(), ['bam']) | |||
|
92 | self.assertEquals(config.Global.bam, 'wow') | |||
81 |
|
93 | |||
82 | def test_add_arguments(self): |
|
94 | def test_add_arguments(self): | |
83 |
|
95 | cl = MyLoader2() | ||
84 | class MyLoader(ArgParseConfigLoader): |
|
|||
85 | def _add_arguments(self): |
|
|||
86 | subparsers = self.parser.add_subparsers(dest='subparser_name') |
|
|||
87 | subparser1 = subparsers.add_parser('1') |
|
|||
88 | subparser1.add_argument('-x',dest='Global.x') |
|
|||
89 | subparser2 = subparsers.add_parser('2') |
|
|||
90 | subparser2.add_argument('y') |
|
|||
91 |
|
||||
92 | cl = MyLoader() |
|
|||
93 | config = cl.load_config('2 frobble'.split()) |
|
96 | config = cl.load_config('2 frobble'.split()) | |
94 | self.assertEquals(config.subparser_name, '2') |
|
97 | self.assertEquals(config.subparser_name, '2') | |
95 | self.assertEquals(config.y, 'frobble') |
|
98 | self.assertEquals(config.y, 'frobble') | |
@@ -97,6 +100,15 b' class TestArgParseCL(TestCase):' | |||||
97 | self.assertEquals(config.subparser_name, '1') |
|
100 | self.assertEquals(config.subparser_name, '1') | |
98 | self.assertEquals(config.Global.x, 'frobble') |
|
101 | self.assertEquals(config.Global.x, 'frobble') | |
99 |
|
102 | |||
|
103 | def test_argv(self): | |||
|
104 | cl = MyLoader1(argv='-f hi -b 10 -n wow'.split()) | |||
|
105 | config = cl.load_config() | |||
|
106 | self.assertEquals(config.Global.foo, 'hi') | |||
|
107 | self.assertEquals(config.MyClass.bar, 10) | |||
|
108 | self.assertEquals(config.n, True) | |||
|
109 | self.assertEquals(config.Global.bam, 'wow') | |||
|
110 | ||||
|
111 | ||||
100 | class TestConfig(TestCase): |
|
112 | class TestConfig(TestCase): | |
101 |
|
113 | |||
102 | def test_setget(self): |
|
114 | def test_setget(self): |
@@ -28,9 +28,9 b' import sys' | |||||
28 | from IPython.core.component import Component |
|
28 | from IPython.core.component import Component | |
29 | from IPython.core.splitinput import split_user_input |
|
29 | from IPython.core.splitinput import split_user_input | |
30 |
|
30 | |||
31 |
from IPython.utils.traitlets import |
|
31 | from IPython.utils.traitlets import List | |
32 | from IPython.utils.genutils import error |
|
|||
33 | from IPython.utils.autoattr import auto_attr |
|
32 | from IPython.utils.autoattr import auto_attr | |
|
33 | from IPython.utils.warn import warn, error | |||
34 |
|
34 | |||
35 | #----------------------------------------------------------------------------- |
|
35 | #----------------------------------------------------------------------------- | |
36 | # Utilities |
|
36 | # Utilities |
@@ -33,7 +33,7 b' import os' | |||||
33 | import sys |
|
33 | import sys | |
34 |
|
34 | |||
35 | from IPython.core import release, crashhandler |
|
35 | from IPython.core import release, crashhandler | |
36 |
from IPython.utils. |
|
36 | from IPython.utils.path import get_ipython_dir, get_ipython_package_dir | |
37 | from IPython.config.loader import ( |
|
37 | from IPython.config.loader import ( | |
38 | PyFileConfigLoader, |
|
38 | PyFileConfigLoader, | |
39 | ArgParseConfigLoader, |
|
39 | ArgParseConfigLoader, | |
@@ -48,108 +48,86 b' class ApplicationError(Exception):' | |||||
48 | pass |
|
48 | pass | |
49 |
|
49 | |||
50 |
|
50 | |||
51 | app_cl_args = ( |
|
51 | class BaseAppConfigLoader(ArgParseConfigLoader): | |
52 | (('--ipython-dir', ), dict( |
|
52 | """Default command line options for IPython based applications.""" | |
|
53 | ||||
|
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 | dest='Global.ipython_dir',type=unicode, |
|
58 | dest='Global.ipython_dir',type=unicode, | |
54 | help= |
|
59 | help= | |
55 | """Set to override default location of the IPython directory |
|
60 | """Set to override default location of the IPython directory | |
56 |
IPYTHON_DIR, stored as Global.ipython_dir. This can also be |
|
61 | IPYTHON_DIR, stored as Global.ipython_dir. This can also be | |
57 | through the environment variable IPYTHON_DIR.""", |
|
62 | specified through the environment variable IPYTHON_DIR.""", | |
58 |
metavar='Global.ipython_dir') |
|
63 | metavar='Global.ipython_dir') | |
59 | (('-p', '--profile',), dict( |
|
64 | ||
60 | dest='Global.profile',type=unicode, |
|
65 | def _add_log_level(self, parser): | |
61 | help= |
|
66 | """Add the --log-level option to the parser.""" | |
62 | """The string name of the ipython profile to be used. Assume that your |
|
67 | paa = parser.add_argument | |
63 | config file is ipython_config-<name>.py (looks in current dir first, |
|
68 | paa('--log-level', | |
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( |
|
|||
71 | dest="Global.log_level",type=int, |
|
69 | dest="Global.log_level",type=int, | |
72 | help='Set the log level (0,10,20,30,40,50). Default is 30.', |
|
70 | help='Set the log level (0,10,20,30,40,50). Default is 30.', | |
73 |
metavar='Global.log_level') |
|
71 | metavar='Global.log_level') | |
74 | (('--config-file',), dict( |
|
72 | ||
75 | dest='Global.config_file',type=unicode, |
|
73 | def _add_arguments(self): | |
76 | help= |
|
74 | self._add_ipython_dir(self.parser) | |
77 | """Set the config file name to override default. Normally IPython |
|
75 | self._add_log_level(self.parser) | |
78 | loads ipython_config.py (from current directory) or |
|
76 | ||
79 | IPYTHON_DIR/ipython_config.py. If the loading of your config file |
|
|||
80 | fails, IPython starts with a bare bones configuration (no modules |
|
|||
81 | loaded at all).""", |
|
|||
82 | metavar='Global.config_file')), |
|
|||
83 | ) |
|
|||
84 |
|
77 | |||
85 | class Application(object): |
|
78 | class Application(object): | |
86 | """Load a config, construct components and set them running. |
|
79 | """Load a config, construct components and set them running. | |
87 |
|
80 | |||
88 |
The configuration of an application can be done via |
|
81 | The configuration of an application can be done via three different Config | |
89 |
objects, which are loaded and ultimately merged into a single one used |
|
82 | objects, which are loaded and ultimately merged into a single one used | |
90 |
that point on by the app. |
|
83 | from that point on by the app. These are: | |
91 |
|
84 | |||
92 | 1. default_config: internal defaults, implemented in code. |
|
85 | 1. default_config: internal defaults, implemented in code. | |
93 | 2. file_config: read from the filesystem. |
|
86 | 2. file_config: read from the filesystem. | |
94 | 3. command_line_config: read from the system's command line flags. |
|
87 | 3. command_line_config: read from the system's command line flags. | |
95 | 4. constructor_config: passed parametrically to the constructor. |
|
|||
96 |
|
88 | |||
97 | During initialization, 3 is actually read before 2, since at the |
|
89 | During initialization, 3 is actually read before 2, since at the | |
98 | command-line one may override the location of the file to be read. But the |
|
90 | command-line one may override the location of the file to be read. But the | |
99 | above is the order in which the merge is made. |
|
91 | above is the order in which the merge is made. | |
100 |
|
||||
101 | There is a final config object can be created and passed to the |
|
|||
102 | constructor: override_config. If it exists, this completely overrides the |
|
|||
103 | configs 2-4 above (the default is still used to ensure that all needed |
|
|||
104 | fields at least are created). This makes it easier to create |
|
|||
105 | parametrically (e.g. in testing or sphinx plugins) objects with a known |
|
|||
106 | configuration, that are unaffected by whatever arguments may be present in |
|
|||
107 | sys.argv or files in the user's various directories. |
|
|||
108 | """ |
|
92 | """ | |
109 |
|
93 | |||
110 | name = u'ipython' |
|
94 | name = u'ipython' | |
111 | description = 'IPython: an enhanced interactive Python shell.' |
|
95 | description = 'IPython: an enhanced interactive Python shell.' | |
112 |
#: |
|
96 | #: Usage message printed by argparse. If None, auto-generate | |
113 | usage = None |
|
97 | usage = None | |
114 | config_file_name = u'ipython_config.py' |
|
98 | #: The command line config loader. Subclass of ArgParseConfigLoader. | |
115 | #: Track the default and actual separately because some messages are |
|
99 | command_line_loader = BaseAppConfigLoader | |
116 | #: only printed if we aren't using the default. |
|
100 | #: The name of the config file to load, determined at runtime | |
117 |
|
|
101 | config_file_name = None | |
|
102 | #: The name of the default config file. Track separately from the actual | |||
|
103 | #: name because some logic happens only if we aren't using the default. | |||
|
104 | default_config_file_name = u'ipython_config.py' | |||
118 | default_log_level = logging.WARN |
|
105 | default_log_level = logging.WARN | |
119 | #: Set by --profile option |
|
106 | #: Set by --profile option | |
120 | profile_name = None |
|
107 | profile_name = None | |
121 | #: User's ipython directory, typically ~/.ipython/ |
|
108 | #: User's ipython directory, typically ~/.ipython/ | |
122 | ipython_dir = None |
|
109 | ipython_dir = None | |
123 |
#: |
|
110 | #: Internal defaults, implemented in code. | |
124 | default_config = None |
|
111 | default_config = None | |
125 |
#: |
|
112 | #: Read from the filesystem. | |
126 | file_config = None |
|
113 | file_config = None | |
127 |
#: |
|
114 | #: Read from the system's command line flags. | |
128 | command_line_config = None |
|
115 | command_line_config = None | |
129 | #: passed parametrically to the constructor. |
|
116 | #: The final config that will be passed to the component. | |
130 |
|
|
117 | master_config = None | |
131 | #: final override, if given supercedes file/command/constructor configs |
|
|||
132 | override_config = None |
|
|||
133 | #: A reference to the argv to be used (typically ends up being sys.argv[1:]) |
|
118 | #: A reference to the argv to be used (typically ends up being sys.argv[1:]) | |
134 | argv = None |
|
119 | argv = None | |
135 | #: Default command line arguments. Subclasses should create a new tuple |
|
|||
136 | #: that *includes* these. |
|
|||
137 | cl_arguments = app_cl_args |
|
|||
138 |
|
||||
139 | #: extra arguments computed by the command-line loader |
|
120 | #: extra arguments computed by the command-line loader | |
140 | extra_args = None |
|
121 | extra_args = None | |
|
122 | #: The class to use as the crash handler. | |||
|
123 | crash_handler_class = crashhandler.CrashHandler | |||
141 |
|
124 | |||
142 | # Private attributes |
|
125 | # Private attributes | |
143 | _exiting = False |
|
126 | _exiting = False | |
144 | _initialized = False |
|
127 | _initialized = False | |
145 |
|
128 | |||
146 | # Class choices for things that will be instantiated at runtime. |
|
129 | def __init__(self, argv=None): | |
147 | _CrashHandler = crashhandler.CrashHandler |
|
|||
148 |
|
||||
149 | def __init__(self, argv=None, constructor_config=None, override_config=None): |
|
|||
150 | self.argv = sys.argv[1:] if argv is None else argv |
|
130 | self.argv = sys.argv[1:] if argv is None else argv | |
151 | self.constructor_config = constructor_config |
|
|||
152 | self.override_config = override_config |
|
|||
153 | self.init_logger() |
|
131 | self.init_logger() | |
154 |
|
132 | |||
155 | def init_logger(self): |
|
133 | def init_logger(self): | |
@@ -194,7 +172,6 b' class Application(object):' | |||||
194 | self.log_default_config() |
|
172 | self.log_default_config() | |
195 | self.set_default_config_log_level() |
|
173 | self.set_default_config_log_level() | |
196 |
|
174 | |||
197 | if self.override_config is None: |
|
|||
198 |
|
|
175 | # Command-line config | |
199 |
|
|
176 | self.pre_load_command_line_config() | |
200 |
|
|
177 | self.load_command_line_config() | |
@@ -209,7 +186,6 b' class Application(object):' | |||||
209 | self.find_config_file_name() |
|
186 | self.find_config_file_name() | |
210 | self.find_config_file_paths() |
|
187 | self.find_config_file_paths() | |
211 |
|
188 | |||
212 | if self.override_config is None: |
|
|||
213 |
|
|
189 | # File-based config | |
214 |
|
|
190 | self.pre_load_file_config() | |
215 |
|
|
191 | self.load_file_config() | |
@@ -240,7 +216,7 b' class Application(object):' | |||||
240 |
|
216 | |||
241 | def create_crash_handler(self): |
|
217 | def create_crash_handler(self): | |
242 | """Create a crash handler, typically setting sys.excepthook to it.""" |
|
218 | """Create a crash handler, typically setting sys.excepthook to it.""" | |
243 |
self.crash_handler = self. |
|
219 | self.crash_handler = self.crash_handler_class(self) | |
244 | sys.excepthook = self.crash_handler |
|
220 | sys.excepthook = self.crash_handler | |
245 |
|
221 | |||
246 | def create_default_config(self): |
|
222 | def create_default_config(self): | |
@@ -270,10 +246,11 b' class Application(object):' | |||||
270 |
|
246 | |||
271 | def create_command_line_config(self): |
|
247 | def create_command_line_config(self): | |
272 | """Create and return a command line config loader.""" |
|
248 | """Create and return a command line config loader.""" | |
273 | return ArgParseConfigLoader(self.argv, self.cl_arguments, |
|
249 | return self.command_line_loader( | |
|
250 | self.argv, | |||
274 |
|
|
251 | description=self.description, | |
275 |
|
|
252 | version=release.version, | |
276 |
|
|
253 | usage=self.usage | |
277 | ) |
|
254 | ) | |
278 |
|
255 | |||
279 | def pre_load_command_line_config(self): |
|
256 | def pre_load_command_line_config(self): | |
@@ -338,18 +315,22 b' class Application(object):' | |||||
338 |
|
315 | |||
339 | If a profile has been set at the command line, this will resolve it. |
|
316 | If a profile has been set at the command line, this will resolve it. | |
340 | """ |
|
317 | """ | |
341 |
|
||||
342 | try: |
|
318 | try: | |
343 | self.config_file_name = self.command_line_config.Global.config_file |
|
319 | self.config_file_name = self.command_line_config.Global.config_file | |
344 | except AttributeError: |
|
320 | except AttributeError: | |
345 | pass |
|
321 | pass | |
|
322 | else: | |||
|
323 | return | |||
346 |
|
324 | |||
347 | try: |
|
325 | try: | |
348 | self.profile_name = self.command_line_config.Global.profile |
|
326 | self.profile_name = self.command_line_config.Global.profile | |
349 | except AttributeError: |
|
327 | except AttributeError: | |
350 | pass |
|
328 | # Just use the default as there is no profile | |
|
329 | self.config_file_name = self.default_config_file_name | |||
351 | else: |
|
330 | else: | |
352 | name_parts = self.config_file_name.split('.') |
|
331 | # Use the default config file name and profile name if set | |
|
332 | # to determine the used config file name. | |||
|
333 | name_parts = self.default_config_file_name.split('.') | |||
353 | name_parts.insert(1, u'_' + self.profile_name + u'.') |
|
334 | name_parts.insert(1, u'_' + self.profile_name + u'.') | |
354 | self.config_file_name = ''.join(name_parts) |
|
335 | self.config_file_name = ''.join(name_parts) | |
355 |
|
336 | |||
@@ -418,13 +399,9 b' class Application(object):' | |||||
418 | """Merge the default, command line and file config objects.""" |
|
399 | """Merge the default, command line and file config objects.""" | |
419 | config = Config() |
|
400 | config = Config() | |
420 | config._merge(self.default_config) |
|
401 | config._merge(self.default_config) | |
421 | if self.override_config is None: |
|
|||
422 |
|
|
402 | config._merge(self.file_config) | |
423 |
|
|
403 | config._merge(self.command_line_config) | |
424 | if self.constructor_config is not None: |
|
404 | ||
425 | config._merge(self.constructor_config) |
|
|||
426 | else: |
|
|||
427 | config._merge(self.override_config) |
|
|||
428 | # XXX fperez - propose to Brian we rename master_config to simply |
|
405 | # XXX fperez - propose to Brian we rename master_config to simply | |
429 | # config, I think this is going to be heavily used in examples and |
|
406 | # config, I think this is going to be heavily used in examples and | |
430 | # application code and the name is shorter/easier to find/remember. |
|
407 | # application code and the name is shorter/easier to find/remember. | |
@@ -456,15 +433,6 b' class Application(object):' | |||||
456 | # Utility methods |
|
433 | # Utility methods | |
457 | #------------------------------------------------------------------------- |
|
434 | #------------------------------------------------------------------------- | |
458 |
|
435 | |||
459 | def abort(self): |
|
|||
460 | """Abort the starting of the application.""" |
|
|||
461 | if self._exiting: |
|
|||
462 | pass |
|
|||
463 | else: |
|
|||
464 | self.log.critical("Aborting application: %s" % self.name, exc_info=True) |
|
|||
465 | self._exiting = True |
|
|||
466 | sys.exit(1) |
|
|||
467 |
|
||||
468 | def exit(self, exit_status=0): |
|
436 | def exit(self, exit_status=0): | |
469 | if self._exiting: |
|
437 | if self._exiting: | |
470 | pass |
|
438 | pass | |
@@ -473,17 +441,13 b' class Application(object):' | |||||
473 | self._exiting = True |
|
441 | self._exiting = True | |
474 | sys.exit(exit_status) |
|
442 | sys.exit(exit_status) | |
475 |
|
443 | |||
476 |
def attempt(self, func |
|
444 | def attempt(self, func): | |
477 | try: |
|
445 | try: | |
478 | func() |
|
446 | func() | |
479 | except SystemExit: |
|
447 | except SystemExit: | |
480 | raise |
|
448 | raise | |
481 | except: |
|
449 | except: | |
482 | if action == 'abort': |
|
|||
483 |
|
|
450 | self.log.critical("Aborting application: %s" % self.name, | |
484 |
|
|
451 | exc_info=True) | |
485 | self.abort() |
|
|||
486 | raise |
|
|||
487 | elif action == 'exit': |
|
|||
488 |
|
|
452 | self.exit(0) | |
489 |
|
453 |
@@ -69,19 +69,20 b' used, and this module (and the readline module) are silently inactive.' | |||||
69 | import __builtin__ |
|
69 | import __builtin__ | |
70 | import __main__ |
|
70 | import __main__ | |
71 | import glob |
|
71 | import glob | |
|
72 | import inspect | |||
72 | import itertools |
|
73 | import itertools | |
73 | import keyword |
|
74 | import keyword | |
74 | import os |
|
75 | import os | |
75 | import re |
|
76 | import re | |
76 | import shlex |
|
77 | import shlex | |
77 | import sys |
|
78 | import sys | |
78 | import types |
|
|||
79 |
|
79 | |||
80 | import IPython.utils.rlineimpl as readline |
|
|||
81 | from IPython.core.error import TryNext |
|
80 | from IPython.core.error import TryNext | |
82 | from IPython.core.prefilter import ESC_MAGIC |
|
81 | from IPython.core.prefilter import ESC_MAGIC | |
83 | from IPython.utils import generics |
|
82 | from IPython.utils import generics | |
84 |
from IPython.utils. |
|
83 | from IPython.utils.frame import debugx | |
|
84 | from IPython.utils.dir2 import dir2 | |||
|
85 | import IPython.utils.rlineimpl as readline | |||
85 |
|
86 | |||
86 | #----------------------------------------------------------------------------- |
|
87 | #----------------------------------------------------------------------------- | |
87 | # Globals |
|
88 | # Globals | |
@@ -216,7 +217,6 b' class Completer:' | |||||
216 | with a __getattr__ hook is evaluated. |
|
217 | with a __getattr__ hook is evaluated. | |
217 |
|
218 | |||
218 | """ |
|
219 | """ | |
219 | import re |
|
|||
220 |
|
220 | |||
221 | #print 'Completer->attr_matches, txt=%r' % text # dbg |
|
221 | #print 'Completer->attr_matches, txt=%r' % text # dbg | |
222 | # Another option, seems to work great. Catches things like ''.<tab> |
|
222 | # Another option, seems to work great. Catches things like ''.<tab> |
@@ -27,7 +27,7 b' from weakref import WeakValueDictionary' | |||||
27 | from IPython.utils.importstring import import_item |
|
27 | from IPython.utils.importstring import import_item | |
28 | from IPython.config.loader import Config |
|
28 | from IPython.config.loader import Config | |
29 | from IPython.utils.traitlets import ( |
|
29 | from IPython.utils.traitlets import ( | |
30 |
HasTraitlets |
|
30 | HasTraitlets, MetaHasTraitlets, Instance, This | |
31 | ) |
|
31 | ) | |
32 |
|
32 | |||
33 |
|
33 |
@@ -1,74 +1,94 b'' | |||||
1 |
# |
|
1 | # encoding: utf-8 | |
2 | """sys.excepthook for IPython itself, leaves a detailed report on disk. |
|
2 | """sys.excepthook for IPython itself, leaves a detailed report on disk. | |
3 |
|
3 | |||
|
4 | Authors: | |||
4 |
|
5 | |||
5 | Authors |
|
6 | * Fernando Perez | |
6 | ------- |
|
7 | * Brian E. Granger | |
7 | - Fernando Perez <Fernando.Perez@berkeley.edu> |
|
|||
8 | """ |
|
8 | """ | |
9 |
|
9 | |||
10 | #***************************************************************************** |
|
10 | #----------------------------------------------------------------------------- | |
11 | # Copyright (C) 2008-2009 The IPython Development Team |
|
|||
12 |
# |
|
11 | # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu> | |
|
12 | # Copyright (C) 2008-2010 The IPython Development Team | |||
13 | # |
|
13 | # | |
14 | # Distributed under the terms of the BSD License. The full license is in |
|
14 | # Distributed under the terms of the BSD License. The full license is in | |
15 | # the file COPYING, distributed as part of this software. |
|
15 | # the file COPYING, distributed as part of this software. | |
16 | #***************************************************************************** |
|
16 | #----------------------------------------------------------------------------- | |
17 |
|
17 | |||
18 | #**************************************************************************** |
|
18 | #----------------------------------------------------------------------------- | |
19 | # Required modules |
|
19 | # Imports | |
|
20 | #----------------------------------------------------------------------------- | |||
20 |
|
21 | |||
21 | # From the standard library |
|
|||
22 | import os |
|
22 | import os | |
23 | import sys |
|
23 | import sys | |
24 | from pprint import pformat |
|
24 | from pprint import pformat | |
25 |
|
25 | |||
26 | # Our own |
|
|||
27 | from IPython.core import release |
|
|||
28 | from IPython.core import ultratb |
|
26 | from IPython.core import ultratb | |
29 | from IPython.utils.genutils import sys_info |
|
|||
30 |
|
||||
31 | from IPython.external.Itpl import itpl |
|
27 | from IPython.external.Itpl import itpl | |
|
28 | from IPython.utils.sysinfo import sys_info | |||
32 |
|
29 | |||
33 | #**************************************************************************** |
|
30 | #----------------------------------------------------------------------------- | |
|
31 | # Code | |||
|
32 | #----------------------------------------------------------------------------- | |||
34 |
|
33 | |||
35 | class CrashHandler(object): |
|
34 | # Template for the user message. | |
36 | """Customizable crash handlers for IPython-based systems. |
|
35 | _default_message_template = """\ | |
|
36 | Oops, $self.app_name crashed. We do our best to make it stable, but... | |||
37 |
|
37 | |||
38 | Instances of this class provide a __call__ method which can be used as a |
|
38 | A crash report was automatically generated with the following information: | |
39 | sys.excepthook, i.e., the __call__ signature is: |
|
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 |
|
62 | Instances of this class provide a :meth:`__call__` method which can be | |
53 | time for internal information. |
|
63 | used as a ``sys.excepthook``. The :meth:`__call__` signature is:: | |
54 |
|
64 | |||
55 | - app_name: a string containing the name of your application. |
|
65 | def __call__(self, etype, evalue, etb) | |
|
66 | """ | |||
56 |
|
67 | |||
57 | - contact_name: a string with the name of the person to contact. |
|
68 | message_template = _default_message_template | |
58 |
|
69 | |||
59 | - contact_email: a string with the email address of the contact. |
|
70 | def __init__(self, app, contact_name=None, contact_email=None, | |
|
71 | bug_tracker=None, show_crash_traceback=True, call_pdb=False): | |||
|
72 | """Create a new crash handler | |||
60 |
|
73 | |||
61 | - bug_tracker: a string with the URL for your project's bug tracker. |
|
74 | Parameters | |
|
75 | ---------- | |||
|
76 | app : Application | |||
|
77 | A running :class:`Application` instance, which will be queried at | |||
|
78 | crash time for internal information. | |||
62 |
|
79 | |||
63 | - crash_report_fname: a string with the filename for the crash report |
|
80 | contact_name : str | |
64 | to be saved in. These reports are left in the ipython user directory |
|
81 | A string with the name of the person to contact. | |
65 | as determined by the running IPython instance. |
|
|||
66 |
|
82 | |||
67 | Optional inputs: |
|
83 | contact_email : str | |
|
84 | A string with the email address of the contact. | |||
68 |
|
85 | |||
69 | - show_crash_traceback(True): if false, don't print the crash |
|
86 | bug_tracker : str | |
70 | traceback on stderr, only generate the on-disk report |
|
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 | Non-argument instance attributes: |
|
93 | Non-argument instance attributes: | |
74 |
|
94 | |||
@@ -76,48 +96,17 b' class CrashHandler(object):' | |||||
76 |
further customization of the crash handler's behavior. |
|
96 | further customization of the crash handler's behavior. Please see the | |
77 | source for further details. |
|
97 | source for further details. | |
78 | """ |
|
98 | """ | |
79 |
|
||||
80 | # apply args into instance |
|
|||
81 | self.app = app |
|
99 | self.app = app | |
82 |
self.app_name = |
|
100 | self.app_name = self.app.name | |
83 | self.contact_name = contact_name |
|
101 | self.contact_name = contact_name | |
84 | self.contact_email = contact_email |
|
102 | self.contact_email = contact_email | |
85 | self.bug_tracker = bug_tracker |
|
103 | self.bug_tracker = bug_tracker | |
86 |
self.crash_report_fname = |
|
104 | self.crash_report_fname = "Crash_report_%s.txt" % self.app_name | |
87 | self.show_crash_traceback = show_crash_traceback |
|
105 | self.show_crash_traceback = show_crash_traceback | |
88 | self.section_sep = '\n\n'+'*'*75+'\n\n' |
|
106 | self.section_sep = '\n\n'+'*'*75+'\n\n' | |
89 | self.call_pdb = call_pdb |
|
107 | self.call_pdb = call_pdb | |
90 | #self.call_pdb = True # dbg |
|
108 | #self.call_pdb = True # dbg | |
91 |
|
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 | def __call__(self,etype, evalue, etb): |
|
110 | def __call__(self, etype, evalue, etb): | |
122 | """Handle an exception, call for compatible with sys.excepthook""" |
|
111 | """Handle an exception, call for compatible with sys.excepthook""" | |
123 |
|
112 | |||
@@ -137,7 +126,8 b' $self.bug_tracker' | |||||
137 | # write the report filename into the instance dict so it can get |
|
126 | # write the report filename into the instance dict so it can get | |
138 | # properly expanded out in the user message template |
|
127 | # properly expanded out in the user message template | |
139 | self.crash_report_fname = report_name |
|
128 | self.crash_report_fname = report_name | |
140 |
TBhandler = ultratb.VerboseTB( |
|
129 | TBhandler = ultratb.VerboseTB( | |
|
130 | color_scheme=color_scheme, | |||
141 |
|
|
131 | long_header=1, | |
142 |
|
|
132 | call_pdb=self.call_pdb, | |
143 | ) |
|
133 | ) | |
@@ -159,7 +149,7 b' $self.bug_tracker' | |||||
159 | return |
|
149 | return | |
160 |
|
150 | |||
161 | # Inform user on stderr of what happened |
|
151 | # Inform user on stderr of what happened | |
162 |
msg = itpl('\n'+'*'*70+'\n'+self. |
|
152 | msg = itpl('\n'+'*'*70+'\n'+self.message_template) | |
163 | print >> sys.stderr, msg |
|
153 | print >> sys.stderr, msg | |
164 |
|
154 | |||
165 | # Construct report on disk |
|
155 | # Construct report on disk | |
@@ -178,7 +168,9 b' $self.bug_tracker' | |||||
178 |
|
168 | |||
179 | try: |
|
169 | try: | |
180 | config = pformat(self.app.config) |
|
170 | config = pformat(self.app.config) | |
181 | rpt_add(sec_sep+'Current user configuration structure:\n\n') |
|
171 | rpt_add(sec_sep) | |
|
172 | rpt_add('Application name: %s\n\n' % self.app_name) | |||
|
173 | rpt_add('Current user configuration structure:\n\n') | |||
182 | rpt_add(config) |
|
174 | rpt_add(config) | |
183 | except: |
|
175 | except: | |
184 | pass |
|
176 | pass | |
@@ -186,38 +178,3 b' $self.bug_tracker' | |||||
186 |
|
178 | |||
187 | return ''.join(report) |
|
179 | return ''.join(report) | |
188 |
|
180 | |||
189 |
|
||||
190 | class IPythonCrashHandler(CrashHandler): |
|
|||
191 | """sys.excepthook for IPython itself, leaves a detailed report on disk.""" |
|
|||
192 |
|
||||
193 | def __init__(self, app, app_name='IPython'): |
|
|||
194 |
|
||||
195 | # Set here which of the IPython authors should be listed as contact |
|
|||
196 | AUTHOR_CONTACT = 'Fernando' |
|
|||
197 |
|
||||
198 | # Set argument defaults |
|
|||
199 | bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug' |
|
|||
200 | contact_name,contact_email = release.authors[AUTHOR_CONTACT][:2] |
|
|||
201 | crash_report_fname = 'IPython_crash_report.txt' |
|
|||
202 | # Call parent constructor |
|
|||
203 | CrashHandler.__init__(self,app,app_name,contact_name,contact_email, |
|
|||
204 | bug_tracker,crash_report_fname) |
|
|||
205 |
|
||||
206 | def make_report(self,traceback): |
|
|||
207 | """Return a string containing a crash report.""" |
|
|||
208 |
|
||||
209 | sec_sep = self.section_sep |
|
|||
210 | # Start with parent report |
|
|||
211 | report = [super(IPythonCrashHandler, self).make_report(traceback)] |
|
|||
212 | # Add interactive-specific info we may have |
|
|||
213 | rpt_add = report.append |
|
|||
214 | try: |
|
|||
215 | rpt_add(sec_sep+"History of session input:") |
|
|||
216 | for line in self.app.shell.user_ns['_ih']: |
|
|||
217 | rpt_add(line) |
|
|||
218 | rpt_add('\n*** Last line of input (may not be in above history):\n') |
|
|||
219 | rpt_add(self.app.shell._last_input_line+'\n') |
|
|||
220 | except: |
|
|||
221 | pass |
|
|||
222 |
|
||||
223 | return ''.join(report) |
|
@@ -26,15 +26,13 b' http://www.python.org/2.2.3/license.html"""' | |||||
26 | #***************************************************************************** |
|
26 | #***************************************************************************** | |
27 |
|
27 | |||
28 | import bdb |
|
28 | import bdb | |
29 | import cmd |
|
|||
30 | import linecache |
|
29 | import linecache | |
31 | import os |
|
|||
32 | import sys |
|
30 | import sys | |
33 |
|
31 | |||
34 | from IPython.utils import PyColorize |
|
32 | from IPython.utils import PyColorize | |
35 | from IPython.core import ipapi |
|
33 | from IPython.core import ipapi | |
36 | from IPython.utils import coloransi |
|
34 | from IPython.utils import coloransi | |
37 |
from IPython.utils. |
|
35 | from IPython.utils.io import Term | |
38 | from IPython.core.excolors import exception_colors |
|
36 | from IPython.core.excolors import exception_colors | |
39 |
|
37 | |||
40 | # See if we can use pydb. |
|
38 | # See if we can use pydb. |
@@ -24,8 +24,6 b' import sys' | |||||
24 |
|
24 | |||
25 | from IPython.core.component import Component |
|
25 | from IPython.core.component import Component | |
26 |
|
26 | |||
27 | from IPython.utils.autoattr import auto_attr |
|
|||
28 |
|
||||
29 | #----------------------------------------------------------------------------- |
|
27 | #----------------------------------------------------------------------------- | |
30 | # Classes and functions |
|
28 | # Classes and functions | |
31 | #----------------------------------------------------------------------------- |
|
29 | #----------------------------------------------------------------------------- |
@@ -24,6 +24,7 b' Notes' | |||||
24 | #----------------------------------------------------------------------------- |
|
24 | #----------------------------------------------------------------------------- | |
25 |
|
25 | |||
26 | from __future__ import with_statement |
|
26 | from __future__ import with_statement | |
|
27 | import __main__ | |||
27 |
|
28 | |||
28 | import sys |
|
29 | import sys | |
29 | from contextlib import nested |
|
30 | from contextlib import nested | |
@@ -33,7 +34,7 b' from IPython.core.iplib import InteractiveShell' | |||||
33 | from IPython.core.ipapp import load_default_config |
|
34 | from IPython.core.ipapp import load_default_config | |
34 |
|
35 | |||
35 | from IPython.utils.traitlets import Bool, Str, CBool |
|
36 | from IPython.utils.traitlets import Bool, Str, CBool | |
36 |
from IPython.utils. |
|
37 | from IPython.utils.io import ask_yes_no | |
37 |
|
38 | |||
38 |
|
39 | |||
39 | #----------------------------------------------------------------------------- |
|
40 | #----------------------------------------------------------------------------- |
@@ -10,8 +10,6 b' Color schemes for exception handling code in IPython.' | |||||
10 | # the file COPYING, distributed as part of this software. |
|
10 | # the file COPYING, distributed as part of this software. | |
11 | #***************************************************************************** |
|
11 | #***************************************************************************** | |
12 |
|
12 | |||
13 | #**************************************************************************** |
|
|||
14 | # Required modules |
|
|||
15 | from IPython.utils.coloransi import ColorSchemeTable, TermColors, ColorScheme |
|
13 | from IPython.utils.coloransi import ColorSchemeTable, TermColors, ColorScheme | |
16 |
|
14 | |||
17 | def exception_colors(): |
|
15 | def exception_colors(): |
@@ -5,7 +5,8 b'' | |||||
5 | import fnmatch |
|
5 | import fnmatch | |
6 | import os |
|
6 | import os | |
7 |
|
7 | |||
8 |
from IPython.utils. |
|
8 | from IPython.utils.io import Term, ask_yes_no | |
|
9 | from IPython.utils.warn import warn | |||
9 | from IPython.core import ipapi |
|
10 | from IPython.core import ipapi | |
10 |
|
11 | |||
11 | def magic_history(self, parameter_s = ''): |
|
12 | def magic_history(self, parameter_s = ''): |
@@ -43,9 +43,12 b' somewhere in your configuration files or ipython command line.' | |||||
43 |
|
43 | |||
44 | import os, bisect |
|
44 | import os, bisect | |
45 | import sys |
|
45 | import sys | |
46 | from IPython.utils.genutils import Term, shell |
|
46 | ||
47 | from pprint import PrettyPrinter |
|
47 | from pprint import PrettyPrinter | |
48 |
|
48 | |||
|
49 | from IPython.utils.io import Term | |||
|
50 | from IPython.utils.process import shell | |||
|
51 | ||||
49 | from IPython.core.error import TryNext |
|
52 | from IPython.core.error import TryNext | |
50 |
|
53 | |||
51 | # List here all the default hooks. For now it's just the editor functions |
|
54 | # List here all the default hooks. For now it's just the editor functions |
@@ -18,8 +18,6 b' has been made into a component, this module will be sent to deathrow.' | |||||
18 | # Imports |
|
18 | # Imports | |
19 | #----------------------------------------------------------------------------- |
|
19 | #----------------------------------------------------------------------------- | |
20 |
|
20 | |||
21 | from IPython.core.error import TryNext, UsageError, IPythonCoreError |
|
|||
22 |
|
||||
23 | #----------------------------------------------------------------------------- |
|
21 | #----------------------------------------------------------------------------- | |
24 | # Classes and functions |
|
22 | # Classes and functions | |
25 | #----------------------------------------------------------------------------- |
|
23 | #----------------------------------------------------------------------------- |
@@ -21,33 +21,60 b' Authors' | |||||
21 | #----------------------------------------------------------------------------- |
|
21 | #----------------------------------------------------------------------------- | |
22 | # Imports |
|
22 | # Imports | |
23 | #----------------------------------------------------------------------------- |
|
23 | #----------------------------------------------------------------------------- | |
|
24 | ||||
24 | from __future__ import absolute_import |
|
25 | from __future__ import absolute_import | |
25 |
|
26 | |||
26 | import logging |
|
27 | import logging | |
27 | import os |
|
28 | import os | |
28 | import sys |
|
29 | import sys | |
29 |
|
30 | |||
30 |
from IPython.core import |
|
31 | from IPython.core import release | |
31 | from IPython.core.application import Application |
|
32 | from IPython.core.crashhandler import CrashHandler | |
|
33 | from IPython.core.application import Application, BaseAppConfigLoader | |||
32 | from IPython.core.iplib import InteractiveShell |
|
34 | from IPython.core.iplib import InteractiveShell | |
33 | from IPython.config.loader import ( |
|
35 | from IPython.config.loader import ( | |
34 | Config, |
|
36 | Config, | |
35 |
PyFileConfigLoader |
|
37 | PyFileConfigLoader | |
36 | # NoConfigDefault, |
|
|||
37 | ) |
|
38 | ) | |
38 | from IPython.lib import inputhook |
|
39 | from IPython.lib import inputhook | |
39 |
from IPython.utils. |
|
40 | from IPython.utils.path import filefind, get_ipython_dir | |
40 | from . import usage |
|
41 | from . import usage | |
41 |
|
42 | |||
42 | #----------------------------------------------------------------------------- |
|
43 | #----------------------------------------------------------------------------- | |
43 | # Globals, utilities and helpers |
|
44 | # Globals, utilities and helpers | |
44 | #----------------------------------------------------------------------------- |
|
45 | #----------------------------------------------------------------------------- | |
45 |
|
46 | |||
|
47 | #: The default config file name for this application. | |||
46 | default_config_file_name = u'ipython_config.py' |
|
48 | default_config_file_name = u'ipython_config.py' | |
47 |
|
49 | |||
48 | cl_args = ( |
|
50 | ||
49 | (('--autocall',), dict( |
|
51 | class IPAppConfigLoader(BaseAppConfigLoader): | |
50 | type=int, dest='InteractiveShell.autocall', |
|
52 | ||
|
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 | help= |
|
78 | help= | |
52 | """Make IPython automatically call any callable object even if you |
|
79 | """Make IPython automatically call any callable object even if you | |
53 | didn't type explicit parentheses. For example, 'str 43' becomes |
|
80 | didn't type explicit parentheses. For example, 'str 43' becomes | |
@@ -57,41 +84,33 b' cl_args = (' | |||||
57 | objects are automatically called (even if no arguments are present). |
|
84 | objects are automatically called (even if no arguments are present). | |
58 | The default is '1'.""", |
|
85 | The default is '1'.""", | |
59 | metavar='InteractiveShell.autocall') |
|
86 | metavar='InteractiveShell.autocall') | |
60 | ), |
|
87 | paa('--autoindent', | |
61 | (('--autoindent',), dict( |
|
|||
62 | action='store_true', dest='InteractiveShell.autoindent', |
|
88 | action='store_true', dest='InteractiveShell.autoindent', | |
63 | help='Turn on autoindenting.') |
|
89 | help='Turn on autoindenting.') | |
64 | ), |
|
90 | paa('--no-autoindent', | |
65 | (('--no-autoindent',), dict( |
|
|||
66 | action='store_false', dest='InteractiveShell.autoindent', |
|
91 | action='store_false', dest='InteractiveShell.autoindent', | |
67 | help='Turn off autoindenting.') |
|
92 | help='Turn off autoindenting.') | |
68 | ), |
|
93 | paa('--automagic', | |
69 | (('--automagic',), dict( |
|
|||
70 | action='store_true', dest='InteractiveShell.automagic', |
|
94 | action='store_true', dest='InteractiveShell.automagic', | |
71 | help='Turn on the auto calling of magic commands.' |
|
95 | help= | |
72 | 'Type %%magic at the IPython prompt for more information.') |
|
96 | """Turn on the auto calling of magic commands. Type %%magic at the | |
73 | ), |
|
97 | IPython prompt for more information.""") | |
74 |
|
|
98 | paa('--no-automagic', | |
75 | action='store_false', dest='InteractiveShell.automagic', |
|
99 | action='store_false', dest='InteractiveShell.automagic', | |
76 | help='Turn off the auto calling of magic commands.') |
|
100 | help='Turn off the auto calling of magic commands.') | |
77 | ), |
|
101 | paa('--autoedit-syntax', | |
78 | (('--autoedit-syntax',), dict( |
|
|||
79 | action='store_true', dest='InteractiveShell.autoedit_syntax', |
|
102 | action='store_true', dest='InteractiveShell.autoedit_syntax', | |
80 | help='Turn on auto editing of files with syntax errors.') |
|
103 | help='Turn on auto editing of files with syntax errors.') | |
81 | ), |
|
104 | paa('--no-autoedit-syntax', | |
82 | (('--no-autoedit-syntax',), dict( |
|
|||
83 | action='store_false', dest='InteractiveShell.autoedit_syntax', |
|
105 | action='store_false', dest='InteractiveShell.autoedit_syntax', | |
84 | help='Turn off auto editing of files with syntax errors.') |
|
106 | help='Turn off auto editing of files with syntax errors.') | |
85 | ), |
|
107 | paa('--banner', | |
86 | (('--banner',), dict( |
|
|||
87 | action='store_true', dest='Global.display_banner', |
|
108 | action='store_true', dest='Global.display_banner', | |
88 | help='Display a banner upon starting IPython.') |
|
109 | help='Display a banner upon starting IPython.') | |
89 | ), |
|
110 | paa('--no-banner', | |
90 | (('--no-banner',), dict( |
|
|||
91 | action='store_false', dest='Global.display_banner', |
|
111 | action='store_false', dest='Global.display_banner', | |
92 | help="Don't display a banner upon starting IPython.") |
|
112 | help="Don't display a banner upon starting IPython.") | |
93 | ), |
|
113 | paa('--cache-size', | |
94 | (('--cache-size',), dict( |
|
|||
95 | type=int, dest='InteractiveShell.cache_size', |
|
114 | type=int, dest='InteractiveShell.cache_size', | |
96 | help= |
|
115 | help= | |
97 | """Set the size of the output cache. The default is 1000, you can |
|
116 | """Set the size of the output cache. The default is 1000, you can | |
@@ -99,20 +118,16 b' cl_args = (' | |||||
99 | disables the caching system, and the minimum value accepted is 20 (if |
|
118 | disables the caching system, and the minimum value accepted is 20 (if | |
100 | you provide a value less than 20, it is reset to 0 and a warning is |
|
119 | you provide a value less than 20, it is reset to 0 and a warning is | |
101 | issued). This limit is defined because otherwise you'll spend more |
|
120 | issued). This limit is defined because otherwise you'll spend more | |
102 |
time re-flushing a too small cache than working |
|
121 | time re-flushing a too small cache than working""", | |
103 | """, |
|
|||
104 | metavar='InteractiveShell.cache_size') |
|
122 | metavar='InteractiveShell.cache_size') | |
105 | ), |
|
123 | paa('--classic', | |
106 | (('--classic',), dict( |
|
|||
107 | action='store_true', dest='Global.classic', |
|
124 | action='store_true', dest='Global.classic', | |
108 | help="Gives IPython a similar feel to the classic Python prompt.") |
|
125 | help="Gives IPython a similar feel to the classic Python prompt.") | |
109 | ), |
|
126 | paa('--colors', | |
110 | (('--colors',), dict( |
|
|||
111 | type=str, dest='InteractiveShell.colors', |
|
127 | type=str, dest='InteractiveShell.colors', | |
112 | help="Set the color scheme (NoColor, Linux, and LightBG).", |
|
128 | help="Set the color scheme (NoColor, Linux, and LightBG).", | |
113 | metavar='InteractiveShell.colors') |
|
129 | metavar='InteractiveShell.colors') | |
114 | ), |
|
130 | paa('--color-info', | |
115 | (('--color-info',), dict( |
|
|||
116 | action='store_true', dest='InteractiveShell.color_info', |
|
131 | action='store_true', dest='InteractiveShell.color_info', | |
117 | help= |
|
132 | help= | |
118 | """IPython can display information about objects via a set of func- |
|
133 | """IPython can display information about objects via a set of func- | |
@@ -123,27 +138,20 b' cl_args = (' | |||||
123 | it and turn it on permanently in your ipython_config.py file if it |
|
138 | it and turn it on permanently in your ipython_config.py file if it | |
124 | works for you. Test it and turn it on permanently if it works with |
|
139 | works for you. Test it and turn it on permanently if it works with | |
125 | your system. The magic function %%color_info allows you to toggle this |
|
140 | your system. The magic function %%color_info allows you to toggle this | |
126 | inter- actively for testing.""" |
|
141 | inter- actively for testing.""") | |
127 | ) |
|
142 | paa('--no-color-info', | |
128 | ), |
|
|||
129 | (('--no-color-info',), dict( |
|
|||
130 | action='store_false', dest='InteractiveShell.color_info', |
|
143 | action='store_false', dest='InteractiveShell.color_info', | |
131 | help="Disable using colors for info related things.") |
|
144 | help="Disable using colors for info related things.") | |
132 | ), |
|
145 | paa('--confirm-exit', | |
133 | (('--confirm-exit',), dict( |
|
|||
134 | action='store_true', dest='InteractiveShell.confirm_exit', |
|
146 | action='store_true', dest='InteractiveShell.confirm_exit', | |
135 | help= |
|
147 | help= | |
136 | """Set to confirm when you try to exit IPython with an EOF (Control-D |
|
148 | """Set to confirm when you try to exit IPython with an EOF (Control-D | |
137 | in Unix, Control-Z/Enter in Windows). By typing 'exit', 'quit' or |
|
149 | in Unix, Control-Z/Enter in Windows). By typing 'exit', 'quit' or | |
138 | '%%Exit', you can force a direct exit without any confirmation. |
|
150 | '%%Exit', you can force a direct exit without any confirmation.""") | |
139 | """ |
|
151 | paa('--no-confirm-exit', | |
140 | ) |
|
|||
141 | ), |
|
|||
142 | (('--no-confirm-exit',), dict( |
|
|||
143 | action='store_false', dest='InteractiveShell.confirm_exit', |
|
152 | action='store_false', dest='InteractiveShell.confirm_exit', | |
144 | help="Don't prompt the user when exiting.") |
|
153 | help="Don't prompt the user when exiting.") | |
145 | ), |
|
154 | paa('--deep-reload', | |
146 | (('--deep-reload',), dict( |
|
|||
147 | action='store_true', dest='InteractiveShell.deep_reload', |
|
155 | action='store_true', dest='InteractiveShell.deep_reload', | |
148 | help= |
|
156 | help= | |
149 | """Enable deep (recursive) reloading by default. IPython can use the |
|
157 | """Enable deep (recursive) reloading by default. IPython can use the | |
@@ -155,47 +163,37 b' cl_args = (' | |||||
155 | deep_reload will still be available as dreload(). This fea- ture is off |
|
163 | deep_reload will still be available as dreload(). This fea- ture is off | |
156 | by default [which means that you have both normal reload() and |
|
164 | by default [which means that you have both normal reload() and | |
157 | dreload()].""") |
|
165 | dreload()].""") | |
158 | ), |
|
166 | paa('--no-deep-reload', | |
159 | (('--no-deep-reload',), dict( |
|
|||
160 | action='store_false', dest='InteractiveShell.deep_reload', |
|
167 | action='store_false', dest='InteractiveShell.deep_reload', | |
161 | help="Disable deep (recursive) reloading by default.") |
|
168 | help="Disable deep (recursive) reloading by default.") | |
162 | ), |
|
169 | paa('--editor', | |
163 | (('--editor',), dict( |
|
|||
164 | type=str, dest='InteractiveShell.editor', |
|
170 | type=str, dest='InteractiveShell.editor', | |
165 | help="Set the editor used by IPython (default to $EDITOR/vi/notepad).", |
|
171 | help="Set the editor used by IPython (default to $EDITOR/vi/notepad).", | |
166 | metavar='InteractiveShell.editor') |
|
172 | metavar='InteractiveShell.editor') | |
167 | ), |
|
173 | paa('--log','-l', | |
168 | (('--log','-l'), dict( |
|
|||
169 | action='store_true', dest='InteractiveShell.logstart', |
|
174 | action='store_true', dest='InteractiveShell.logstart', | |
170 | help="Start logging to the default log file (./ipython_log.py).") |
|
175 | help="Start logging to the default log file (./ipython_log.py).") | |
171 | ), |
|
176 | paa('--logfile','-lf', | |
172 | (('--logfile','-lf'), dict( |
|
|||
173 | type=unicode, dest='InteractiveShell.logfile', |
|
177 | type=unicode, dest='InteractiveShell.logfile', | |
174 | help="Start logging to logfile with this name.", |
|
178 | help="Start logging to logfile with this name.", | |
175 | metavar='InteractiveShell.logfile') |
|
179 | metavar='InteractiveShell.logfile') | |
176 | ), |
|
180 | paa('--log-append','-la', | |
177 | (('--log-append','-la'), dict( |
|
|||
178 | type=unicode, dest='InteractiveShell.logappend', |
|
181 | type=unicode, dest='InteractiveShell.logappend', | |
179 | help="Start logging to the given file in append mode.", |
|
182 | help="Start logging to the given file in append mode.", | |
180 | metavar='InteractiveShell.logfile') |
|
183 | metavar='InteractiveShell.logfile') | |
181 | ), |
|
184 | paa('--pdb', | |
182 | (('--pdb',), dict( |
|
|||
183 | action='store_true', dest='InteractiveShell.pdb', |
|
185 | action='store_true', dest='InteractiveShell.pdb', | |
184 | help="Enable auto calling the pdb debugger after every exception.") |
|
186 | help="Enable auto calling the pdb debugger after every exception.") | |
185 | ), |
|
187 | paa('--no-pdb', | |
186 | (('--no-pdb',), dict( |
|
|||
187 | action='store_false', dest='InteractiveShell.pdb', |
|
188 | action='store_false', dest='InteractiveShell.pdb', | |
188 | help="Disable auto calling the pdb debugger after every exception.") |
|
189 | help="Disable auto calling the pdb debugger after every exception.") | |
189 | ), |
|
190 | paa('--pprint', | |
190 | (('--pprint',), dict( |
|
|||
191 | action='store_true', dest='InteractiveShell.pprint', |
|
191 | action='store_true', dest='InteractiveShell.pprint', | |
192 | help="Enable auto pretty printing of results.") |
|
192 | help="Enable auto pretty printing of results.") | |
193 | ), |
|
193 | paa('--no-pprint', | |
194 | (('--no-pprint',), dict( |
|
|||
195 | action='store_false', dest='InteractiveShell.pprint', |
|
194 | action='store_false', dest='InteractiveShell.pprint', | |
196 | help="Disable auto auto pretty printing of results.") |
|
195 | help="Disable auto auto pretty printing of results.") | |
197 | ), |
|
196 | paa('--prompt-in1','-pi1', | |
198 | (('--prompt-in1','-pi1'), dict( |
|
|||
199 | type=str, dest='InteractiveShell.prompt_in1', |
|
197 | type=str, dest='InteractiveShell.prompt_in1', | |
200 | help= |
|
198 | help= | |
201 | """Set the main input prompt ('In [\#]: '). Note that if you are using |
|
199 | """Set the main input prompt ('In [\#]: '). Note that if you are using | |
@@ -206,8 +204,7 b' cl_args = (' | |||||
206 | prompt escapes are described in detail in the Customization section of |
|
204 | prompt escapes are described in detail in the Customization section of | |
207 | the IPython manual.""", |
|
205 | the IPython manual.""", | |
208 | metavar='InteractiveShell.prompt_in1') |
|
206 | metavar='InteractiveShell.prompt_in1') | |
209 | ), |
|
207 | paa('--prompt-in2','-pi2', | |
210 | (('--prompt-in2','-pi2'), dict( |
|
|||
211 | type=str, dest='InteractiveShell.prompt_in2', |
|
208 | type=str, dest='InteractiveShell.prompt_in2', | |
212 | help= |
|
209 | help= | |
213 | """Set the secondary input prompt (' .\D.: '). Similar to the previous |
|
210 | """Set the secondary input prompt (' .\D.: '). Similar to the previous | |
@@ -217,25 +214,20 b' cl_args = (' | |||||
217 | Default: ' .\D.: ' (note three spaces at the start for alignment with |
|
214 | Default: ' .\D.: ' (note three spaces at the start for alignment with | |
218 | 'In [\#]')""", |
|
215 | 'In [\#]')""", | |
219 | metavar='InteractiveShell.prompt_in2') |
|
216 | metavar='InteractiveShell.prompt_in2') | |
220 | ), |
|
217 | paa('--prompt-out','-po', | |
221 | (('--prompt-out','-po'), dict( |
|
|||
222 | type=str, dest='InteractiveShell.prompt_out', |
|
218 | type=str, dest='InteractiveShell.prompt_out', | |
223 | help="Set the output prompt ('Out[\#]:')", |
|
219 | help="Set the output prompt ('Out[\#]:')", | |
224 | metavar='InteractiveShell.prompt_out') |
|
220 | metavar='InteractiveShell.prompt_out') | |
225 | ), |
|
221 | paa('--quick', | |
226 | (('--quick',), dict( |
|
|||
227 | action='store_true', dest='Global.quick', |
|
222 | action='store_true', dest='Global.quick', | |
228 | help="Enable quick startup with no config files.") |
|
223 | help="Enable quick startup with no config files.") | |
229 | ), |
|
224 | paa('--readline', | |
230 | (('--readline',), dict( |
|
|||
231 | action='store_true', dest='InteractiveShell.readline_use', |
|
225 | action='store_true', dest='InteractiveShell.readline_use', | |
232 | help="Enable readline for command line usage.") |
|
226 | help="Enable readline for command line usage.") | |
233 | ), |
|
227 | paa('--no-readline', | |
234 | (('--no-readline',), dict( |
|
|||
235 | action='store_false', dest='InteractiveShell.readline_use', |
|
228 | action='store_false', dest='InteractiveShell.readline_use', | |
236 | help="Disable readline for command line usage.") |
|
229 | help="Disable readline for command line usage.") | |
237 | ), |
|
230 | paa('--screen-length','-sl', | |
238 | (('--screen-length','-sl'), dict( |
|
|||
239 | type=int, dest='InteractiveShell.screen_length', |
|
231 | type=int, dest='InteractiveShell.screen_length', | |
240 | help= |
|
232 | help= | |
241 | """Number of lines of your screen, used to control printing of very |
|
233 | """Number of lines of your screen, used to control printing of very | |
@@ -248,35 +240,28 b' cl_args = (' | |||||
248 | curses support), specify it yourself. Otherwise don't change the |
|
240 | curses support), specify it yourself. Otherwise don't change the | |
249 | default.""", |
|
241 | default.""", | |
250 | metavar='InteractiveShell.screen_length') |
|
242 | metavar='InteractiveShell.screen_length') | |
251 | ), |
|
243 | paa('--separate-in','-si', | |
252 | (('--separate-in','-si'), dict( |
|
|||
253 | type=str, dest='InteractiveShell.separate_in', |
|
244 | type=str, dest='InteractiveShell.separate_in', | |
254 | help="Separator before input prompts. Default '\\n'.", |
|
245 | help="Separator before input prompts. Default '\\n'.", | |
255 | metavar='InteractiveShell.separate_in') |
|
246 | metavar='InteractiveShell.separate_in') | |
256 | ), |
|
247 | paa('--separate-out','-so', | |
257 | (('--separate-out','-so'), dict( |
|
|||
258 | type=str, dest='InteractiveShell.separate_out', |
|
248 | type=str, dest='InteractiveShell.separate_out', | |
259 | help="Separator before output prompts. Default 0 (nothing).", |
|
249 | help="Separator before output prompts. Default 0 (nothing).", | |
260 | metavar='InteractiveShell.separate_out') |
|
250 | metavar='InteractiveShell.separate_out') | |
261 | ), |
|
251 | paa('--separate-out2','-so2', | |
262 | (('--separate-out2','-so2'), dict( |
|
|||
263 | type=str, dest='InteractiveShell.separate_out2', |
|
252 | type=str, dest='InteractiveShell.separate_out2', | |
264 | help="Separator after output prompts. Default 0 (nonight).", |
|
253 | help="Separator after output prompts. Default 0 (nonight).", | |
265 | metavar='InteractiveShell.separate_out2') |
|
254 | metavar='InteractiveShell.separate_out2') | |
266 | ), |
|
255 | paa('--no-sep', | |
267 | (('-no-sep',), dict( |
|
|||
268 | action='store_true', dest='Global.nosep', |
|
256 | action='store_true', dest='Global.nosep', | |
269 | help="Eliminate all spacing between prompts.") |
|
257 | help="Eliminate all spacing between prompts.") | |
270 | ), |
|
258 | paa('--term-title', | |
271 | (('--term-title',), dict( |
|
|||
272 | action='store_true', dest='InteractiveShell.term_title', |
|
259 | action='store_true', dest='InteractiveShell.term_title', | |
273 | help="Enable auto setting the terminal title.") |
|
260 | help="Enable auto setting the terminal title.") | |
274 | ), |
|
261 | paa('--no-term-title', | |
275 | (('--no-term-title',), dict( |
|
|||
276 | action='store_false', dest='InteractiveShell.term_title', |
|
262 | action='store_false', dest='InteractiveShell.term_title', | |
277 | help="Disable auto setting the terminal title.") |
|
263 | help="Disable auto setting the terminal title.") | |
278 | ), |
|
264 | paa('--xmode', | |
279 | (('--xmode',), dict( |
|
|||
280 | type=str, dest='InteractiveShell.xmode', |
|
265 | type=str, dest='InteractiveShell.xmode', | |
281 | help= |
|
266 | help= | |
282 | """Exception reporting mode ('Plain','Context','Verbose'). Plain: |
|
267 | """Exception reporting mode ('Plain','Context','Verbose'). Plain: | |
@@ -291,104 +276,121 b' cl_args = (' | |||||
291 | it more than once). |
|
276 | it more than once). | |
292 | """, |
|
277 | """, | |
293 | metavar='InteractiveShell.xmode') |
|
278 | metavar='InteractiveShell.xmode') | |
294 | ), |
|
279 | paa('--ext', | |
295 | (('--ext',), dict( |
|
|||
296 | type=str, dest='Global.extra_extension', |
|
280 | type=str, dest='Global.extra_extension', | |
297 | help="The dotted module name of an IPython extension to load.", |
|
281 | help="The dotted module name of an IPython extension to load.", | |
298 | metavar='Global.extra_extension') |
|
282 | metavar='Global.extra_extension') | |
299 | ), |
|
283 | paa('-c', | |
300 | (('-c',), dict( |
|
|||
301 | type=str, dest='Global.code_to_run', |
|
284 | type=str, dest='Global.code_to_run', | |
302 | help="Execute the given command string.", |
|
285 | help="Execute the given command string.", | |
303 | metavar='Global.code_to_run') |
|
286 | metavar='Global.code_to_run') | |
304 | ), |
|
287 | paa('-i', | |
305 | (('-i',), dict( |
|
|||
306 | action='store_true', dest='Global.force_interact', |
|
288 | action='store_true', dest='Global.force_interact', | |
307 | help= |
|
289 | help= | |
308 | "If running code from the command line, become interactive afterwards." |
|
290 | "If running code from the command line, become interactive afterwards.") | |
309 | ) |
|
|||
310 | ), |
|
|||
311 |
|
291 | |||
312 | # Options to start with GUI control enabled from the beginning |
|
292 | # Options to start with GUI control enabled from the beginning | |
313 |
|
|
293 | paa('--gui', | |
314 | type=str, dest='Global.gui', |
|
294 | type=str, dest='Global.gui', | |
315 | help="Enable GUI event loop integration ('qt', 'wx', 'gtk').", |
|
295 | help="Enable GUI event loop integration ('qt', 'wx', 'gtk').", | |
316 | metavar='gui-mode') |
|
296 | metavar='gui-mode') | |
317 | ), |
|
297 | paa('--pylab','-pylab', | |
318 |
|
||||
319 | (('--pylab','-pylab'), dict( |
|
|||
320 | type=str, dest='Global.pylab', |
|
298 | type=str, dest='Global.pylab', | |
321 | nargs='?', const='auto', metavar='gui-mode', |
|
299 | nargs='?', const='auto', metavar='gui-mode', | |
322 | help="Pre-load matplotlib and numpy for interactive use. "+ |
|
300 | help="Pre-load matplotlib and numpy for interactive use. "+ | |
323 | "If no value is given, the gui backend is matplotlib's, else use "+ |
|
301 | "If no value is given, the gui backend is matplotlib's, else use "+ | |
324 | "one of: ['tk', 'qt', 'wx', 'gtk'].") |
|
302 | "one of: ['tk', 'qt', 'wx', 'gtk'].") | |
325 | ), |
|
|||
326 |
|
303 | |||
327 | # Legacy GUI options. Leave them in for backwards compatibility, but the |
|
304 | # Legacy GUI options. Leave them in for backwards compatibility, but the | |
328 | # 'thread' names are really a misnomer now. |
|
305 | # 'thread' names are really a misnomer now. | |
329 |
|
|
306 | paa('--wthread', '-wthread', | |
330 | action='store_true', dest='Global.wthread', |
|
307 | action='store_true', dest='Global.wthread', | |
331 | help="Enable wxPython event loop integration "+ |
|
308 | help= | |
332 |
|
|
309 | """Enable wxPython event loop integration. (DEPRECATED, use --gui wx)""") | |
333 | ), |
|
310 | paa('--q4thread', '--qthread', '-q4thread', '-qthread', | |
334 | (('--q4thread','--qthread','-q4thread','-qthread'), dict( |
|
|||
335 | action='store_true', dest='Global.q4thread', |
|
311 | action='store_true', dest='Global.q4thread', | |
336 | help="Enable Qt4 event loop integration. Qt3 is no longer supported. "+ |
|
312 | help= | |
337 | "(DEPRECATED, use --gui qt)") |
|
313 | """Enable Qt4 event loop integration. Qt3 is no longer supported. | |
338 | ), |
|
314 | (DEPRECATED, use --gui qt)""") | |
339 |
|
|
315 | paa('--gthread', '-gthread', | |
340 | action='store_true', dest='Global.gthread', |
|
316 | action='store_true', dest='Global.gthread', | |
341 | help="Enable GTK event loop integration. "+ |
|
317 | help= | |
342 |
|
|
318 | """Enable GTK event loop integration. (DEPRECATED, use --gui gtk)""") | |
343 | ), |
|
319 | ||
344 | ) |
|
|||
345 |
|
320 | |||
346 | #----------------------------------------------------------------------------- |
|
321 | #----------------------------------------------------------------------------- | |
347 | # Main classes and functions |
|
322 | # Crash handler for this application | |
348 | #----------------------------------------------------------------------------- |
|
323 | #----------------------------------------------------------------------------- | |
349 |
|
324 | |||
350 | class IPythonApp(Application): |
|
|||
351 | name = u'ipython' |
|
|||
352 | #: argparse formats better the 'usage' than the 'description' field |
|
|||
353 | description = None |
|
|||
354 | #: usage message printed by argparse. If None, auto-generate |
|
|||
355 | usage = usage.cl_usage |
|
|||
356 |
|
325 | |||
357 | config_file_name = default_config_file_name |
|
326 | _message_template = """\ | |
|
327 | Oops, $self.app_name crashed. We do our best to make it stable, but... | |||
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 |
|
334 | It was left in the file named: | |
362 | _CrashHandler = crashhandler.IPythonCrashHandler |
|
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, |
|
339 | You can mail it to: $self.contact_name at $self.contact_email | |
365 | constructor_config=None, override_config=None, |
|
340 | with the subject '$self.app_name Crash Report'. | |
366 | **shell_params): |
|
|||
367 | """Create a new IPythonApp. |
|
|||
368 |
|
341 | |||
369 | See the parent class for details on how configuration is handled. |
|
342 | If you want to do it now, the following command will work (under Unix): | |
|
343 | mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname | |||
370 |
|
344 | |||
371 | Parameters |
|
345 | To ensure accurate tracking of this issue, please file a report about it at: | |
372 | ---------- |
|
346 | $self.bug_tracker | |
373 | argv : optional, list |
|
347 | """ | |
374 | If given, used as the command-line argv environment to read arguments |
|
|||
375 | from. |
|
|||
376 |
|
348 | |||
377 | constructor_config : optional, Config |
|
349 | class IPAppCrashHandler(CrashHandler): | |
378 | If given, additional config that is merged last, after internal |
|
350 | """sys.excepthook for IPython itself, leaves a detailed report on disk.""" | |
379 | defaults, command-line and file-based configs. |
|
|||
380 |
|
351 | |||
381 | override_config : optional, Config |
|
352 | message_template = _message_template | |
382 | If given, config that overrides all others unconditionally (except |
|
353 | ||
383 | for internal defaults, which ensure that all parameters exist). |
|
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 |
|
381 | ||
386 | All other keywords are passed to the :class:`iplib.InteractiveShell` |
|
382 | #----------------------------------------------------------------------------- | |
387 | constructor. |
|
383 | # Main classes and functions | |
388 | """ |
|
384 | #----------------------------------------------------------------------------- | |
389 | super(IPythonApp, self).__init__(argv, constructor_config, |
|
385 | ||
390 | override_config) |
|
386 | class IPythonApp(Application): | |
391 | self.shell_params = shell_params |
|
387 | name = u'ipython' | |
|
388 | #: argparse formats better the 'usage' than the 'description' field | |||
|
389 | description = None | |||
|
390 | usage = usage.cl_usage | |||
|
391 | command_line_loader = IPAppConfigLoader | |||
|
392 | default_config_file_name = default_config_file_name | |||
|
393 | crash_handler_class = IPAppCrashHandler | |||
392 |
|
394 | |||
393 | def create_default_config(self): |
|
395 | def create_default_config(self): | |
394 | super(IPythonApp, self).create_default_config() |
|
396 | super(IPythonApp, self).create_default_config() | |
@@ -474,8 +476,7 b' class IPythonApp(Application):' | |||||
474 | sys.path.insert(0, '') |
|
476 | sys.path.insert(0, '') | |
475 |
|
477 | |||
476 | # Create an InteractiveShell instance |
|
478 | # Create an InteractiveShell instance | |
477 |
self.shell = InteractiveShell(None, self.master_config |
|
479 | self.shell = InteractiveShell(None, self.master_config) | |
478 | **self.shell_params ) |
|
|||
479 |
|
480 | |||
480 | def post_construct(self): |
|
481 | def post_construct(self): | |
481 | """Do actions after construct, but before starting the app.""" |
|
482 | """Do actions after construct, but before starting the app.""" | |
@@ -485,7 +486,6 b' class IPythonApp(Application):' | |||||
485 | # based app, because we call shell.show_banner() by hand below |
|
486 | # based app, because we call shell.show_banner() by hand below | |
486 | # so the banner shows *before* all extension loading stuff. |
|
487 | # so the banner shows *before* all extension loading stuff. | |
487 | self.shell.display_banner = False |
|
488 | self.shell.display_banner = False | |
488 |
|
||||
489 | if config.Global.display_banner and \ |
|
489 | if config.Global.display_banner and \ | |
490 | config.Global.interact: |
|
490 | config.Global.interact: | |
491 | self.shell.show_banner() |
|
491 | self.shell.show_banner() | |
@@ -499,7 +499,6 b' class IPythonApp(Application):' | |||||
499 | self._run_exec_lines() |
|
499 | self._run_exec_lines() | |
500 | self._run_exec_files() |
|
500 | self._run_exec_files() | |
501 | self._run_cmd_line_code() |
|
501 | self._run_cmd_line_code() | |
502 | self._configure_xmode() |
|
|||
503 |
|
502 | |||
504 | def _enable_gui_pylab(self): |
|
503 | def _enable_gui_pylab(self): | |
505 | """Enable GUI event loop integration, taking pylab into account.""" |
|
504 | """Enable GUI event loop integration, taking pylab into account.""" | |
@@ -624,11 +623,6 b' class IPythonApp(Application):' | |||||
624 | self.log.warn("Error in executing file in user namespace: %s" % fname) |
|
623 | self.log.warn("Error in executing file in user namespace: %s" % fname) | |
625 | self.shell.showtraceback() |
|
624 | self.shell.showtraceback() | |
626 |
|
625 | |||
627 | def _configure_xmode(self): |
|
|||
628 | # XXX - shouldn't this be read from the config? I'm still a little |
|
|||
629 | # lost with all the details of handling the new config guys... |
|
|||
630 | self.shell.InteractiveTB.set_mode(mode=self.shell.xmode) |
|
|||
631 |
|
||||
632 | def start_app(self): |
|
626 | def start_app(self): | |
633 | if self.master_config.Global.interact: |
|
627 | if self.master_config.Global.interact: | |
634 | self.log.debug("Starting IPython's mainloop...") |
|
628 | self.log.debug("Starting IPython's mainloop...") | |
@@ -653,3 +647,7 b' def launch_new_instance():' | |||||
653 | """Create and run a full blown IPython instance""" |
|
647 | """Create and run a full blown IPython instance""" | |
654 | app = IPythonApp() |
|
648 | app = IPythonApp() | |
655 | app.start() |
|
649 | app.start() | |
|
650 | ||||
|
651 | ||||
|
652 | if __name__ == '__main__': | |||
|
653 | launch_new_instance() |
@@ -20,7 +20,6 b' from __future__ import with_statement' | |||||
20 | from __future__ import absolute_import |
|
20 | from __future__ import absolute_import | |
21 |
|
21 | |||
22 | import __builtin__ |
|
22 | import __builtin__ | |
23 | import StringIO |
|
|||
24 | import bdb |
|
23 | import bdb | |
25 | import codeop |
|
24 | import codeop | |
26 | import exceptions |
|
25 | import exceptions | |
@@ -47,29 +46,35 b' from IPython.core.logger import Logger' | |||||
47 | from IPython.core.magic import Magic |
|
46 | from IPython.core.magic import Magic | |
48 | from IPython.core.prefilter import PrefilterManager |
|
47 | from IPython.core.prefilter import PrefilterManager | |
49 | from IPython.core.prompts import CachedOutput |
|
48 | from IPython.core.prompts import CachedOutput | |
50 | from IPython.core.pylabtools import pylab_activate |
|
|||
51 | from IPython.core.usage import interactive_usage, default_banner |
|
49 | from IPython.core.usage import interactive_usage, default_banner | |
|
50 | import IPython.core.hooks | |||
52 | from IPython.external.Itpl import ItplNS |
|
51 | from IPython.external.Itpl import ItplNS | |
53 | from IPython.lib.inputhook import enable_gui |
|
52 | from IPython.lib.inputhook import enable_gui | |
54 | from IPython.lib.backgroundjobs import BackgroundJobManager |
|
53 | from IPython.lib.backgroundjobs import BackgroundJobManager | |
|
54 | from IPython.lib.pylabtools import pylab_activate | |||
55 | from IPython.utils import PyColorize |
|
55 | from IPython.utils import PyColorize | |
56 | from IPython.utils import pickleshare |
|
56 | from IPython.utils import pickleshare | |
57 |
from IPython.utils. |
|
57 | from IPython.utils.doctestreload import doctest_reload | |
58 | from IPython.utils.ipstruct import Struct |
|
58 | from IPython.utils.ipstruct import Struct | |
59 |
from IPython.utils. |
|
59 | from IPython.utils.io import Term, ask_yes_no | |
|
60 | from IPython.utils.path import get_home_dir, get_ipython_dir, HomeDirError | |||
|
61 | from IPython.utils.process import ( | |||
|
62 | abbrev_cwd, | |||
|
63 | getoutput, | |||
|
64 | getoutputerror | |||
|
65 | ) | |||
|
66 | # import IPython.utils.rlineimpl as readline | |||
60 | from IPython.utils.strdispatch import StrDispatch |
|
67 | from IPython.utils.strdispatch import StrDispatch | |
61 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
68 | from IPython.utils.syspathcontext import prepended_to_syspath | |
62 |
|
69 | from IPython.utils.terminal import toggle_set_term_title, set_term_title | ||
63 | # XXX - need to clean up this import * line |
|
70 | from IPython.utils.warn import warn, error, fatal | |
64 | from IPython.utils.genutils import * |
|
|||
65 |
|
||||
66 | # from IPython.utils import growl |
|
|||
67 | # growl.start("IPython") |
|
|||
68 |
|
||||
69 | from IPython.utils.traitlets import ( |
|
71 | from IPython.utils.traitlets import ( | |
70 | Int, Str, CBool, CaselessStrEnum, Enum, List, Unicode |
|
72 | Int, Str, CBool, CaselessStrEnum, Enum, List, Unicode | |
71 | ) |
|
73 | ) | |
72 |
|
74 | |||
|
75 | # from IPython.utils import growl | |||
|
76 | # growl.start("IPython") | |||
|
77 | ||||
73 | #----------------------------------------------------------------------------- |
|
78 | #----------------------------------------------------------------------------- | |
74 | # Globals |
|
79 | # Globals | |
75 | #----------------------------------------------------------------------------- |
|
80 | #----------------------------------------------------------------------------- | |
@@ -186,64 +191,6 b' class SeparateStr(Str):' | |||||
186 | return super(SeparateStr, self).validate(obj, value) |
|
191 | return super(SeparateStr, self).validate(obj, value) | |
187 |
|
192 | |||
188 |
|
193 | |||
189 | def make_user_namespaces(user_ns=None, user_global_ns=None): |
|
|||
190 | """Return a valid local and global user interactive namespaces. |
|
|||
191 |
|
||||
192 | This builds a dict with the minimal information needed to operate as a |
|
|||
193 | valid IPython user namespace, which you can pass to the various |
|
|||
194 | embedding classes in ipython. The default implementation returns the |
|
|||
195 | same dict for both the locals and the globals to allow functions to |
|
|||
196 | refer to variables in the namespace. Customized implementations can |
|
|||
197 | return different dicts. The locals dictionary can actually be anything |
|
|||
198 | following the basic mapping protocol of a dict, but the globals dict |
|
|||
199 | must be a true dict, not even a subclass. It is recommended that any |
|
|||
200 | custom object for the locals namespace synchronize with the globals |
|
|||
201 | dict somehow. |
|
|||
202 |
|
||||
203 | Raises TypeError if the provided globals namespace is not a true dict. |
|
|||
204 |
|
||||
205 | Parameters |
|
|||
206 | ---------- |
|
|||
207 | user_ns : dict-like, optional |
|
|||
208 | The current user namespace. The items in this namespace should |
|
|||
209 | be included in the output. If None, an appropriate blank |
|
|||
210 | namespace should be created. |
|
|||
211 | user_global_ns : dict, optional |
|
|||
212 | The current user global namespace. The items in this namespace |
|
|||
213 | should be included in the output. If None, an appropriate |
|
|||
214 | blank namespace should be created. |
|
|||
215 |
|
||||
216 | Returns |
|
|||
217 | ------- |
|
|||
218 | A pair of dictionary-like object to be used as the local namespace |
|
|||
219 | of the interpreter and a dict to be used as the global namespace. |
|
|||
220 | """ |
|
|||
221 |
|
||||
222 |
|
||||
223 | # We must ensure that __builtin__ (without the final 's') is always |
|
|||
224 | # available and pointing to the __builtin__ *module*. For more details: |
|
|||
225 | # http://mail.python.org/pipermail/python-dev/2001-April/014068.html |
|
|||
226 |
|
||||
227 | if user_ns is None: |
|
|||
228 | # Set __name__ to __main__ to better match the behavior of the |
|
|||
229 | # normal interpreter. |
|
|||
230 | user_ns = {'__name__' :'__main__', |
|
|||
231 | '__builtin__' : __builtin__, |
|
|||
232 | '__builtins__' : __builtin__, |
|
|||
233 | } |
|
|||
234 | else: |
|
|||
235 | user_ns.setdefault('__name__','__main__') |
|
|||
236 | user_ns.setdefault('__builtin__',__builtin__) |
|
|||
237 | user_ns.setdefault('__builtins__',__builtin__) |
|
|||
238 |
|
||||
239 | if user_global_ns is None: |
|
|||
240 | user_global_ns = user_ns |
|
|||
241 | if type(user_global_ns) is not dict: |
|
|||
242 | raise TypeError("user_global_ns must be a true dict; got %r" |
|
|||
243 | % type(user_global_ns)) |
|
|||
244 |
|
||||
245 | return user_ns, user_global_ns |
|
|||
246 |
|
||||
247 | #----------------------------------------------------------------------------- |
|
194 | #----------------------------------------------------------------------------- | |
248 | # Main IPython class |
|
195 | # Main IPython class | |
249 | #----------------------------------------------------------------------------- |
|
196 | #----------------------------------------------------------------------------- | |
@@ -658,7 +605,6 b' class InteractiveShell(Component, Magic):' | |||||
658 | self.strdispatchers = {} |
|
605 | self.strdispatchers = {} | |
659 |
|
606 | |||
660 | # Set all default hooks, defined in the IPython.hooks module. |
|
607 | # Set all default hooks, defined in the IPython.hooks module. | |
661 | import IPython.core.hooks |
|
|||
662 | hooks = IPython.core.hooks |
|
608 | hooks = IPython.core.hooks | |
663 | for hook_name in hooks.__all__: |
|
609 | for hook_name in hooks.__all__: | |
664 | # default hooks have priority 100, i.e. low; user hooks should have |
|
610 | # default hooks have priority 100, i.e. low; user hooks should have | |
@@ -876,7 +822,7 b' class InteractiveShell(Component, Magic):' | |||||
876 | # These routines return properly built dicts as needed by the rest of |
|
822 | # These routines return properly built dicts as needed by the rest of | |
877 | # the code, and can also be used by extension writers to generate |
|
823 | # the code, and can also be used by extension writers to generate | |
878 | # properly initialized namespaces. |
|
824 | # properly initialized namespaces. | |
879 | user_ns, user_global_ns = make_user_namespaces(user_ns, user_global_ns) |
|
825 | user_ns, user_global_ns = self.make_user_namespaces(user_ns, user_global_ns) | |
880 |
|
826 | |||
881 | # Assign namespaces |
|
827 | # Assign namespaces | |
882 | # This is the namespace where all normal user variables live |
|
828 | # This is the namespace where all normal user variables live | |
@@ -887,7 +833,7 b' class InteractiveShell(Component, Magic):' | |||||
887 | # loaded at startup, so we can list later only variables defined in |
|
833 | # loaded at startup, so we can list later only variables defined in | |
888 | # actual interactive use. Since it is always a subset of user_ns, it |
|
834 | # actual interactive use. Since it is always a subset of user_ns, it | |
889 | # doesn't need to be separately tracked in the ns_table. |
|
835 | # doesn't need to be separately tracked in the ns_table. | |
890 |
self.user_ |
|
836 | self.user_ns_hidden = {} | |
891 |
|
837 | |||
892 | # A namespace to keep track of internal data structures to prevent |
|
838 | # A namespace to keep track of internal data structures to prevent | |
893 | # them from cluttering user-visible stuff. Will be updated later |
|
839 | # them from cluttering user-visible stuff. Will be updated later | |
@@ -933,9 +879,67 b' class InteractiveShell(Component, Magic):' | |||||
933 | # Similarly, track all namespaces where references can be held and that |
|
879 | # Similarly, track all namespaces where references can be held and that | |
934 | # we can safely clear (so it can NOT include builtin). This one can be |
|
880 | # we can safely clear (so it can NOT include builtin). This one can be | |
935 | # a simple list. |
|
881 | # a simple list. | |
936 |
self.ns_refs_table = [ user_ns, user_global_ns, self.user_ |
|
882 | self.ns_refs_table = [ user_ns, user_global_ns, self.user_ns_hidden, | |
937 | self.internal_ns, self._main_ns_cache ] |
|
883 | self.internal_ns, self._main_ns_cache ] | |
938 |
|
884 | |||
|
885 | def make_user_namespaces(self, user_ns=None, user_global_ns=None): | |||
|
886 | """Return a valid local and global user interactive namespaces. | |||
|
887 | ||||
|
888 | This builds a dict with the minimal information needed to operate as a | |||
|
889 | valid IPython user namespace, which you can pass to the various | |||
|
890 | embedding classes in ipython. The default implementation returns the | |||
|
891 | same dict for both the locals and the globals to allow functions to | |||
|
892 | refer to variables in the namespace. Customized implementations can | |||
|
893 | return different dicts. The locals dictionary can actually be anything | |||
|
894 | following the basic mapping protocol of a dict, but the globals dict | |||
|
895 | must be a true dict, not even a subclass. It is recommended that any | |||
|
896 | custom object for the locals namespace synchronize with the globals | |||
|
897 | dict somehow. | |||
|
898 | ||||
|
899 | Raises TypeError if the provided globals namespace is not a true dict. | |||
|
900 | ||||
|
901 | Parameters | |||
|
902 | ---------- | |||
|
903 | user_ns : dict-like, optional | |||
|
904 | The current user namespace. The items in this namespace should | |||
|
905 | be included in the output. If None, an appropriate blank | |||
|
906 | namespace should be created. | |||
|
907 | user_global_ns : dict, optional | |||
|
908 | The current user global namespace. The items in this namespace | |||
|
909 | should be included in the output. If None, an appropriate | |||
|
910 | blank namespace should be created. | |||
|
911 | ||||
|
912 | Returns | |||
|
913 | ------- | |||
|
914 | A pair of dictionary-like object to be used as the local namespace | |||
|
915 | of the interpreter and a dict to be used as the global namespace. | |||
|
916 | """ | |||
|
917 | ||||
|
918 | ||||
|
919 | # We must ensure that __builtin__ (without the final 's') is always | |||
|
920 | # available and pointing to the __builtin__ *module*. For more details: | |||
|
921 | # http://mail.python.org/pipermail/python-dev/2001-April/014068.html | |||
|
922 | ||||
|
923 | if user_ns is None: | |||
|
924 | # Set __name__ to __main__ to better match the behavior of the | |||
|
925 | # normal interpreter. | |||
|
926 | user_ns = {'__name__' :'__main__', | |||
|
927 | '__builtin__' : __builtin__, | |||
|
928 | '__builtins__' : __builtin__, | |||
|
929 | } | |||
|
930 | else: | |||
|
931 | user_ns.setdefault('__name__','__main__') | |||
|
932 | user_ns.setdefault('__builtin__',__builtin__) | |||
|
933 | user_ns.setdefault('__builtins__',__builtin__) | |||
|
934 | ||||
|
935 | if user_global_ns is None: | |||
|
936 | user_global_ns = user_ns | |||
|
937 | if type(user_global_ns) is not dict: | |||
|
938 | raise TypeError("user_global_ns must be a true dict; got %r" | |||
|
939 | % type(user_global_ns)) | |||
|
940 | ||||
|
941 | return user_ns, user_global_ns | |||
|
942 | ||||
939 | def init_sys_modules(self): |
|
943 | def init_sys_modules(self): | |
940 | # We need to insert into sys.modules something that looks like a |
|
944 | # We need to insert into sys.modules something that looks like a | |
941 | # module but which accesses the IPython namespace, for shelve and |
|
945 | # module but which accesses the IPython namespace, for shelve and | |
@@ -974,7 +978,7 b' class InteractiveShell(Component, Magic):' | |||||
974 | therm. |
|
978 | therm. | |
975 | """ |
|
979 | """ | |
976 | # This function works in two parts: first we put a few things in |
|
980 | # This function works in two parts: first we put a few things in | |
977 |
# user_ns, and we sync that contents into user_ |
|
981 | # user_ns, and we sync that contents into user_ns_hidden so that these | |
978 | # initial variables aren't shown by %who. After the sync, we add the |
|
982 | # initial variables aren't shown by %who. After the sync, we add the | |
979 | # rest of what we *do* want the user to see with %who even on a new |
|
983 | # rest of what we *do* want the user to see with %who even on a new | |
980 | # session (probably nothing, so theye really only see their own stuff) |
|
984 | # session (probably nothing, so theye really only see their own stuff) | |
@@ -1014,9 +1018,9 b' class InteractiveShell(Component, Magic):' | |||||
1014 | # Store myself as the public api!!! |
|
1018 | # Store myself as the public api!!! | |
1015 | ns['get_ipython'] = self.get_ipython |
|
1019 | ns['get_ipython'] = self.get_ipython | |
1016 |
|
1020 | |||
1017 |
# Sync what we've added so far to user_ |
|
1021 | # Sync what we've added so far to user_ns_hidden so these aren't seen | |
1018 | # by %who |
|
1022 | # by %who | |
1019 |
self.user_ |
|
1023 | self.user_ns_hidden.update(ns) | |
1020 |
|
1024 | |||
1021 | # Anything put into ns now would show up in %who. Think twice before |
|
1025 | # Anything put into ns now would show up in %who. Think twice before | |
1022 | # putting anything here, as we really want %who to show the user their |
|
1026 | # putting anything here, as we really want %who to show the user their | |
@@ -1089,7 +1093,7 b' class InteractiveShell(Component, Magic):' | |||||
1089 | self.user_ns.update(vdict) |
|
1093 | self.user_ns.update(vdict) | |
1090 |
|
1094 | |||
1091 | # And configure interactive visibility |
|
1095 | # And configure interactive visibility | |
1092 |
config_ns = self.user_ |
|
1096 | config_ns = self.user_ns_hidden | |
1093 | if interactive: |
|
1097 | if interactive: | |
1094 | for name, val in vdict.iteritems(): |
|
1098 | for name, val in vdict.iteritems(): | |
1095 | config_ns.pop(name, None) |
|
1099 | config_ns.pop(name, None) | |
@@ -1164,7 +1168,9 b' class InteractiveShell(Component, Magic):' | |||||
1164 | Convert func into callable that saves & restores |
|
1168 | Convert func into callable that saves & restores | |
1165 | history around the call """ |
|
1169 | history around the call """ | |
1166 |
|
1170 | |||
1167 |
if |
|
1171 | if self.has_readline: | |
|
1172 | from IPython.utils import rlineimpl as readline | |||
|
1173 | else: | |||
1168 | return func |
|
1174 | return func | |
1169 |
|
1175 | |||
1170 | def wrapper(): |
|
1176 | def wrapper(): | |
@@ -1198,6 +1204,9 b' class InteractiveShell(Component, Magic):' | |||||
1198 | # and add any custom exception handlers the user may have specified |
|
1204 | # and add any custom exception handlers the user may have specified | |
1199 | self.set_custom_exc(*custom_exceptions) |
|
1205 | self.set_custom_exc(*custom_exceptions) | |
1200 |
|
1206 | |||
|
1207 | # Set the exception mode | |||
|
1208 | self.InteractiveTB.set_mode(mode=self.xmode) | |||
|
1209 | ||||
1201 | def set_custom_exc(self,exc_tuple,handler): |
|
1210 | def set_custom_exc(self,exc_tuple,handler): | |
1202 | """set_custom_exc(exc_tuple,handler) |
|
1211 | """set_custom_exc(exc_tuple,handler) | |
1203 |
|
1212 | |||
@@ -2351,6 +2360,9 b' class InteractiveShell(Component, Magic):' | |||||
2351 | to make it easy to write extensions, you can also put your extensions |
|
2360 | to make it easy to write extensions, you can also put your extensions | |
2352 | in ``os.path.join(self.ipython_dir, 'extensions')``. This directory |
|
2361 | in ``os.path.join(self.ipython_dir, 'extensions')``. This directory | |
2353 | is added to ``sys.path`` automatically. |
|
2362 | is added to ``sys.path`` automatically. | |
|
2363 | ||||
|
2364 | If :func:`load_ipython_extension` returns anything, this function | |||
|
2365 | will return that object. | |||
2354 | """ |
|
2366 | """ | |
2355 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
2367 | from IPython.utils.syspathcontext import prepended_to_syspath | |
2356 |
|
2368 | |||
@@ -2495,11 +2507,11 b' class InteractiveShell(Component, Magic):' | |||||
2495 | # We want to prevent the loading of pylab to pollute the user's |
|
2507 | # We want to prevent the loading of pylab to pollute the user's | |
2496 | # namespace as shown by the %who* magics, so we execute the activation |
|
2508 | # namespace as shown by the %who* magics, so we execute the activation | |
2497 | # code in an empty namespace, and we update *both* user_ns and |
|
2509 | # code in an empty namespace, and we update *both* user_ns and | |
2498 |
# user_ |
|
2510 | # user_ns_hidden with this information. | |
2499 | ns = {} |
|
2511 | ns = {} | |
2500 | gui = pylab_activate(ns, gui) |
|
2512 | gui = pylab_activate(ns, gui) | |
2501 | self.user_ns.update(ns) |
|
2513 | self.user_ns.update(ns) | |
2502 |
self.user_ |
|
2514 | self.user_ns_hidden.update(ns) | |
2503 | # Now we must activate the gui pylab wants to use, and fix %run to take |
|
2515 | # Now we must activate the gui pylab wants to use, and fix %run to take | |
2504 | # plot updates into account |
|
2516 | # plot updates into account | |
2505 | enable_gui(gui) |
|
2517 | enable_gui(gui) |
@@ -7,7 +7,7 b'' | |||||
7 | # the file COPYING, distributed as part of this software. |
|
7 | # the file COPYING, distributed as part of this software. | |
8 | #***************************************************************************** |
|
8 | #***************************************************************************** | |
9 |
|
9 | |||
10 |
from IPython.utils. |
|
10 | from IPython.utils.io import Term | |
11 | from IPython.core.autocall import IPyAutocall |
|
11 | from IPython.core.autocall import IPyAutocall | |
12 |
|
12 | |||
13 | class Macro(IPyAutocall): |
|
13 | class Macro(IPyAutocall): |
@@ -1,35 +1,33 b'' | |||||
1 |
# |
|
1 | # encoding: utf-8 | |
2 | """Magic functions for InteractiveShell. |
|
2 | """Magic functions for InteractiveShell. | |
3 | """ |
|
3 | """ | |
4 |
|
4 | |||
5 | #***************************************************************************** |
|
5 | #----------------------------------------------------------------------------- | |
6 |
# |
|
6 | # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and | |
7 |
# |
|
7 | # Copyright (C) 2001-2007 Fernando Perez <fperez@colorado.edu> | |
8 | # |
|
8 | # Copyright (C) 2008-2009 The IPython Development Team | |
|
9 | ||||
9 | # Distributed under the terms of the BSD License. The full license is in |
|
10 | # Distributed under the terms of the BSD License. The full license is in | |
10 | # the file COPYING, distributed as part of this software. |
|
11 | # the file COPYING, distributed as part of this software. | |
11 | #***************************************************************************** |
|
12 | #----------------------------------------------------------------------------- | |
12 |
|
13 | |||
13 | #**************************************************************************** |
|
14 | #----------------------------------------------------------------------------- | |
14 | # Modules and globals |
|
15 | # Imports | |
|
16 | #----------------------------------------------------------------------------- | |||
15 |
|
17 | |||
16 | # Python standard modules |
|
|||
17 | import __builtin__ |
|
18 | import __builtin__ | |
18 | import bdb |
|
19 | import bdb | |
19 | import inspect |
|
20 | import inspect | |
20 | import os |
|
21 | import os | |
21 | import pdb |
|
|||
22 | import pydoc |
|
|||
23 | import sys |
|
22 | import sys | |
24 | import shutil |
|
23 | import shutil | |
25 | import re |
|
24 | import re | |
26 | import tempfile |
|
|||
27 | import time |
|
25 | import time | |
28 | import cPickle as pickle |
|
|||
29 | import textwrap |
|
26 | import textwrap | |
|
27 | import types | |||
30 | from cStringIO import StringIO |
|
28 | from cStringIO import StringIO | |
31 | from getopt import getopt,GetoptError |
|
29 | from getopt import getopt,GetoptError | |
32 |
from pprint import |
|
30 | from pprint import pformat | |
33 |
|
31 | |||
34 | # cProfile was added in Python2.5 |
|
32 | # cProfile was added in Python2.5 | |
35 | try: |
|
33 | try: | |
@@ -42,10 +40,7 b' except ImportError:' | |||||
42 | except ImportError: |
|
40 | except ImportError: | |
43 | profile = pstats = None |
|
41 | profile = pstats = None | |
44 |
|
42 | |||
45 | # Homebrewed |
|
|||
46 | import IPython |
|
43 | import IPython | |
47 | import IPython.utils.generics |
|
|||
48 |
|
||||
49 | from IPython.core import debugger, oinspect |
|
44 | from IPython.core import debugger, oinspect | |
50 | from IPython.core.error import TryNext |
|
45 | from IPython.core.error import TryNext | |
51 | from IPython.core.error import UsageError |
|
46 | from IPython.core.error import UsageError | |
@@ -53,20 +48,24 b' from IPython.core.fakemodule import FakeModule' | |||||
53 | from IPython.core.macro import Macro |
|
48 | from IPython.core.macro import Macro | |
54 | from IPython.core.page import page |
|
49 | from IPython.core.page import page | |
55 | from IPython.core.prefilter import ESC_MAGIC |
|
50 | from IPython.core.prefilter import ESC_MAGIC | |
56 |
from IPython. |
|
51 | from IPython.lib.pylabtools import mpl_runner | |
57 | from IPython.lib.inputhook import enable_gui |
|
52 | from IPython.lib.inputhook import enable_gui | |
58 |
from IPython.external.Itpl import |
|
53 | from IPython.external.Itpl import itpl, printpl | |
59 | from IPython.testing import decorators as testdec |
|
54 | from IPython.testing import decorators as testdec | |
60 |
from IPython.utils import |
|
55 | from IPython.utils.io import Term, file_read, nlprint | |
61 |
from IPython.utils import |
|
56 | from IPython.utils.path import get_py_filename | |
62 |
from IPython.utils. |
|
57 | from IPython.utils.process import arg_split, abbrev_cwd | |
|
58 | from IPython.utils.terminal import set_term_title | |||
|
59 | from IPython.utils.text import LSString, SList, StringTypes | |||
|
60 | from IPython.utils.timing import clock, clock2 | |||
|
61 | from IPython.utils.warn import warn, error | |||
63 | from IPython.utils.ipstruct import Struct |
|
62 | from IPython.utils.ipstruct import Struct | |
|
63 | import IPython.utils.generics | |||
64 |
|
64 | |||
65 | # XXX - We need to switch to explicit imports here with genutils |
|
65 | #----------------------------------------------------------------------------- | |
66 | from IPython.utils.genutils import * |
|
|||
67 |
|
||||
68 | #*************************************************************************** |
|
|||
69 | # Utility functions |
|
66 | # Utility functions | |
|
67 | #----------------------------------------------------------------------------- | |||
|
68 | ||||
70 | def on_off(tag): |
|
69 | def on_off(tag): | |
71 | """Return an ON/OFF string for a 1/0 input. Simple utility function.""" |
|
70 | """Return an ON/OFF string for a 1/0 input. Simple utility function.""" | |
72 | return ['OFF','ON'][tag] |
|
71 | return ['OFF','ON'][tag] | |
@@ -94,6 +93,9 b' def compress_dhist(dh):' | |||||
94 | # on construction of the main InteractiveShell object. Something odd is going |
|
93 | # on construction of the main InteractiveShell object. Something odd is going | |
95 | # on with super() calls, Component and the MRO... For now leave it as-is, but |
|
94 | # on with super() calls, Component and the MRO... For now leave it as-is, but | |
96 | # eventually this needs to be clarified. |
|
95 | # eventually this needs to be clarified. | |
|
96 | # BG: This is because InteractiveShell inherits from this, but is itself a | |||
|
97 | # Component. This messes up the MRO in some way. The fix is that we need to | |||
|
98 | # make Magic a component that InteractiveShell does not subclass. | |||
97 |
|
99 | |||
98 | class Magic: |
|
100 | class Magic: | |
99 | """Magic functions for InteractiveShell. |
|
101 | """Magic functions for InteractiveShell. | |
@@ -277,7 +279,7 b' python-profiler package from non-free.""")' | |||||
277 | def arg_err(self,func): |
|
279 | def arg_err(self,func): | |
278 | """Print docstring if incorrect arguments were passed""" |
|
280 | """Print docstring if incorrect arguments were passed""" | |
279 | print 'Error in arguments:' |
|
281 | print 'Error in arguments:' | |
280 |
print |
|
282 | print oinspect.getdoc(func) | |
281 |
|
283 | |||
282 | def format_latex(self,strng): |
|
284 | def format_latex(self,strng): | |
283 | """Format a string for latex inclusion.""" |
|
285 | """Format a string for latex inclusion.""" | |
@@ -884,10 +886,10 b' Currently the magic system has the following functions:\\n"""' | |||||
884 |
|
886 | |||
885 | user_ns = self.shell.user_ns |
|
887 | user_ns = self.shell.user_ns | |
886 | internal_ns = self.shell.internal_ns |
|
888 | internal_ns = self.shell.internal_ns | |
887 |
user_ |
|
889 | user_ns_hidden = self.shell.user_ns_hidden | |
888 | out = [ i for i in user_ns |
|
890 | out = [ i for i in user_ns | |
889 | if not i.startswith('_') \ |
|
891 | if not i.startswith('_') \ | |
890 |
and not (i in internal_ns or i in user_ |
|
892 | and not (i in internal_ns or i in user_ns_hidden) ] | |
891 |
|
893 | |||
892 | typelist = parameter_s.split() |
|
894 | typelist = parameter_s.split() | |
893 | if typelist: |
|
895 | if typelist: | |
@@ -1170,7 +1172,7 b' Currently the magic system has the following functions:\\n"""' | |||||
1170 | started = logger.logstart(logfname,loghead,logmode, |
|
1172 | started = logger.logstart(logfname,loghead,logmode, | |
1171 | log_output,timestamp,log_raw_input) |
|
1173 | log_output,timestamp,log_raw_input) | |
1172 | except: |
|
1174 | except: | |
1173 |
|
|
1175 | self.shell.logfile = old_logfile | |
1174 | warn("Couldn't start log: %s" % sys.exc_info()[1]) |
|
1176 | warn("Couldn't start log: %s" % sys.exc_info()[1]) | |
1175 | else: |
|
1177 | else: | |
1176 | # log input history up to this point, optionally interleaving |
|
1178 | # log input history up to this point, optionally interleaving | |
@@ -2811,7 +2813,7 b' Defaulting color scheme to \'NoColor\'"""' | |||||
2811 | try: |
|
2813 | try: | |
2812 | os.chdir(os.path.expanduser(ps)) |
|
2814 | os.chdir(os.path.expanduser(ps)) | |
2813 | if self.shell.term_title: |
|
2815 | if self.shell.term_title: | |
2814 |
|
|
2816 | set_term_title('IPython: ' + abbrev_cwd()) | |
2815 | except OSError: |
|
2817 | except OSError: | |
2816 | print sys.exc_info()[1] |
|
2818 | print sys.exc_info()[1] | |
2817 | else: |
|
2819 | else: | |
@@ -2824,7 +2826,7 b' Defaulting color scheme to \'NoColor\'"""' | |||||
2824 | else: |
|
2826 | else: | |
2825 | os.chdir(self.shell.home_dir) |
|
2827 | os.chdir(self.shell.home_dir) | |
2826 | if self.shell.term_title: |
|
2828 | if self.shell.term_title: | |
2827 |
|
|
2829 | set_term_title('IPython: ' + '~') | |
2828 | cwd = os.getcwd() |
|
2830 | cwd = os.getcwd() | |
2829 | dhist = self.shell.user_ns['_dh'] |
|
2831 | dhist = self.shell.user_ns['_dh'] | |
2830 |
|
2832 |
@@ -27,10 +27,11 b' import sys' | |||||
27 | import types |
|
27 | import types | |
28 |
|
28 | |||
29 | # IPython's own |
|
29 | # IPython's own | |
30 | from IPython.utils import PyColorize |
|
|||
31 | from IPython.utils.genutils import indent, Term |
|
|||
32 | from IPython.core.page import page |
|
30 | from IPython.core.page import page | |
33 | from IPython.external.Itpl import itpl |
|
31 | from IPython.external.Itpl import itpl | |
|
32 | from IPython.utils import PyColorize | |||
|
33 | from IPython.utils.io import Term | |||
|
34 | from IPython.utils.text import indent | |||
34 | from IPython.utils.wildcard import list_namespace |
|
35 | from IPython.utils.wildcard import list_namespace | |
35 | from IPython.utils.coloransi import * |
|
36 | from IPython.utils.coloransi import * | |
36 |
|
37 |
@@ -30,15 +30,15 b' rid of that dependency, we could move it there.' | |||||
30 | import os |
|
30 | import os | |
31 | import re |
|
31 | import re | |
32 | import sys |
|
32 | import sys | |
|
33 | import tempfile | |||
33 |
|
34 | |||
34 | from IPython.core import ipapi |
|
35 | from IPython.core import ipapi | |
35 | from IPython.core.error import TryNext |
|
36 | from IPython.core.error import TryNext | |
36 |
from IPython.utils. |
|
37 | from IPython.utils.cursesimport import use_curses | |
37 | chop, Term, USE_CURSES |
|
38 | from IPython.utils.data import chop | |
38 | ) |
|
39 | from IPython.utils.io import Term | |
39 |
|
40 | from IPython.utils.process import xsys | ||
40 | if os.name == "nt": |
|
41 | from IPython.utils.terminal import get_terminal_size | |
41 | from IPython.utils.winconsole import get_console_size |
|
|||
42 |
|
42 | |||
43 |
|
43 | |||
44 | #----------------------------------------------------------------------------- |
|
44 | #----------------------------------------------------------------------------- | |
@@ -69,7 +69,7 b' def page_dumb(strng,start=0,screen_lines=25):' | |||||
69 | last_escape = esc_list[-1] |
|
69 | last_escape = esc_list[-1] | |
70 | print >>Term.cout, last_escape + os.linesep.join(screens[-1]) |
|
70 | print >>Term.cout, last_escape + os.linesep.join(screens[-1]) | |
71 |
|
71 | |||
72 | #---------------------------------------------------------------------------- |
|
72 | ||
73 |
def page(strng,start=0,screen_lines=0,pager_cmd |
|
73 | def page(strng, start=0, screen_lines=0, pager_cmd=None): | |
74 | """Print a string, piping through a pager after a certain length. |
|
74 | """Print a string, piping through a pager after a certain length. | |
75 |
|
75 | |||
@@ -120,19 +120,16 b' def page(strng,start=0,screen_lines=0,pager_cmd = None):' | |||||
120 | # terminals. If someone later feels like refining it, it's not hard. |
|
120 | # terminals. If someone later feels like refining it, it's not hard. | |
121 | numlines = max(num_newlines,int(len_str/80)+1) |
|
121 | numlines = max(num_newlines,int(len_str/80)+1) | |
122 |
|
122 | |||
123 | if os.name == "nt": |
|
123 | screen_lines_def = get_terminal_size()[1] | |
124 | screen_lines_def = get_console_size(defaulty=25)[1] |
|
|||
125 | else: |
|
|||
126 | screen_lines_def = 25 # default value if we can't auto-determine |
|
|||
127 |
|
124 | |||
128 | # auto-determine screen size |
|
125 | # auto-determine screen size | |
129 | if screen_lines <= 0: |
|
126 | if screen_lines <= 0: | |
130 | if TERM=='xterm' or TERM=='xterm-color': |
|
127 | if TERM=='xterm' or TERM=='xterm-color': | |
131 |
use_curses = |
|
128 | local_use_curses = use_curses | |
132 | else: |
|
129 | else: | |
133 | # curses causes problems on many terminals other than xterm. |
|
130 | # curses causes problems on many terminals other than xterm. | |
134 | use_curses = False |
|
131 | local_use_curses = False | |
135 | if use_curses: |
|
132 | if local_use_curses: | |
136 | import termios |
|
133 | import termios | |
137 | import curses |
|
134 | import curses | |
138 | # There is a bug in curses, where *sometimes* it fails to properly |
|
135 | # There is a bug in curses, where *sometimes* it fails to properly | |
@@ -201,7 +198,7 b' def page(strng,start=0,screen_lines=0,pager_cmd = None):' | |||||
201 | if retval is not None: |
|
198 | if retval is not None: | |
202 | page_dumb(strng,screen_lines=screen_lines) |
|
199 | page_dumb(strng,screen_lines=screen_lines) | |
203 |
|
200 | |||
204 | #---------------------------------------------------------------------------- |
|
201 | ||
205 |
def page_file(fname,start |
|
202 | def page_file(fname, start=0, pager_cmd=None): | |
206 | """Page a file, using an optional pager command and starting line. |
|
203 | """Page a file, using an optional pager command and starting line. | |
207 | """ |
|
204 | """ | |
@@ -221,12 +218,12 b' def page_file(fname,start = 0, pager_cmd = None):' | |||||
221 | except: |
|
218 | except: | |
222 | print 'Unable to show file',`fname` |
|
219 | print 'Unable to show file',`fname` | |
223 |
|
220 | |||
224 | #---------------------------------------------------------------------------- |
|
221 | ||
225 |
def get_pager_cmd(pager_cmd |
|
222 | def get_pager_cmd(pager_cmd=None): | |
226 | """Return a pager command. |
|
223 | """Return a pager command. | |
227 |
|
224 | |||
228 |
Makes some attempts at finding an OS-correct one. |
|
225 | Makes some attempts at finding an OS-correct one. | |
229 |
|
226 | """ | ||
230 | if os.name == 'posix': |
|
227 | if os.name == 'posix': | |
231 | default_pager_cmd = 'less -r' # -r for color control sequences |
|
228 | default_pager_cmd = 'less -r' # -r for color control sequences | |
232 | elif os.name in ['nt','dos']: |
|
229 | elif os.name in ['nt','dos']: | |
@@ -239,7 +236,7 b' def get_pager_cmd(pager_cmd = None):' | |||||
239 | pager_cmd = default_pager_cmd |
|
236 | pager_cmd = default_pager_cmd | |
240 | return pager_cmd |
|
237 | return pager_cmd | |
241 |
|
238 | |||
242 | #----------------------------------------------------------------------------- |
|
239 | ||
243 | def get_pager_start(pager,start): |
|
240 | def get_pager_start(pager, start): | |
244 | """Return the string for paging files with an offset. |
|
241 | """Return the string for paging files with an offset. | |
245 |
|
242 | |||
@@ -255,8 +252,8 b' def get_pager_start(pager,start):' | |||||
255 | start_string = '' |
|
252 | start_string = '' | |
256 | return start_string |
|
253 | return start_string | |
257 |
|
254 | |||
258 | #---------------------------------------------------------------------------- |
|
255 | ||
259 |
# (X)emacs on |
|
256 | # (X)emacs on win32 doesn't like to be bypassed with msvcrt.getch() | |
260 | if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs': |
|
257 | if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs': | |
261 | import msvcrt |
|
258 | import msvcrt | |
262 | def page_more(): |
|
259 | def page_more(): | |
@@ -280,7 +277,7 b' else:' | |||||
280 | else: |
|
277 | else: | |
281 | return True |
|
278 | return True | |
282 |
|
279 | |||
283 | #---------------------------------------------------------------------------- |
|
280 | ||
284 | def snip_print(str,width = 75,print_full = 0,header = ''): |
|
281 | def snip_print(str,width = 75,print_full = 0,header = ''): | |
285 | """Print a string snipping the midsection to fit in width. |
|
282 | """Print a string snipping the midsection to fit in width. | |
286 |
|
283 | |||
@@ -306,3 +303,4 b" def snip_print(str,width = 75,print_full = 0,header = ''):" | |||||
306 | if raw_input(header+' Snipped. View (y/n)? [N]').lower() == 'y': |
|
303 | if raw_input(header+' Snipped. View (y/n)? [N]').lower() == 'y': | |
307 | page(str) |
|
304 | page(str) | |
308 | return snip |
|
305 | return snip | |
|
306 |
@@ -27,10 +27,7 b' Authors:' | |||||
27 |
|
27 | |||
28 | import __builtin__ |
|
28 | import __builtin__ | |
29 | import codeop |
|
29 | import codeop | |
30 | import keyword |
|
|||
31 | import os |
|
|||
32 | import re |
|
30 | import re | |
33 | import sys |
|
|||
34 |
|
31 | |||
35 | from IPython.core.alias import AliasManager |
|
32 | from IPython.core.alias import AliasManager | |
36 | from IPython.core.autocall import IPyAutocall |
|
33 | from IPython.core.autocall import IPyAutocall | |
@@ -39,7 +36,8 b' from IPython.core.splitinput import split_user_input' | |||||
39 | from IPython.core.page import page |
|
36 | from IPython.core.page import page | |
40 |
|
37 | |||
41 | from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool |
|
38 | from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool | |
42 |
from IPython.utils. |
|
39 | from IPython.utils.io import Term | |
|
40 | from IPython.utils.text import make_quoted_expr | |||
43 | from IPython.utils.autoattr import auto_attr |
|
41 | from IPython.utils.autoattr import auto_attr | |
44 |
|
42 | |||
45 | #----------------------------------------------------------------------------- |
|
43 | #----------------------------------------------------------------------------- | |
@@ -158,6 +156,7 b' class LineInfo(object):' | |||||
158 | without worrying about *further* damaging state. |
|
156 | without worrying about *further* damaging state. | |
159 | """ |
|
157 | """ | |
160 | if not self._oinfo: |
|
158 | if not self._oinfo: | |
|
159 | # ip.shell._ofind is actually on the Magic class! | |||
161 | self._oinfo = ip.shell._ofind(self.ifun) |
|
160 | self._oinfo = ip.shell._ofind(self.ifun) | |
162 | return self._oinfo |
|
161 | return self._oinfo | |
163 |
|
162 |
@@ -12,23 +12,20 b' Classes for handling input/output prompts.' | |||||
12 | #***************************************************************************** |
|
12 | #***************************************************************************** | |
13 |
|
13 | |||
14 | #**************************************************************************** |
|
14 | #**************************************************************************** | |
15 | # Required modules |
|
15 | ||
16 | import __builtin__ |
|
16 | import __builtin__ | |
17 | import os |
|
17 | import os | |
|
18 | import re | |||
18 | import socket |
|
19 | import socket | |
19 | import sys |
|
20 | import sys | |
20 | import time |
|
|||
21 |
|
21 | |||
22 | # IPython's own |
|
|||
23 | from IPython.utils import coloransi |
|
|||
24 | from IPython.core import release |
|
22 | from IPython.core import release | |
25 | from IPython.external.Itpl import ItplNS |
|
23 | from IPython.external.Itpl import ItplNS | |
26 | from IPython.core.error import TryNext |
|
24 | from IPython.core.error import TryNext | |
27 |
from IPython.utils |
|
25 | from IPython.utils import coloransi | |
28 | from IPython.core.macro import Macro |
|
|||
29 | import IPython.utils.generics |
|
26 | import IPython.utils.generics | |
30 |
|
27 | from IPython.utils.warn import warn | ||
31 |
from IPython.utils. |
|
28 | from IPython.utils.io import Term | |
32 |
|
29 | |||
33 | #**************************************************************************** |
|
30 | #**************************************************************************** | |
34 | #Color schemes for Prompts. |
|
31 | #Color schemes for Prompts. |
@@ -19,7 +19,11 b' Authors' | |||||
19 | # Imports |
|
19 | # Imports | |
20 | #----------------------------------------------------------------------------- |
|
20 | #----------------------------------------------------------------------------- | |
21 |
|
21 | |||
22 | import sys |
|
22 | ||
|
23 | #----------------------------------------------------------------------------- | |||
|
24 | # Code | |||
|
25 | #----------------------------------------------------------------------------- | |||
|
26 | ||||
23 |
|
27 | |||
24 | class Quitter(object): |
|
28 | class Quitter(object): | |
25 | """Simple class to handle exit, similar to Python 2.5's. |
|
29 | """Simple class to handle exit, similar to Python 2.5's. | |
@@ -40,4 +44,4 b' class Quitter(object):' | |||||
40 | # Repr MUST return a string, else display like pprint hooks get confused |
|
44 | # Repr MUST return a string, else display like pprint hooks get confused | |
41 | def __repr__(self): |
|
45 | def __repr__(self): | |
42 | self.shell.ask_exit() |
|
46 | self.shell.ask_exit() | |
43 |
return ' |
|
47 | return '' |
@@ -21,9 +21,9 b" name = 'ipython'" | |||||
21 | # bdist_deb does not accept underscores (a Debian convention). |
|
21 | # bdist_deb does not accept underscores (a Debian convention). | |
22 |
|
22 | |||
23 | development = True # change this to False to do a release |
|
23 | development = True # change this to False to do a release | |
24 | version_base = '0.11' |
|
24 | version_base = '0.11.alpha1' | |
25 | branch = 'ipython' |
|
25 | branch = 'ipython' | |
26 |
revision = '13 |
|
26 | revision = '1223' | |
27 |
|
27 | |||
28 | if development: |
|
28 | if development: | |
29 | if branch == 'ipython': |
|
29 | if branch == 'ipython': |
@@ -30,9 +30,9 b' ip = get_ipython()' | |||||
30 | @dec.parametric |
|
30 | @dec.parametric | |
31 | def test_reset(): |
|
31 | def test_reset(): | |
32 | """reset must clear most namespaces.""" |
|
32 | """reset must clear most namespaces.""" | |
33 |
# The number of variables in the private user_ |
|
33 | # The number of variables in the private user_ns_hidden is not zero, but it | |
34 | # should be constant regardless of what we do |
|
34 | # should be constant regardless of what we do | |
35 |
nvars_config_ns = len(ip.user_ |
|
35 | nvars_config_ns = len(ip.user_ns_hidden) | |
36 |
|
36 | |||
37 | # Check that reset runs without error |
|
37 | # Check that reset runs without error | |
38 | ip.reset() |
|
38 | ip.reset() | |
@@ -51,7 +51,7 b' def test_reset():' | |||||
51 | for ns in ip.ns_refs_table: |
|
51 | for ns in ip.ns_refs_table: | |
52 | if ns is ip.user_ns: |
|
52 | if ns is ip.user_ns: | |
53 | nvars_expected = nvars_user_ns |
|
53 | nvars_expected = nvars_user_ns | |
54 |
elif ns is ip.user_ |
|
54 | elif ns is ip.user_ns_hidden: | |
55 | nvars_expected = nvars_config_ns |
|
55 | nvars_expected = nvars_config_ns | |
56 | else: |
|
56 | else: | |
57 | nvars_expected = 0 |
|
57 | nvars_expected = 0 |
@@ -8,19 +8,15 b' from __future__ import absolute_import' | |||||
8 | # Imports |
|
8 | # Imports | |
9 | #----------------------------------------------------------------------------- |
|
9 | #----------------------------------------------------------------------------- | |
10 |
|
10 | |||
11 | # stdlib |
|
|||
12 | import os |
|
11 | import os | |
13 | import sys |
|
12 | import sys | |
14 | import tempfile |
|
13 | import tempfile | |
15 | import types |
|
14 | import types | |
16 | from cStringIO import StringIO |
|
15 | from cStringIO import StringIO | |
17 |
|
16 | |||
18 | # third-party |
|
|||
19 | import nose.tools as nt |
|
17 | import nose.tools as nt | |
20 |
|
18 | |||
21 | # our own |
|
19 | from IPython.utils.path import get_long_path_name | |
22 | from IPython.utils import genutils |
|
|||
23 | from IPython.utils.platutils import find_cmd, get_long_path_name |
|
|||
24 | from IPython.testing import decorators as dec |
|
20 | from IPython.testing import decorators as dec | |
25 | from IPython.testing import tools as tt |
|
21 | from IPython.testing import tools as tt | |
26 |
|
22 |
@@ -12,17 +12,12 b' from __future__ import absolute_import' | |||||
12 | # Imports |
|
12 | # Imports | |
13 | #----------------------------------------------------------------------------- |
|
13 | #----------------------------------------------------------------------------- | |
14 |
|
14 | |||
15 | # stdlib |
|
|||
16 | import os |
|
15 | import os | |
17 | import sys |
|
16 | import sys | |
18 | import tempfile |
|
17 | import tempfile | |
19 |
|
18 | |||
20 | # third-party |
|
|||
21 | import nose.tools as nt |
|
19 | import nose.tools as nt | |
22 |
|
20 | |||
23 | # our own |
|
|||
24 | from IPython.utils.platutils import find_cmd |
|
|||
25 | from IPython.utils import genutils |
|
|||
26 | from IPython.testing import decorators as dec |
|
21 | from IPython.testing import decorators as dec | |
27 | from IPython.testing import tools as tt |
|
22 | from IPython.testing import tools as tt | |
28 |
|
23 | |||
@@ -142,10 +137,10 b' class TestMagicRunSimple(tt.TempFileMixin):' | |||||
142 | _ip.runlines('t = isinstance(f(), foo)') |
|
137 | _ip.runlines('t = isinstance(f(), foo)') | |
143 | nt.assert_true(_ip.user_ns['t']) |
|
138 | nt.assert_true(_ip.user_ns['t']) | |
144 |
|
139 | |||
145 |
# We have to skip these in win32 because ge |
|
140 | # We have to skip these in win32 because getoutputerr() crashes, | |
146 | # due to the fact that subprocess does not support close_fds when |
|
141 | # due to the fact that subprocess does not support close_fds when | |
147 | # redirecting stdout/err. So unless someone who knows more tells us how to |
|
142 | # redirecting stdout/err. So unless someone who knows more tells us how to | |
148 |
# implement ge |
|
143 | # implement getoutputerr() in win32, we're stuck avoiding these. | |
149 | @dec.skip_win32 |
|
144 | @dec.skip_win32 | |
150 | def test_obj_del(self): |
|
145 | def test_obj_del(self): | |
151 | """Test that object's __del__ methods are called on exit.""" |
|
146 | """Test that object's __del__ methods are called on exit.""" |
@@ -93,9 +93,10 b' from inspect import getsourcefile, getfile, getmodule,\\' | |||||
93 | from IPython.utils import PyColorize |
|
93 | from IPython.utils import PyColorize | |
94 | from IPython.core import debugger, ipapi |
|
94 | from IPython.core import debugger, ipapi | |
95 | from IPython.core.display_trap import DisplayTrap |
|
95 | from IPython.core.display_trap import DisplayTrap | |
96 | from IPython.utils.ipstruct import Struct |
|
|||
97 | from IPython.core.excolors import exception_colors |
|
96 | from IPython.core.excolors import exception_colors | |
98 |
from IPython.utils. |
|
97 | from IPython.utils.data import uniq_stable | |
|
98 | from IPython.utils.io import Term | |||
|
99 | from IPython.utils.warn import info, error | |||
99 |
|
100 | |||
100 | # Globals |
|
101 | # Globals | |
101 | # amount of space to put line numbers before verbose tracebacks |
|
102 | # amount of space to put line numbers before verbose tracebacks | |
@@ -384,7 +385,8 b' class ListTB(TBTools):' | |||||
384 |
|
385 | |||
385 | def __call__(self, etype, value, elist): |
|
386 | def __call__(self, etype, value, elist): | |
386 | Term.cout.flush() |
|
387 | Term.cout.flush() | |
387 |
Term.cerr.write |
|
388 | Term.cerr.write(self.text(etype,value,elist)) | |
|
389 | Term.cerr.write('\n') | |||
388 |
|
390 | |||
389 | def text(self, etype, value, elist, context=5): |
|
391 | def text(self, etype, value, elist, context=5): | |
390 | """Return a color formatted string with the traceback info. |
|
392 | """Return a color formatted string with the traceback info. | |
@@ -909,7 +911,8 b' class VerboseTB(TBTools):' | |||||
909 | (etype, evalue, etb) = info or sys.exc_info() |
|
911 | (etype, evalue, etb) = info or sys.exc_info() | |
910 | self.tb = etb |
|
912 | self.tb = etb | |
911 | Term.cout.flush() |
|
913 | Term.cout.flush() | |
912 |
Term.cerr.write |
|
914 | Term.cerr.write(self.text(etype, evalue, etb)) | |
|
915 | Term.cerr.write('\n') | |||
913 |
|
916 | |||
914 | # Changed so an instance can just be called as VerboseTB_inst() and print |
|
917 | # Changed so an instance can just be called as VerboseTB_inst() and print | |
915 | # out the right info on its own. |
|
918 | # out the right info on its own. |
@@ -53,7 +53,7 b" __all__ = ['Gnuplot','gp','gp_new','Data','File','Func','GridData'," | |||||
53 | 'pm3d_config','eps_fix_bbox'] |
|
53 | 'pm3d_config','eps_fix_bbox'] | |
54 |
|
54 | |||
55 | import os,tempfile,sys |
|
55 | import os,tempfile,sys | |
56 |
from IPython.utils. |
|
56 | from IPython.utils.process import getoutput | |
57 |
|
57 | |||
58 | #--------------------------------------------------------------------------- |
|
58 | #--------------------------------------------------------------------------- | |
59 | # Notes on mouse support for Gnuplot.py |
|
59 | # Notes on mouse support for Gnuplot.py |
@@ -133,10 +133,10 b' from IPython.external import simplegeneric' | |||||
133 | from IPython.external import path |
|
133 | from IPython.external import path | |
134 |
|
134 | |||
135 | try: |
|
135 | try: | |
136 |
from IPython.utils import |
|
136 | from IPython.utils.io import Term | |
137 | from IPython.utils import generics |
|
137 | from IPython.utils import generics | |
138 | except ImportError: |
|
138 | except ImportError: | |
139 |
|
|
139 | Term = None | |
140 | generics = None |
|
140 | generics = None | |
141 |
|
141 | |||
142 | from IPython.core import ipapi |
|
142 | from IPython.core import ipapi | |
@@ -2168,7 +2168,7 b' class idump(Display):' | |||||
2168 | self.datasepchar = "|" |
|
2168 | self.datasepchar = "|" | |
2169 |
|
2169 | |||
2170 | def display(self): |
|
2170 | def display(self): | |
2171 |
stream = |
|
2171 | stream = Term.cout | |
2172 | allattrs = [] |
|
2172 | allattrs = [] | |
2173 | attrset = set() |
|
2173 | attrset = set() | |
2174 | colwidths = {} |
|
2174 | colwidths = {} |
@@ -54,7 +54,7 b' from enthought.traits import api as T' | |||||
54 | # IPython imports |
|
54 | # IPython imports | |
55 | from IPython.core.error import TryNext |
|
55 | from IPython.core.error import TryNext | |
56 | from IPython.core.ipapi import get as ipget |
|
56 | from IPython.core.ipapi import get as ipget | |
57 |
from IPython.utils. |
|
57 | from IPython.utils.dir2 import dir2 | |
58 | try: |
|
58 | try: | |
59 | set |
|
59 | set | |
60 | except: |
|
60 | except: |
@@ -14,7 +14,9 b' from IPython.core.iplib import InteractiveShell' | |||||
14 | from IPython.utils.ipstruct import Struct |
|
14 | from IPython.utils.ipstruct import Struct | |
15 | import Queue,thread,threading,signal |
|
15 | import Queue,thread,threading,signal | |
16 | from signal import signal, SIGINT |
|
16 | from signal import signal, SIGINT | |
17 |
from IPython.utils. |
|
17 | from IPython.utils.io import Term, ask_yes_no | |
|
18 | from IPython.utils.warn import warn, error | |||
|
19 | from IPython.utils.decorators import flag_calls | |||
18 | from IPython.core import shellglobals |
|
20 | from IPython.core import shellglobals | |
19 |
|
21 | |||
20 | def install_gtk2(): |
|
22 | def install_gtk2(): |
@@ -39,7 +39,7 b' from IPython.core.error import TryNext' | |||||
39 | from IPython.external import pretty |
|
39 | from IPython.external import pretty | |
40 | from IPython.core.component import Component |
|
40 | from IPython.core.component import Component | |
41 | from IPython.utils.traitlets import Bool, List |
|
41 | from IPython.utils.traitlets import Bool, List | |
42 |
from IPython.utils. |
|
42 | from IPython.utils.io import Term | |
43 | from IPython.utils.autoattr import auto_attr |
|
43 | from IPython.utils.autoattr import auto_attr | |
44 | from IPython.utils.importstring import import_item |
|
44 | from IPython.utils.importstring import import_item | |
45 |
|
45 | |||
@@ -128,9 +128,8 b' class PrettyResultDisplay(Component):' | |||||
128 | #----------------------------------------------------------------------------- |
|
128 | #----------------------------------------------------------------------------- | |
129 |
|
129 | |||
130 |
|
130 | |||
131 |
def load_ipython_extension(ip |
|
131 | def load_ipython_extension(ip): | |
132 | """Load the extension in IPython as a hook.""" |
|
132 | """Load the extension in IPython as a hook.""" | |
133 | if ip is None: ip = get_ipython() |
|
|||
134 | global _loaded |
|
133 | global _loaded | |
135 | if not _loaded: |
|
134 | if not _loaded: | |
136 | prd = PrettyResultDisplay(ip, name='pretty_result_display') |
|
135 | prd = PrettyResultDisplay(ip, name='pretty_result_display') |
@@ -213,7 +213,7 b' def main():' | |||||
213 | print "\n".join(expand(sys.argv[1:])), |
|
213 | print "\n".join(expand(sys.argv[1:])), | |
214 |
|
214 | |||
215 | def mglob_f(self, arg): |
|
215 | def mglob_f(self, arg): | |
216 |
from IPython.utils. |
|
216 | from IPython.utils.text import SList | |
217 | if arg.strip(): |
|
217 | if arg.strip(): | |
218 | return SList(expand(arg)) |
|
218 | return SList(expand(arg)) | |
219 | print "Please specify pattern!" |
|
219 | print "Please specify pattern!" |
@@ -39,9 +39,10 b' def common_prefix(strings):' | |||||
39 |
|
39 | |||
40 | return prefix |
|
40 | return prefix | |
41 |
|
41 | |||
42 |
#----------------------------------------------------------------------------- |
|
42 | #----------------------------------------------------------------------------- | |
43 | # Base class for the line-oriented front ends |
|
43 | # Base class for the line-oriented front ends | |
44 |
#----------------------------------------------------------------------------- |
|
44 | #----------------------------------------------------------------------------- | |
|
45 | ||||
45 | class LineFrontEndBase(FrontEndBase): |
|
46 | class LineFrontEndBase(FrontEndBase): | |
46 | """ Concrete implementation of the FrontEndBase class. This is meant |
|
47 | """ Concrete implementation of the FrontEndBase class. This is meant | |
47 | to be the base class behind all the frontend that are line-oriented, |
|
48 | to be the base class behind all the frontend that are line-oriented, |
@@ -26,12 +26,12 b' import os' | |||||
26 | import re |
|
26 | import re | |
27 | import __builtin__ |
|
27 | import __builtin__ | |
28 |
|
28 | |||
29 |
from IPython.core.ip |
|
29 | from IPython.core.iplib import InteractiveShell | |
30 | from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap |
|
30 | from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap | |
31 |
|
31 | |||
32 | from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap |
|
32 | from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap | |
33 |
|
33 | |||
34 |
from IPython.utils. |
|
34 | from IPython.utils.io import Term | |
35 |
|
35 | |||
36 | from linefrontendbase import LineFrontEndBase, common_prefix |
|
36 | from linefrontendbase import LineFrontEndBase, common_prefix | |
37 |
|
37 | |||
@@ -50,9 +50,10 b' def mk_system_call(system_call_function, command):' | |||||
50 | my_system_call.__doc__ = "Calls %s" % command |
|
50 | my_system_call.__doc__ = "Calls %s" % command | |
51 | return my_system_call |
|
51 | return my_system_call | |
52 |
|
52 | |||
53 |
#----------------------------------------------------------------------------- |
|
53 | #----------------------------------------------------------------------------- | |
54 | # Frontend class using ipython0 to do the prefiltering. |
|
54 | # Frontend class using ipython0 to do the prefiltering. | |
55 |
#----------------------------------------------------------------------------- |
|
55 | #----------------------------------------------------------------------------- | |
|
56 | ||||
56 | class PrefilterFrontEnd(LineFrontEndBase): |
|
57 | class PrefilterFrontEnd(LineFrontEndBase): | |
57 | """ Class that uses ipython0 to do prefilter the input, do the |
|
58 | """ Class that uses ipython0 to do prefilter the input, do the | |
58 | completion and the magics. |
|
59 | completion and the magics. | |
@@ -65,19 +66,13 b' class PrefilterFrontEnd(LineFrontEndBase):' | |||||
65 |
|
66 | |||
66 | debug = False |
|
67 | debug = False | |
67 |
|
68 | |||
68 |
def __init__(self, ipython0=None, |
|
69 | def __init__(self, ipython0=None, *args, **kwargs): | |
69 | """ Parameters |
|
70 | """ Parameters | |
70 | ---------- |
|
71 | ---------- | |
71 |
|
72 | |||
72 | ipython0: an optional ipython0 instance to use for command |
|
73 | ipython0: an optional ipython0 instance to use for command | |
73 | prefiltering and completion. |
|
74 | prefiltering and completion. | |
74 |
|
||||
75 | argv : list, optional |
|
|||
76 | Used as the instance's argv value. If not given, [] is used. |
|
|||
77 | """ |
|
75 | """ | |
78 | if argv is None: |
|
|||
79 | argv = ['--no-banner'] |
|
|||
80 |
|
||||
81 | LineFrontEndBase.__init__(self, *args, **kwargs) |
|
76 | LineFrontEndBase.__init__(self, *args, **kwargs) | |
82 | self.shell.output_trap = RedirectorOutputTrap( |
|
77 | self.shell.output_trap = RedirectorOutputTrap( | |
83 | out_callback=self.write, |
|
78 | out_callback=self.write, | |
@@ -90,22 +85,19 b' class PrefilterFrontEnd(LineFrontEndBase):' | |||||
90 | # Start the ipython0 instance: |
|
85 | # Start the ipython0 instance: | |
91 | self.save_output_hooks() |
|
86 | self.save_output_hooks() | |
92 | if ipython0 is None: |
|
87 | if ipython0 is None: | |
93 |
# Instanciate an IPython0 |
|
88 | # Instanciate an IPython0 InteractiveShell to be able to use the | |
94 | # prefiltering. |
|
89 | # prefiltering. | |
95 | # Suppress all key input, to avoid waiting |
|
90 | # Suppress all key input, to avoid waiting | |
96 | def my_rawinput(x=None): |
|
91 | def my_rawinput(x=None): | |
97 | return '\n' |
|
92 | return '\n' | |
98 | old_rawinput = __builtin__.raw_input |
|
93 | old_rawinput = __builtin__.raw_input | |
99 | __builtin__.raw_input = my_rawinput |
|
94 | __builtin__.raw_input = my_rawinput | |
100 |
ipython0 = I |
|
95 | ipython0 = InteractiveShell( | |
101 |
|
|
96 | parent=None, user_ns=self.shell.user_ns, | |
102 |
|
|
97 | user_global_ns=self.shell.user_global_ns | |
103 | ipython0.initialize() |
|
98 | ) | |
104 | __builtin__.raw_input = old_rawinput |
|
99 | __builtin__.raw_input = old_rawinput | |
105 | # XXX This will need to be updated as we refactor things, but for now, |
|
100 | self.ipython0 = ipython0 | |
106 | # the .shell attribute of the ipythonapp instance conforms to the old |
|
|||
107 | # api. |
|
|||
108 | self.ipython0 = ipython0.shell |
|
|||
109 | # Set the pager: |
|
101 | # Set the pager: | |
110 | self.ipython0.set_hook('show_in_pager', |
|
102 | self.ipython0.set_hook('show_in_pager', | |
111 | lambda s, string: self.write("\n" + string)) |
|
103 | lambda s, string: self.write("\n" + string)) |
@@ -1,14 +1,8 b'' | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 |
|
||||
3 | """This file contains unittests for the asyncfrontendbase module.""" |
|
2 | """This file contains unittests for the asyncfrontendbase module.""" | |
4 |
|
3 | |||
5 | __docformat__ = "restructuredtext en" |
|
|||
6 |
|
||||
7 | # Tell nose to skip this module |
|
|||
8 | __test__ = {} |
|
|||
9 |
|
||||
10 | #--------------------------------------------------------------------------- |
|
4 | #--------------------------------------------------------------------------- | |
11 | # Copyright (C) 2008 The IPython Development Team |
|
5 | # Copyright (C) 2008-2009 The IPython Development Team | |
12 | # |
|
6 | # | |
13 | # Distributed under the terms of the BSD License. The full license is in |
|
7 | # Distributed under the terms of the BSD License. The full license is in | |
14 | # the file COPYING, distributed as part of this software. |
|
8 | # the file COPYING, distributed as part of this software. |
@@ -21,7 +21,6 b' from nose.tools import assert_equal' | |||||
21 |
|
21 | |||
22 | from IPython.frontend.prefilterfrontend import PrefilterFrontEnd |
|
22 | from IPython.frontend.prefilterfrontend import PrefilterFrontEnd | |
23 | from IPython.testing.globalipapp import get_ipython |
|
23 | from IPython.testing.globalipapp import get_ipython | |
24 | from IPython.testing.tools import default_argv |
|
|||
25 |
|
24 | |||
26 | #----------------------------------------------------------------------------- |
|
25 | #----------------------------------------------------------------------------- | |
27 | # Support utilities |
|
26 | # Support utilities | |
@@ -35,7 +34,7 b' class TestPrefilterFrontEnd(PrefilterFrontEnd):' | |||||
35 |
|
34 | |||
36 | def __init__(self): |
|
35 | def __init__(self): | |
37 | self.out = StringIO() |
|
36 | self.out = StringIO() | |
38 |
PrefilterFrontEnd.__init__(self |
|
37 | PrefilterFrontEnd.__init__(self) | |
39 | # Some more code for isolation (yeah, crazy) |
|
38 | # Some more code for isolation (yeah, crazy) | |
40 | self._on_enter() |
|
39 | self._on_enter() | |
41 | self.out.flush() |
|
40 | self.out.flush() |
@@ -47,6 +47,7 b' def test_io():' | |||||
47 | assert result == test_string |
|
47 | assert result == test_string | |
48 |
|
48 | |||
49 |
|
49 | |||
|
50 | @testdec.skip_win32 | |||
50 | def test_kill(): |
|
51 | def test_kill(): | |
51 | """ Check that we can kill a process, and its subprocess. |
|
52 | """ Check that we can kill a process, and its subprocess. | |
52 | """ |
|
53 | """ |
@@ -135,9 +135,10 b' else:' | |||||
135 | } |
|
135 | } | |
136 |
|
136 | |||
137 |
|
137 | |||
138 |
#----------------------------------------------------------------------------- |
|
138 | #----------------------------------------------------------------------------- | |
139 | # The console widget class |
|
139 | # The console widget class | |
140 |
#----------------------------------------------------------------------------- |
|
140 | #----------------------------------------------------------------------------- | |
|
141 | ||||
141 | class ConsoleWidget(editwindow.EditWindow): |
|
142 | class ConsoleWidget(editwindow.EditWindow): | |
142 | """ Specialized styled text control view for console-like workflow. |
|
143 | """ Specialized styled text control view for console-like workflow. | |
143 |
|
144 |
@@ -47,7 +47,7 b' class IPythonXController(WxController):' | |||||
47 | self._input_state = 'subprocess' |
|
47 | self._input_state = 'subprocess' | |
48 | self.write('\n', refresh=False) |
|
48 | self.write('\n', refresh=False) | |
49 | self.capture_output() |
|
49 | self.capture_output() | |
50 |
self.ipython0. |
|
50 | self.ipython0.exit() | |
51 | self.release_output() |
|
51 | self.release_output() | |
52 | if not self.ipython0.exit_now: |
|
52 | if not self.ipython0.exit_now: | |
53 | wx.CallAfter(self.new_prompt, |
|
53 | wx.CallAfter(self.new_prompt, |
@@ -23,9 +23,8 b' import os' | |||||
23 | import locale |
|
23 | import locale | |
24 | from thread_ex import ThreadEx |
|
24 | from thread_ex import ThreadEx | |
25 |
|
25 | |||
26 | import IPython |
|
26 | from IPython.core import iplib | |
27 |
from IPython. |
|
27 | from IPython.utils.io import Term | |
28 | from IPython.utils import genutils |
|
|||
29 |
|
28 | |||
30 | ############################################################################## |
|
29 | ############################################################################## | |
31 | class _Helper(object): |
|
30 | class _Helper(object): | |
@@ -88,12 +87,10 b' class NonBlockingIPShell(object):' | |||||
88 | via raise_exc() |
|
87 | via raise_exc() | |
89 | ''' |
|
88 | ''' | |
90 |
|
89 | |||
91 |
def __init__(self |
|
90 | def __init__(self, user_ns={}, user_global_ns=None, | |
92 | cin=None, cout=None, cerr=None, |
|
91 | cin=None, cout=None, cerr=None, | |
93 | ask_exit_handler=None): |
|
92 | ask_exit_handler=None): | |
94 | ''' |
|
93 | ''' | |
95 | @param argv: Command line options for IPython |
|
|||
96 | @type argv: list |
|
|||
97 | @param user_ns: User namespace. |
|
94 | @param user_ns: User namespace. | |
98 | @type user_ns: dictionary |
|
95 | @type user_ns: dictionary | |
99 | @param user_global_ns: User global namespace. |
|
96 | @param user_global_ns: User global namespace. | |
@@ -111,7 +108,7 b' class NonBlockingIPShell(object):' | |||||
111 | ''' |
|
108 | ''' | |
112 | #ipython0 initialisation |
|
109 | #ipython0 initialisation | |
113 | self._IP = None |
|
110 | self._IP = None | |
114 |
self.init_ipython0( |
|
111 | self.init_ipython0(user_ns, user_global_ns, | |
115 | cin, cout, cerr, |
|
112 | cin, cout, cerr, | |
116 | ask_exit_handler) |
|
113 | ask_exit_handler) | |
117 |
|
114 | |||
@@ -131,7 +128,7 b' class NonBlockingIPShell(object):' | |||||
131 | self._help_text = None |
|
128 | self._help_text = None | |
132 | self._add_button = None |
|
129 | self._add_button = None | |
133 |
|
130 | |||
134 |
def init_ipython0(self |
|
131 | def init_ipython0(self, user_ns={}, user_global_ns=None, | |
135 | cin=None, cout=None, cerr=None, |
|
132 | cin=None, cout=None, cerr=None, | |
136 | ask_exit_handler=None): |
|
133 | ask_exit_handler=None): | |
137 | ''' Initialize an ipython0 instance ''' |
|
134 | ''' Initialize an ipython0 instance ''' | |
@@ -141,27 +138,22 b' class NonBlockingIPShell(object):' | |||||
141 | #only one instance can be instanciated else tehre will be |
|
138 | #only one instance can be instanciated else tehre will be | |
142 | #cin/cout/cerr clash... |
|
139 | #cin/cout/cerr clash... | |
143 | if cin: |
|
140 | if cin: | |
144 |
|
|
141 | Term.cin = cin | |
145 | if cout: |
|
142 | if cout: | |
146 |
|
|
143 | Term.cout = cout | |
147 | if cerr: |
|
144 | if cerr: | |
148 |
|
|
145 | Term.cerr = cerr | |
149 |
|
146 | |||
150 | excepthook = sys.excepthook |
|
147 | excepthook = sys.excepthook | |
151 |
|
148 | |||
152 | #Hack to save sys.displayhook, because ipython seems to overwrite it... |
|
149 | #Hack to save sys.displayhook, because ipython seems to overwrite it... | |
153 | self.sys_displayhook_ori = sys.displayhook |
|
150 | self.sys_displayhook_ori = sys.displayhook | |
154 |
|
151 | ipython0 = iplib.InteractiveShell( | ||
155 | ipython0 = ipapp.IPythonApp(argv,user_ns=user_ns, |
|
152 | parent=None, config=None, | |
156 | user_global_ns=user_global_ns) |
|
153 | user_ns=user_ns, | |
157 | ipython0.initialize() |
|
154 | user_global_ns=user_global_ns | |
158 | self._IP = ipython0.shell |
|
155 | ) | |
159 |
|
156 | self._IP = ipython0 | ||
160 | ## self._IP = IPython.shell.make_IPython( |
|
|||
161 | ## argv,user_ns=user_ns, |
|
|||
162 | ## user_global_ns=user_global_ns, |
|
|||
163 | ## embedded=True, |
|
|||
164 | ## shell_class=IPython.shell.InteractiveShell) |
|
|||
165 |
|
157 | |||
166 | #we save ipython0 displayhook and we restore sys.displayhook |
|
158 | #we save ipython0 displayhook and we restore sys.displayhook | |
167 | self.displayhook = sys.displayhook |
|
159 | self.displayhook = sys.displayhook | |
@@ -185,12 +177,10 b' class NonBlockingIPShell(object):' | |||||
185 | #we replace the help command |
|
177 | #we replace the help command | |
186 | self._IP.user_ns['help'] = _Helper(self._pager_help) |
|
178 | self._IP.user_ns['help'] = _Helper(self._pager_help) | |
187 |
|
179 | |||
188 | #we disable cpase magic... until we found a way to use it properly. |
|
180 | #we disable cpaste magic... until we found a way to use it properly. | |
189 | from IPython.core import ipapi |
|
|||
190 | ip = ipapi.get() |
|
|||
191 | def bypass_magic(self, arg): |
|
181 | def bypass_magic(self, arg): | |
192 | print '%this magic is currently disabled.' |
|
182 | print '%this magic is currently disabled.' | |
193 | ip.define_magic('cpaste', bypass_magic) |
|
183 | ipython0.define_magic('cpaste', bypass_magic) | |
194 |
|
184 | |||
195 | import __builtin__ |
|
185 | import __builtin__ | |
196 | __builtin__.raw_input = self._raw_input |
|
186 | __builtin__.raw_input = self._raw_input | |
@@ -471,7 +461,7 b' class NonBlockingIPShell(object):' | |||||
471 | ''' |
|
461 | ''' | |
472 |
|
462 | |||
473 | orig_stdout = sys.stdout |
|
463 | orig_stdout = sys.stdout | |
474 |
sys.stdout = |
|
464 | sys.stdout = Term.cout | |
475 | #self.sys_displayhook_ori = sys.displayhook |
|
465 | #self.sys_displayhook_ori = sys.displayhook | |
476 | #sys.displayhook = self.displayhook |
|
466 | #sys.displayhook = self.displayhook | |
477 |
|
467 |
@@ -11,6 +11,7 b' __author__ = "Laurent Dufrechou"' | |||||
11 | __email__ = "laurent.dufrechou _at_ gmail.com" |
|
11 | __email__ = "laurent.dufrechou _at_ gmail.com" | |
12 | __license__ = "BSD" |
|
12 | __license__ = "BSD" | |
13 | #----------------------------------------- |
|
13 | #----------------------------------------- | |
|
14 | ||||
14 | class IPythonHistoryPanel(wx.Panel): |
|
15 | class IPythonHistoryPanel(wx.Panel): | |
15 |
|
16 | |||
16 | def __init__(self, parent,flt_empty=True, |
|
17 | def __init__(self, parent,flt_empty=True, |
@@ -24,16 +24,25 b' The main classes in this module are:' | |||||
24 | #----------------------------------------------------------------------------- |
|
24 | #----------------------------------------------------------------------------- | |
25 |
|
25 | |||
26 | #----------------------------------------------------------------------------- |
|
26 | #----------------------------------------------------------------------------- | |
27 | # Imports |
|
27 | # Warnings control | |
28 | #----------------------------------------------------------------------------- |
|
28 | #----------------------------------------------------------------------------- | |
29 |
|
29 | |||
30 | from cStringIO import StringIO |
|
|||
31 | import sys |
|
|||
32 | import warnings |
|
30 | import warnings | |
33 |
|
31 | |||
34 | # from IPython.utils import growl |
|
32 | # Twisted generates annoying warnings with Python 2.6, as will do other code | |
35 | # growl.start("IPython1 Client") |
|
33 | # that imports 'sets' as of today | |
|
34 | warnings.filterwarnings('ignore', 'the sets module is deprecated', | |||
|
35 | DeprecationWarning ) | |||
|
36 | ||||
|
37 | # This one also comes from Twisted | |||
|
38 | warnings.filterwarnings('ignore', 'the sha module is deprecated', | |||
|
39 | DeprecationWarning) | |||
|
40 | ||||
|
41 | #----------------------------------------------------------------------------- | |||
|
42 | # Imports | |||
|
43 | #----------------------------------------------------------------------------- | |||
36 |
|
44 | |||
|
45 | import sys | |||
37 |
|
46 | |||
38 | from twisted.internet import reactor |
|
47 | from twisted.internet import reactor | |
39 | from twisted.internet.error import PotentialZombieWarning |
|
48 | from twisted.internet.error import PotentialZombieWarning | |
@@ -74,8 +83,6 b' rit.setDaemon(True)' | |||||
74 | rit.start() |
|
83 | rit.start() | |
75 |
|
84 | |||
76 |
|
85 | |||
77 |
|
||||
78 |
|
||||
79 | __all__ = [ |
|
86 | __all__ = [ | |
80 | 'MapTask', |
|
87 | 'MapTask', | |
81 | 'StringTask', |
|
88 | 'StringTask', |
@@ -1,6 +1,4 b'' | |||||
1 | #!/usr/bin/env python |
|
|||
2 |
|
|
1 | # encoding: utf-8 | |
3 |
|
||||
4 | """Facilities for handling client connections to the controller.""" |
|
2 | """Facilities for handling client connections to the controller.""" | |
5 |
|
3 | |||
6 | #----------------------------------------------------------------------------- |
|
4 | #----------------------------------------------------------------------------- | |
@@ -20,6 +18,8 b' import os' | |||||
20 | from IPython.kernel.fcutil import ( |
|
18 | from IPython.kernel.fcutil import ( | |
21 | Tub, |
|
19 | Tub, | |
22 | find_furl, |
|
20 | find_furl, | |
|
21 | is_valid_furl, | |||
|
22 | is_valid_furl_file, | |||
23 | is_valid_furl_or_file, |
|
23 | is_valid_furl_or_file, | |
24 | validate_furl_or_file, |
|
24 | validate_furl_or_file, | |
25 | FURLError |
|
25 | FURLError | |
@@ -33,7 +33,7 b' from IPython.kernel.twistedutil import (' | |||||
33 | sleep_deferred |
|
33 | sleep_deferred | |
34 | ) |
|
34 | ) | |
35 | from IPython.utils.importstring import import_item |
|
35 | from IPython.utils.importstring import import_item | |
36 |
from IPython.utils. |
|
36 | from IPython.utils.path import get_ipython_dir | |
37 |
|
37 | |||
38 | from twisted.internet import defer |
|
38 | from twisted.internet import defer | |
39 | from twisted.internet.defer import inlineCallbacks, returnValue |
|
39 | from twisted.internet.defer import inlineCallbacks, returnValue | |
@@ -66,18 +66,30 b' class AsyncClientConnector(object):' | |||||
66 | def _find_furl(self, profile='default', cluster_dir=None, |
|
66 | def _find_furl(self, profile='default', cluster_dir=None, | |
67 | furl_or_file=None, furl_file_name=None, |
|
67 | furl_or_file=None, furl_file_name=None, | |
68 | ipython_dir=None): |
|
68 | ipython_dir=None): | |
69 | """Find a FURL file by profile+ipython_dir or cluster dir. |
|
69 | """Find a FURL file. | |
|
70 | ||||
|
71 | If successful, this returns a FURL file that exists on the file | |||
|
72 | system. The contents of the file have not been checked though. This | |||
|
73 | is because we often have to deal with FURL file whose buffers have | |||
|
74 | not been flushed. | |||
70 |
|
75 | |||
71 | This raises an :exc:`~IPython.kernel.fcutil.FURLError` exception |
|
76 | This raises an :exc:`~IPython.kernel.fcutil.FURLError` exception | |
72 | if a FURL file can't be found. |
|
77 | if a FURL file can't be found. | |
|
78 | ||||
|
79 | This tries the following: | |||
|
80 | ||||
|
81 | 1. By the name ``furl_or_file``. | |||
|
82 | 2. By ``cluster_dir`` and ``furl_file_name``. | |||
|
83 | 3. By cluster profile with a default of ``default``. This uses | |||
|
84 | ``ipython_dir``. | |||
73 | """ |
|
85 | """ | |
74 | # Try by furl_or_file |
|
86 | # Try by furl_or_file | |
75 | if furl_or_file is not None: |
|
87 | if furl_or_file is not None: | |
76 |
valid |
|
88 | if is_valid_furl_or_file(furl_or_file): | |
77 | return furl_or_file |
|
89 | return furl_or_file | |
78 |
|
90 | |||
79 | if furl_file_name is None: |
|
91 | if furl_file_name is None: | |
80 | raise FURLError('A furl_file_name must be provided') |
|
92 | raise FURLError('A furl_file_name must be provided if furl_or_file is not') | |
81 |
|
93 | |||
82 | # Try by cluster_dir |
|
94 | # Try by cluster_dir | |
83 | if cluster_dir is not None: |
|
95 | if cluster_dir is not None: | |
@@ -151,7 +163,7 b' class AsyncClientConnector(object):' | |||||
151 | The full path to a cluster directory. This is useful if profiles |
|
163 | The full path to a cluster directory. This is useful if profiles | |
152 | are not being used. |
|
164 | are not being used. | |
153 | furl_or_file : str |
|
165 | furl_or_file : str | |
154 |
A furl or a filename containing a FURL |
|
166 | A furl or a filename containing a FURL. This is useful if you | |
155 | simply know the location of the FURL file. |
|
167 | simply know the location of the FURL file. | |
156 | ipython_dir : str |
|
168 | ipython_dir : str | |
157 | The location of the ipython_dir if different from the default. |
|
169 | The location of the ipython_dir if different from the default. | |
@@ -193,7 +205,7 b' class AsyncClientConnector(object):' | |||||
193 | The full path to a cluster directory. This is useful if profiles |
|
205 | The full path to a cluster directory. This is useful if profiles | |
194 | are not being used. |
|
206 | are not being used. | |
195 | furl_or_file : str |
|
207 | furl_or_file : str | |
196 |
A furl or a filename containing a FURL |
|
208 | A furl or a filename containing a FURL. This is useful if you | |
197 | simply know the location of the FURL file. |
|
209 | simply know the location of the FURL file. | |
198 | ipython_dir : str |
|
210 | ipython_dir : str | |
199 | The location of the ipython_dir if different from the default. |
|
211 | The location of the ipython_dir if different from the default. | |
@@ -259,6 +271,9 b' class AsyncClientConnector(object):' | |||||
259 | profile, cluster_dir, furl_or_file, |
|
271 | profile, cluster_dir, furl_or_file, | |
260 | furl_file_name, ipython_dir |
|
272 | furl_file_name, ipython_dir | |
261 | ) |
|
273 | ) | |
|
274 | # If this succeeds, we know the furl file exists and has a .furl | |||
|
275 | # extension, but it could still be empty. That is checked each | |||
|
276 | # connection attempt. | |||
262 | except FURLError: |
|
277 | except FURLError: | |
263 | return defer.fail(failure.Failure()) |
|
278 | return defer.fail(failure.Failure()) | |
264 |
|
279 | |||
@@ -349,7 +364,7 b' class ClientConnector(object):' | |||||
349 | The full path to a cluster directory. This is useful if profiles |
|
364 | The full path to a cluster directory. This is useful if profiles | |
350 | are not being used. |
|
365 | are not being used. | |
351 | furl_or_file : str |
|
366 | furl_or_file : str | |
352 |
A furl or a filename containing a FURL |
|
367 | A furl or a filename containing a FURL. This is useful if you | |
353 | simply know the location of the FURL file. |
|
368 | simply know the location of the FURL file. | |
354 | ipython_dir : str |
|
369 | ipython_dir : str | |
355 | The location of the ipython_dir if different from the default. |
|
370 | The location of the ipython_dir if different from the default. | |
@@ -390,7 +405,7 b' class ClientConnector(object):' | |||||
390 | The full path to a cluster directory. This is useful if profiles |
|
405 | The full path to a cluster directory. This is useful if profiles | |
391 | are not being used. |
|
406 | are not being used. | |
392 | furl_or_file : str |
|
407 | furl_or_file : str | |
393 |
A furl or a filename containing a FURL |
|
408 | A furl or a filename containing a FURL. This is useful if you | |
394 | simply know the location of the FURL file. |
|
409 | simply know the location of the FURL file. | |
395 | ipython_dir : str |
|
410 | ipython_dir : str | |
396 | The location of the ipython_dir if different from the default. |
|
411 | The location of the ipython_dir if different from the default. |
@@ -15,7 +15,7 b' __docformat__ = "restructuredtext en"' | |||||
15 | # Imports |
|
15 | # Imports | |
16 | #------------------------------------------------------------------------------- |
|
16 | #------------------------------------------------------------------------------- | |
17 |
|
17 | |||
18 |
from zope.interface import Interface |
|
18 | from zope.interface import Interface | |
19 |
|
19 | |||
20 | class IFCClientInterfaceProvider(Interface): |
|
20 | class IFCClientInterfaceProvider(Interface): | |
21 |
|
21 |
@@ -24,13 +24,16 b' import warnings' | |||||
24 |
|
24 | |||
25 | from twisted.python import log |
|
25 | from twisted.python import log | |
26 |
|
26 | |||
27 | from IPython.core import release |
|
|||
28 | from IPython.config.loader import PyFileConfigLoader |
|
27 | from IPython.config.loader import PyFileConfigLoader | |
29 | from IPython.core.application import Application |
|
28 | from IPython.core.application import Application, BaseAppConfigLoader | |
30 | from IPython.core.component import Component |
|
29 | from IPython.core.component import Component | |
31 | from IPython.utils.genutils import get_ipython_dir, get_ipython_package_dir |
|
30 | from IPython.core.crashhandler import CrashHandler | |
32 |
from IPython. |
|
31 | from IPython.core import release | |
33 |
from IPython.utils import |
|
32 | from IPython.utils.path import ( | |
|
33 | get_ipython_package_dir, | |||
|
34 | expand_path | |||
|
35 | ) | |||
|
36 | from IPython.utils.traitlets import Unicode | |||
34 |
|
37 | |||
35 | #----------------------------------------------------------------------------- |
|
38 | #----------------------------------------------------------------------------- | |
36 | # Warnings control |
|
39 | # Warnings control | |
@@ -45,7 +48,7 b" warnings.filterwarnings('ignore', 'the sha module is deprecated'," | |||||
45 | DeprecationWarning) |
|
48 | DeprecationWarning) | |
46 |
|
49 | |||
47 | #----------------------------------------------------------------------------- |
|
50 | #----------------------------------------------------------------------------- | |
48 | # Classes and functions |
|
51 | # Module errors | |
49 | #----------------------------------------------------------------------------- |
|
52 | #----------------------------------------------------------------------------- | |
50 |
|
53 | |||
51 | class ClusterDirError(Exception): |
|
54 | class ClusterDirError(Exception): | |
@@ -56,6 +59,10 b' class PIDFileError(Exception):' | |||||
56 | pass |
|
59 | pass | |
57 |
|
60 | |||
58 |
|
61 | |||
|
62 | #----------------------------------------------------------------------------- | |||
|
63 | # Class for managing cluster directories | |||
|
64 | #----------------------------------------------------------------------------- | |||
|
65 | ||||
59 | class ClusterDir(Component): |
|
66 | class ClusterDir(Component): | |
60 | """An object to manage the cluster directory and its resources. |
|
67 | """An object to manage the cluster directory and its resources. | |
61 |
|
68 | |||
@@ -225,44 +232,110 b' class ClusterDir(Component):' | |||||
225 | The path of the cluster directory. This is expanded using |
|
232 | The path of the cluster directory. This is expanded using | |
226 | :func:`IPython.utils.genutils.expand_path`. |
|
233 | :func:`IPython.utils.genutils.expand_path`. | |
227 | """ |
|
234 | """ | |
228 |
cluster_dir = |
|
235 | cluster_dir = expand_path(cluster_dir) | |
229 | if not os.path.isdir(cluster_dir): |
|
236 | if not os.path.isdir(cluster_dir): | |
230 | raise ClusterDirError('Cluster directory not found: %s' % cluster_dir) |
|
237 | raise ClusterDirError('Cluster directory not found: %s' % cluster_dir) | |
231 | return ClusterDir(cluster_dir) |
|
238 | return ClusterDir(cluster_dir) | |
232 |
|
239 | |||
233 |
|
240 | |||
234 | # Default command line options for IPython cluster applications. |
|
241 | #----------------------------------------------------------------------------- | |
235 | cl_args = ( |
|
242 | # Command line options | |
236 | (('--ipython-dir',), dict( |
|
243 | #----------------------------------------------------------------------------- | |
237 | dest='Global.ipython_dir',type=unicode, |
|
244 | ||
238 | help='Set to override default location of Global.ipython_dir.', |
|
245 | class ClusterDirConfigLoader(BaseAppConfigLoader): | |
239 | metavar='Global.ipython_dir') ), |
|
246 | ||
240 | (('-p', '--profile',), dict( |
|
247 | def _add_cluster_profile(self, parser): | |
|
248 | paa = parser.add_argument | |||
|
249 | paa('-p', '--profile', | |||
241 | dest='Global.profile',type=unicode, |
|
250 | dest='Global.profile',type=unicode, | |
242 | help= |
|
251 | help= | |
243 | """The string name of the profile to be used. This determines the name |
|
252 | """The string name of the profile to be used. This determines the name | |
244 | of the cluster dir as: cluster_<profile>. The default profile is named |
|
253 | of the cluster dir as: cluster_<profile>. The default profile is named | |
245 | 'default'. The cluster directory is resolve this way if the |
|
254 | 'default'. The cluster directory is resolve this way if the | |
246 | --cluster-dir option is not used.""", |
|
255 | --cluster-dir option is not used.""", | |
247 |
metavar='Global.profile') |
|
256 | metavar='Global.profile') | |
248 | (('--cluster-dir',), dict( |
|
257 | ||
|
258 | def _add_cluster_dir(self, parser): | |||
|
259 | paa = parser.add_argument | |||
|
260 | paa('--cluster-dir', | |||
249 | dest='Global.cluster_dir',type=unicode, |
|
261 | dest='Global.cluster_dir',type=unicode, | |
250 | help="""Set the cluster dir. This overrides the logic used by the |
|
262 | help="""Set the cluster dir. This overrides the logic used by the | |
251 | --profile option.""", |
|
263 | --profile option.""", | |
252 |
metavar='Global.cluster_dir') |
|
264 | metavar='Global.cluster_dir') | |
253 | (('--work-dir',), dict( |
|
265 | ||
|
266 | def _add_work_dir(self, parser): | |||
|
267 | paa = parser.add_argument | |||
|
268 | paa('--work-dir', | |||
254 | dest='Global.work_dir',type=unicode, |
|
269 | dest='Global.work_dir',type=unicode, | |
255 | help='Set the working dir for the process.', |
|
270 | help='Set the working dir for the process.', | |
256 |
metavar='Global.work_dir') |
|
271 | metavar='Global.work_dir') | |
257 | (('--clean-logs',), dict( |
|
272 | ||
|
273 | def _add_clean_logs(self, parser): | |||
|
274 | paa = parser.add_argument | |||
|
275 | paa('--clean-logs', | |||
258 | dest='Global.clean_logs', action='store_true', |
|
276 | dest='Global.clean_logs', action='store_true', | |
259 |
help='Delete old log flies before starting.') |
|
277 | help='Delete old log flies before starting.') | |
260 | (('--no-clean-logs',), dict( |
|
278 | ||
|
279 | def _add_no_clean_logs(self, parser): | |||
|
280 | paa = parser.add_argument | |||
|
281 | paa('--no-clean-logs', | |||
261 | dest='Global.clean_logs', action='store_false', |
|
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 | class ApplicationWithClusterDir(Application): |
|
339 | class ApplicationWithClusterDir(Application): | |
267 | """An application that puts everything into a cluster directory. |
|
340 | """An application that puts everything into a cluster directory. | |
268 |
|
341 | |||
@@ -282,10 +355,10 b' class ApplicationWithClusterDir(Application):' | |||||
282 | dir and named the value of the ``config_file_name`` class attribute. |
|
355 | dir and named the value of the ``config_file_name`` class attribute. | |
283 | """ |
|
356 | """ | |
284 |
|
357 | |||
|
358 | command_line_loader = ClusterDirConfigLoader | |||
|
359 | crash_handler_class = ClusterDirCrashHandler | |||
285 | auto_create_cluster_dir = True |
|
360 | auto_create_cluster_dir = True | |
286 |
|
361 | |||
287 | cl_arguments = Application.cl_arguments + cl_args |
|
|||
288 |
|
||||
289 | def create_default_config(self): |
|
362 | def create_default_config(self): | |
290 | super(ApplicationWithClusterDir, self).create_default_config() |
|
363 | super(ApplicationWithClusterDir, self).create_default_config() | |
291 | self.default_config.Global.profile = u'default' |
|
364 | self.default_config.Global.profile = u'default' | |
@@ -316,7 +389,7 b' class ApplicationWithClusterDir(Application):' | |||||
316 | cluster_dir = self.command_line_config.Global.cluster_dir |
|
389 | cluster_dir = self.command_line_config.Global.cluster_dir | |
317 | except AttributeError: |
|
390 | except AttributeError: | |
318 | cluster_dir = self.default_config.Global.cluster_dir |
|
391 | cluster_dir = self.default_config.Global.cluster_dir | |
319 |
cluster_dir = |
|
392 | cluster_dir = expand_path(cluster_dir) | |
320 | try: |
|
393 | try: | |
321 | self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir) |
|
394 | self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir) | |
322 | except ClusterDirError: |
|
395 | except ClusterDirError: | |
@@ -368,15 +441,17 b' class ApplicationWithClusterDir(Application):' | |||||
368 | def find_config_file_name(self): |
|
441 | def find_config_file_name(self): | |
369 | """Find the config file name for this application.""" |
|
442 | """Find the config file name for this application.""" | |
370 | # For this type of Application it should be set as a class attribute. |
|
443 | # For this type of Application it should be set as a class attribute. | |
371 | if not hasattr(self, 'config_file_name'): |
|
444 | if not hasattr(self, 'default_config_file_name'): | |
372 | self.log.critical("No config filename found") |
|
445 | self.log.critical("No config filename found") | |
|
446 | else: | |||
|
447 | self.config_file_name = self.default_config_file_name | |||
373 |
|
448 | |||
374 | def find_config_file_paths(self): |
|
449 | def find_config_file_paths(self): | |
375 | # Include our own config directory last, so that users can still find |
|
450 | # Set the search path to to the cluster directory. We should NOT | |
376 | # our shipped copies of builtin config files even if they don't have |
|
451 | # include IPython.config.default here as the default config files | |
377 |
# |
|
452 | # are ALWAYS automatically moved to the cluster directory. | |
378 | conf_dir = os.path.join(get_ipython_package_dir(), 'config', 'default') |
|
453 | conf_dir = os.path.join(get_ipython_package_dir(), 'config', 'default') | |
379 |
self.config_file_paths = (self.cluster_dir, |
|
454 | self.config_file_paths = (self.cluster_dir,) | |
380 |
|
455 | |||
381 | def pre_construct(self): |
|
456 | def pre_construct(self): | |
382 | # The log and security dirs were set earlier, but here we put them |
|
457 | # The log and security dirs were set earlier, but here we put them | |
@@ -389,7 +464,7 b' class ApplicationWithClusterDir(Application):' | |||||
389 | pdir = self.cluster_dir_obj.pid_dir |
|
464 | pdir = self.cluster_dir_obj.pid_dir | |
390 | self.pid_dir = config.Global.pid_dir = pdir |
|
465 | self.pid_dir = config.Global.pid_dir = pdir | |
391 | self.log.info("Cluster directory set to: %s" % self.cluster_dir) |
|
466 | self.log.info("Cluster directory set to: %s" % self.cluster_dir) | |
392 |
config.Global.work_dir = unicode( |
|
467 | config.Global.work_dir = unicode(expand_path(config.Global.work_dir)) | |
393 | # Change to the working directory. We do this just before construct |
|
468 | # Change to the working directory. We do this just before construct | |
394 | # is called so all the components there have the right working dir. |
|
469 | # is called so all the components there have the right working dir. | |
395 | self.to_work_dir() |
|
470 | self.to_work_dir() | |
@@ -461,3 +536,4 b' class ApplicationWithClusterDir(Application):' | |||||
461 | return pid |
|
536 | return pid | |
462 | else: |
|
537 | else: | |
463 | raise PIDFileError('pid file not found: %s' % pid_file) |
|
538 | raise PIDFileError('pid file not found: %s' % pid_file) | |
|
539 |
@@ -37,20 +37,18 b' __docformat__ = "restructuredtext en"' | |||||
37 | # Imports |
|
37 | # Imports | |
38 | #------------------------------------------------------------------------------- |
|
38 | #------------------------------------------------------------------------------- | |
39 |
|
39 | |||
40 |
import os |
|
40 | import os | |
41 |
|
41 | |||
42 | from twisted.application import service |
|
42 | from twisted.application import service | |
43 |
from twisted. |
|
43 | from twisted.python import log | |
44 | from twisted.python import log, components |
|
|||
45 | from zope.interface import Interface, implements, Attribute |
|
44 | from zope.interface import Interface, implements, Attribute | |
46 | import zope.interface as zi |
|
|||
47 |
|
45 | |||
48 | from IPython.kernel.engineservice import \ |
|
46 | from IPython.kernel.engineservice import \ | |
49 | IEngineCore, \ |
|
47 | IEngineCore, \ | |
50 | IEngineSerialized, \ |
|
48 | IEngineSerialized, \ | |
51 | IEngineQueued |
|
49 | IEngineQueued | |
52 |
|
50 | |||
53 |
from IPython.utils. |
|
51 | from IPython.utils.path import get_ipython_dir | |
54 | from IPython.kernel import codeutil |
|
52 | from IPython.kernel import codeutil | |
55 |
|
53 | |||
56 | #------------------------------------------------------------------------------- |
|
54 | #------------------------------------------------------------------------------- |
@@ -21,6 +21,8 b' __docformat__ = "restructuredtext en"' | |||||
21 |
|
21 | |||
22 | # Required modules |
|
22 | # Required modules | |
23 | import __builtin__ |
|
23 | import __builtin__ | |
|
24 | import os | |||
|
25 | import re | |||
24 | import socket |
|
26 | import socket | |
25 | import sys |
|
27 | import sys | |
26 |
|
28 | |||
@@ -30,7 +32,8 b' from IPython.external.Itpl import ItplNS' | |||||
30 | from IPython.utils import coloransi |
|
32 | from IPython.utils import coloransi | |
31 | from IPython.core import release |
|
33 | from IPython.core import release | |
32 | from IPython.core.error import TryNext |
|
34 | from IPython.core.error import TryNext | |
33 |
from IPython.utils. |
|
35 | from IPython.utils.io import Term | |
|
36 | from IPython.utils.warn import warn | |||
34 | import IPython.utils.generics |
|
37 | import IPython.utils.generics | |
35 |
|
38 | |||
36 | #**************************************************************************** |
|
39 | #**************************************************************************** |
@@ -22,15 +22,15 b' import sys' | |||||
22 |
|
22 | |||
23 | from twisted.trial import unittest |
|
23 | from twisted.trial import unittest | |
24 |
|
24 | |||
|
25 | from IPython.testing import decorators_trial as dec | |||
|
26 | ||||
25 | #----------------------------------------------------------------------------- |
|
27 | #----------------------------------------------------------------------------- | |
26 | # Tests |
|
28 | # Tests | |
27 | #----------------------------------------------------------------------------- |
|
29 | #----------------------------------------------------------------------------- | |
28 |
|
30 | |||
29 | class TestRedirector(unittest.TestCase): |
|
31 | class TestRedirector(unittest.TestCase): | |
30 |
|
32 | |||
31 | if sys.platform == 'win32': |
|
33 | @dec.skip_win32 | |
32 | skip = True |
|
|||
33 |
|
||||
34 | def test_redirector(self): |
|
34 | def test_redirector(self): | |
35 | """Checks that the redirector can be used to do synchronous capture. |
|
35 | """Checks that the redirector can be used to do synchronous capture. | |
36 | """ |
|
36 | """ | |
@@ -51,6 +51,7 b' class TestRedirector(unittest.TestCase):' | |||||
51 | result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10)) |
|
51 | result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10)) | |
52 | self.assertEquals(result1, result2) |
|
52 | self.assertEquals(result1, result2) | |
53 |
|
53 | |||
|
54 | @dec.skip_win32 | |||
54 | def test_redirector_output_trap(self): |
|
55 | def test_redirector_output_trap(self): | |
55 | """Check the greedy trapping behavior of the traps. |
|
56 | """Check the greedy trapping behavior of the traps. | |
56 |
|
57 |
@@ -17,8 +17,7 b'' | |||||
17 | import os |
|
17 | import os | |
18 | import cPickle as pickle |
|
18 | import cPickle as pickle | |
19 |
|
19 | |||
20 |
from twisted.python import log |
|
20 | from twisted.python import log | |
21 | from twisted.internet import defer |
|
|||
22 | from twisted.internet.defer import inlineCallbacks, returnValue |
|
21 | from twisted.internet.defer import inlineCallbacks, returnValue | |
23 |
|
22 | |||
24 | from IPython.kernel.fcutil import find_furl, validate_furl_or_file |
|
23 | from IPython.kernel.fcutil import find_furl, validate_furl_or_file |
@@ -19,40 +19,36 b' __docformat__ = "restructuredtext en"' | |||||
19 | # Imports |
|
19 | # Imports | |
20 | #------------------------------------------------------------------------------- |
|
20 | #------------------------------------------------------------------------------- | |
21 |
|
21 | |||
22 | import os, time |
|
|||
23 | import cPickle as pickle |
|
22 | import cPickle as pickle | |
24 |
|
23 | |||
25 | from twisted.python import components, log, failure |
|
24 | from twisted.python import components, log, failure | |
26 |
from twisted. |
|
25 | from twisted.internet import defer, threads | |
27 | from twisted.internet import defer, reactor, threads |
|
26 | from zope.interface import Interface, implements | |
28 | from twisted.internet.interfaces import IProtocolFactory |
|
|||
29 | from zope.interface import Interface, implements, Attribute |
|
|||
30 |
|
27 | |||
31 | from twisted.internet.base import DelayedCall |
|
28 | from twisted.internet.base import DelayedCall | |
32 | DelayedCall.debug = True |
|
29 | DelayedCall.debug = True | |
33 |
|
30 | |||
|
31 | try: | |||
|
32 | from foolscap.api import Referenceable, DeadReferenceError | |||
|
33 | except ImportError: | |||
34 | from foolscap import Referenceable, DeadReferenceError |
|
34 | from foolscap import Referenceable, DeadReferenceError | |
35 | from foolscap.referenceable import RemoteReference |
|
35 | from foolscap.referenceable import RemoteReference | |
36 |
|
36 | |||
37 | from IPython.kernel.pbutil import packageFailure, unpackageFailure |
|
37 | from IPython.kernel.pbutil import packageFailure, unpackageFailure | |
38 | from IPython.kernel.util import printer |
|
|||
39 | from IPython.kernel.twistedutil import gatherBoth |
|
|||
40 | from IPython.kernel import newserialized |
|
|||
41 | from IPython.kernel.error import ProtocolError |
|
|||
42 | from IPython.kernel import controllerservice |
|
|||
43 | from IPython.kernel.controllerservice import IControllerBase |
|
38 | from IPython.kernel.controllerservice import IControllerBase | |
44 |
from IPython.kernel.engineservice import |
|
39 | from IPython.kernel.engineservice import ( | |
45 |
IEngineBase, |
|
40 | IEngineBase, | |
46 |
IEngineQueued, |
|
41 | IEngineQueued, | |
47 | EngineService, \ |
|
|||
48 | StrictDict |
|
42 | StrictDict | |
49 | from IPython.kernel.pickleutil import \ |
|
43 | ) | |
50 | can, \ |
|
44 | from IPython.kernel.pickleutil import ( | |
51 |
can |
|
45 | can, | |
52 |
can |
|
46 | canDict, | |
53 | uncan, \ |
|
47 | canSequence, | |
54 |
uncan |
|
48 | uncan, | |
|
49 | uncanDict, | |||
55 | uncanSequence |
|
50 | uncanSequence | |
|
51 | ) | |||
56 |
|
52 | |||
57 |
|
53 | |||
58 | #------------------------------------------------------------------------------- |
|
54 | #------------------------------------------------------------------------------- |
@@ -17,6 +17,9 b' __test__ = {}' | |||||
17 | #------------------------------------------------------------------------------- |
|
17 | #------------------------------------------------------------------------------- | |
18 | # Imports |
|
18 | # Imports | |
19 | #------------------------------------------------------------------------------- |
|
19 | #------------------------------------------------------------------------------- | |
|
20 | ||||
|
21 | from twisted.python import failure | |||
|
22 | ||||
20 | from IPython.kernel.core import error |
|
23 | from IPython.kernel.core import error | |
21 |
|
24 | |||
22 | #------------------------------------------------------------------------------- |
|
25 | #------------------------------------------------------------------------------- | |
@@ -26,6 +29,7 b' from IPython.kernel.core import error' | |||||
26 | class KernelError(error.IPythonError): |
|
29 | class KernelError(error.IPythonError): | |
27 | pass |
|
30 | pass | |
28 |
|
31 | |||
|
32 | ||||
29 | class NotDefined(KernelError): |
|
33 | class NotDefined(KernelError): | |
30 | def __init__(self, name): |
|
34 | def __init__(self, name): | |
31 | self.name = name |
|
35 | self.name = name | |
@@ -36,78 +40,102 b' class NotDefined(KernelError):' | |||||
36 |
|
40 | |||
37 | __str__ = __repr__ |
|
41 | __str__ = __repr__ | |
38 |
|
42 | |||
|
43 | ||||
39 | class QueueCleared(KernelError): |
|
44 | class QueueCleared(KernelError): | |
40 | pass |
|
45 | pass | |
41 |
|
46 | |||
|
47 | ||||
42 | class IdInUse(KernelError): |
|
48 | class IdInUse(KernelError): | |
43 | pass |
|
49 | pass | |
44 |
|
50 | |||
|
51 | ||||
45 | class ProtocolError(KernelError): |
|
52 | class ProtocolError(KernelError): | |
46 | pass |
|
53 | pass | |
47 |
|
54 | |||
|
55 | ||||
48 | class ConnectionError(KernelError): |
|
56 | class ConnectionError(KernelError): | |
49 | pass |
|
57 | pass | |
50 |
|
58 | |||
|
59 | ||||
51 | class InvalidEngineID(KernelError): |
|
60 | class InvalidEngineID(KernelError): | |
52 | pass |
|
61 | pass | |
53 |
|
62 | |||
|
63 | ||||
54 | class NoEnginesRegistered(KernelError): |
|
64 | class NoEnginesRegistered(KernelError): | |
55 | pass |
|
65 | pass | |
56 |
|
66 | |||
|
67 | ||||
57 | class InvalidClientID(KernelError): |
|
68 | class InvalidClientID(KernelError): | |
58 | pass |
|
69 | pass | |
59 |
|
70 | |||
|
71 | ||||
60 | class InvalidDeferredID(KernelError): |
|
72 | class InvalidDeferredID(KernelError): | |
61 | pass |
|
73 | pass | |
62 |
|
74 | |||
|
75 | ||||
63 | class SerializationError(KernelError): |
|
76 | class SerializationError(KernelError): | |
64 | pass |
|
77 | pass | |
65 |
|
78 | |||
|
79 | ||||
66 | class MessageSizeError(KernelError): |
|
80 | class MessageSizeError(KernelError): | |
67 | pass |
|
81 | pass | |
68 |
|
82 | |||
|
83 | ||||
69 | class PBMessageSizeError(MessageSizeError): |
|
84 | class PBMessageSizeError(MessageSizeError): | |
70 | pass |
|
85 | pass | |
71 |
|
86 | |||
|
87 | ||||
72 | class ResultNotCompleted(KernelError): |
|
88 | class ResultNotCompleted(KernelError): | |
73 | pass |
|
89 | pass | |
74 |
|
90 | |||
|
91 | ||||
75 | class ResultAlreadyRetrieved(KernelError): |
|
92 | class ResultAlreadyRetrieved(KernelError): | |
76 | pass |
|
93 | pass | |
77 |
|
94 | |||
78 | class ClientError(KernelError): |
|
95 | class ClientError(KernelError): | |
79 | pass |
|
96 | pass | |
80 |
|
97 | |||
|
98 | ||||
81 | class TaskAborted(KernelError): |
|
99 | class TaskAborted(KernelError): | |
82 | pass |
|
100 | pass | |
83 |
|
101 | |||
|
102 | ||||
84 | class TaskTimeout(KernelError): |
|
103 | class TaskTimeout(KernelError): | |
85 | pass |
|
104 | pass | |
86 |
|
105 | |||
|
106 | ||||
87 | class NotAPendingResult(KernelError): |
|
107 | class NotAPendingResult(KernelError): | |
88 | pass |
|
108 | pass | |
89 |
|
109 | |||
|
110 | ||||
90 | class UnpickleableException(KernelError): |
|
111 | class UnpickleableException(KernelError): | |
91 | pass |
|
112 | pass | |
92 |
|
113 | |||
|
114 | ||||
93 | class AbortedPendingDeferredError(KernelError): |
|
115 | class AbortedPendingDeferredError(KernelError): | |
94 | pass |
|
116 | pass | |
95 |
|
117 | |||
|
118 | ||||
96 | class InvalidProperty(KernelError): |
|
119 | class InvalidProperty(KernelError): | |
97 | pass |
|
120 | pass | |
98 |
|
121 | |||
|
122 | ||||
99 | class MissingBlockArgument(KernelError): |
|
123 | class MissingBlockArgument(KernelError): | |
100 | pass |
|
124 | pass | |
101 |
|
125 | |||
|
126 | ||||
102 | class StopLocalExecution(KernelError): |
|
127 | class StopLocalExecution(KernelError): | |
103 | pass |
|
128 | pass | |
104 |
|
129 | |||
|
130 | ||||
105 | class SecurityError(KernelError): |
|
131 | class SecurityError(KernelError): | |
106 | pass |
|
132 | pass | |
107 |
|
133 | |||
|
134 | ||||
108 | class FileTimeoutError(KernelError): |
|
135 | class FileTimeoutError(KernelError): | |
109 | pass |
|
136 | pass | |
110 |
|
137 | |||
|
138 | ||||
111 | class TaskRejectError(KernelError): |
|
139 | class TaskRejectError(KernelError): | |
112 | """Exception to raise when a task should be rejected by an engine. |
|
140 | """Exception to raise when a task should be rejected by an engine. | |
113 |
|
141 | |||
@@ -122,6 +150,7 b' class TaskRejectError(KernelError):' | |||||
122 | properties don't have to be managed or tested by the controller. |
|
150 | properties don't have to be managed or tested by the controller. | |
123 | """ |
|
151 | """ | |
124 |
|
152 | |||
|
153 | ||||
125 | class CompositeError(KernelError): |
|
154 | class CompositeError(KernelError): | |
126 | def __init__(self, message, elist): |
|
155 | def __init__(self, message, elist): | |
127 | Exception.__init__(self, *(message, elist)) |
|
156 | Exception.__init__(self, *(message, elist)) | |
@@ -176,9 +205,8 b' class CompositeError(KernelError):' | |||||
176 | else: |
|
205 | else: | |
177 | raise et, ev, etb |
|
206 | raise et, ev, etb | |
178 |
|
207 | |||
179 | def collect_exceptions(rlist, method): |
|
|||
180 | from twisted.python import failure |
|
|||
181 |
|
208 | |||
|
209 | def collect_exceptions(rlist, method): | |||
182 | elist = [] |
|
210 | elist = [] | |
183 | for r in rlist: |
|
211 | for r in rlist: | |
184 | if isinstance(r, failure.Failure): |
|
212 | if isinstance(r, failure.Failure): | |
@@ -204,4 +232,3 b' def collect_exceptions(rlist, method):' | |||||
204 | except CompositeError, e: |
|
232 | except CompositeError, e: | |
205 | raise e |
|
233 | raise e | |
206 |
|
234 | |||
207 |
|
@@ -23,16 +23,19 b' import tempfile' | |||||
23 | from twisted.internet import reactor, defer |
|
23 | from twisted.internet import reactor, defer | |
24 | from twisted.python import log |
|
24 | from twisted.python import log | |
25 |
|
25 | |||
|
26 | import foolscap | |||
|
27 | try: | |||
|
28 | from foolscap.api import Tub, UnauthenticatedTub | |||
|
29 | except ImportError: | |||
26 | from foolscap import Tub, UnauthenticatedTub |
|
30 | from foolscap import Tub, UnauthenticatedTub | |
27 |
|
31 | |||
28 | from IPython.config.loader import Config |
|
32 | from IPython.config.loader import Config | |
29 |
|
||||
30 | from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory |
|
33 | from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory | |
31 |
|
||||
32 | from IPython.kernel.error import SecurityError |
|
34 | from IPython.kernel.error import SecurityError | |
33 |
|
35 | |||
34 | from IPython.utils.traitlets import Int, Str, Bool, Instance |
|
|||
35 | from IPython.utils.importstring import import_item |
|
36 | from IPython.utils.importstring import import_item | |
|
37 | from IPython.utils.path import expand_path | |||
|
38 | from IPython.utils.traitlets import Int, Str, Bool, Instance | |||
36 |
|
39 | |||
37 | #----------------------------------------------------------------------------- |
|
40 | #----------------------------------------------------------------------------- | |
38 | # Code |
|
41 | # Code | |
@@ -57,17 +60,17 b' class FURLError(Exception):' | |||||
57 |
|
60 | |||
58 | def check_furl_file_security(furl_file, secure): |
|
61 | def check_furl_file_security(furl_file, secure): | |
59 | """Remove the old furl_file if changing security modes.""" |
|
62 | """Remove the old furl_file if changing security modes.""" | |
|
63 | furl_file = expand_path(furl_file) | |||
60 | if os.path.isfile(furl_file): |
|
64 | if os.path.isfile(furl_file): | |
61 |
|
|
65 | with open(furl_file, 'r') as f: | |
62 | oldfurl = f.read().strip() |
|
66 | oldfurl = f.read().strip() | |
63 | f.close() |
|
|||
64 | if (oldfurl.startswith('pb://') and not secure) or (oldfurl.startswith('pbu://') and secure): |
|
67 | if (oldfurl.startswith('pb://') and not secure) or (oldfurl.startswith('pbu://') and secure): | |
65 | os.remove(furl_file) |
|
68 | os.remove(furl_file) | |
66 |
|
69 | |||
67 |
|
70 | |||
68 | def is_secure(furl): |
|
71 | def is_secure(furl): | |
69 | """Is the given FURL secure or not.""" |
|
72 | """Is the given FURL secure or not.""" | |
70 | if is_valid(furl): |
|
73 | if is_valid_furl(furl): | |
71 | if furl.startswith("pb://"): |
|
74 | if furl.startswith("pb://"): | |
72 | return True |
|
75 | return True | |
73 | elif furl.startswith("pbu://"): |
|
76 | elif furl.startswith("pbu://"): | |
@@ -76,26 +79,45 b' def is_secure(furl):' | |||||
76 | raise FURLError("invalid FURL: %s" % furl) |
|
79 | raise FURLError("invalid FURL: %s" % furl) | |
77 |
|
80 | |||
78 |
|
81 | |||
79 | def is_valid(furl): |
|
82 | def is_valid_furl(furl): | |
80 | """Is the str a valid FURL or not.""" |
|
83 | """Is the str a valid FURL or not.""" | |
81 | if isinstance(furl, str): |
|
84 | if isinstance(furl, str): | |
82 | if furl.startswith("pb://") or furl.startswith("pbu://"): |
|
85 | if furl.startswith("pb://") or furl.startswith("pbu://"): | |
83 | return True |
|
86 | return True | |
84 | else: |
|
87 | else: | |
85 | return False |
|
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 | def find_furl(furl_or_file): |
|
106 | def find_furl(furl_or_file): | |
89 |
"""Find, validate and return a FURL in a string or file. |
|
107 | """Find, validate and return a FURL in a string or file. | |
90 | if isinstance(furl_or_file, str): |
|
108 | ||
91 | if is_valid(furl_or_file): |
|
109 | This calls :func:`IPython.utils.path.expand_path` on the argument to | |
|
110 | properly handle ``~`` and ``$`` variables in the path. | |||
|
111 | """ | |||
|
112 | if is_valid_furl(furl_or_file): | |||
92 |
|
|
113 | return furl_or_file | |
93 |
|
|
114 | furl_or_file = expand_path(furl_or_file) | |
|
115 | if is_valid_furl_file(furl_or_file): | |||
94 | with open(furl_or_file, 'r') as f: |
|
116 | with open(furl_or_file, 'r') as f: | |
95 | furl = f.read().strip() |
|
117 | furl = f.read().strip() | |
96 | if is_valid(furl): |
|
118 | if is_valid_furl(furl): | |
97 | return furl |
|
119 | return furl | |
98 |
raise FURLError("Not a valid FURL or FURL file: % |
|
120 | raise FURLError("Not a valid FURL or FURL file: %r" % furl_or_file) | |
99 |
|
121 | |||
100 |
|
122 | |||
101 | def is_valid_furl_or_file(furl_or_file): |
|
123 | def is_valid_furl_or_file(furl_or_file): | |
@@ -106,17 +128,14 b' def is_valid_furl_or_file(furl_or_file):' | |||||
106 | if the FURL file exists or to read its contents. This is useful for |
|
128 | if the FURL file exists or to read its contents. This is useful for | |
107 | cases where auto re-connection is being used. |
|
129 | cases where auto re-connection is being used. | |
108 | """ |
|
130 | """ | |
109 | if isinstance(furl_or_file, str): |
|
131 | if is_valid_furl(furl_or_file) or is_valid_furl_file(furl_or_file): | |
110 | if is_valid(furl_or_file): |
|
|||
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'): |
|
|||
115 |
|
|
132 | return True | |
|
133 | else: | |||
116 | return False |
|
134 | return False | |
117 |
|
135 | |||
118 |
|
136 | |||
119 | def validate_furl_or_file(furl_or_file): |
|
137 | def validate_furl_or_file(furl_or_file): | |
|
138 | """Like :func:`is_valid_furl_or_file`, but raises an error.""" | |||
120 | if not is_valid_furl_or_file(furl_or_file): |
|
139 | if not is_valid_furl_or_file(furl_or_file): | |
121 | raise FURLError('Not a valid FURL or FURL file: %r' % furl_or_file) |
|
140 | raise FURLError('Not a valid FURL or FURL file: %r' % furl_or_file) | |
122 |
|
141 | |||
@@ -265,8 +284,12 b' class FCServiceFactory(AdaptedConfiguredObjectFactory):' | |||||
265 | """Register the reference with the FURL file. |
|
284 | """Register the reference with the FURL file. | |
266 |
|
285 | |||
267 | The FURL file is created and then moved to make sure that when the |
|
286 | The FURL file is created and then moved to make sure that when the | |
268 | file appears, the buffer has been flushed and the file closed. |
|
287 | file appears, the buffer has been flushed and the file closed. This | |
|
288 | is not done if we are re-using FURLS however. | |||
269 | """ |
|
289 | """ | |
|
290 | if self.reuse_furls: | |||
|
291 | self.tub.registerReference(ref, furlFile=furl_file) | |||
|
292 | else: | |||
270 | temp_furl_file = get_temp_furlfile(furl_file) |
|
293 | temp_furl_file = get_temp_furlfile(furl_file) | |
271 | self.tub.registerReference(ref, furlFile=temp_furl_file) |
|
294 | self.tub.registerReference(ref, furlFile=temp_furl_file) | |
272 | os.rename(temp_furl_file, furl_file) |
|
295 | os.rename(temp_furl_file, furl_file) |
@@ -22,161 +22,203 b' import signal' | |||||
22 | if os.name=='posix': |
|
22 | if os.name=='posix': | |
23 | from twisted.scripts._twistd_unix import daemonize |
|
23 | from twisted.scripts._twistd_unix import daemonize | |
24 |
|
24 | |||
25 | from IPython.core import release |
|
25 | from twisted.internet import reactor, defer | |
26 | from IPython.external.argparse import ArgumentParser |
|
26 | from twisted.python import log, failure | |
27 | from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault |
|
27 | ||
28 | from IPython.utils.importstring import import_item |
|
|||
29 |
|
28 | |||
|
29 | from IPython.external.argparse import ArgumentParser, SUPPRESS | |||
|
30 | from IPython.utils.importstring import import_item | |||
30 | from IPython.kernel.clusterdir import ( |
|
31 | from IPython.kernel.clusterdir import ( | |
31 |
ApplicationWithClusterDir, 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 | # Exit codes for ipcluster |
|
58 | # Exit codes for ipcluster | |
44 |
|
59 | |||
45 | # This will be the exit code if the ipcluster appears to be running because |
|
60 | # This will be the exit code if the ipcluster appears to be running because | |
46 | # a .pid file exists |
|
61 | # a .pid file exists | |
47 | ALREADY_STARTED = 10 |
|
62 | ALREADY_STARTED = 10 | |
48 |
|
63 | |||
|
64 | ||||
49 | # This will be the exit code if ipcluster stop is run, but there is not .pid |
|
65 | # This will be the exit code if ipcluster stop is run, but there is not .pid | |
50 | # file to be found. |
|
66 | # file to be found. | |
51 | ALREADY_STOPPED = 11 |
|
67 | ALREADY_STOPPED = 11 | |
52 |
|
68 | |||
53 |
|
69 | |||
54 | class IPClusterCLLoader(ArgParseConfigLoader): |
|
70 | #----------------------------------------------------------------------------- | |
|
71 | # Command line options | |||
|
72 | #----------------------------------------------------------------------------- | |||
|
73 | ||||
|
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 | # This has all the common options that all subcommands use |
|
82 | # This has all the common options that all subcommands use | |
58 |
parent_parser1 = ArgumentParser( |
|
83 | parent_parser1 = ArgumentParser( | |
59 | argument_default=NoConfigDefault) |
|
84 | add_help=False, | |
60 | parent_parser1.add_argument('--ipython-dir', |
|
85 | argument_default=SUPPRESS | |
61 | dest='Global.ipython_dir',type=unicode, |
|
86 | ) | |
62 | help='Set to override default location of Global.ipython_dir.', |
|
87 | self._add_ipython_dir(parent_parser1) | |
63 | metavar='Global.ipython_dir') |
|
88 | self._add_log_level(parent_parser1) | |
64 | parent_parser1.add_argument('--log-level', |
|
|||
65 | dest="Global.log_level",type=int, |
|
|||
66 | help='Set the log level (0,10,20,30,40,50). Default is 30.', |
|
|||
67 | metavar='Global.log_level') |
|
|||
68 |
|
89 | |||
69 | # This has all the common options that other subcommands use |
|
90 | # This has all the common options that other subcommands use | |
70 |
parent_parser2 = ArgumentParser( |
|
91 | parent_parser2 = ArgumentParser( | |
71 | argument_default=NoConfigDefault) |
|
92 | add_help=False, | |
72 | parent_parser2.add_argument('-p','--profile', |
|
93 | argument_default=SUPPRESS | |
73 | dest='Global.profile',type=unicode, |
|
|||
74 | help='The string name of the profile to be used. This determines ' |
|
|||
75 | 'the name of the cluster dir as: cluster_<profile>. The default profile ' |
|
|||
76 | 'is named "default". The cluster directory is resolve this way ' |
|
|||
77 | 'if the --cluster-dir option is not used.', |
|
|||
78 | metavar='Global.profile') |
|
|||
79 | parent_parser2.add_argument('--cluster-dir', |
|
|||
80 | dest='Global.cluster_dir',type=unicode, |
|
|||
81 | help='Set the cluster dir. This overrides the logic used by the ' |
|
|||
82 | '--profile option.', |
|
|||
83 | metavar='Global.cluster_dir'), |
|
|||
84 | parent_parser2.add_argument('--work-dir', |
|
|||
85 | dest='Global.work_dir',type=unicode, |
|
|||
86 | help='Set the working dir for the process.', |
|
|||
87 | metavar='Global.work_dir') |
|
|||
88 | parent_parser2.add_argument('--log-to-file', |
|
|||
89 | action='store_true', dest='Global.log_to_file', |
|
|||
90 | help='Log to a file in the log directory (default is stdout)' |
|
|||
91 | ) |
|
94 | ) | |
|
95 | self._add_cluster_profile(parent_parser2) | |||
|
96 | self._add_cluster_dir(parent_parser2) | |||
|
97 | self._add_work_dir(parent_parser2) | |||
|
98 | paa = parent_parser2.add_argument | |||
|
99 | paa('--log-to-file', | |||
|
100 | action='store_true', dest='Global.log_to_file', | |||
|
101 | help='Log to a file in the log directory (default is stdout)') | |||
92 |
|
102 | |||
|
103 | # Create the object used to create the subparsers. | |||
93 | subparsers = self.parser.add_subparsers( |
|
104 | subparsers = self.parser.add_subparsers( | |
94 | dest='Global.subcommand', |
|
105 | dest='Global.subcommand', | |
95 | title='ipcluster subcommands', |
|
106 | title='ipcluster subcommands', | |
96 | description='ipcluster has a variety of subcommands. ' |
|
107 | description= | |
97 | 'The general way of running ipcluster is "ipcluster <cmd> ' |
|
108 | """ipcluster has a variety of subcommands. The general way of | |
98 | ' [options]""', |
|
109 | running ipcluster is 'ipcluster <cmd> [options]'. To get help | |
99 | help='For more help, type "ipcluster <cmd> -h"') |
|
110 | on a particular subcommand do 'ipcluster <cmd> -h'.""" | |
|
111 | # help="For more help, type 'ipcluster <cmd> -h'", | |||
|
112 | ) | |||
100 |
|
113 | |||
|
114 | # The "list" subcommand parser | |||
101 | parser_list = subparsers.add_parser( |
|
115 | parser_list = subparsers.add_parser( | |
102 | 'list', |
|
116 | 'list', | |
103 | help='List all clusters in cwd and ipython_dir.', |
|
117 | parents=[parent_parser1], | |
104 | parents=[parent_parser1] |
|
118 | argument_default=SUPPRESS, | |
|
119 | help="List all clusters in cwd and ipython_dir.", | |||
|
120 | description= | |||
|
121 | """List all available clusters, by cluster directory, that can | |||
|
122 | be found in the current working directly or in the ipython | |||
|
123 | directory. Cluster directories are named using the convention | |||
|
124 | 'cluster_<profile>'.""" | |||
105 | ) |
|
125 | ) | |
106 |
|
126 | |||
|
127 | # The "create" subcommand parser | |||
107 | parser_create = subparsers.add_parser( |
|
128 | parser_create = subparsers.add_parser( | |
108 | 'create', |
|
129 | 'create', | |
109 | help='Create a new cluster directory.', |
|
130 | parents=[parent_parser1, parent_parser2], | |
110 | parents=[parent_parser1, parent_parser2] |
|
131 | argument_default=SUPPRESS, | |
|
132 | help="Create a new cluster directory.", | |||
|
133 | description= | |||
|
134 | """Create an ipython cluster directory by its profile name or | |||
|
135 | cluster directory path. Cluster directories contain | |||
|
136 | configuration, log and security related files and are named | |||
|
137 | using the convention 'cluster_<profile>'. By default they are | |||
|
138 | located in your ipython directory. Once created, you will | |||
|
139 | probably need to edit the configuration files in the cluster | |||
|
140 | directory to configure your cluster. Most users will create a | |||
|
141 | cluster directory by profile name, | |||
|
142 | 'ipcluster create -p mycluster', which will put the directory | |||
|
143 | in '<ipython_dir>/cluster_mycluster'. | |||
|
144 | """ | |||
111 | ) |
|
145 | ) | |
112 |
parser_create.add_argument |
|
146 | paa = parser_create.add_argument | |
113 |
|
|
147 | paa('--reset-config', | |
114 | dest='Global.reset_config', action='store_true', |
|
148 | dest='Global.reset_config', action='store_true', | |
115 | default=NoConfigDefault, |
|
149 | help= | |
116 |
|
|
150 | """Recopy the default config files to the cluster directory. | |
117 |
|
|
151 | You will loose any modifications you have made to these files.""") | |
118 | ) |
|
|||
119 |
|
152 | |||
|
153 | # The "start" subcommand parser | |||
120 | parser_start = subparsers.add_parser( |
|
154 | parser_start = subparsers.add_parser( | |
121 | 'start', |
|
155 | 'start', | |
122 | help='Start a cluster.', |
|
156 | parents=[parent_parser1, parent_parser2], | |
123 | parents=[parent_parser1, parent_parser2] |
|
157 | argument_default=SUPPRESS, | |
|
158 | help="Start a cluster.", | |||
|
159 | description= | |||
|
160 | """Start an ipython cluster by its profile name or cluster | |||
|
161 | directory. Cluster directories contain configuration, log and | |||
|
162 | security related files and are named using the convention | |||
|
163 | 'cluster_<profile>' and should be creating using the 'start' | |||
|
164 | subcommand of 'ipcluster'. If your cluster directory is in | |||
|
165 | the cwd or the ipython directory, you can simply refer to it | |||
|
166 | using its profile name, 'ipcluster start -n 4 -p <profile>`, | |||
|
167 | otherwise use the '--cluster-dir' option. | |||
|
168 | """ | |||
124 | ) |
|
169 | ) | |
125 |
parser_start.add_argument |
|
170 | paa = parser_start.add_argument | |
126 |
|
|
171 | paa('-n', '--number', | |
127 | type=int, dest='Global.n', |
|
172 | type=int, dest='Global.n', | |
128 | help='The number of engines to start.', |
|
173 | help='The number of engines to start.', | |
129 | metavar='Global.n' |
|
174 | metavar='Global.n') | |
130 | ) |
|
175 | paa('--clean-logs', | |
131 | parser_start.add_argument('--clean-logs', |
|
|||
132 | dest='Global.clean_logs', action='store_true', |
|
176 | dest='Global.clean_logs', action='store_true', | |
133 |
help='Delete old log flies before starting.' |
|
177 | help='Delete old log flies before starting.') | |
134 | ) |
|
178 | paa('--no-clean-logs', | |
135 | parser_start.add_argument('--no-clean-logs', |
|
|||
136 | dest='Global.clean_logs', action='store_false', |
|
179 | dest='Global.clean_logs', action='store_false', | |
137 |
help="Don't delete old log flies before starting." |
|
180 | help="Don't delete old log flies before starting.") | |
138 | ) |
|
181 | paa('--daemon', | |
139 | parser_start.add_argument('--daemon', |
|
|||
140 | dest='Global.daemonize', action='store_true', |
|
182 | dest='Global.daemonize', action='store_true', | |
141 |
help='Daemonize the ipcluster program. This implies --log-to-file' |
|
183 | help='Daemonize the ipcluster program. This implies --log-to-file') | |
142 | ) |
|
184 | paa('--no-daemon', | |
143 | parser_start.add_argument('--no-daemon', |
|
|||
144 | dest='Global.daemonize', action='store_false', |
|
185 | dest='Global.daemonize', action='store_false', | |
145 |
help="Dont't daemonize the ipcluster program." |
|
186 | help="Dont't daemonize the ipcluster program.") | |
146 | ) |
|
|||
147 |
|
187 | |||
148 | parser_start = subparsers.add_parser( |
|
188 | # The "stop" subcommand parser | |
|
189 | parser_stop = subparsers.add_parser( | |||
149 | 'stop', |
|
190 | 'stop', | |
150 | help='Stop a cluster.', |
|
191 | parents=[parent_parser1, parent_parser2], | |
151 | parents=[parent_parser1, parent_parser2] |
|
192 | argument_default=SUPPRESS, | |
|
193 | help="Stop a running cluster.", | |||
|
194 | description= | |||
|
195 | """Stop a running ipython cluster by its profile name or cluster | |||
|
196 | directory. Cluster directories are named using the convention | |||
|
197 | 'cluster_<profile>'. If your cluster directory is in | |||
|
198 | the cwd or the ipython directory, you can simply refer to it | |||
|
199 | using its profile name, 'ipcluster stop -p <profile>`, otherwise | |||
|
200 | use the '--cluster-dir' option. | |||
|
201 | """ | |||
152 | ) |
|
202 | ) | |
153 |
parser_st |
|
203 | paa = parser_stop.add_argument | |
|
204 | paa('--signal', | |||
154 | dest='Global.signal', type=int, |
|
205 | dest='Global.signal', type=int, | |
155 | help="The signal number to use in stopping the cluster (default=2).", |
|
206 | help="The signal number to use in stopping the cluster (default=2).", | |
156 |
metavar="Global.signal" |
|
207 | metavar="Global.signal") | |
157 | ) |
|
|||
158 |
|
208 | |||
159 |
|
209 | |||
160 | default_config_file_name = u'ipcluster_config.py' |
|
210 | #----------------------------------------------------------------------------- | |
161 |
|
211 | # Main application | ||
162 |
|
212 | #----------------------------------------------------------------------------- | ||
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 | """ |
|
|||
173 |
|
213 | |||
174 |
|
214 | |||
175 | class IPClusterApp(ApplicationWithClusterDir): |
|
215 | class IPClusterApp(ApplicationWithClusterDir): | |
176 |
|
216 | |||
177 | name = u'ipcluster' |
|
217 | name = u'ipcluster' | |
178 | description = _description |
|
218 | description = _description | |
179 | config_file_name = default_config_file_name |
|
219 | usage = None | |
|
220 | command_line_loader = IPClusterAppConfigLoader | |||
|
221 | default_config_file_name = default_config_file_name | |||
180 | default_log_level = logging.INFO |
|
222 | default_log_level = logging.INFO | |
181 | auto_create_cluster_dir = False |
|
223 | auto_create_cluster_dir = False | |
182 |
|
224 | |||
@@ -192,13 +234,6 b' class IPClusterApp(ApplicationWithClusterDir):' | |||||
192 | self.default_config.Global.signal = 2 |
|
234 | self.default_config.Global.signal = 2 | |
193 | self.default_config.Global.daemonize = False |
|
235 | self.default_config.Global.daemonize = False | |
194 |
|
236 | |||
195 | def create_command_line_config(self): |
|
|||
196 | """Create and return a command line config loader.""" |
|
|||
197 | return IPClusterCLLoader( |
|
|||
198 | description=self.description, |
|
|||
199 | version=release.version |
|
|||
200 | ) |
|
|||
201 |
|
||||
202 | def find_resources(self): |
|
237 | def find_resources(self): | |
203 | subcommand = self.command_line_config.Global.subcommand |
|
238 | subcommand = self.command_line_config.Global.subcommand | |
204 | if subcommand=='list': |
|
239 | if subcommand=='list': | |
@@ -361,8 +396,11 b' class IPClusterApp(ApplicationWithClusterDir):' | |||||
361 | log.msg('Unexpected error in ipcluster:') |
|
396 | log.msg('Unexpected error in ipcluster:') | |
362 | log.msg(r.getTraceback()) |
|
397 | log.msg(r.getTraceback()) | |
363 | log.msg("IPython cluster: stopping") |
|
398 | log.msg("IPython cluster: stopping") | |
364 | self.stop_engines() |
|
399 | # These return deferreds. We are not doing anything with them | |
365 | self.stop_controller() |
|
400 | # but we are holding refs to them as a reminder that they | |
|
401 | # do return deferreds. | |||
|
402 | d1 = self.stop_engines() | |||
|
403 | d2 = self.stop_controller() | |||
366 | # Wait a few seconds to let things shut down. |
|
404 | # Wait a few seconds to let things shut down. | |
367 | reactor.callLater(4.0, reactor.stop) |
|
405 | reactor.callLater(4.0, reactor.stop) | |
368 |
|
406 | |||
@@ -449,6 +487,7 b' class IPClusterApp(ApplicationWithClusterDir):' | |||||
449 | # old .pid files. |
|
487 | # old .pid files. | |
450 | self.remove_pid_file() |
|
488 | self.remove_pid_file() | |
451 |
|
489 | |||
|
490 | ||||
452 | def launch_new_instance(): |
|
491 | def launch_new_instance(): | |
453 | """Create and run the IPython cluster.""" |
|
492 | """Create and run the IPython cluster.""" | |
454 | app = IPClusterApp() |
|
493 | app = IPClusterApp() |
@@ -18,20 +18,40 b' The IPython controller application.' | |||||
18 | from __future__ import with_statement |
|
18 | from __future__ import with_statement | |
19 |
|
19 | |||
20 | import copy |
|
20 | import copy | |
21 | import os |
|
|||
22 | import sys |
|
21 | import sys | |
23 |
|
22 | |||
24 | from twisted.application import service |
|
23 | from twisted.application import service | |
25 | from twisted.internet import reactor |
|
24 | from twisted.internet import reactor | |
26 | from twisted.python import log |
|
25 | from twisted.python import log | |
27 |
|
26 | |||
28 |
from IPython.config.loader import Config |
|
27 | from IPython.config.loader import Config | |
29 | from IPython.core import release |
|
|||
30 | from IPython.core.application import Application |
|
|||
31 | from IPython.kernel import controllerservice |
|
28 | from IPython.kernel import controllerservice | |
32 |
from IPython.kernel.clusterdir import |
|
29 | from IPython.kernel.clusterdir import ( | |
33 | from IPython.kernel.fcutil import FCServiceFactory |
|
30 | ApplicationWithClusterDir, | |
34 | from IPython.utils.traitlets import Str, Instance, Unicode |
|
31 | ClusterDirConfigLoader | |
|
32 | ) | |||
|
33 | from IPython.kernel.fcutil import FCServiceFactory, FURLError | |||
|
34 | from IPython.utils.traitlets import Instance, Unicode | |||
|
35 | ||||
|
36 | ||||
|
37 | #----------------------------------------------------------------------------- | |||
|
38 | # Module level variables | |||
|
39 | #----------------------------------------------------------------------------- | |||
|
40 | ||||
|
41 | ||||
|
42 | #: The default config file name for this application | |||
|
43 | default_config_file_name = u'ipcontroller_config.py' | |||
|
44 | ||||
|
45 | ||||
|
46 | _description = """Start the IPython controller for parallel computing. | |||
|
47 | ||||
|
48 | The IPython controller provides a gateway between the IPython engines and | |||
|
49 | clients. The controller needs to be started before the engines and can be | |||
|
50 | configured using command line options or using a cluster directory. Cluster | |||
|
51 | directories contain config, log and security files and are usually located in | |||
|
52 | your .ipython directory and named as "cluster_<profile>". See the --profile | |||
|
53 | and --cluster-dir options for details. | |||
|
54 | """ | |||
35 |
|
55 | |||
36 | #----------------------------------------------------------------------------- |
|
56 | #----------------------------------------------------------------------------- | |
37 | # Default interfaces |
|
57 | # Default interfaces | |
@@ -92,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 | # Client config |
|
124 | # Client config | |
101 |
|
|
125 | paa('--client-ip', | |
102 | type=str, dest='FCClientServiceFactory.ip', |
|
126 | type=str, dest='FCClientServiceFactory.ip', | |
103 | help='The IP address or hostname the controller will listen on for ' |
|
127 | help='The IP address or hostname the controller will listen on for ' | |
104 | 'client connections.', |
|
128 | 'client connections.', | |
105 | metavar='FCClientServiceFactory.ip') |
|
129 | metavar='FCClientServiceFactory.ip') | |
106 | ), |
|
130 | paa('--client-port', | |
107 | (('--client-port',), dict( |
|
|||
108 | type=int, dest='FCClientServiceFactory.port', |
|
131 | type=int, dest='FCClientServiceFactory.port', | |
109 | help='The port the controller will listen on for client connections. ' |
|
132 | help='The port the controller will listen on for client connections. ' | |
110 | 'The default is to use 0, which will autoselect an open port.', |
|
133 | 'The default is to use 0, which will autoselect an open port.', | |
111 | metavar='FCClientServiceFactory.port') |
|
134 | metavar='FCClientServiceFactory.port') | |
112 | ), |
|
135 | paa('--client-location',), dict( | |
113 | (('--client-location',), dict( |
|
|||
114 | type=str, dest='FCClientServiceFactory.location', |
|
136 | type=str, dest='FCClientServiceFactory.location', | |
115 | help='The hostname or IP that clients should connect to. This does ' |
|
137 | help='The hostname or IP that clients should connect to. This does ' | |
116 | 'not control which interface the controller listens on. Instead, this ' |
|
138 | 'not control which interface the controller listens on. Instead, this ' | |
@@ -118,21 +140,18 b' cl_args = (' | |||||
118 | 'clients know where to connect. Useful if the controller is listening ' |
|
140 | 'clients know where to connect. Useful if the controller is listening ' | |
119 | 'on multiple interfaces.', |
|
141 | 'on multiple interfaces.', | |
120 | metavar='FCClientServiceFactory.location') |
|
142 | metavar='FCClientServiceFactory.location') | |
121 | ), |
|
|||
122 | # Engine config |
|
143 | # Engine config | |
123 |
|
|
144 | paa('--engine-ip', | |
124 | type=str, dest='FCEngineServiceFactory.ip', |
|
145 | type=str, dest='FCEngineServiceFactory.ip', | |
125 | help='The IP address or hostname the controller will listen on for ' |
|
146 | help='The IP address or hostname the controller will listen on for ' | |
126 | 'engine connections.', |
|
147 | 'engine connections.', | |
127 | metavar='FCEngineServiceFactory.ip') |
|
148 | metavar='FCEngineServiceFactory.ip') | |
128 | ), |
|
149 | paa('--engine-port', | |
129 | (('--engine-port',), dict( |
|
|||
130 | type=int, dest='FCEngineServiceFactory.port', |
|
150 | type=int, dest='FCEngineServiceFactory.port', | |
131 | help='The port the controller will listen on for engine connections. ' |
|
151 | help='The port the controller will listen on for engine connections. ' | |
132 | 'The default is to use 0, which will autoselect an open port.', |
|
152 | 'The default is to use 0, which will autoselect an open port.', | |
133 | metavar='FCEngineServiceFactory.port') |
|
153 | metavar='FCEngineServiceFactory.port') | |
134 | ), |
|
154 | paa('--engine-location', | |
135 | (('--engine-location',), dict( |
|
|||
136 | type=str, dest='FCEngineServiceFactory.location', |
|
155 | type=str, dest='FCEngineServiceFactory.location', | |
137 | help='The hostname or IP that engines should connect to. This does ' |
|
156 | help='The hostname or IP that engines should connect to. This does ' | |
138 | 'not control which interface the controller listens on. Instead, this ' |
|
157 | 'not control which interface the controller listens on. Instead, this ' | |
@@ -140,60 +159,49 b' cl_args = (' | |||||
140 | 'engines know where to connect. Useful if the controller is listening ' |
|
159 | 'engines know where to connect. Useful if the controller is listening ' | |
141 | 'on multiple interfaces.', |
|
160 | 'on multiple interfaces.', | |
142 | metavar='FCEngineServiceFactory.location') |
|
161 | metavar='FCEngineServiceFactory.location') | |
143 | ), |
|
|||
144 | # Global config |
|
162 | # Global config | |
145 |
|
|
163 | paa('--log-to-file', | |
146 | action='store_true', dest='Global.log_to_file', |
|
164 | action='store_true', dest='Global.log_to_file', | |
147 | help='Log to a file in the log directory (default is stdout)') |
|
165 | help='Log to a file in the log directory (default is stdout)') | |
148 | ), |
|
166 | paa('-r','--reuse-furls', | |
149 | (('-r','--reuse-furls'), dict( |
|
|||
150 | action='store_true', dest='Global.reuse_furls', |
|
167 | action='store_true', dest='Global.reuse_furls', | |
151 | help='Try to reuse all FURL files. If this is not set all FURL files ' |
|
168 | help='Try to reuse all FURL files. If this is not set all FURL files ' | |
152 | 'are deleted before the controller starts. This must be set if ' |
|
169 | 'are deleted before the controller starts. This must be set if ' | |
153 | 'specific ports are specified by --engine-port or --client-port.') |
|
170 | 'specific ports are specified by --engine-port or --client-port.') | |
154 | ), |
|
171 | paa('--no-secure', | |
155 | (('--no-secure',), dict( |
|
|||
156 | action='store_false', dest='Global.secure', |
|
172 | action='store_false', dest='Global.secure', | |
157 | help='Turn off SSL encryption for all connections.') |
|
173 | help='Turn off SSL encryption for all connections.') | |
158 | ), |
|
174 | paa('--secure', | |
159 | (('--secure',), dict( |
|
|||
160 | action='store_true', dest='Global.secure', |
|
175 | action='store_true', dest='Global.secure', | |
161 | help='Turn off SSL encryption for all connections.') |
|
176 | help='Turn off SSL encryption for all connections.') | |
162 | ) |
|
|||
163 | ) |
|
|||
164 |
|
177 | |||
165 |
|
178 | |||
166 | _description = """Start the IPython controller for parallel computing. |
|
179 | #----------------------------------------------------------------------------- | |
167 |
|
180 | # The main application | ||
168 | The IPython controller provides a gateway between the IPython engines and |
|
181 | #----------------------------------------------------------------------------- | |
169 | clients. The controller needs to be started before the engines and can be |
|
|||
170 | configured using command line options or using a cluster directory. Cluster |
|
|||
171 | directories contain config, log and security files and are usually located in |
|
|||
172 | your .ipython directory and named as "cluster_<profile>". See the --profile |
|
|||
173 | and --cluster-dir options for details. |
|
|||
174 | """ |
|
|||
175 |
|
||||
176 | default_config_file_name = u'ipcontroller_config.py' |
|
|||
177 |
|
182 | |||
178 |
|
183 | |||
179 | class IPControllerApp(ApplicationWithClusterDir): |
|
184 | class IPControllerApp(ApplicationWithClusterDir): | |
180 |
|
185 | |||
181 | name = u'ipcontroller' |
|
186 | name = u'ipcontroller' | |
182 | description = _description |
|
187 | description = _description | |
183 | config_file_name = default_config_file_name |
|
188 | command_line_loader = IPControllerAppConfigLoader | |
|
189 | default_config_file_name = default_config_file_name | |||
184 | auto_create_cluster_dir = True |
|
190 | auto_create_cluster_dir = True | |
185 | cl_arguments = Application.cl_arguments + cl_args |
|
|||
186 |
|
191 | |||
187 | def create_default_config(self): |
|
192 | def create_default_config(self): | |
188 | super(IPControllerApp, self).create_default_config() |
|
193 | super(IPControllerApp, self).create_default_config() | |
189 |
|
|
194 | # Don't set defaults for Global.secure or Global.reuse_furls | |
190 | self.default_config.Global.secure = True |
|
195 | # as those are set in a component. | |
191 | self.default_config.Global.import_statements = [] |
|
196 | self.default_config.Global.import_statements = [] | |
192 | self.default_config.Global.clean_logs = True |
|
197 | self.default_config.Global.clean_logs = True | |
193 |
|
198 | |||
194 | def post_load_command_line_config(self): |
|
199 | def pre_construct(self): | |
195 | # Now setup reuse_furls |
|
200 | super(IPControllerApp, self).pre_construct() | |
196 |
c = self. |
|
201 | c = self.master_config | |
|
202 | # The defaults for these are set in FCClientServiceFactory and | |||
|
203 | # FCEngineServiceFactory, so we only set them here if the global | |||
|
204 | # options have be set to override the class level defaults. | |||
197 | if hasattr(c.Global, 'reuse_furls'): |
|
205 | if hasattr(c.Global, 'reuse_furls'): | |
198 | c.FCClientServiceFactory.reuse_furls = c.Global.reuse_furls |
|
206 | c.FCClientServiceFactory.reuse_furls = c.Global.reuse_furls | |
199 | c.FCEngineServiceFactory.reuse_furls = c.Global.reuse_furls |
|
207 | c.FCEngineServiceFactory.reuse_furls = c.Global.reuse_furls | |
@@ -216,11 +224,19 b' class IPControllerApp(ApplicationWithClusterDir):' | |||||
216 | controller_service = controllerservice.ControllerService() |
|
224 | controller_service = controllerservice.ControllerService() | |
217 | controller_service.setServiceParent(self.main_service) |
|
225 | controller_service.setServiceParent(self.main_service) | |
218 | # The client tub and all its refereceables |
|
226 | # The client tub and all its refereceables | |
|
227 | try: | |||
219 | csfactory = FCClientServiceFactory(self.master_config, controller_service) |
|
228 | csfactory = FCClientServiceFactory(self.master_config, controller_service) | |
|
229 | except FURLError, e: | |||
|
230 | log.err(e) | |||
|
231 | self.exit(0) | |||
220 | client_service = csfactory.create() |
|
232 | client_service = csfactory.create() | |
221 | client_service.setServiceParent(self.main_service) |
|
233 | client_service.setServiceParent(self.main_service) | |
222 | # The engine tub |
|
234 | # The engine tub | |
|
235 | try: | |||
223 | esfactory = FCEngineServiceFactory(self.master_config, controller_service) |
|
236 | esfactory = FCEngineServiceFactory(self.master_config, controller_service) | |
|
237 | except FURLError, e: | |||
|
238 | log.err(e) | |||
|
239 | self.exit(0) | |||
224 | engine_service = esfactory.create() |
|
240 | engine_service = esfactory.create() | |
225 | engine_service.setServiceParent(self.main_service) |
|
241 | engine_service.setServiceParent(self.main_service) | |
226 |
|
242 |
@@ -22,39 +22,21 b' from twisted.application import service' | |||||
22 | from twisted.internet import reactor |
|
22 | from twisted.internet import reactor | |
23 | from twisted.python import log |
|
23 | from twisted.python import log | |
24 |
|
24 | |||
25 | from IPython.core.application import Application |
|
25 | from IPython.kernel.clusterdir import ( | |
26 |
|
|
26 | ApplicationWithClusterDir, | |
|
27 | ClusterDirConfigLoader | |||
|
28 | ) | |||
27 | from IPython.kernel.engineconnector import EngineConnector |
|
29 | from IPython.kernel.engineconnector import EngineConnector | |
28 | from IPython.kernel.engineservice import EngineService |
|
30 | from IPython.kernel.engineservice import EngineService | |
29 | from IPython.kernel.fcutil import Tub |
|
31 | from IPython.kernel.fcutil import Tub | |
30 | from IPython.utils.importstring import import_item |
|
32 | from IPython.utils.importstring import import_item | |
31 |
|
33 | |||
32 | #----------------------------------------------------------------------------- |
|
34 | #----------------------------------------------------------------------------- | |
33 | # The main application |
|
35 | # Module level variables | |
34 | #----------------------------------------------------------------------------- |
|
36 | #----------------------------------------------------------------------------- | |
35 |
|
37 | |||
36 | cl_args = ( |
|
38 | #: The default config file name for this application | |
37 | # Controller config |
|
39 | default_config_file_name = u'ipengine_config.py' | |
38 | (('--furl-file',), dict( |
|
|||
39 | type=unicode, dest='Global.furl_file', |
|
|||
40 | help='The full location of the file containing the FURL of the ' |
|
|||
41 | 'controller. If this is not given, the FURL file must be in the ' |
|
|||
42 | 'security directory of the cluster directory. This location is ' |
|
|||
43 | 'resolved using the --profile and --app-dir options.', |
|
|||
44 | metavar='Global.furl_file') |
|
|||
45 | ), |
|
|||
46 | # MPI |
|
|||
47 | (('--mpi',), dict( |
|
|||
48 | type=str, dest='MPI.use', |
|
|||
49 | help='How to enable MPI (mpi4py, pytrilinos, or empty string to disable).', |
|
|||
50 | metavar='MPI.use') |
|
|||
51 | ), |
|
|||
52 | # Global config |
|
|||
53 | (('--log-to-file',), dict( |
|
|||
54 | action='store_true', dest='Global.log_to_file', |
|
|||
55 | help='Log to a file in the log directory (default is stdout)') |
|
|||
56 | ) |
|
|||
57 | ) |
|
|||
58 |
|
40 | |||
59 |
|
41 | |||
60 | mpi4py_init = """from mpi4py import MPI as mpi |
|
42 | mpi4py_init = """from mpi4py import MPI as mpi | |
@@ -62,6 +44,7 b' mpi.size = mpi.COMM_WORLD.Get_size()' | |||||
62 | mpi.rank = mpi.COMM_WORLD.Get_rank() |
|
44 | mpi.rank = mpi.COMM_WORLD.Get_rank() | |
63 | """ |
|
45 | """ | |
64 |
|
46 | |||
|
47 | ||||
65 | pytrilinos_init = """from PyTrilinos import Epetra |
|
48 | pytrilinos_init = """from PyTrilinos import Epetra | |
66 | class SimpleStruct: |
|
49 | class SimpleStruct: | |
67 | pass |
|
50 | pass | |
@@ -71,9 +54,6 b' mpi.size = 0' | |||||
71 | """ |
|
54 | """ | |
72 |
|
55 | |||
73 |
|
56 | |||
74 | default_config_file_name = u'ipengine_config.py' |
|
|||
75 |
|
||||
76 |
|
||||
77 | _description = """Start an IPython engine for parallel computing.\n\n |
|
57 | _description = """Start an IPython engine for parallel computing.\n\n | |
78 |
|
58 | |||
79 | IPython engines run in parallel and perform computations on behalf of a client |
|
59 | IPython engines run in parallel and perform computations on behalf of a client | |
@@ -84,14 +64,47 b' usually located in your .ipython directory and named as "cluster_<profile>".' | |||||
84 | See the --profile and --cluster-dir options for details. |
|
64 | See the --profile and --cluster-dir options for details. | |
85 | """ |
|
65 | """ | |
86 |
|
66 | |||
|
67 | #----------------------------------------------------------------------------- | |||
|
68 | # Command line options | |||
|
69 | #----------------------------------------------------------------------------- | |||
|
70 | ||||
|
71 | ||||
|
72 | class IPEngineAppConfigLoader(ClusterDirConfigLoader): | |||
|
73 | ||||
|
74 | def _add_arguments(self): | |||
|
75 | super(IPEngineAppConfigLoader, self)._add_arguments() | |||
|
76 | paa = self.parser.add_argument | |||
|
77 | # Controller config | |||
|
78 | paa('--furl-file', | |||
|
79 | type=unicode, dest='Global.furl_file', | |||
|
80 | help='The full location of the file containing the FURL of the ' | |||
|
81 | 'controller. If this is not given, the FURL file must be in the ' | |||
|
82 | 'security directory of the cluster directory. This location is ' | |||
|
83 | 'resolved using the --profile and --app-dir options.', | |||
|
84 | metavar='Global.furl_file') | |||
|
85 | # MPI | |||
|
86 | paa('--mpi', | |||
|
87 | type=str, dest='MPI.use', | |||
|
88 | help='How to enable MPI (mpi4py, pytrilinos, or empty string to disable).', | |||
|
89 | metavar='MPI.use') | |||
|
90 | # Global config | |||
|
91 | paa('--log-to-file', | |||
|
92 | action='store_true', dest='Global.log_to_file', | |||
|
93 | help='Log to a file in the log directory (default is stdout)') | |||
|
94 | ||||
|
95 | ||||
|
96 | #----------------------------------------------------------------------------- | |||
|
97 | # Main application | |||
|
98 | #----------------------------------------------------------------------------- | |||
|
99 | ||||
87 |
|
100 | |||
88 | class IPEngineApp(ApplicationWithClusterDir): |
|
101 | class IPEngineApp(ApplicationWithClusterDir): | |
89 |
|
102 | |||
90 | name = u'ipengine' |
|
103 | name = u'ipengine' | |
91 | description = _description |
|
104 | description = _description | |
92 | config_file_name = default_config_file_name |
|
105 | command_line_loader = IPEngineAppConfigLoader | |
|
106 | default_config_file_name = default_config_file_name | |||
93 | auto_create_cluster_dir = True |
|
107 | auto_create_cluster_dir = True | |
94 | cl_arguments = Application.cl_arguments + cl_args |
|
|||
95 |
|
108 | |||
96 | def create_default_config(self): |
|
109 | def create_default_config(self): | |
97 | super(IPEngineApp, self).create_default_config() |
|
110 | super(IPEngineApp, self).create_default_config() |
@@ -21,11 +21,15 b' import sys' | |||||
21 |
|
21 | |||
22 | from IPython.core.component import Component |
|
22 | from IPython.core.component import Component | |
23 | from IPython.external import Itpl |
|
23 | from IPython.external import Itpl | |
24 |
from IPython.utils.traitlets import Str, Int, List, Unicode |
|
24 | from IPython.utils.traitlets import Str, Int, List, Unicode | |
25 |
from IPython.utils.p |
|
25 | from IPython.utils.path import get_ipython_module_path | |
26 | from IPython.kernel.twistedutil import gatherBoth, make_deferred, sleep_deferred |
|
26 | from IPython.utils.process import find_cmd, pycmd2argv, FindCmdError | |
|
27 | from IPython.kernel.twistedutil import ( | |||
|
28 | gatherBoth, | |||
|
29 | make_deferred, | |||
|
30 | sleep_deferred | |||
|
31 | ) | |||
27 | from IPython.kernel.winhpcjob import ( |
|
32 | from IPython.kernel.winhpcjob import ( | |
28 | WinHPCJob, WinHPCTask, |
|
|||
29 | IPControllerTask, IPEngineTask, |
|
33 | IPControllerTask, IPEngineTask, | |
30 | IPControllerJob, IPEngineSetJob |
|
34 | IPControllerJob, IPEngineSetJob | |
31 | ) |
|
35 | ) | |
@@ -38,46 +42,23 b' from twisted.internet.error import ProcessDone, ProcessTerminated' | |||||
38 | from twisted.python import log |
|
42 | from twisted.python import log | |
39 | from twisted.python.failure import Failure |
|
43 | from twisted.python.failure import Failure | |
40 |
|
44 | |||
|
45 | ||||
41 | #----------------------------------------------------------------------------- |
|
46 | #----------------------------------------------------------------------------- | |
42 | # Utilities |
|
47 | # Paths to the kernel apps | |
43 | #----------------------------------------------------------------------------- |
|
48 | #----------------------------------------------------------------------------- | |
44 |
|
49 | |||
45 |
|
50 | |||
46 | def find_controller_cmd(): |
|
51 | ipcluster_cmd_argv = pycmd2argv(get_ipython_module_path( | |
47 | """Find the command line ipcontroller program in a cross platform way.""" |
|
52 | 'IPython.kernel.ipclusterapp' | |
48 | if sys.platform == 'win32': |
|
53 | )) | |
49 | # This logic is needed because the ipcontroller script doesn't |
|
54 | ||
50 | # always get installed in the same way or in the same location. |
|
55 | ipengine_cmd_argv = pycmd2argv(get_ipython_module_path( | |
51 |
|
|
56 | 'IPython.kernel.ipengineapp' | |
52 | script_location = ipcontrollerapp.__file__.replace('.pyc', '.py') |
|
57 | )) | |
53 | # The -u option here turns on unbuffered output, which is required |
|
|||
54 | # on Win32 to prevent wierd conflict and problems with Twisted. |
|
|||
55 | # Also, use sys.executable to make sure we are picking up the |
|
|||
56 | # right python exe. |
|
|||
57 | cmd = [sys.executable, '-u', script_location] |
|
|||
58 | else: |
|
|||
59 | # ipcontroller has to be on the PATH in this case. |
|
|||
60 | cmd = ['ipcontroller'] |
|
|||
61 | return cmd |
|
|||
62 |
|
||||
63 |
|
||||
64 | def find_engine_cmd(): |
|
|||
65 | """Find the command line ipengine program in a cross platform way.""" |
|
|||
66 | if sys.platform == 'win32': |
|
|||
67 | # This logic is needed because the ipengine script doesn't |
|
|||
68 | # always get installed in the same way or in the same location. |
|
|||
69 | from IPython.kernel import ipengineapp |
|
|||
70 | script_location = ipengineapp.__file__.replace('.pyc', '.py') |
|
|||
71 | # The -u option here turns on unbuffered output, which is required |
|
|||
72 | # on Win32 to prevent wierd conflict and problems with Twisted. |
|
|||
73 | # Also, use sys.executable to make sure we are picking up the |
|
|||
74 | # right python exe. |
|
|||
75 | cmd = [sys.executable, '-u', script_location] |
|
|||
76 | else: |
|
|||
77 | # ipcontroller has to be on the PATH in this case. |
|
|||
78 | cmd = ['ipengine'] |
|
|||
79 | return cmd |
|
|||
80 |
|
58 | |||
|
59 | ipcontroller_cmd_argv = pycmd2argv(get_ipython_module_path( | |||
|
60 | 'IPython.kernel.ipcontrollerapp' | |||
|
61 | )) | |||
81 |
|
62 | |||
82 | #----------------------------------------------------------------------------- |
|
63 | #----------------------------------------------------------------------------- | |
83 | # Base launchers and errors |
|
64 | # Base launchers and errors | |
@@ -333,7 +314,7 b' class LocalProcessLauncher(BaseLauncher):' | |||||
333 | class LocalControllerLauncher(LocalProcessLauncher): |
|
314 | class LocalControllerLauncher(LocalProcessLauncher): | |
334 | """Launch a controller as a regular external process.""" |
|
315 | """Launch a controller as a regular external process.""" | |
335 |
|
316 | |||
336 |
controller_cmd = List( |
|
317 | controller_cmd = List(ipcontroller_cmd_argv, config=True) | |
337 | # Command line arguments to ipcontroller. |
|
318 | # Command line arguments to ipcontroller. | |
338 | controller_args = List(['--log-to-file','--log-level', '40'], config=True) |
|
319 | controller_args = List(['--log-to-file','--log-level', '40'], config=True) | |
339 |
|
320 | |||
@@ -351,7 +332,7 b' class LocalControllerLauncher(LocalProcessLauncher):' | |||||
351 | class LocalEngineLauncher(LocalProcessLauncher): |
|
332 | class LocalEngineLauncher(LocalProcessLauncher): | |
352 | """Launch a single engine as a regular externall process.""" |
|
333 | """Launch a single engine as a regular externall process.""" | |
353 |
|
334 | |||
354 |
engine_cmd = List( |
|
335 | engine_cmd = List(ipengine_cmd_argv, config=True) | |
355 | # Command line arguments for ipengine. |
|
336 | # Command line arguments for ipengine. | |
356 | engine_args = List( |
|
337 | engine_args = List( | |
357 | ['--log-to-file','--log-level', '40'], config=True |
|
338 | ['--log-to-file','--log-level', '40'], config=True | |
@@ -462,7 +443,7 b' class MPIExecLauncher(LocalProcessLauncher):' | |||||
462 | class MPIExecControllerLauncher(MPIExecLauncher): |
|
443 | class MPIExecControllerLauncher(MPIExecLauncher): | |
463 | """Launch a controller using mpiexec.""" |
|
444 | """Launch a controller using mpiexec.""" | |
464 |
|
445 | |||
465 |
controller_cmd = List( |
|
446 | controller_cmd = List(ipcontroller_cmd_argv, config=True) | |
466 | # Command line arguments to ipcontroller. |
|
447 | # Command line arguments to ipcontroller. | |
467 | controller_args = List(['--log-to-file','--log-level', '40'], config=True) |
|
448 | controller_args = List(['--log-to-file','--log-level', '40'], config=True) | |
468 | n = Int(1, config=False) |
|
449 | n = Int(1, config=False) | |
@@ -481,7 +462,7 b' class MPIExecControllerLauncher(MPIExecLauncher):' | |||||
481 |
|
462 | |||
482 | class MPIExecEngineSetLauncher(MPIExecLauncher): |
|
463 | class MPIExecEngineSetLauncher(MPIExecLauncher): | |
483 |
|
464 | |||
484 |
engine_cmd = List( |
|
465 | engine_cmd = List(ipengine_cmd_argv, config=True) | |
485 | # Command line arguments for ipengine. |
|
466 | # Command line arguments for ipengine. | |
486 | engine_args = List( |
|
467 | engine_args = List( | |
487 | ['--log-to-file','--log-level', '40'], config=True |
|
468 | ['--log-to-file','--log-level', '40'], config=True | |
@@ -557,7 +538,10 b' class SSHEngineSetLauncher(BaseLauncher):' | |||||
557 | # This is only used on Windows. |
|
538 | # This is only used on Windows. | |
558 | def find_job_cmd(): |
|
539 | def find_job_cmd(): | |
559 | if os.name=='nt': |
|
540 | if os.name=='nt': | |
|
541 | try: | |||
560 | return find_cmd('job') |
|
542 | return find_cmd('job') | |
|
543 | except FindCmdError: | |||
|
544 | return 'job' | |||
561 | else: |
|
545 | else: | |
562 | return 'job' |
|
546 | return 'job' | |
563 |
|
547 | |||
@@ -831,28 +815,10 b' class PBSEngineSetLauncher(PBSLauncher):' | |||||
831 | #----------------------------------------------------------------------------- |
|
815 | #----------------------------------------------------------------------------- | |
832 |
|
816 | |||
833 |
|
817 | |||
834 | def find_ipcluster_cmd(): |
|
|||
835 | """Find the command line ipcluster program in a cross platform way.""" |
|
|||
836 | if sys.platform == 'win32': |
|
|||
837 | # This logic is needed because the ipcluster script doesn't |
|
|||
838 | # always get installed in the same way or in the same location. |
|
|||
839 | from IPython.kernel import ipclusterapp |
|
|||
840 | script_location = ipclusterapp.__file__.replace('.pyc', '.py') |
|
|||
841 | # The -u option here turns on unbuffered output, which is required |
|
|||
842 | # on Win32 to prevent wierd conflict and problems with Twisted. |
|
|||
843 | # Also, use sys.executable to make sure we are picking up the |
|
|||
844 | # right python exe. |
|
|||
845 | cmd = [sys.executable, '-u', script_location] |
|
|||
846 | else: |
|
|||
847 | # ipcontroller has to be on the PATH in this case. |
|
|||
848 | cmd = ['ipcluster'] |
|
|||
849 | return cmd |
|
|||
850 |
|
||||
851 |
|
||||
852 | class IPClusterLauncher(LocalProcessLauncher): |
|
818 | class IPClusterLauncher(LocalProcessLauncher): | |
853 | """Launch the ipcluster program in an external process.""" |
|
819 | """Launch the ipcluster program in an external process.""" | |
854 |
|
820 | |||
855 |
ipcluster_cmd = List( |
|
821 | ipcluster_cmd = List(ipcluster_cmd_argv, config=True) | |
856 | # Command line arguments to pass to ipcluster. |
|
822 | # Command line arguments to pass to ipcluster. | |
857 | ipcluster_args = List( |
|
823 | ipcluster_args = List( | |
858 | ['--clean-logs', '--log-to-file', '--log-level', '40'], config=True) |
|
824 | ['--clean-logs', '--log-to-file', '--log-level', '40'], config=True) |
@@ -21,7 +21,7 b' __docformat__ = "restructuredtext en"' | |||||
21 |
|
21 | |||
22 | import types |
|
22 | import types | |
23 |
|
23 | |||
24 |
from IPython.utils. |
|
24 | from IPython.utils.data import flatten as utils_flatten | |
25 |
|
25 | |||
26 | #------------------------------------------------------------------------------- |
|
26 | #------------------------------------------------------------------------------- | |
27 | # Figure out which array packages are present and their array types |
|
27 | # Figure out which array packages are present and their array types | |
@@ -87,7 +87,7 b' class Map:' | |||||
87 | return m['module'].concatenate(listOfPartitions) |
|
87 | return m['module'].concatenate(listOfPartitions) | |
88 | # Next try for Python sequence types |
|
88 | # Next try for Python sequence types | |
89 | if isinstance(testObject, (types.ListType, types.TupleType)): |
|
89 | if isinstance(testObject, (types.ListType, types.TupleType)): | |
90 |
return |
|
90 | return utils_flatten(listOfPartitions) | |
91 | # If we have scalars, just return listOfPartitions |
|
91 | # If we have scalars, just return listOfPartitions | |
92 | return listOfPartitions |
|
92 | return listOfPartitions | |
93 |
|
93 |
@@ -18,8 +18,7 b' __docformat__ = "restructuredtext en"' | |||||
18 | from types import FunctionType |
|
18 | from types import FunctionType | |
19 | from zope.interface import Interface, implements |
|
19 | from zope.interface import Interface, implements | |
20 | from IPython.kernel.task import MapTask |
|
20 | from IPython.kernel.task import MapTask | |
21 |
from IPython.kernel.twistedutil import |
|
21 | from IPython.kernel.twistedutil import gatherBoth | |
22 | from IPython.kernel.util import printer |
|
|||
23 | from IPython.kernel.error import collect_exceptions |
|
22 | from IPython.kernel.error import collect_exceptions | |
24 |
|
23 | |||
25 | #---------------------------------------------------------------------------- |
|
24 | #---------------------------------------------------------------------------- |
@@ -27,24 +27,17 b' __docformat__ = "restructuredtext en"' | |||||
27 | # Imports |
|
27 | # Imports | |
28 | #------------------------------------------------------------------------------- |
|
28 | #------------------------------------------------------------------------------- | |
29 |
|
29 | |||
30 | from new import instancemethod |
|
|||
31 | from types import FunctionType |
|
|||
32 |
|
||||
33 | from twisted.application import service |
|
|||
34 | from twisted.internet import defer, reactor |
|
30 | from twisted.internet import defer, reactor | |
35 | from twisted.python import log, components, failure |
|
31 | from twisted.python import log, components, failure | |
36 |
from zope.interface import Interface, implements |
|
32 | from zope.interface import Interface, implements | |
37 |
|
33 | |||
38 | from IPython.utils import growl |
|
|||
39 | from IPython.kernel.util import printer |
|
|||
40 | from IPython.kernel.twistedutil import gatherBoth |
|
34 | from IPython.kernel.twistedutil import gatherBoth | |
41 | from IPython.kernel import map as Map |
|
|||
42 | from IPython.kernel import error |
|
35 | from IPython.kernel import error | |
43 | from IPython.kernel.pendingdeferred import PendingDeferredManager, two_phase |
|
36 | from IPython.kernel.pendingdeferred import PendingDeferredManager, two_phase | |
44 |
from IPython.kernel.controllerservice import |
|
37 | from IPython.kernel.controllerservice import ( | |
45 |
ControllerAdapterBase, |
|
38 | ControllerAdapterBase, | |
46 | ControllerService, \ |
|
|||
47 | IControllerBase |
|
39 | IControllerBase | |
|
40 | ) | |||
48 |
|
41 | |||
49 |
|
42 | |||
50 | #------------------------------------------------------------------------------- |
|
43 | #------------------------------------------------------------------------------- |
@@ -17,13 +17,17 b' __docformat__ = "restructuredtext en"' | |||||
17 | #------------------------------------------------------------------------------- |
|
17 | #------------------------------------------------------------------------------- | |
18 |
|
18 | |||
19 | import sys |
|
19 | import sys | |
20 | import linecache |
|
|||
21 | import warnings |
|
20 | import warnings | |
22 |
|
21 | |||
23 | from twisted.python import components |
|
22 | from twisted.python import components | |
24 | from twisted.python.failure import Failure |
|
23 | from twisted.python.failure import Failure | |
25 | from zope.interface import Interface, implements, Attribute |
|
24 | from zope.interface import Interface, implements, Attribute | |
26 |
|
25 | |||
|
26 | try: | |||
|
27 | from foolscap.api import DeadReferenceError | |||
|
28 | except ImportError: | |||
|
29 | from foolscap import DeadReferenceError | |||
|
30 | ||||
27 | from IPython.utils.coloransi import TermColors |
|
31 | from IPython.utils.coloransi import TermColors | |
28 |
|
32 | |||
29 | from IPython.kernel.twistedutil import blockingCallFromThread |
|
33 | from IPython.kernel.twistedutil import blockingCallFromThread | |
@@ -307,85 +311,6 b' class InteractiveMultiEngineClient(object):' | |||||
307 | """Return the number of available engines.""" |
|
311 | """Return the number of available engines.""" | |
308 | return len(self.get_ids()) |
|
312 | return len(self.get_ids()) | |
309 |
|
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 | # The top-level MultiEngine client adaptor |
|
316 | # The top-level MultiEngine client adaptor | |
@@ -445,17 +370,30 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):' | |||||
445 | def _findTargetsAndBlock(self, targets=None, block=None): |
|
370 | def _findTargetsAndBlock(self, targets=None, block=None): | |
446 | return self._findTargets(targets), self._findBlock(block) |
|
371 | return self._findTargets(targets), self._findBlock(block) | |
447 |
|
372 | |||
|
373 | def _bcft(self, *args, **kwargs): | |||
|
374 | try: | |||
|
375 | result = blockingCallFromThread(*args, **kwargs) | |||
|
376 | except DeadReferenceError: | |||
|
377 | raise error.ConnectionError( | |||
|
378 | """A connection error has occurred in trying to connect to the | |||
|
379 | controller. This is usually caused by the controller dying or | |||
|
380 | being restarted. To resolve this issue try recreating the | |||
|
381 | multiengine client.""" | |||
|
382 | ) | |||
|
383 | else: | |||
|
384 | return result | |||
|
385 | ||||
448 | def _blockFromThread(self, function, *args, **kwargs): |
|
386 | def _blockFromThread(self, function, *args, **kwargs): | |
449 | block = kwargs.get('block', None) |
|
387 | block = kwargs.get('block', None) | |
450 | if block is None: |
|
388 | if block is None: | |
451 | raise error.MissingBlockArgument("'block' keyword argument is missing") |
|
389 | raise error.MissingBlockArgument("'block' keyword argument is missing") | |
452 |
result = |
|
390 | result = self._bcft(function, *args, **kwargs) | |
453 | if not block: |
|
391 | if not block: | |
454 | result = PendingResult(self, result) |
|
392 | result = PendingResult(self, result) | |
455 | return result |
|
393 | return result | |
456 |
|
394 | |||
457 | def get_pending_deferred(self, deferredID, block): |
|
395 | def get_pending_deferred(self, deferredID, block): | |
458 |
return |
|
396 | return self._bcft(self.smultiengine.get_pending_deferred, deferredID, block) | |
459 |
|
397 | |||
460 | def barrier(self, pendingResults): |
|
398 | def barrier(self, pendingResults): | |
461 | """Synchronize a set of `PendingResults`. |
|
399 | """Synchronize a set of `PendingResults`. | |
@@ -505,7 +443,7 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):' | |||||
505 | controller. This method allows the user to clear out all un-retrieved |
|
443 | controller. This method allows the user to clear out all un-retrieved | |
506 | results on the controller. |
|
444 | results on the controller. | |
507 | """ |
|
445 | """ | |
508 |
r = |
|
446 | r = self._bcft(self.smultiengine.clear_pending_deferreds) | |
509 | return r |
|
447 | return r | |
510 |
|
448 | |||
511 | clear_pending_results = flush |
|
449 | clear_pending_results = flush | |
@@ -529,7 +467,7 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):' | |||||
529 | at a later time. |
|
467 | at a later time. | |
530 | """ |
|
468 | """ | |
531 | targets, block = self._findTargetsAndBlock(targets, block) |
|
469 | targets, block = self._findTargetsAndBlock(targets, block) | |
532 |
result = |
|
470 | result = self._bcft(self.smultiengine.execute, lines, | |
533 | targets=targets, block=block) |
|
471 | targets=targets, block=block) | |
534 | if block: |
|
472 | if block: | |
535 | result = ResultList(result) |
|
473 | result = ResultList(result) | |
@@ -647,7 +585,7 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):' | |||||
647 | at a later time. |
|
585 | at a later time. | |
648 | """ |
|
586 | """ | |
649 | targets, block = self._findTargetsAndBlock(targets, block) |
|
587 | targets, block = self._findTargetsAndBlock(targets, block) | |
650 |
result = |
|
588 | result = self._bcft(self.smultiengine.get_result, i, targets=targets, block=block) | |
651 | if block: |
|
589 | if block: | |
652 | result = ResultList(result) |
|
590 | result = ResultList(result) | |
653 | else: |
|
591 | else: | |
@@ -773,7 +711,7 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):' | |||||
773 | """ |
|
711 | """ | |
774 | Returns the ids of currently registered engines. |
|
712 | Returns the ids of currently registered engines. | |
775 | """ |
|
713 | """ | |
776 |
result = |
|
714 | result = self._bcft(self.smultiengine.get_ids) | |
777 | return result |
|
715 | return result | |
778 |
|
716 | |||
779 | #--------------------------------------------------------------------------- |
|
717 | #--------------------------------------------------------------------------- |
@@ -22,12 +22,14 b' from types import FunctionType' | |||||
22 |
|
22 | |||
23 | from zope.interface import Interface, implements |
|
23 | from zope.interface import Interface, implements | |
24 | from twisted.internet import defer |
|
24 | from twisted.internet import defer | |
25 |
from twisted.python import components, failure |
|
25 | from twisted.python import components, failure | |
26 |
|
26 | |||
|
27 | try: | |||
|
28 | from foolscap.api import Referenceable | |||
|
29 | except ImportError: | |||
27 | from foolscap import Referenceable |
|
30 | from foolscap import Referenceable | |
28 |
|
31 | |||
29 | from IPython.kernel import error |
|
32 | from IPython.kernel import error | |
30 | from IPython.kernel.util import printer |
|
|||
31 | from IPython.kernel import map as Map |
|
33 | from IPython.kernel import map as Map | |
32 | from IPython.kernel.parallelfunction import ParallelFunction |
|
34 | from IPython.kernel.parallelfunction import ParallelFunction | |
33 | from IPython.kernel.mapper import ( |
|
35 | from IPython.kernel.mapper import ( | |
@@ -36,14 +38,15 b' from IPython.kernel.mapper import (' | |||||
36 | IMapper |
|
38 | IMapper | |
37 | ) |
|
39 | ) | |
38 | from IPython.kernel.twistedutil import gatherBoth |
|
40 | from IPython.kernel.twistedutil import gatherBoth | |
39 |
from IPython.kernel.multiengine import ( |
|
41 | from IPython.kernel.multiengine import ( | |
40 | IMultiEngine, |
|
42 | IMultiEngine, | |
41 | IFullSynchronousMultiEngine, |
|
43 | IFullSynchronousMultiEngine, | |
42 | ISynchronousMultiEngine) |
|
44 | ISynchronousMultiEngine) | |
43 | from IPython.kernel.multiengineclient import wrapResultList |
|
|||
44 | from IPython.kernel.pendingdeferred import PendingDeferredManager |
|
45 | from IPython.kernel.pendingdeferred import PendingDeferredManager | |
45 |
from IPython.kernel.pickleutil import ( |
|
46 | from IPython.kernel.pickleutil import ( | |
46 | canSequence, uncan, uncanDict, uncanSequence) |
|
47 | canDict, | |
|
48 | canSequence, uncanDict, uncanSequence | |||
|
49 | ) | |||
47 |
|
50 | |||
48 | from IPython.kernel.clientinterfaces import ( |
|
51 | from IPython.kernel.clientinterfaces import ( | |
49 | IFCClientInterfaceProvider, |
|
52 | IFCClientInterfaceProvider, |
@@ -19,7 +19,6 b' import cPickle as pickle' | |||||
19 |
|
19 | |||
20 | from twisted.python.failure import Failure |
|
20 | from twisted.python.failure import Failure | |
21 | from twisted.python import failure |
|
21 | from twisted.python import failure | |
22 | import threading, sys |
|
|||
23 |
|
22 | |||
24 | from IPython.kernel import pbconfig |
|
23 | from IPython.kernel import pbconfig | |
25 | from IPython.kernel.error import PBMessageSizeError, UnpickleableException |
|
24 | from IPython.kernel.error import PBMessageSizeError, UnpickleableException | |
@@ -58,7 +57,7 b' def unpackageFailure(r):' | |||||
58 | result = pickle.loads(r[8:]) |
|
57 | result = pickle.loads(r[8:]) | |
59 | except pickle.PickleError: |
|
58 | except pickle.PickleError: | |
60 | return failure.Failure( \ |
|
59 | return failure.Failure( \ | |
61 |
|
|
60 | UnpickleableException("Could not unpickle failure.")) | |
62 | else: |
|
61 | else: | |
63 | return result |
|
62 | return result | |
64 | return r |
|
63 | return r |
@@ -22,15 +22,11 b' __docformat__ = "restructuredtext en"' | |||||
22 | # Imports |
|
22 | # Imports | |
23 | #------------------------------------------------------------------------------- |
|
23 | #------------------------------------------------------------------------------- | |
24 |
|
24 | |||
25 |
from twisted. |
|
25 | from twisted.internet import defer | |
26 |
from twisted. |
|
26 | from twisted.python import failure | |
27 | from twisted.python import log, components, failure |
|
|||
28 | from zope.interface import Interface, implements, Attribute |
|
|||
29 |
|
27 | |||
30 | from IPython.kernel.twistedutil import gatherBoth |
|
|||
31 | from IPython.kernel import error |
|
28 | from IPython.kernel import error | |
32 | from IPython.external import guid |
|
29 | from IPython.external import guid | |
33 | from IPython.utils import growl |
|
|||
34 |
|
30 | |||
35 | class PendingDeferredManager(object): |
|
31 | class PendingDeferredManager(object): | |
36 | """A class to track pending deferreds. |
|
32 | """A class to track pending deferreds. |
@@ -16,7 +16,6 b' __docformat__ = "restructuredtext en"' | |||||
16 | #------------------------------------------------------------------------------- |
|
16 | #------------------------------------------------------------------------------- | |
17 |
|
17 | |||
18 | from types import FunctionType |
|
18 | from types import FunctionType | |
19 | from twisted.python import log |
|
|||
20 |
|
19 | |||
21 | class CannedObject(object): |
|
20 | class CannedObject(object): | |
22 | pass |
|
21 | pass |
@@ -19,19 +19,18 b' __docformat__ = "restructuredtext en"' | |||||
19 | # Tell nose to skip the testing of this module |
|
19 | # Tell nose to skip the testing of this module | |
20 | __test__ = {} |
|
20 | __test__ = {} | |
21 |
|
21 | |||
22 |
import |
|
22 | import time | |
23 | from types import FunctionType |
|
23 | from types import FunctionType | |
24 |
|
24 | |||
25 |
import zope.interface as zi |
|
25 | import zope.interface as zi | |
26 | from twisted.internet import defer, reactor |
|
26 | from twisted.internet import defer, reactor | |
27 | from twisted.python import components, log, failure |
|
27 | from twisted.python import components, log, failure | |
28 |
|
28 | |||
29 | from IPython.kernel.util import printer |
|
|||
30 | from IPython.kernel import engineservice as es, error |
|
29 | from IPython.kernel import engineservice as es, error | |
31 | from IPython.kernel import controllerservice as cs |
|
30 | from IPython.kernel import controllerservice as cs | |
32 |
from IPython.kernel.twistedutil import |
|
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 | # Definition of the Task objects |
|
36 | # Definition of the Task objects |
@@ -19,7 +19,12 b' __docformat__ = "restructuredtext en"' | |||||
19 | #------------------------------------------------------------------------------- |
|
19 | #------------------------------------------------------------------------------- | |
20 |
|
20 | |||
21 | from zope.interface import Interface, implements |
|
21 | from zope.interface import Interface, implements | |
22 |
from twisted.python import components |
|
22 | from twisted.python import components | |
|
23 | ||||
|
24 | try: | |||
|
25 | from foolscap.api import DeadReferenceError | |||
|
26 | except ImportError: | |||
|
27 | from foolscap import DeadReferenceError | |||
23 |
|
28 | |||
24 | from IPython.kernel.twistedutil import blockingCallFromThread |
|
29 | from IPython.kernel.twistedutil import blockingCallFromThread | |
25 | from IPython.kernel import task, error |
|
30 | from IPython.kernel import task, error | |
@@ -59,6 +64,19 b' class BlockingTaskClient(object):' | |||||
59 | self.task_controller = task_controller |
|
64 | self.task_controller = task_controller | |
60 | self.block = True |
|
65 | self.block = True | |
61 |
|
66 | |||
|
67 | def _bcft(self, *args, **kwargs): | |||
|
68 | try: | |||
|
69 | result = blockingCallFromThread(*args, **kwargs) | |||
|
70 | except DeadReferenceError: | |||
|
71 | raise error.ConnectionError( | |||
|
72 | """A connection error has occurred in trying to connect to the | |||
|
73 | controller. This is usually caused by the controller dying or | |||
|
74 | being restarted. To resolve this issue try recreating the | |||
|
75 | task client.""" | |||
|
76 | ) | |||
|
77 | else: | |||
|
78 | return result | |||
|
79 | ||||
62 | def run(self, task, block=False): |
|
80 | def run(self, task, block=False): | |
63 | """Run a task on the `TaskController`. |
|
81 | """Run a task on the `TaskController`. | |
64 |
|
82 | |||
@@ -71,7 +89,7 b' class BlockingTaskClient(object):' | |||||
71 | :Returns: The int taskid of the submitted task. Pass this to |
|
89 | :Returns: The int taskid of the submitted task. Pass this to | |
72 | `get_task_result` to get the `TaskResult` object. |
|
90 | `get_task_result` to get the `TaskResult` object. | |
73 | """ |
|
91 | """ | |
74 |
tid = |
|
92 | tid = self._bcft(self.task_controller.run, task) | |
75 | if block: |
|
93 | if block: | |
76 | return self.get_task_result(tid, block=True) |
|
94 | return self.get_task_result(tid, block=True) | |
77 | else: |
|
95 | else: | |
@@ -89,7 +107,7 b' class BlockingTaskClient(object):' | |||||
89 |
|
107 | |||
90 | :Returns: A `TaskResult` object that encapsulates the task result. |
|
108 | :Returns: A `TaskResult` object that encapsulates the task result. | |
91 | """ |
|
109 | """ | |
92 |
return |
|
110 | return self._bcft(self.task_controller.get_task_result, | |
93 | taskid, block) |
|
111 | taskid, block) | |
94 |
|
112 | |||
95 | def abort(self, taskid): |
|
113 | def abort(self, taskid): | |
@@ -100,7 +118,7 b' class BlockingTaskClient(object):' | |||||
100 | taskid : int |
|
118 | taskid : int | |
101 | The taskid of the task to be aborted. |
|
119 | The taskid of the task to be aborted. | |
102 | """ |
|
120 | """ | |
103 |
return |
|
121 | return self._bcft(self.task_controller.abort, taskid) | |
104 |
|
122 | |||
105 | def barrier(self, taskids): |
|
123 | def barrier(self, taskids): | |
106 | """Block until a set of tasks are completed. |
|
124 | """Block until a set of tasks are completed. | |
@@ -109,7 +127,7 b' class BlockingTaskClient(object):' | |||||
109 | taskids : list, tuple |
|
127 | taskids : list, tuple | |
110 | A sequence of taskids to block on. |
|
128 | A sequence of taskids to block on. | |
111 | """ |
|
129 | """ | |
112 |
return |
|
130 | return self._bcft(self.task_controller.barrier, taskids) | |
113 |
|
131 | |||
114 | def spin(self): |
|
132 | def spin(self): | |
115 | """ |
|
133 | """ | |
@@ -118,7 +136,7 b' class BlockingTaskClient(object):' | |||||
118 | This method only needs to be called in unusual situations where the |
|
136 | This method only needs to be called in unusual situations where the | |
119 | scheduler is idle for some reason. |
|
137 | scheduler is idle for some reason. | |
120 | """ |
|
138 | """ | |
121 |
return |
|
139 | return self._bcft(self.task_controller.spin) | |
122 |
|
140 | |||
123 | def queue_status(self, verbose=False): |
|
141 | def queue_status(self, verbose=False): | |
124 | """ |
|
142 | """ | |
@@ -132,7 +150,7 b' class BlockingTaskClient(object):' | |||||
132 | :Returns: |
|
150 | :Returns: | |
133 | A dict with the queue status. |
|
151 | A dict with the queue status. | |
134 | """ |
|
152 | """ | |
135 |
return |
|
153 | return self._bcft(self.task_controller.queue_status, verbose) | |
136 |
|
154 | |||
137 | def clear(self): |
|
155 | def clear(self): | |
138 | """ |
|
156 | """ | |
@@ -143,7 +161,7 b' class BlockingTaskClient(object):' | |||||
143 | tasks. Users should call this periodically to clean out these |
|
161 | tasks. Users should call this periodically to clean out these | |
144 | cached task results. |
|
162 | cached task results. | |
145 | """ |
|
163 | """ | |
146 |
return |
|
164 | return self._bcft(self.task_controller.clear) | |
147 |
|
165 | |||
148 | def map(self, func, *sequences): |
|
166 | def map(self, func, *sequences): | |
149 | """ |
|
167 | """ |
@@ -19,17 +19,17 b' __docformat__ = "restructuredtext en"' | |||||
19 | #------------------------------------------------------------------------------- |
|
19 | #------------------------------------------------------------------------------- | |
20 |
|
20 | |||
21 | import cPickle as pickle |
|
21 | import cPickle as pickle | |
22 | import xmlrpclib, copy |
|
|||
23 |
|
22 | |||
24 | from zope.interface import Interface, implements |
|
23 | from zope.interface import Interface, implements | |
25 | from twisted.internet import defer |
|
24 | from twisted.internet import defer | |
26 |
from twisted.python import components |
|
25 | from twisted.python import components | |
27 |
|
26 | |||
|
27 | try: | |||
|
28 | from foolscap.api import Referenceable | |||
|
29 | except ImportError: | |||
28 | from foolscap import Referenceable |
|
30 | from foolscap import Referenceable | |
29 |
|
31 | |||
30 | from IPython.kernel.twistedutil import blockingCallFromThread |
|
32 | from IPython.kernel import task as taskmodule | |
31 | from IPython.kernel import error, task as taskmodule, taskclient |
|
|||
32 | from IPython.kernel.pickleutil import can, uncan |
|
|||
33 | from IPython.kernel.clientinterfaces import ( |
|
33 | from IPython.kernel.clientinterfaces import ( | |
34 | IFCClientInterfaceProvider, |
|
34 | IFCClientInterfaceProvider, | |
35 | IBlockingClientAdaptor |
|
35 | IBlockingClientAdaptor |
@@ -53,8 +53,8 b' class EngineFCTest(DeferredTestCase,' | |||||
53 | # Start a server and append to self.servers |
|
53 | # Start a server and append to self.servers | |
54 | self.controller_reference = FCRemoteEngineRefFromService(self) |
|
54 | self.controller_reference = FCRemoteEngineRefFromService(self) | |
55 | self.controller_tub = Tub() |
|
55 | self.controller_tub = Tub() | |
56 |
self.controller_tub.listenOn('tcp:101 |
|
56 | self.controller_tub.listenOn('tcp:10111:interface=127.0.0.1') | |
57 |
self.controller_tub.setLocation('127.0.0.1:101 |
|
57 | self.controller_tub.setLocation('127.0.0.1:10111') | |
58 |
|
58 | |||
59 | furl = self.controller_tub.registerReference(self.controller_reference) |
|
59 | furl = self.controller_tub.registerReference(self.controller_reference) | |
60 | self.controller_tub.startService() |
|
60 | self.controller_tub.startService() |
@@ -27,7 +27,7 b' from IPython.kernel.multiengine import IMultiEngine' | |||||
27 | from IPython.kernel.tests.multienginetest import IFullSynchronousMultiEngineTestCase |
|
27 | from IPython.kernel.tests.multienginetest import IFullSynchronousMultiEngineTestCase | |
28 | from IPython.kernel.multienginefc import IFCSynchronousMultiEngine |
|
28 | from IPython.kernel.multienginefc import IFCSynchronousMultiEngine | |
29 | from IPython.kernel import multiengine as me |
|
29 | from IPython.kernel import multiengine as me | |
30 | from IPython.kernel.clientconnector import ClientConnector |
|
30 | from IPython.kernel.clientconnector import AsyncClientConnector | |
31 | from IPython.kernel.parallelfunction import ParallelFunction |
|
31 | from IPython.kernel.parallelfunction import ParallelFunction | |
32 | from IPython.kernel.error import CompositeError |
|
32 | from IPython.kernel.error import CompositeError | |
33 | from IPython.kernel.util import printer |
|
33 | from IPython.kernel.util import printer | |
@@ -40,15 +40,8 b' def _raise_it(f):' | |||||
40 | e.raise_exception() |
|
40 | e.raise_exception() | |
41 |
|
41 | |||
42 |
|
42 | |||
43 | class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMultiEngineTestCase): |
|
43 | class FullSynchronousMultiEngineTestCase( | |
44 |
|
44 | DeferredTestCase, IFullSynchronousMultiEngineTestCase): | ||
45 | # XXX (fperez) this is awful: I'm fully disabling this entire test class. |
|
|||
46 | # Right now it's blocking the tests from running at all, and I don't know |
|
|||
47 | # how to fix it. I hope Brian can have a stab at it, but at least by doing |
|
|||
48 | # this we can run the entire suite to completion. |
|
|||
49 | # Once the problem is cleared, remove this skip method. |
|
|||
50 | skip = True |
|
|||
51 | # END XXX |
|
|||
52 |
|
45 | |||
53 | def setUp(self): |
|
46 | def setUp(self): | |
54 |
|
47 | |||
@@ -60,14 +53,14 b' class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMulti' | |||||
60 | self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine) |
|
53 | self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine) | |
61 |
|
54 | |||
62 | self.controller_tub = Tub() |
|
55 | self.controller_tub = Tub() | |
63 |
self.controller_tub.listenOn('tcp:101 |
|
56 | self.controller_tub.listenOn('tcp:10111:interface=127.0.0.1') | |
64 |
self.controller_tub.setLocation('127.0.0.1:101 |
|
57 | self.controller_tub.setLocation('127.0.0.1:10111') | |
65 |
|
58 | |||
66 | furl = self.controller_tub.registerReference(self.mec_referenceable) |
|
59 | furl = self.controller_tub.registerReference(self.mec_referenceable) | |
67 | self.controller_tub.startService() |
|
60 | self.controller_tub.startService() | |
68 |
|
61 | |||
69 | self.client_tub = ClientConnector() |
|
62 | self.client_tub = AsyncClientConnector() | |
70 | d = self.client_tub.get_multiengine_client(furl) |
|
63 | d = self.client_tub.get_multiengine_client(furl_or_file=furl) | |
71 | d.addCallback(self.handle_got_client) |
|
64 | d.addCallback(self.handle_got_client) | |
72 | return d |
|
65 | return d | |
73 |
|
66 | |||
@@ -150,3 +143,4 b' class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMulti' | |||||
150 | d = f(range(10)) |
|
143 | d = f(range(10)) | |
151 | d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f)) |
|
144 | d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f)) | |
152 | return d |
|
145 | return d | |
|
146 |
@@ -31,7 +31,7 b' from IPython.kernel.multienginefc import IFCSynchronousMultiEngine' | |||||
31 | from IPython.kernel.taskfc import IFCTaskController |
|
31 | from IPython.kernel.taskfc import IFCTaskController | |
32 | from IPython.kernel.util import printer |
|
32 | from IPython.kernel.util import printer | |
33 | from IPython.kernel.tests.tasktest import ITaskControllerTestCase |
|
33 | from IPython.kernel.tests.tasktest import ITaskControllerTestCase | |
34 | from IPython.kernel.clientconnector import ClientConnector |
|
34 | from IPython.kernel.clientconnector import AsyncClientConnector | |
35 | from IPython.kernel.error import CompositeError |
|
35 | from IPython.kernel.error import CompositeError | |
36 | from IPython.kernel.parallelfunction import ParallelFunction |
|
36 | from IPython.kernel.parallelfunction import ParallelFunction | |
37 |
|
37 | |||
@@ -48,14 +48,6 b' def _raise_it(f):' | |||||
48 |
|
48 | |||
49 | class TaskTest(DeferredTestCase, ITaskControllerTestCase): |
|
49 | class TaskTest(DeferredTestCase, ITaskControllerTestCase): | |
50 |
|
50 | |||
51 | # XXX (fperez) this is awful: I'm fully disabling this entire test class. |
|
|||
52 | # Right now it's blocking the tests from running at all, and I don't know |
|
|||
53 | # how to fix it. I hope Brian can have a stab at it, but at least by doing |
|
|||
54 | # this we can run the entire suite to completion. |
|
|||
55 | # Once the problem is cleared, remove this skip method. |
|
|||
56 | skip = True |
|
|||
57 | # END XXX |
|
|||
58 |
|
||||
59 | def setUp(self): |
|
51 | def setUp(self): | |
60 |
|
52 | |||
61 | self.engines = [] |
|
53 | self.engines = [] | |
@@ -70,17 +62,17 b' class TaskTest(DeferredTestCase, ITaskControllerTestCase):' | |||||
70 | self.tc_referenceable = IFCTaskController(self.itc) |
|
62 | self.tc_referenceable = IFCTaskController(self.itc) | |
71 |
|
63 | |||
72 | self.controller_tub = Tub() |
|
64 | self.controller_tub = Tub() | |
73 |
self.controller_tub.listenOn('tcp:101 |
|
65 | self.controller_tub.listenOn('tcp:10111:interface=127.0.0.1') | |
74 |
self.controller_tub.setLocation('127.0.0.1:101 |
|
66 | self.controller_tub.setLocation('127.0.0.1:10111') | |
75 |
|
67 | |||
76 | mec_furl = self.controller_tub.registerReference(self.mec_referenceable) |
|
68 | mec_furl = self.controller_tub.registerReference(self.mec_referenceable) | |
77 | tc_furl = self.controller_tub.registerReference(self.tc_referenceable) |
|
69 | tc_furl = self.controller_tub.registerReference(self.tc_referenceable) | |
78 | self.controller_tub.startService() |
|
70 | self.controller_tub.startService() | |
79 |
|
71 | |||
80 | self.client_tub = ClientConnector() |
|
72 | self.client_tub = AsyncClientConnector() | |
81 | d = self.client_tub.get_multiengine_client(mec_furl) |
|
73 | d = self.client_tub.get_multiengine_client(furl_or_file=mec_furl) | |
82 | d.addCallback(self.handle_mec_client) |
|
74 | d.addCallback(self.handle_mec_client) | |
83 | d.addCallback(lambda _: self.client_tub.get_task_client(tc_furl)) |
|
75 | d.addCallback(lambda _: self.client_tub.get_task_client(furl_or_file=tc_furl)) | |
84 | d.addCallback(self.handle_tc_client) |
|
76 | d.addCallback(self.handle_tc_client) | |
85 | return d |
|
77 | return d | |
86 |
|
78 | |||
@@ -167,3 +159,4 b' class TaskTest(DeferredTestCase, ITaskControllerTestCase):' | |||||
167 | d = f(range(10)) |
|
159 | d = f(range(10)) | |
168 | d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f)) |
|
160 | d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f)) | |
169 | return d |
|
161 | return d | |
|
162 |
@@ -15,7 +15,7 b'' | |||||
15 | #----------------------------------------------------------------------------- |
|
15 | #----------------------------------------------------------------------------- | |
16 |
|
16 | |||
17 | import os, sys |
|
17 | import os, sys | |
18 |
import threading, Queue |
|
18 | import threading, Queue | |
19 |
|
19 | |||
20 | import twisted |
|
20 | import twisted | |
21 | from twisted.internet import defer, reactor |
|
21 | from twisted.internet import defer, reactor |
@@ -23,12 +23,10 b' import re' | |||||
23 | import uuid |
|
23 | import uuid | |
24 |
|
24 | |||
25 | from xml.etree import ElementTree as ET |
|
25 | from xml.etree import ElementTree as ET | |
26 | from xml.dom import minidom |
|
|||
27 |
|
26 | |||
28 | from IPython.core.component import Component |
|
27 | from IPython.core.component import Component | |
29 | from IPython.external import Itpl |
|
|||
30 | from IPython.utils.traitlets import ( |
|
28 | from IPython.utils.traitlets import ( | |
31 |
Str, Int, List, |
|
29 | Str, Int, List, Instance, | |
32 | Enum, Bool, CStr |
|
30 | Enum, Bool, CStr | |
33 | ) |
|
31 | ) | |
34 |
|
32 |
@@ -31,7 +31,7 b' import sys' | |||||
31 | import threading |
|
31 | import threading | |
32 |
|
32 | |||
33 | from IPython.core.ultratb import AutoFormattedTB |
|
33 | from IPython.core.ultratb import AutoFormattedTB | |
34 |
from IPython.utils. |
|
34 | from IPython.utils.warn import warn, error | |
35 |
|
35 | |||
36 | class BackgroundJobManager: |
|
36 | class BackgroundJobManager: | |
37 | """Class to manage a pool of backgrounded threaded jobs. |
|
37 | """Class to manage a pool of backgrounded threaded jobs. |
@@ -1,15 +1,18 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | A module to change reload() so that it acts recursively. |
|
3 | A module to change reload() so that it acts recursively. | |
4 | To enable it type: |
|
4 | To enable it type:: | |
5 | >>> import __builtin__, deepreload |
|
|||
6 | >>> __builtin__.reload = deepreload.reload |
|
|||
7 |
|
5 | |||
8 | You can then disable it with: |
|
6 | import __builtin__, deepreload | |
9 |
|
|
7 | __builtin__.reload = deepreload.reload | |
10 |
|
8 | |||
11 | Alternatively, you can add a dreload builtin alongside normal reload with: |
|
9 | You can then disable it with:: | |
12 | >>> __builtin__.dreload = deepreload.reload |
|
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 | This code is almost entirely based on knee.py from the standard library. |
|
17 | This code is almost entirely based on knee.py from the standard library. | |
15 | """ |
|
18 | """ |
@@ -176,7 +176,8 b' import shlex' | |||||
176 | import sys |
|
176 | import sys | |
177 |
|
177 | |||
178 | from IPython.utils.PyColorize import Parser |
|
178 | from IPython.utils.PyColorize import Parser | |
179 |
from IPython.utils. |
|
179 | from IPython.utils.io import file_read, file_readlines, Term | |
|
180 | from IPython.utils.text import marquee | |||
180 |
|
181 | |||
181 | __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError'] |
|
182 | __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError'] | |
182 |
|
183 | |||
@@ -543,7 +544,7 b' class ClearMixin(object):' | |||||
543 | """Method called before executing each block. |
|
544 | """Method called before executing each block. | |
544 |
|
545 | |||
545 | This one simply clears the screen.""" |
|
546 | This one simply clears the screen.""" | |
546 |
from IPython.utils. |
|
547 | from IPython.utils.terminal import term_clear | |
547 | term_clear() |
|
548 | term_clear() | |
548 |
|
549 | |||
549 | class ClearDemo(ClearMixin,Demo): |
|
550 | class ClearDemo(ClearMixin,Demo): |
@@ -12,10 +12,12 b' Fernando Perez.' | |||||
12 | # Distributed under the terms of the BSD License. The full license is in |
|
12 | # Distributed under the terms of the BSD License. The full license is in | |
13 | # the file COPYING, distributed as part of this software. |
|
13 | # the file COPYING, distributed as part of this software. | |
14 | #----------------------------------------------------------------------------- |
|
14 | #----------------------------------------------------------------------------- | |
|
15 | ||||
15 | #----------------------------------------------------------------------------- |
|
16 | #----------------------------------------------------------------------------- | |
16 | # Imports |
|
17 | # Imports | |
17 | #----------------------------------------------------------------------------- |
|
18 | #----------------------------------------------------------------------------- | |
18 | from IPython.utils.genutils import flag_calls |
|
19 | ||
|
20 | from IPython.utils.decorators import flag_calls | |||
19 |
|
21 | |||
20 | #----------------------------------------------------------------------------- |
|
22 | #----------------------------------------------------------------------------- | |
21 | # Main classes and functions |
|
23 | # Main classes and functions |
@@ -50,7 +50,7 b' del InteractiveShell,prefilter_shell' | |||||
50 |
|
50 | |||
51 | # Provide pysh and further shell-oriented services |
|
51 | # Provide pysh and further shell-oriented services | |
52 | import os,sys,shutil |
|
52 | import os,sys,shutil | |
53 |
from IPython.utils. |
|
53 | from IPython.utils.process import system,shell,getoutput,getoutputerror | |
54 |
|
54 | |||
55 | # Short aliases for getting shell output as a string and a list |
|
55 | # Short aliases for getting shell output as a string and a list | |
56 | sout = getoutput |
|
56 | sout = getoutput |
@@ -10,6 +10,7 b' var = !ls' | |||||
10 |
|
10 | |||
11 | from IPython.core import ipapi |
|
11 | from IPython.core import ipapi | |
12 | from IPython.core.error import TryNext |
|
12 | from IPython.core.error import TryNext | |
|
13 | from IPython.utils.text import make_quoted_expr | |||
13 | from IPython.utils.genutils import * |
|
14 | from IPython.utils.genutils import * | |
14 |
|
15 | |||
15 | ip = ipapi.get() |
|
16 | ip = ipapi.get() |
@@ -12,7 +12,7 b' do the same in default completer.' | |||||
12 | from IPython.core import ipapi |
|
12 | from IPython.core import ipapi | |
13 | from IPython.core.error import TryNext |
|
13 | from IPython.core.error import TryNext | |
14 | from IPython.utils import generics |
|
14 | from IPython.utils import generics | |
15 |
from IPython.utils. |
|
15 | from IPython.utils.dir2 import dir2 | |
16 |
|
16 | |||
17 | def attr_matches(self, text): |
|
17 | def attr_matches(self, text): | |
18 | """Compute matches when text contains a dot. |
|
18 | """Compute matches when text contains a dot. |
@@ -16,6 +16,7 b' import pickleshare' | |||||
16 | import inspect,pickle,os,sys,textwrap |
|
16 | import inspect,pickle,os,sys,textwrap | |
17 | from IPython.core.fakemodule import FakeModule |
|
17 | from IPython.core.fakemodule import FakeModule | |
18 | from IPython.utils.ipstruct import Struct |
|
18 | from IPython.utils.ipstruct import Struct | |
|
19 | from IPython.utils.warn import error | |||
19 |
|
20 | |||
20 |
|
21 | |||
21 | def refresh_variables(ip, key=None): |
|
22 | def refresh_variables(ip, key=None): |
@@ -1,6 +1,6 b'' | |||||
1 | import inspect |
|
1 | import inspect | |
2 | from IPython.core import ipapi |
|
2 | from IPython.core import ipapi | |
3 |
from IPython.utils. |
|
3 | from IPython.utils.process import arg_split | |
4 | ip = ipapi.get() |
|
4 | ip = ipapi.get() | |
5 |
|
5 | |||
6 | from IPython.core import debugger |
|
6 | from IPython.core import debugger |
@@ -45,10 +45,9 b' from subprocess import *' | |||||
45 | import os,shlex,sys,time |
|
45 | import os,shlex,sys,time | |
46 | import threading,Queue |
|
46 | import threading,Queue | |
47 |
|
47 | |||
48 | from IPython.utils import genutils |
|
|||
49 |
|
||||
50 | from IPython.core import ipapi |
|
48 | from IPython.core import ipapi | |
51 | from IPython.core.error import TryNext |
|
49 | from IPython.core.error import TryNext | |
|
50 | from IPython.utils.text import make_quoted_expr | |||
52 |
|
51 | |||
53 | if os.name == 'nt': |
|
52 | if os.name == 'nt': | |
54 | def kill_process(pid): |
|
53 | def kill_process(pid): | |
@@ -126,8 +125,8 b' def jobctrl_prefilter_f(self,line):' | |||||
126 |
|
125 | |||
127 | line = ip.expand_aliases(fn,rest) |
|
126 | line = ip.expand_aliases(fn,rest) | |
128 | if not _jobq: |
|
127 | if not _jobq: | |
129 |
return 'get_ipython().startjob(%s)' % |
|
128 | return 'get_ipython().startjob(%s)' % make_quoted_expr(line) | |
130 |
return 'get_ipython().jobq(%s)' % |
|
129 | return 'get_ipython().jobq(%s)' % make_quoted_expr(line) | |
131 |
|
130 | |||
132 | raise TryNext |
|
131 | raise TryNext | |
133 |
|
132 |
@@ -1,6 +1,17 b'' | |||||
1 | """Testing support (tools to test IPython itself). |
|
1 | """Testing support (tools to test IPython itself). | |
2 | """ |
|
2 | """ | |
3 |
|
3 | |||
|
4 | #----------------------------------------------------------------------------- | |||
|
5 | # Copyright (C) 2009 The IPython Development Team | |||
|
6 | # | |||
|
7 | # Distributed under the terms of the BSD License. The full license is in | |||
|
8 | # the file COPYING, distributed as part of this software. | |||
|
9 | #----------------------------------------------------------------------------- | |||
|
10 | ||||
|
11 | #----------------------------------------------------------------------------- | |||
|
12 | # Functions | |||
|
13 | #----------------------------------------------------------------------------- | |||
|
14 | ||||
4 | # User-level entry point for testing |
|
15 | # User-level entry point for testing | |
5 | def test(): |
|
16 | def test(): | |
6 | """Run the entire IPython test suite. |
|
17 | """Run the entire IPython test suite. |
@@ -4,6 +4,17 b' This is just so we can use 2.6 features when running in 2.5, the code below is' | |||||
4 | copied verbatim from the stdlib's collections and doctest modules. |
|
4 | copied verbatim from the stdlib's collections and doctest modules. | |
5 | """ |
|
5 | """ | |
6 |
|
6 | |||
|
7 | #----------------------------------------------------------------------------- | |||
|
8 | # Copyright (C) 2009 The IPython Development Team | |||
|
9 | # | |||
|
10 | # Distributed under the terms of the BSD License. The full license is in | |||
|
11 | # the file COPYING, distributed as part of this software. | |||
|
12 | #----------------------------------------------------------------------------- | |||
|
13 | ||||
|
14 | #----------------------------------------------------------------------------- | |||
|
15 | # Imports | |||
|
16 | #----------------------------------------------------------------------------- | |||
|
17 | ||||
7 | from keyword import iskeyword as _iskeyword |
|
18 | from keyword import iskeyword as _iskeyword | |
8 | from operator import itemgetter as _itemgetter |
|
19 | from operator import itemgetter as _itemgetter | |
9 | import sys as _sys |
|
20 | import sys as _sys |
@@ -1,10 +1,17 b'' | |||||
1 | """Implementation of the parametric test support for Python 2.x |
|
1 | """Implementation of the parametric test support for Python 2.x | |
2 | """ |
|
2 | """ | |
|
3 | ||||
|
4 | #----------------------------------------------------------------------------- | |||
|
5 | # Copyright (C) 2009 The IPython Development Team | |||
|
6 | # | |||
|
7 | # Distributed under the terms of the BSD License. The full license is in | |||
|
8 | # the file COPYING, distributed as part of this software. | |||
|
9 | #----------------------------------------------------------------------------- | |||
|
10 | ||||
3 | #----------------------------------------------------------------------------- |
|
11 | #----------------------------------------------------------------------------- | |
4 | # Imports |
|
12 | # Imports | |
5 | #----------------------------------------------------------------------------- |
|
13 | #----------------------------------------------------------------------------- | |
6 |
|
14 | |||
7 | # Stdlib |
|
|||
8 | import unittest |
|
15 | import unittest | |
9 | from compiler.consts import CO_GENERATOR |
|
16 | from compiler.consts import CO_GENERATOR | |
10 |
|
17 |
@@ -3,11 +3,18 b'' | |||||
3 | Thanks for the py3 version to Robert Collins, from the Testing in Python |
|
3 | Thanks for the py3 version to Robert Collins, from the Testing in Python | |
4 | mailing list. |
|
4 | mailing list. | |
5 | """ |
|
5 | """ | |
|
6 | ||||
|
7 | #----------------------------------------------------------------------------- | |||
|
8 | # Copyright (C) 2009 The IPython Development Team | |||
|
9 | # | |||
|
10 | # Distributed under the terms of the BSD License. The full license is in | |||
|
11 | # the file COPYING, distributed as part of this software. | |||
|
12 | #----------------------------------------------------------------------------- | |||
|
13 | ||||
6 | #----------------------------------------------------------------------------- |
|
14 | #----------------------------------------------------------------------------- | |
7 | # Imports |
|
15 | # Imports | |
8 | #----------------------------------------------------------------------------- |
|
16 | #----------------------------------------------------------------------------- | |
9 |
|
17 | |||
10 | # Stdlib |
|
|||
11 | import unittest |
|
18 | import unittest | |
12 | from unittest import TestSuite |
|
19 | from unittest import TestSuite | |
13 |
|
20 |
@@ -9,18 +9,22 b' done.' | |||||
9 | from __future__ import absolute_import |
|
9 | from __future__ import absolute_import | |
10 |
|
10 | |||
11 | #----------------------------------------------------------------------------- |
|
11 | #----------------------------------------------------------------------------- | |
12 | # Module imports |
|
12 | # Copyright (C) 2009 The IPython Development Team | |
|
13 | # | |||
|
14 | # Distributed under the terms of the BSD License. The full license is in | |||
|
15 | # the file COPYING, distributed as part of this software. | |||
|
16 | #----------------------------------------------------------------------------- | |||
|
17 | ||||
|
18 | #----------------------------------------------------------------------------- | |||
|
19 | # Imports | |||
13 | #----------------------------------------------------------------------------- |
|
20 | #----------------------------------------------------------------------------- | |
14 |
|
21 | |||
15 | # From the standard library |
|
|||
16 | import __builtin__ |
|
22 | import __builtin__ | |
17 | import commands |
|
23 | import commands | |
18 | import new |
|
|||
19 | import os |
|
24 | import os | |
20 | import sys |
|
25 | import sys | |
21 |
|
26 | |||
22 | from . import tools |
|
27 | from . import tools | |
23 | from IPython.utils.genutils import Term |
|
|||
24 |
|
28 | |||
25 | #----------------------------------------------------------------------------- |
|
29 | #----------------------------------------------------------------------------- | |
26 | # Functions |
|
30 | # Functions | |
@@ -35,7 +39,7 b' class py_file_finder(object):' | |||||
35 | self.test_filename = test_filename |
|
39 | self.test_filename = test_filename | |
36 |
|
40 | |||
37 | def __call__(self,name): |
|
41 | def __call__(self,name): | |
38 |
from IPython.utils. |
|
42 | from IPython.utils.path import get_py_filename | |
39 | try: |
|
43 | try: | |
40 | return get_py_filename(name) |
|
44 | return get_py_filename(name) | |
41 | except IOError: |
|
45 | except IOError: | |
@@ -99,6 +103,7 b' def get_ipython():' | |||||
99 | # This will get replaced by the real thing once we start IPython below |
|
103 | # This will get replaced by the real thing once we start IPython below | |
100 | return start_ipython() |
|
104 | return start_ipython() | |
101 |
|
105 | |||
|
106 | ||||
102 | def start_ipython(): |
|
107 | def start_ipython(): | |
103 | """Start a global IPython shell, which we need for IPython-specific syntax. |
|
108 | """Start a global IPython shell, which we need for IPython-specific syntax. | |
104 | """ |
|
109 | """ | |
@@ -110,7 +115,7 b' def start_ipython():' | |||||
110 | start_ipython.already_called = True |
|
115 | start_ipython.already_called = True | |
111 |
|
116 | |||
112 | # Ok, first time we're called, go ahead |
|
117 | # Ok, first time we're called, go ahead | |
113 |
from IPython.core import |
|
118 | from IPython.core import iplib | |
114 |
|
119 | |||
115 | def xsys(cmd): |
|
120 | def xsys(cmd): | |
116 | """Execute a command and print its output. |
|
121 | """Execute a command and print its output. | |
@@ -128,26 +133,27 b' def start_ipython():' | |||||
128 | _main = sys.modules.get('__main__') |
|
133 | _main = sys.modules.get('__main__') | |
129 |
|
134 | |||
130 | # Create custom argv and namespaces for our IPython to be test-friendly |
|
135 | # Create custom argv and namespaces for our IPython to be test-friendly | |
131 |
|
|
136 | config = tools.default_config() | |
132 | user_ns, global_ns = iplib.make_user_namespaces(ipnsdict(), {}) |
|
|||
133 |
|
137 | |||
134 | # Create and initialize our test-friendly IPython instance. |
|
138 | # Create and initialize our test-friendly IPython instance. | |
135 | ip = ipapp.IPythonApp(argv, user_ns=user_ns, user_global_ns=global_ns) |
|
139 | shell = iplib.InteractiveShell( | |
136 | ip.initialize() |
|
140 | parent=None, config=config, | |
|
141 | user_ns=ipnsdict(), user_global_ns={} | |||
|
142 | ) | |||
137 |
|
143 | |||
138 | # A few more tweaks needed for playing nicely with doctests... |
|
144 | # A few more tweaks needed for playing nicely with doctests... | |
139 |
|
145 | |||
140 | # These traps are normally only active for interactive use, set them |
|
146 | # These traps are normally only active for interactive use, set them | |
141 | # permanently since we'll be mocking interactive sessions. |
|
147 | # permanently since we'll be mocking interactive sessions. | |
142 |
|
|
148 | shell.builtin_trap.set() | |
143 |
|
149 | |||
144 | # Set error printing to stdout so nose can doctest exceptions |
|
150 | # Set error printing to stdout so nose can doctest exceptions | |
145 |
|
|
151 | shell.InteractiveTB.out_stream = 'stdout' | |
146 |
|
152 | |||
147 | # Modify the IPython system call with one that uses getoutput, so that we |
|
153 | # Modify the IPython system call with one that uses getoutput, so that we | |
148 | # can capture subcommands and print them to Python's stdout, otherwise the |
|
154 | # can capture subcommands and print them to Python's stdout, otherwise the | |
149 | # doctest machinery would miss them. |
|
155 | # doctest machinery would miss them. | |
150 |
|
|
156 | shell.system = xsys | |
151 |
|
157 | |||
152 | # IPython is ready, now clean up some global state... |
|
158 | # IPython is ready, now clean up some global state... | |
153 |
|
159 | |||
@@ -160,7 +166,7 b' def start_ipython():' | |||||
160 | # So that ipython magics and aliases can be doctested (they work by making |
|
166 | # So that ipython magics and aliases can be doctested (they work by making | |
161 | # a call into a global _ip object). Also make the top-level get_ipython |
|
167 | # a call into a global _ip object). Also make the top-level get_ipython | |
162 | # now return this without recursively calling here again. |
|
168 | # now return this without recursively calling here again. | |
163 |
_ip = |
|
169 | _ip = shell | |
164 | get_ipython = _ip.get_ipython |
|
170 | get_ipython = _ip.get_ipython | |
165 | __builtin__._ip = _ip |
|
171 | __builtin__._ip = _ip | |
166 | __builtin__.get_ipython = get_ipython |
|
172 | __builtin__.get_ipython = get_ipython |
@@ -17,13 +17,19 b' will change in the future.' | |||||
17 | """ |
|
17 | """ | |
18 |
|
18 | |||
19 | #----------------------------------------------------------------------------- |
|
19 | #----------------------------------------------------------------------------- | |
20 | # Module imports |
|
20 | # Copyright (C) 2009 The IPython Development Team | |
|
21 | # | |||
|
22 | # Distributed under the terms of the BSD License. The full license is in | |||
|
23 | # the file COPYING, distributed as part of this software. | |||
|
24 | #----------------------------------------------------------------------------- | |||
|
25 | ||||
|
26 | #----------------------------------------------------------------------------- | |||
|
27 | # Imports | |||
21 | #----------------------------------------------------------------------------- |
|
28 | #----------------------------------------------------------------------------- | |
22 |
|
29 | |||
23 | # Stdlib |
|
30 | # Stdlib | |
24 | import os |
|
31 | import os | |
25 | import os.path as path |
|
32 | import os.path as path | |
26 | import platform |
|
|||
27 | import signal |
|
33 | import signal | |
28 | import sys |
|
34 | import sys | |
29 | import subprocess |
|
35 | import subprocess | |
@@ -31,15 +37,6 b' import tempfile' | |||||
31 | import time |
|
37 | import time | |
32 | import warnings |
|
38 | import warnings | |
33 |
|
39 | |||
34 |
|
||||
35 | # Ugly, but necessary hack to ensure the test suite finds our version of |
|
|||
36 | # IPython and not a possibly different one that may exist system-wide. |
|
|||
37 | # Note that this must be done here, so the imports that come next work |
|
|||
38 | # correctly even if IPython isn't installed yet. |
|
|||
39 | p = os.path |
|
|||
40 | ippath = p.abspath(p.join(p.dirname(__file__),'..','..')) |
|
|||
41 | sys.path.insert(0, ippath) |
|
|||
42 |
|
||||
43 | # Note: monkeypatch! |
|
40 | # Note: monkeypatch! | |
44 | # We need to monkeypatch a small problem in nose itself first, before importing |
|
41 | # We need to monkeypatch a small problem in nose itself first, before importing | |
45 | # it for actual use. This should get into nose upstream, but its release cycle |
|
42 | # it for actual use. This should get into nose upstream, but its release cycle | |
@@ -50,11 +47,11 b' import nose.plugins.builtin' | |||||
50 | from nose.core import TestProgram |
|
47 | from nose.core import TestProgram | |
51 |
|
48 | |||
52 | # Our own imports |
|
49 | # Our own imports | |
53 | from IPython.core import release |
|
50 | from IPython.utils.path import get_ipython_module_path | |
54 |
from IPython.utils import |
|
51 | from IPython.utils.process import find_cmd, pycmd2argv | |
55 |
from IPython.utils. |
|
52 | from IPython.utils.sysinfo import sys_info | |
|
53 | ||||
56 | from IPython.testing import globalipapp |
|
54 | from IPython.testing import globalipapp | |
57 | from IPython.testing import tools |
|
|||
58 | from IPython.testing.plugin.ipdoctest import IPythonDoctest |
|
55 | from IPython.testing.plugin.ipdoctest import IPythonDoctest | |
59 |
|
56 | |||
60 | pjoin = path.join |
|
57 | pjoin = path.join | |
@@ -64,16 +61,11 b' pjoin = path.join' | |||||
64 | # Globals |
|
61 | # Globals | |
65 | #----------------------------------------------------------------------------- |
|
62 | #----------------------------------------------------------------------------- | |
66 |
|
63 | |||
67 | # By default, we assume IPython has been installed. But if the test suite is |
|
|||
68 | # being run from a source tree that has NOT been installed yet, this flag can |
|
|||
69 | # be set to False by the entry point scripts, to let us know that we must call |
|
|||
70 | # the source tree versions of the scripts which manipulate sys.path instead of |
|
|||
71 | # assuming that things exist system-wide. |
|
|||
72 | INSTALLED = True |
|
|||
73 |
|
64 | |||
74 | #----------------------------------------------------------------------------- |
|
65 | #----------------------------------------------------------------------------- | |
75 | # Warnings control |
|
66 | # Warnings control | |
76 | #----------------------------------------------------------------------------- |
|
67 | #----------------------------------------------------------------------------- | |
|
68 | ||||
77 | # Twisted generates annoying warnings with Python 2.6, as will do other code |
|
69 | # Twisted generates annoying warnings with Python 2.6, as will do other code | |
78 | # that imports 'sets' as of today |
|
70 | # that imports 'sets' as of today | |
79 | warnings.filterwarnings('ignore', 'the sets module is deprecated', |
|
71 | warnings.filterwarnings('ignore', 'the sets module is deprecated', | |
@@ -124,9 +116,7 b" have['gobject'] = test_for('gobject')" | |||||
124 | def report(): |
|
116 | def report(): | |
125 | """Return a string with a summary report of test-related variables.""" |
|
117 | """Return a string with a summary report of test-related variables.""" | |
126 |
|
118 | |||
127 |
out = [ |
|
119 | out = [ sys_info() ] | |
128 |
|
||||
129 | out.append('\nRunning from an installed IPython: %s\n' % INSTALLED) |
|
|||
130 |
|
120 | |||
131 | avail = [] |
|
121 | avail = [] | |
132 | not_avail = [] |
|
122 | not_avail = [] | |
@@ -155,8 +145,8 b' def make_exclude():' | |||||
155 |
|
145 | |||
156 | For the IPythonDoctest plugin, we need to exclude certain patterns that |
|
146 | For the IPythonDoctest plugin, we need to exclude certain patterns that | |
157 | cause testing problems. We should strive to minimize the number of |
|
147 | cause testing problems. We should strive to minimize the number of | |
158 |
skipped modules, since this means untested code. |
|
148 | skipped modules, since this means untested code. | |
159 | machinery solidifies, this list should eventually become empty. |
|
149 | ||
160 | These modules and packages will NOT get scanned by nose at all for tests. |
|
150 | These modules and packages will NOT get scanned by nose at all for tests. | |
161 | """ |
|
151 | """ | |
162 | # Simple utility to make IPython paths more readably, we need a lot of |
|
152 | # Simple utility to make IPython paths more readably, we need a lot of | |
@@ -198,18 +188,12 b' def make_exclude():' | |||||
198 | if not have['objc']: |
|
188 | if not have['objc']: | |
199 | exclusions.append(ipjoin('frontend', 'cocoa')) |
|
189 | exclusions.append(ipjoin('frontend', 'cocoa')) | |
200 |
|
190 | |||
201 | if not sys.platform == 'win32': |
|
|||
202 | exclusions.append(ipjoin('utils', 'platutils_win32')) |
|
|||
203 |
|
||||
204 | # These have to be skipped on win32 because the use echo, rm, cd, etc. |
|
191 | # These have to be skipped on win32 because the use echo, rm, cd, etc. | |
205 | # See ticket https://bugs.launchpad.net/bugs/366982 |
|
192 | # See ticket https://bugs.launchpad.net/bugs/366982 | |
206 | if sys.platform == 'win32': |
|
193 | if sys.platform == 'win32': | |
207 | exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip')) |
|
194 | exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip')) | |
208 | exclusions.append(ipjoin('testing', 'plugin', 'dtexample')) |
|
195 | exclusions.append(ipjoin('testing', 'plugin', 'dtexample')) | |
209 |
|
196 | |||
210 | if not os.name == 'posix': |
|
|||
211 | exclusions.append(ipjoin('utils', 'platutils_posix')) |
|
|||
212 |
|
||||
213 | if not have['pexpect']: |
|
197 | if not have['pexpect']: | |
214 | exclusions.extend([ipjoin('scripts', 'irunner'), |
|
198 | exclusions.extend([ipjoin('scripts', 'irunner'), | |
215 | ipjoin('lib', 'irunner')]) |
|
199 | ipjoin('lib', 'irunner')]) | |
@@ -255,20 +239,13 b' class IPTester(object):' | |||||
255 | """Create new test runner.""" |
|
239 | """Create new test runner.""" | |
256 | p = os.path |
|
240 | p = os.path | |
257 | if runner == 'iptest': |
|
241 | if runner == 'iptest': | |
258 | if INSTALLED: |
|
242 | iptest_app = get_ipython_module_path('IPython.testing.iptest') | |
259 |
|
|
243 | self.runner = pycmd2argv(iptest_app) + sys.argv[1:] | |
260 | p.abspath(find_cmd('iptest'))) + sys.argv[1:] |
|
244 | elif runner == 'trial': | |
261 | else: |
|
|||
262 | # Find our own 'iptest' script OS-level entry point. Don't |
|
|||
263 | # look system-wide, so we are sure we pick up *this one*. And |
|
|||
264 | # pass through to subprocess call our own sys.argv |
|
|||
265 | ippath = p.abspath(p.join(p.dirname(__file__),'..','..')) |
|
|||
266 | script = p.join(ippath, 'iptest.py') |
|
|||
267 | self.runner = tools.cmd2argv(script) + sys.argv[1:] |
|
|||
268 |
|
||||
269 | else: |
|
|||
270 | # For trial, it needs to be installed system-wide |
|
245 | # For trial, it needs to be installed system-wide | |
271 |
self.runner = |
|
246 | self.runner = pycmd2argv(p.abspath(find_cmd('trial'))) | |
|
247 | else: | |||
|
248 | raise Exception('Not a valid test runner: %s' % repr(runner)) | |||
272 | if params is None: |
|
249 | if params is None: | |
273 | params = [] |
|
250 | params = [] | |
274 | if isinstance(params, str): |
|
251 | if isinstance(params, str): | |
@@ -290,6 +267,8 b' class IPTester(object):' | |||||
290 | # fashioned' way to do it, but it works just fine. If someone |
|
267 | # fashioned' way to do it, but it works just fine. If someone | |
291 | # later can clean this up that's fine, as long as the tests run |
|
268 | # later can clean this up that's fine, as long as the tests run | |
292 | # reliably in win32. |
|
269 | # reliably in win32. | |
|
270 | # What types of problems are you having. They may be related to | |||
|
271 | # running Python in unboffered mode. BG. | |||
293 | return os.system(' '.join(self.call_args)) |
|
272 | return os.system(' '.join(self.call_args)) | |
294 | else: |
|
273 | else: | |
295 | def _run_cmd(self): |
|
274 | def _run_cmd(self): | |
@@ -343,9 +322,9 b' def make_runners():' | |||||
343 |
|
322 | |||
344 | # And add twisted ones if conditions are met |
|
323 | # And add twisted ones if conditions are met | |
345 | if have['zope.interface'] and have['twisted'] and have['foolscap']: |
|
324 | if have['zope.interface'] and have['twisted'] and have['foolscap']: | |
346 | # Note that we list the kernel here, though the bulk of it is |
|
325 | # We only list IPython.kernel for testing using twisted.trial as | |
347 | # twisted-based, because nose picks up doctests that twisted doesn't. |
|
326 | # nose and twisted.trial have conflicts that make the testing system | |
348 | nose_pkg_names.append('kernel') |
|
327 | # unstable. | |
349 | trial_pkg_names.append('kernel') |
|
328 | trial_pkg_names.append('kernel') | |
350 |
|
329 | |||
351 | # For debugging this code, only load quick stuff |
|
330 | # For debugging this code, only load quick stuff |
@@ -31,7 +31,6 b' from __future__ import absolute_import' | |||||
31 | # the file COPYING, distributed as part of this software. |
|
31 | # the file COPYING, distributed as part of this software. | |
32 | #----------------------------------------------------------------------------- |
|
32 | #----------------------------------------------------------------------------- | |
33 |
|
33 | |||
34 |
|
||||
35 | #----------------------------------------------------------------------------- |
|
34 | #----------------------------------------------------------------------------- | |
36 | # Imports |
|
35 | # Imports | |
37 | #----------------------------------------------------------------------------- |
|
36 | #----------------------------------------------------------------------------- |
@@ -38,7 +38,7 b' import tempfile' | |||||
38 |
|
38 | |||
39 | # IPython-specific libraries |
|
39 | # IPython-specific libraries | |
40 | from IPython.lib import irunner |
|
40 | from IPython.lib import irunner | |
41 |
from IPython.utils. |
|
41 | from IPython.utils.warn import fatal | |
42 |
|
42 | |||
43 | class IndentOut(object): |
|
43 | class IndentOut(object): | |
44 | """A simple output stream that indents all output by a fixed amount. |
|
44 | """A simple output stream that indents all output by a fixed amount. |
@@ -5,10 +5,25 b' Once this is fixed in upstream nose we can disable it.' | |||||
5 |
|
5 | |||
6 | Note: merely importing this module causes the monkeypatch to be applied.""" |
|
6 | Note: merely importing this module causes the monkeypatch to be applied.""" | |
7 |
|
7 | |||
|
8 | #----------------------------------------------------------------------------- | |||
|
9 | # Copyright (C) 2009 The IPython Development Team | |||
|
10 | # | |||
|
11 | # Distributed under the terms of the BSD License. The full license is in | |||
|
12 | # the file COPYING, distributed as part of this software. | |||
|
13 | #----------------------------------------------------------------------------- | |||
|
14 | ||||
|
15 | #----------------------------------------------------------------------------- | |||
|
16 | # Imports | |||
|
17 | #----------------------------------------------------------------------------- | |||
|
18 | ||||
8 | import unittest |
|
19 | import unittest | |
9 | import nose.loader |
|
20 | import nose.loader | |
10 | from inspect import ismethod, isfunction |
|
21 | from inspect import ismethod, isfunction | |
11 |
|
22 | |||
|
23 | #----------------------------------------------------------------------------- | |||
|
24 | # Classes and functions | |||
|
25 | #----------------------------------------------------------------------------- | |||
|
26 | ||||
12 | def getTestCaseNames(self, testCaseClass): |
|
27 | def getTestCaseNames(self, testCaseClass): | |
13 | """Override to select with selector, unless |
|
28 | """Override to select with selector, unless | |
14 | config.getTestCaseNamesCompat is True |
|
29 | config.getTestCaseNamesCompat is True |
@@ -5,10 +5,27 b" parametric code. We just need to double-check that the new code doesn't clash" | |||||
5 | with Twisted (we know it works with nose and unittest). |
|
5 | with Twisted (we know it works with nose and unittest). | |
6 | """ |
|
6 | """ | |
7 |
|
7 | |||
8 | __all__ = ['parametric','Parametric'] |
|
8 | #----------------------------------------------------------------------------- | |
|
9 | # Copyright (C) 2009 The IPython Development Team | |||
|
10 | # | |||
|
11 | # Distributed under the terms of the BSD License. The full license is in | |||
|
12 | # the file COPYING, distributed as part of this software. | |||
|
13 | #----------------------------------------------------------------------------- | |||
|
14 | ||||
|
15 | #----------------------------------------------------------------------------- | |||
|
16 | # Imports | |||
|
17 | #----------------------------------------------------------------------------- | |||
|
18 | ||||
9 |
|
19 | |||
10 | from twisted.trial.unittest import TestCase |
|
20 | from twisted.trial.unittest import TestCase | |
11 |
|
21 | |||
|
22 | #----------------------------------------------------------------------------- | |||
|
23 | # Classes and functions | |||
|
24 | #----------------------------------------------------------------------------- | |||
|
25 | ||||
|
26 | __all__ = ['parametric','Parametric'] | |||
|
27 | ||||
|
28 | ||||
12 | def partial(f, *partial_args, **partial_kwargs): |
|
29 | def partial(f, *partial_args, **partial_kwargs): | |
13 | """Generate a partial class method. |
|
30 | """Generate a partial class method. | |
14 |
|
31 | |||
@@ -20,6 +37,7 b' def partial(f, *partial_args, **partial_kwargs):' | |||||
20 |
|
37 | |||
21 | return partial_func |
|
38 | return partial_func | |
22 |
|
39 | |||
|
40 | ||||
23 | def parametric(f): |
|
41 | def parametric(f): | |
24 | """Mark f as a parametric test. |
|
42 | """Mark f as a parametric test. | |
25 |
|
43 | |||
@@ -27,6 +45,7 b' def parametric(f):' | |||||
27 | f._parametric = True |
|
45 | f._parametric = True | |
28 | return classmethod(f) |
|
46 | return classmethod(f) | |
29 |
|
47 | |||
|
48 | ||||
30 | def Parametric(cls): |
|
49 | def Parametric(cls): | |
31 | """Register parametric tests with a class. |
|
50 | """Register parametric tests with a class. | |
32 |
|
51 | |||
@@ -56,3 +75,4 b' def Parametric(cls):' | |||||
56 |
|
75 | |||
57 | # rename test generator so it isn't called again by nose |
|
76 | # rename test generator so it isn't called again by nose | |
58 | test_gen.im_func.func_name = '__done_' + test_name |
|
77 | test_gen.im_func.func_name = '__done_' + test_name | |
|
78 |
@@ -1,6 +1,6 b'' | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 | """ |
|
2 | """ | |
3 | Tests for decorators.py compatibility with Twisted.trial |
|
3 | Tests for decorators_trial.py | |
4 | """ |
|
4 | """ | |
5 |
|
5 | |||
6 | #----------------------------------------------------------------------------- |
|
6 | #----------------------------------------------------------------------------- | |
@@ -14,24 +14,19 b' Tests for decorators.py compatibility with Twisted.trial' | |||||
14 | # Imports |
|
14 | # Imports | |
15 | #----------------------------------------------------------------------------- |
|
15 | #----------------------------------------------------------------------------- | |
16 |
|
16 | |||
17 |
# Tell nose to skip this module |
|
17 | # Tell nose to skip this module | |
18 | __test__ = {} |
|
18 | __test__ = {} | |
19 |
|
19 | |||
20 | import os |
|
20 | import os | |
21 | import sys |
|
21 | import sys | |
22 |
|
22 | |||
23 | from twisted.trial import unittest |
|
23 | from twisted.trial import unittest | |
24 | import IPython.testing.decorators as dec |
|
24 | import IPython.testing.decorators_trial as dec | |
25 |
|
25 | |||
26 | #----------------------------------------------------------------------------- |
|
26 | #----------------------------------------------------------------------------- | |
27 | # Tests |
|
27 | # Tests | |
28 | #----------------------------------------------------------------------------- |
|
28 | #----------------------------------------------------------------------------- | |
29 |
|
29 | |||
30 | # Note: this code is identical to that in test_decorators, but that one uses |
|
|||
31 | # stdlib unittest, not the one from twisted, which we are using here. While |
|
|||
32 | # somewhat redundant, we want to check both with the stdlib and with twisted, |
|
|||
33 | # so the duplication is OK. |
|
|||
34 |
|
||||
35 | class TestDecoratorsTrial(unittest.TestCase): |
|
30 | class TestDecoratorsTrial(unittest.TestCase): | |
36 |
|
31 | |||
37 | @dec.skip() |
|
32 | @dec.skip() |
@@ -15,22 +15,22 b' Authors' | |||||
15 | - Fernando Perez <Fernando.Perez@berkeley.edu> |
|
15 | - Fernando Perez <Fernando.Perez@berkeley.edu> | |
16 | """ |
|
16 | """ | |
17 |
|
17 | |||
18 | #***************************************************************************** |
|
18 | from __future__ import absolute_import | |
|
19 | ||||
|
20 | #----------------------------------------------------------------------------- | |||
19 |
# |
|
21 | # Copyright (C) 2009 The IPython Development Team | |
20 | # |
|
22 | # | |
21 | # Distributed under the terms of the BSD License. The full license is in |
|
23 | # Distributed under the terms of the BSD License. The full license is in | |
22 | # the file COPYING, distributed as part of this software. |
|
24 | # the file COPYING, distributed as part of this software. | |
23 | #***************************************************************************** |
|
25 | #----------------------------------------------------------------------------- | |
24 |
|
26 | |||
25 | #----------------------------------------------------------------------------- |
|
27 | #----------------------------------------------------------------------------- | |
26 | # Required modules and packages |
|
28 | # Imports | |
27 | #----------------------------------------------------------------------------- |
|
29 | #----------------------------------------------------------------------------- | |
28 | from __future__ import absolute_import |
|
|||
29 |
|
30 | |||
30 | import os |
|
31 | import os | |
31 | import re |
|
32 | import re | |
32 | import sys |
|
33 | import sys | |
33 | import tempfile |
|
|||
34 |
|
34 | |||
35 | try: |
|
35 | try: | |
36 | # These tools are used by parts of the runtime, so we make the nose |
|
36 | # These tools are used by parts of the runtime, so we make the nose | |
@@ -41,7 +41,10 b' try:' | |||||
41 | except ImportError: |
|
41 | except ImportError: | |
42 | has_nose = False |
|
42 | has_nose = False | |
43 |
|
43 | |||
44 | from IPython.utils import genutils, platutils |
|
44 | from IPython.config.loader import Config | |
|
45 | from IPython.utils.process import find_cmd, getoutputerror | |||
|
46 | from IPython.utils.text import list_strings | |||
|
47 | from IPython.utils.io import temp_pyfile | |||
45 |
|
48 | |||
46 | from . import decorators as dec |
|
49 | from . import decorators as dec | |
47 |
|
50 | |||
@@ -49,13 +52,6 b' from . import decorators as dec' | |||||
49 | # Globals |
|
52 | # Globals | |
50 | #----------------------------------------------------------------------------- |
|
53 | #----------------------------------------------------------------------------- | |
51 |
|
54 | |||
52 | # By default, we assume IPython has been installed. But if the test suite is |
|
|||
53 | # being run from a source tree that has NOT been installed yet, this flag can |
|
|||
54 | # be set to False by the entry point scripts, to let us know that we must call |
|
|||
55 | # the source tree versions of the scripts which manipulate sys.path instead of |
|
|||
56 | # assuming that things exist system-wide. |
|
|||
57 | INSTALLED = True |
|
|||
58 |
|
||||
59 | # Make a bunch of nose.tools assert wrappers that can be used in test |
|
55 | # Make a bunch of nose.tools assert wrappers that can be used in test | |
60 | # generators. This will expose an assert* function for each one in nose.tools. |
|
56 | # generators. This will expose an assert* function for each one in nose.tools. | |
61 |
|
57 | |||
@@ -107,7 +103,7 b' def full_path(startPath,files):' | |||||
107 | ['/a.txt'] |
|
103 | ['/a.txt'] | |
108 | """ |
|
104 | """ | |
109 |
|
105 | |||
110 |
files = |
|
106 | files = list_strings(files) | |
111 | base = os.path.split(startPath)[0] |
|
107 | base = os.path.split(startPath)[0] | |
112 | return [ os.path.join(base,f) for f in files ] |
|
108 | return [ os.path.join(base,f) for f in files ] | |
113 |
|
109 | |||
@@ -156,63 +152,6 b' def parse_test_output(txt):' | |||||
156 | parse_test_output.__test__ = False |
|
152 | parse_test_output.__test__ = False | |
157 |
|
153 | |||
158 |
|
154 | |||
159 | def cmd2argv(cmd): |
|
|||
160 | r"""Take the path of a command and return a list (argv-style). |
|
|||
161 |
|
||||
162 | For a given path ``cmd``, this returns [cmd] if cmd's extension is .exe, |
|
|||
163 | .com or .bat, and ['python', cmd] otherwise. |
|
|||
164 |
|
||||
165 | This is mostly a Windows utility, to deal with the fact that the scripts in |
|
|||
166 | Windows get wrapped in .exe entry points, so we have to call them |
|
|||
167 | differently. |
|
|||
168 |
|
||||
169 | Parameters |
|
|||
170 | ---------- |
|
|||
171 | cmd : string |
|
|||
172 | The path of the command. |
|
|||
173 |
|
||||
174 | Returns |
|
|||
175 | ------- |
|
|||
176 | argv-style list. |
|
|||
177 |
|
||||
178 | Examples |
|
|||
179 | -------- |
|
|||
180 | In [2]: cmd2argv('/usr/bin/ipython') |
|
|||
181 | Out[2]: ['python', '/usr/bin/ipython'] |
|
|||
182 |
|
||||
183 | In [3]: cmd2argv(r'C:\Python26\Scripts\ipython.exe') |
|
|||
184 | Out[3]: ['C:\\Python26\\Scripts\\ipython.exe'] |
|
|||
185 | """ |
|
|||
186 | ext = os.path.splitext(cmd)[1] |
|
|||
187 | if ext in ['.exe', '.com', '.bat']: |
|
|||
188 | return [cmd] |
|
|||
189 | else: |
|
|||
190 | return ['python', cmd] |
|
|||
191 |
|
||||
192 |
|
||||
193 | def temp_pyfile(src, ext='.py'): |
|
|||
194 | """Make a temporary python file, return filename and filehandle. |
|
|||
195 |
|
||||
196 | Parameters |
|
|||
197 | ---------- |
|
|||
198 | src : string or list of strings (no need for ending newlines if list) |
|
|||
199 | Source code to be written to the file. |
|
|||
200 |
|
||||
201 | ext : optional, string |
|
|||
202 | Extension for the generated file. |
|
|||
203 |
|
||||
204 | Returns |
|
|||
205 | ------- |
|
|||
206 | (filename, open filehandle) |
|
|||
207 | It is the caller's responsibility to close the open file and unlink it. |
|
|||
208 | """ |
|
|||
209 | fname = tempfile.mkstemp(ext)[1] |
|
|||
210 | f = open(fname,'w') |
|
|||
211 | f.write(src) |
|
|||
212 | f.flush() |
|
|||
213 | return fname, f |
|
|||
214 |
|
||||
215 |
|
||||
216 | def default_argv(): |
|
155 | def default_argv(): | |
217 | """Return a valid default argv for creating testing instances of ipython""" |
|
156 | """Return a valid default argv for creating testing instances of ipython""" | |
218 |
|
157 | |||
@@ -222,6 +161,15 b' def default_argv():' | |||||
222 | '--autocall=0'] |
|
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 | def ipexec(fname, options=None): |
|
173 | def ipexec(fname, options=None): | |
226 | """Utility to call 'ipython filename'. |
|
174 | """Utility to call 'ipython filename'. | |
227 |
|
175 | |||
@@ -252,20 +200,12 b' def ipexec(fname, options=None):' | |||||
252 | _ip = get_ipython() |
|
200 | _ip = get_ipython() | |
253 | test_dir = os.path.dirname(__file__) |
|
201 | test_dir = os.path.dirname(__file__) | |
254 |
|
202 | |||
255 | # Find the ipython script from the package we're using, so that the test |
|
203 | ipython_cmd = find_cmd('ipython') | |
256 | # suite can be run from the source tree without an installed IPython |
|
|||
257 | p = os.path |
|
|||
258 | if INSTALLED: |
|
|||
259 | ipython_cmd = platutils.find_cmd('ipython') |
|
|||
260 | else: |
|
|||
261 | ippath = p.abspath(p.join(p.dirname(__file__),'..','..')) |
|
|||
262 | ipython_script = p.join(ippath, 'ipython.py') |
|
|||
263 | ipython_cmd = 'python "%s"' % ipython_script |
|
|||
264 | # Absolute path for filename |
|
204 | # Absolute path for filename | |
265 | full_fname = p.join(test_dir, fname) |
|
205 | full_fname = os.path.join(test_dir, fname) | |
266 | full_cmd = '%s %s %s' % (ipython_cmd, cmdargs, full_fname) |
|
206 | full_cmd = '%s %s %s' % (ipython_cmd, cmdargs, full_fname) | |
267 | #print >> sys.stderr, 'FULL CMD:', full_cmd # dbg |
|
207 | #print >> sys.stderr, 'FULL CMD:', full_cmd # dbg | |
268 |
return |
|
208 | return getoutputerror(full_cmd) | |
269 |
|
209 | |||
270 |
|
210 | |||
271 | def ipexec_validate(fname, expected_out, expected_err='', |
|
211 | def ipexec_validate(fname, expected_out, expected_err='', |
@@ -1,23 +1,24 b'' | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 | """This file contains utility classes for performing tests with Deferreds. |
|
2 | """This file contains utility classes for performing tests with Deferreds. | |
3 | """ |
|
3 | """ | |
4 | __docformat__ = "restructuredtext en" |
|
4 | #----------------------------------------------------------------------------- | |
5 | #------------------------------------------------------------------------------- |
|
5 | # Copyright (C) 2009 The IPython Development Team | |
6 | # Copyright (C) 2005 Fernando Perez <fperez@colorado.edu> |
|
|||
7 | # Brian E Granger <ellisonbg@gmail.com> |
|
|||
8 | # Benjamin Ragan-Kelley <benjaminrk@gmail.com> |
|
|||
9 | # |
|
6 | # | |
10 | # Distributed under the terms of the BSD License. The full license is in |
|
7 | # Distributed under the terms of the BSD License. The full license is in | |
11 | # the file COPYING, distributed as part of this software. |
|
8 | # the file COPYING, distributed as part of this software. | |
12 |
#----------------------------------------------------------------------------- |
|
9 | #----------------------------------------------------------------------------- | |
13 |
|
10 | |||
14 |
#----------------------------------------------------------------------------- |
|
11 | #----------------------------------------------------------------------------- | |
15 | # Imports |
|
12 | # Imports | |
16 |
#----------------------------------------------------------------------------- |
|
13 | #----------------------------------------------------------------------------- | |
17 |
|
14 | |||
18 | from twisted.trial import unittest |
|
15 | from twisted.trial import unittest | |
19 | from twisted.internet import defer |
|
16 | from twisted.internet import defer | |
20 |
|
17 | |||
|
18 | #----------------------------------------------------------------------------- | |||
|
19 | # Classes and functions | |||
|
20 | #----------------------------------------------------------------------------- | |||
|
21 | ||||
21 | class DeferredTestCase(unittest.TestCase): |
|
22 | class DeferredTestCase(unittest.TestCase): | |
22 |
|
23 | |||
23 | def assertDeferredEquals(self, deferred, expectedResult, |
|
24 | def assertDeferredEquals(self, deferred, expectedResult, |
@@ -1,8 +1,9 b'' | |||||
|
1 | # encoding: utf-8 | |||
1 | """Generic functions for extending IPython. |
|
2 | """Generic functions for extending IPython. | |
2 |
|
3 | |||
3 | See http://cheeseshop.python.org/pypi/simplegeneric. |
|
4 | See http://cheeseshop.python.org/pypi/simplegeneric. | |
4 |
|
5 | |||
5 |
Here is an example from |
|
6 | Here is an example from IPython.utils.text:: | |
6 |
|
7 | |||
7 | def print_lsstring(arg): |
|
8 | def print_lsstring(arg): | |
8 | "Prettier (non-repr-like) and more informative printer for LSString" |
|
9 | "Prettier (non-repr-like) and more informative printer for LSString" | |
@@ -12,19 +13,37 b' Here is an example from genutils.py::' | |||||
12 | print_lsstring = result_display.when_type(LSString)(print_lsstring) |
|
13 | print_lsstring = result_display.when_type(LSString)(print_lsstring) | |
13 | """ |
|
14 | """ | |
14 |
|
15 | |||
|
16 | #----------------------------------------------------------------------------- | |||
|
17 | # Copyright (C) 2008-2009 The IPython Development Team | |||
|
18 | # | |||
|
19 | # Distributed under the terms of the BSD License. The full license is in | |||
|
20 | # the file COPYING, distributed as part of this software. | |||
|
21 | #----------------------------------------------------------------------------- | |||
|
22 | ||||
|
23 | #----------------------------------------------------------------------------- | |||
|
24 | # Imports | |||
|
25 | #----------------------------------------------------------------------------- | |||
|
26 | ||||
15 | from IPython.core.error import TryNext |
|
27 | from IPython.core.error import TryNext | |
16 | from IPython.external.simplegeneric import generic |
|
28 | from IPython.external.simplegeneric import generic | |
17 |
|
29 | |||
|
30 | #----------------------------------------------------------------------------- | |||
|
31 | # Imports | |||
|
32 | #----------------------------------------------------------------------------- | |||
|
33 | ||||
|
34 | ||||
18 | @generic |
|
35 | @generic | |
19 | def result_display(result): |
|
36 | def result_display(result): | |
20 | """Print the result of computation.""" |
|
37 | """Print the result of computation.""" | |
21 | raise TryNext |
|
38 | raise TryNext | |
22 |
|
39 | |||
|
40 | ||||
23 | @generic |
|
41 | @generic | |
24 | def inspect_object(obj): |
|
42 | def inspect_object(obj): | |
25 | """Called when you do obj?""" |
|
43 | """Called when you do obj?""" | |
26 | raise TryNext |
|
44 | raise TryNext | |
27 |
|
45 | |||
|
46 | ||||
28 | @generic |
|
47 | @generic | |
29 | def complete_object(obj, prev_completions): |
|
48 | def complete_object(obj, prev_completions): | |
30 | """Custom completer dispatching for python objects. |
|
49 | """Custom completer dispatching for python objects. | |
@@ -41,3 +60,5 b' def complete_object(obj, prev_completions):' | |||||
41 | own_attrs + prev_completions. |
|
60 | own_attrs + prev_completions. | |
42 | """ |
|
61 | """ | |
43 | raise TryNext |
|
62 | raise TryNext | |
|
63 | ||||
|
64 |
@@ -1,6 +1,22 b'' | |||||
1 | #!/usr/bin/env python |
|
|||
2 |
|
|
1 | # encoding: utf-8 | |
|
2 | """ | |||
|
3 | Utilities using Growl on OS X for notifications. | |||
|
4 | """ | |||
3 |
|
5 | |||
|
6 | #----------------------------------------------------------------------------- | |||
|
7 | # Copyright (C) 2008-2009 The IPython Development Team | |||
|
8 | # | |||
|
9 | # Distributed under the terms of the BSD License. The full license is in | |||
|
10 | # the file COPYING, distributed as part of this software. | |||
|
11 | #----------------------------------------------------------------------------- | |||
|
12 | ||||
|
13 | #----------------------------------------------------------------------------- | |||
|
14 | # Imports | |||
|
15 | #----------------------------------------------------------------------------- | |||
|
16 | ||||
|
17 | #----------------------------------------------------------------------------- | |||
|
18 | # Code | |||
|
19 | #----------------------------------------------------------------------------- | |||
4 |
|
20 | |||
5 | class IPythonGrowlError(Exception): |
|
21 | class IPythonGrowlError(Exception): | |
6 | pass |
|
22 | pass |
@@ -1,4 +1,3 b'' | |||||
1 | #!/usr/bin/env python |
|
|||
2 |
|
|
1 | # encoding: utf-8 | |
3 | """ |
|
2 | """ | |
4 | A simple utility to import something by its string name. |
|
3 | A simple utility to import something by its string name. |
@@ -19,9 +19,7 b' Authors:' | |||||
19 | # Imports |
|
19 | # Imports | |
20 | #----------------------------------------------------------------------------- |
|
20 | #----------------------------------------------------------------------------- | |
21 |
|
21 | |||
22 | import pprint |
|
22 | from IPython.utils.data import list2dict2 | |
23 |
|
||||
24 | from IPython.utils.genutils import list2dict2 |
|
|||
25 |
|
23 | |||
26 | __all__ = ['Struct'] |
|
24 | __all__ = ['Struct'] | |
27 |
|
25 |
@@ -37,7 +37,6 b' from IPython.external.path import path as Path' | |||||
37 | import os,stat,time |
|
37 | import os,stat,time | |
38 | import cPickle as pickle |
|
38 | import cPickle as pickle | |
39 | import UserDict |
|
39 | import UserDict | |
40 | import warnings |
|
|||
41 | import glob |
|
40 | import glob | |
42 |
|
41 | |||
43 | def gethashfile(key): |
|
42 | def gethashfile(key): |
@@ -1,10 +1,12 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ Imports and provides the 'correct' version of readline for the platform. |
|
2 | """ Imports and provides the 'correct' version of readline for the platform. | |
3 |
|
3 | |||
4 |
Readline is used throughout IPython as |
|
4 | Readline is used throughout IPython as:: | |
|
5 | ||||
|
6 | import IPython.utils.rlineimpl as readline | |||
5 |
|
7 | |||
6 | In addition to normal readline stuff, this module provides have_readline |
|
8 | In addition to normal readline stuff, this module provides have_readline | |
7 |
boolean and _outputfile variable used in |
|
9 | boolean and _outputfile variable used in IPython.utils. | |
8 | """ |
|
10 | """ | |
9 |
|
11 | |||
10 | import sys |
|
12 | import sys |
@@ -19,9 +19,12 b' Authors:' | |||||
19 | # Imports |
|
19 | # Imports | |
20 | #----------------------------------------------------------------------------- |
|
20 | #----------------------------------------------------------------------------- | |
21 |
|
21 | |||
22 |
|
||||
23 | import sys |
|
22 | import sys | |
24 |
|
23 | |||
|
24 | #----------------------------------------------------------------------------- | |||
|
25 | # Code | |||
|
26 | #----------------------------------------------------------------------------- | |||
|
27 | ||||
25 | class appended_to_syspath(object): |
|
28 | class appended_to_syspath(object): | |
26 | """A context for appending a directory to sys.path for a second.""" |
|
29 | """A context for appending a directory to sys.path for a second.""" | |
27 |
|
30 |
@@ -3,21 +3,12 b'' | |||||
3 | def test_import_coloransi(): |
|
3 | def test_import_coloransi(): | |
4 | from IPython.utils import coloransi |
|
4 | from IPython.utils import coloransi | |
5 |
|
5 | |||
6 | def test_import_DPyGetOpt(): |
|
|||
7 | from IPython.utils import DPyGetOpt |
|
|||
8 |
|
||||
9 | def test_import_generics(): |
|
6 | def test_import_generics(): | |
10 | from IPython.utils import generics |
|
7 | from IPython.utils import generics | |
11 |
|
8 | |||
12 | def test_import_genutils(): |
|
|||
13 | from IPython.utils import genutils |
|
|||
14 |
|
||||
15 | def test_import_ipstruct(): |
|
9 | def test_import_ipstruct(): | |
16 | from IPython.utils import ipstruct |
|
10 | from IPython.utils import ipstruct | |
17 |
|
11 | |||
18 | def test_import_platutils(): |
|
|||
19 | from IPython.utils import platutils |
|
|||
20 |
|
||||
21 | def test_import_PyColorize(): |
|
12 | def test_import_PyColorize(): | |
22 | from IPython.utils import PyColorize |
|
13 | from IPython.utils import PyColorize | |
23 |
|
14 | |||
@@ -33,5 +24,3 b' def test_import_upgradedir():' | |||||
33 | def test_import_wildcard(): |
|
24 | def test_import_wildcard(): | |
34 | from IPython.utils import wildcard |
|
25 | from IPython.utils import wildcard | |
35 |
|
26 | |||
36 | def test_import_winconsole(): |
|
|||
37 | from IPython.utils import winconsole |
|
@@ -15,11 +15,7 b'' | |||||
15 |
|
15 | |||
16 | import unittest |
|
16 | import unittest | |
17 |
|
17 | |||
18 |
from IPython.utils.notification import |
|
18 | from IPython.utils.notification import shared_center | |
19 | NotificationCenter, |
|
|||
20 | NotificationError, |
|
|||
21 | shared_center |
|
|||
22 | ) |
|
|||
23 |
|
19 | |||
24 | #----------------------------------------------------------------------------- |
|
20 | #----------------------------------------------------------------------------- | |
25 | # Support Classes |
|
21 | # Support Classes |
@@ -1,8 +1,5 b'' | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 |
|
2 | """Tests for IPython.utils.path.py""" | ||
3 | """Tests for genutils.py""" |
|
|||
4 |
|
||||
5 | __docformat__ = "restructuredtext en" |
|
|||
6 |
|
3 | |||
7 | #----------------------------------------------------------------------------- |
|
4 | #----------------------------------------------------------------------------- | |
8 | # Copyright (C) 2008 The IPython Development Team |
|
5 | # Copyright (C) 2008 The IPython Development Team | |
@@ -15,27 +12,21 b' __docformat__ = "restructuredtext en"' | |||||
15 | # Imports |
|
12 | # Imports | |
16 | #----------------------------------------------------------------------------- |
|
13 | #----------------------------------------------------------------------------- | |
17 |
|
14 | |||
18 | # stdlib |
|
|||
19 | import os |
|
15 | import os | |
20 | import shutil |
|
16 | import shutil | |
21 | import sys |
|
17 | import sys | |
22 | import tempfile |
|
18 | import tempfile | |
23 | import unittest |
|
|||
24 |
|
19 | |||
25 | from cStringIO import StringIO |
|
|||
26 | from os.path import join, abspath, split |
|
20 | from os.path import join, abspath, split | |
27 |
|
21 | |||
28 | # third-party |
|
|||
29 | import nose.tools as nt |
|
22 | import nose.tools as nt | |
30 |
|
23 | |||
31 | from nose import with_setup |
|
24 | from nose import with_setup | |
32 | from nose.tools import raises |
|
|||
33 |
|
25 | |||
34 | # Our own |
|
|||
35 | import IPython |
|
26 | import IPython | |
36 | from IPython.utils import genutils |
|
|||
37 | from IPython.testing import decorators as dec |
|
27 | from IPython.testing import decorators as dec | |
38 |
from IPython.testing.decorators import skipif, skip_ |
|
28 | from IPython.testing.decorators import skip_if_not_win32, skip_win32 | |
|
29 | from IPython.utils import path | |||
39 |
|
30 | |||
40 | # Platform-dependent imports |
|
31 | # Platform-dependent imports | |
41 | try: |
|
32 | try: | |
@@ -69,6 +60,7 b' def setup():' | |||||
69 | # problem because that exception is only defined on Windows... |
|
60 | # problem because that exception is only defined on Windows... | |
70 | os.makedirs(IP_TEST_DIR) |
|
61 | os.makedirs(IP_TEST_DIR) | |
71 |
|
62 | |||
|
63 | ||||
72 | def teardown(): |
|
64 | def teardown(): | |
73 | """Teardown testenvironment for the module: |
|
65 | """Teardown testenvironment for the module: | |
74 |
|
66 | |||
@@ -88,7 +80,7 b' def setup_environment():' | |||||
88 | each testfunction needs a pristine environment. |
|
80 | each testfunction needs a pristine environment. | |
89 | """ |
|
81 | """ | |
90 | global oldstuff, platformstuff |
|
82 | global oldstuff, platformstuff | |
91 |
oldstuff = (env.copy(), os.name, |
|
83 | oldstuff = (env.copy(), os.name, path.get_home_dir, IPython.__file__) | |
92 |
|
84 | |||
93 | if os.name == 'nt': |
|
85 | if os.name == 'nt': | |
94 | platformstuff = (wreg.OpenKey, wreg.QueryValueEx,) |
|
86 | platformstuff = (wreg.OpenKey, wreg.QueryValueEx,) | |
@@ -97,7 +89,7 b' def setup_environment():' | |||||
97 | def teardown_environment(): |
|
89 | def teardown_environment(): | |
98 | """Restore things that were remebered by the setup_environment function |
|
90 | """Restore things that were remebered by the setup_environment function | |
99 | """ |
|
91 | """ | |
100 |
(oldenv, os.name, |
|
92 | (oldenv, os.name, get_home_dir, IPython.__file__,) = oldstuff | |
101 |
|
93 | |||
102 | for key in env.keys(): |
|
94 | for key in env.keys(): | |
103 | if key not in oldenv: |
|
95 | if key not in oldenv: | |
@@ -112,10 +104,6 b' def teardown_environment():' | |||||
112 | with_environment = with_setup(setup_environment, teardown_environment) |
|
104 | with_environment = with_setup(setup_environment, teardown_environment) | |
113 |
|
105 | |||
114 |
|
106 | |||
115 | # |
|
|||
116 | # Tests for get_home_dir |
|
|||
117 | # |
|
|||
118 |
|
||||
119 | @skip_if_not_win32 |
|
107 | @skip_if_not_win32 | |
120 | @with_environment |
|
108 | @with_environment | |
121 | def test_get_home_dir_1(): |
|
109 | def test_get_home_dir_1(): | |
@@ -126,9 +114,10 b' def test_get_home_dir_1():' | |||||
126 | #fake filename for IPython.__init__ |
|
114 | #fake filename for IPython.__init__ | |
127 | IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py")) |
|
115 | IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py")) | |
128 |
|
116 | |||
129 |
home_dir = |
|
117 | home_dir = path.get_home_dir() | |
130 | nt.assert_equal(home_dir, abspath(HOME_TEST_DIR)) |
|
118 | nt.assert_equal(home_dir, abspath(HOME_TEST_DIR)) | |
131 |
|
119 | |||
|
120 | ||||
132 | @skip_if_not_win32 |
|
121 | @skip_if_not_win32 | |
133 | @with_environment |
|
122 | @with_environment | |
134 | def test_get_home_dir_2(): |
|
123 | def test_get_home_dir_2(): | |
@@ -138,61 +127,78 b' def test_get_home_dir_2():' | |||||
138 | #fake filename for IPython.__init__ |
|
127 | #fake filename for IPython.__init__ | |
139 | IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower() |
|
128 | IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower() | |
140 |
|
129 | |||
141 |
home_dir = |
|
130 | home_dir = path.get_home_dir() | |
142 | nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower()) |
|
131 | nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower()) | |
143 |
|
132 | |||
|
133 | ||||
144 | @with_environment |
|
134 | @with_environment | |
|
135 | @skip_win32 | |||
145 | def test_get_home_dir_3(): |
|
136 | def test_get_home_dir_3(): | |
146 | """Testcase $HOME is set, then use its value as home directory.""" |
|
137 | """Testcase $HOME is set, then use its value as home directory.""" | |
147 | env["HOME"] = HOME_TEST_DIR |
|
138 | env["HOME"] = HOME_TEST_DIR | |
148 |
home_dir = |
|
139 | home_dir = path.get_home_dir() | |
149 | nt.assert_equal(home_dir, env["HOME"]) |
|
140 | nt.assert_equal(home_dir, env["HOME"]) | |
150 |
|
141 | |||
|
142 | ||||
151 | @with_environment |
|
143 | @with_environment | |
152 | def test_get_home_dir_4(): |
|
144 | def test_get_home_dir_4(): | |
153 | """Testcase $HOME is not set, os=='poix'. |
|
145 | """Testcase $HOME is not set, os=='posix'. | |
154 | This should fail with HomeDirError""" |
|
146 | This should fail with HomeDirError""" | |
155 |
|
147 | |||
156 | os.name = 'posix' |
|
148 | os.name = 'posix' | |
157 | if 'HOME' in env: del env['HOME'] |
|
149 | if 'HOME' in env: del env['HOME'] | |
158 |
nt.assert_raises( |
|
150 | nt.assert_raises(path.HomeDirError, path.get_home_dir) | |
|
151 | ||||
159 |
|
152 | |||
160 | @skip_if_not_win32 |
|
153 | @skip_if_not_win32 | |
161 | @with_environment |
|
154 | @with_environment | |
162 | def test_get_home_dir_5(): |
|
155 | def test_get_home_dir_5(): | |
163 | """Testcase $HOME is not set, os=='nt' |
|
156 | """Using HOMEDRIVE + HOMEPATH, os=='nt'. | |
164 | env['HOMEDRIVE'],env['HOMEPATH'] points to path.""" |
|
157 | ||
|
158 | HOMESHARE is missing. | |||
|
159 | """ | |||
165 |
|
160 | |||
166 | os.name = 'nt' |
|
161 | os.name = 'nt' | |
167 | if 'HOME' in env: del env['HOME'] |
|
162 | env.pop('HOMESHARE', None) | |
168 | env['HOMEDRIVE'], env['HOMEPATH'] = os.path.splitdrive(HOME_TEST_DIR) |
|
163 | env['HOMEDRIVE'], env['HOMEPATH'] = os.path.splitdrive(HOME_TEST_DIR) | |
169 |
|
164 | home_dir = path.get_home_dir() | ||
170 | home_dir = genutils.get_home_dir() |
|
|||
171 | nt.assert_equal(home_dir, abspath(HOME_TEST_DIR)) |
|
165 | nt.assert_equal(home_dir, abspath(HOME_TEST_DIR)) | |
172 |
|
166 | |||
|
167 | ||||
173 | @skip_if_not_win32 |
|
168 | @skip_if_not_win32 | |
174 | @with_environment |
|
169 | @with_environment | |
175 | def test_get_home_dir_6(): |
|
170 | def test_get_home_dir_6(): | |
176 | """Testcase $HOME is not set, os=='nt' |
|
171 | """Using USERPROFILE, os=='nt'. | |
177 | env['HOMEDRIVE'],env['HOMEPATH'] do not point to path. |
|
172 | ||
178 | env['USERPROFILE'] points to path |
|
173 | HOMESHARE, HOMEDRIVE, HOMEPATH are missing. | |
179 | """ |
|
174 | """ | |
180 |
|
175 | |||
181 | os.name = 'nt' |
|
176 | os.name = 'nt' | |
182 | if 'HOME' in env: del env['HOME'] |
|
177 | env.pop('HOMESHARE', None) | |
183 | env['HOMEDRIVE'], env['HOMEPATH'] = os.path.abspath(TEST_FILE_PATH), "DOES NOT EXIST" |
|
178 | env.pop('HOMEDRIVE', None) | |
|
179 | env.pop('HOMEPATH', None) | |||
184 | env["USERPROFILE"] = abspath(HOME_TEST_DIR) |
|
180 | env["USERPROFILE"] = abspath(HOME_TEST_DIR) | |
|
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 | nt.assert_equal(home_dir, abspath(HOME_TEST_DIR)) |
|
193 | nt.assert_equal(home_dir, abspath(HOME_TEST_DIR)) | |
188 |
|
194 | |||
189 | # Should we stub wreg fully so we can run the test on all platforms? |
|
195 | # Should we stub wreg fully so we can run the test on all platforms? | |
190 | @skip_if_not_win32 |
|
196 | @skip_if_not_win32 | |
191 | @with_environment |
|
197 | @with_environment | |
192 |
def test_get_home_dir_ |
|
198 | def test_get_home_dir_8(): | |
193 | """Testcase $HOME is not set, os=='nt' |
|
199 | """Using registry hack for 'My Documents', os=='nt' | |
194 |
|
200 | |||
195 |
|
|
201 | HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing. | |
196 | """ |
|
202 | """ | |
197 | os.name = 'nt' |
|
203 | os.name = 'nt' | |
198 | # Remove from stub environment all keys that may be set |
|
204 | # Remove from stub environment all keys that may be set | |
@@ -211,116 +217,56 b' def test_get_home_dir_7():' | |||||
211 | wreg.OpenKey = OpenKey |
|
217 | wreg.OpenKey = OpenKey | |
212 | wreg.QueryValueEx = QueryValueEx |
|
218 | wreg.QueryValueEx = QueryValueEx | |
213 |
|
219 | |||
214 |
home_dir = |
|
220 | home_dir = path.get_home_dir() | |
215 | nt.assert_equal(home_dir, abspath(HOME_TEST_DIR)) |
|
221 | nt.assert_equal(home_dir, abspath(HOME_TEST_DIR)) | |
216 |
|
222 | |||
217 | # |
|
|||
218 | # Tests for get_ipython_dir |
|
|||
219 | # |
|
|||
220 |
|
223 | |||
221 | @with_environment |
|
224 | @with_environment | |
222 | def test_get_ipython_dir_1(): |
|
225 | def test_get_ipython_dir_1(): | |
223 | """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions.""" |
|
226 | """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions.""" | |
224 | env['IPYTHON_DIR'] = "someplace/.ipython" |
|
227 | env['IPYTHON_DIR'] = "someplace/.ipython" | |
225 |
ipdir = |
|
228 | ipdir = path.get_ipython_dir() | |
226 | nt.assert_equal(ipdir, "someplace/.ipython") |
|
229 | nt.assert_equal(ipdir, "someplace/.ipython") | |
227 |
|
230 | |||
228 |
|
231 | |||
229 | @with_environment |
|
232 | @with_environment | |
230 | def test_get_ipython_dir_2(): |
|
233 | def test_get_ipython_dir_2(): | |
231 | """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions.""" |
|
234 | """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions.""" | |
232 |
|
|
235 | path.get_home_dir = lambda : "someplace" | |
233 | os.name = "posix" |
|
236 | os.name = "posix" | |
234 | env.pop('IPYTHON_DIR', None) |
|
237 | env.pop('IPYTHON_DIR', None) | |
235 | env.pop('IPYTHONDIR', None) |
|
238 | env.pop('IPYTHONDIR', None) | |
236 |
ipdir = |
|
239 | ipdir = path.get_ipython_dir() | |
237 | nt.assert_equal(ipdir, os.path.join("someplace", ".ipython")) |
|
240 | nt.assert_equal(ipdir, os.path.join("someplace", ".ipython")) | |
238 |
|
241 | |||
239 | # |
|
|||
240 | # Tests for popkey |
|
|||
241 | # |
|
|||
242 |
|
||||
243 | def test_popkey_1(): |
|
|||
244 | """test_popkey_1, Basic usage test of popkey |
|
|||
245 | """ |
|
|||
246 | dct = dict(a=1, b=2, c=3) |
|
|||
247 | nt.assert_equal(genutils.popkey(dct, "a"), 1) |
|
|||
248 | nt.assert_equal(dct, dict(b=2, c=3)) |
|
|||
249 | nt.assert_equal(genutils.popkey(dct, "b"), 2) |
|
|||
250 | nt.assert_equal(dct, dict(c=3)) |
|
|||
251 | nt.assert_equal(genutils.popkey(dct, "c"), 3) |
|
|||
252 | nt.assert_equal(dct, dict()) |
|
|||
253 |
|
||||
254 | def test_popkey_2(): |
|
|||
255 | """test_popkey_2, Test to see that popkey of non occuring keys |
|
|||
256 | generates a KeyError exception |
|
|||
257 | """ |
|
|||
258 | dct = dict(a=1, b=2, c=3) |
|
|||
259 | nt.assert_raises(KeyError, genutils.popkey, dct, "d") |
|
|||
260 |
|
||||
261 | def test_popkey_3(): |
|
|||
262 | """test_popkey_3, Tests to see that popkey calls returns the correct value |
|
|||
263 | and that the key/value was removed from the dict. |
|
|||
264 | """ |
|
|||
265 | dct = dict(a=1, b=2, c=3) |
|
|||
266 | nt.assert_equal(genutils.popkey(dct, "A", 13), 13) |
|
|||
267 | nt.assert_equal(dct, dict(a=1, b=2, c=3)) |
|
|||
268 | nt.assert_equal(genutils.popkey(dct, "B", 14), 14) |
|
|||
269 | nt.assert_equal(dct, dict(a=1, b=2, c=3)) |
|
|||
270 | nt.assert_equal(genutils.popkey(dct, "C", 15), 15) |
|
|||
271 | nt.assert_equal(dct, dict(a=1, b=2, c=3)) |
|
|||
272 | nt.assert_equal(genutils.popkey(dct, "a"), 1) |
|
|||
273 | nt.assert_equal(dct, dict(b=2, c=3)) |
|
|||
274 | nt.assert_equal(genutils.popkey(dct, "b"), 2) |
|
|||
275 | nt.assert_equal(dct, dict(c=3)) |
|
|||
276 | nt.assert_equal(genutils.popkey(dct, "c"), 3) |
|
|||
277 | nt.assert_equal(dct, dict()) |
|
|||
278 |
|
||||
279 |
|
242 | |||
280 | def test_filefind(): |
|
243 | def test_filefind(): | |
281 | """Various tests for filefind""" |
|
244 | """Various tests for filefind""" | |
282 | f = tempfile.NamedTemporaryFile() |
|
245 | f = tempfile.NamedTemporaryFile() | |
283 | print 'fname:',f.name |
|
246 | # print 'fname:',f.name | |
284 |
alt_dirs = |
|
247 | alt_dirs = path.get_ipython_dir() | |
285 |
t = |
|
248 | t = path.filefind(f.name, alt_dirs) | |
286 | print 'found:',t |
|
249 | # print 'found:',t | |
287 |
|
250 | |||
288 |
|
251 | |||
289 | def test_get_ipython_package_dir(): |
|
252 | def test_get_ipython_package_dir(): | |
290 |
ipdir = |
|
253 | ipdir = path.get_ipython_package_dir() | |
291 | nt.assert_true(os.path.isdir(ipdir)) |
|
254 | nt.assert_true(os.path.isdir(ipdir)) | |
292 |
|
255 | |||
293 |
|
256 | |||
294 | def test_tee_simple(): |
|
257 | def test_get_ipython_module_path(): | |
295 | "Very simple check with stdout only" |
|
258 | ipapp_path = path.get_ipython_module_path('IPython.core.ipapp') | |
296 | chan = StringIO() |
|
259 | nt.assert_true(os.path.isfile(ipapp_path)) | |
297 | text = 'Hello' |
|
|||
298 | tee = genutils.Tee(chan, channel='stdout') |
|
|||
299 | print >> chan, text, |
|
|||
300 | nt.assert_equal(chan.getvalue(), text) |
|
|||
301 |
|
||||
302 |
|
260 | |||
303 | class TeeTestCase(dec.ParametricTestCase): |
|
|||
304 |
|
261 | |||
305 | def tchan(self, channel, check='close'): |
|
262 | @dec.skip_if_not_win32 | |
306 | trap = StringIO() |
|
263 | def test_get_long_path_name_win32(): | |
307 | chan = StringIO() |
|
264 | p = path.get_long_path_name('c:\\docume~1') | |
308 | text = 'Hello' |
|
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) |
|
268 | @dec.skip_win32 | |
314 | print >> chan, text, |
|
269 | def test_get_long_path_name(): | |
315 | setattr(sys, channel, std_ori) |
|
270 | p = path.get_long_path_name('/usr/local') | |
316 | trap_val = trap.getvalue() |
|
271 | nt.assert_equals(p,'/usr/local') | |
317 | nt.assert_equals(chan.getvalue(), text) |
|
|||
318 | if check=='close': |
|
|||
319 | tee.close() |
|
|||
320 | else: |
|
|||
321 | del tee |
|
|||
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 | # Imports |
|
14 | # Imports | |
15 | #----------------------------------------------------------------------------- |
|
15 | #----------------------------------------------------------------------------- | |
16 |
|
16 | |||
17 | import os |
|
|||
18 | import sys |
|
17 | import sys | |
19 |
|
18 | |||
20 | import nose.tools as nt |
|
19 | import nose.tools as nt | |
21 |
|
20 | |||
22 |
from IPython.utils.p |
|
21 | from IPython.utils.process import find_cmd, FindCmdError | |
23 | from IPython.testing import decorators as dec |
|
22 | from IPython.testing import decorators as dec | |
24 |
|
23 | |||
25 | #----------------------------------------------------------------------------- |
|
24 | #----------------------------------------------------------------------------- | |
@@ -60,13 +59,4 b' def test_find_cmd_fail():' | |||||
60 | nt.assert_raises(FindCmdError,find_cmd,'asdfasdf') |
|
59 | nt.assert_raises(FindCmdError,find_cmd,'asdfasdf') | |
61 |
|
60 | |||
62 |
|
61 | |||
63 | @dec.skip_if_not_win32 |
|
|||
64 | def test_get_long_path_name_win32(): |
|
|||
65 | p = get_long_path_name('c:\\docume~1') |
|
|||
66 | nt.assert_equals(p,u'c:\\Documents and Settings') |
|
|||
67 |
|
62 | |||
68 |
|
||||
69 | @dec.skip_win32 |
|
|||
70 | def test_get_long_path_name(): |
|
|||
71 | p = get_long_path_name('/usr/local') |
|
|||
72 | nt.assert_equals(p,'/usr/local') |
|
@@ -22,15 +22,11 b' Authors:' | |||||
22 | # Imports |
|
22 | # Imports | |
23 | #----------------------------------------------------------------------------- |
|
23 | #----------------------------------------------------------------------------- | |
24 |
|
24 | |||
25 | import sys |
|
|||
26 | import os |
|
|||
27 |
|
||||
28 |
|
||||
29 | from unittest import TestCase |
|
25 | from unittest import TestCase | |
30 |
|
26 | |||
31 | from IPython.utils.traitlets import ( |
|
27 | from IPython.utils.traitlets import ( | |
32 | HasTraitlets, MetaHasTraitlets, TraitletType, Any, |
|
28 | HasTraitlets, MetaHasTraitlets, TraitletType, Any, | |
33 |
Int, Long, Float, Complex, Str, Unicode, |
|
29 | Int, Long, Float, Complex, Str, Unicode, TraitletError, | |
34 | Undefined, Type, This, Instance |
|
30 | Undefined, Type, This, Instance | |
35 | ) |
|
31 | ) | |
36 |
|
32 |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
@@ -14,13 +14,10 b' Authors' | |||||
14 | #***************************************************************************** |
|
14 | #***************************************************************************** | |
15 |
|
15 | |||
16 | import __builtin__ |
|
16 | import __builtin__ | |
17 | import exceptions |
|
|||
18 | import pdb |
|
|||
19 | import pprint |
|
|||
20 | import re |
|
17 | import re | |
21 | import types |
|
18 | import types | |
22 |
|
19 | |||
23 |
from IPython.utils. |
|
20 | from IPython.utils.dir2 import dir2 | |
24 |
|
21 | |||
25 | def create_typestr2type_dicts(dont_include_in_type2type2str=["lambda"]): |
|
22 | def create_typestr2type_dicts(dont_include_in_type2type2str=["lambda"]): | |
26 | """Return dictionaries mapping lower case typename to type objects, from |
|
23 | """Return dictionaries mapping lower case typename to type objects, from |
@@ -1,5 +1,4 b'' | |||||
1 | include ipython.py |
|
1 | include ipython.py | |
2 | include iptest.py |
|
|||
3 | include setupbase.py |
|
2 | include setupbase.py | |
4 | include setupegg.py |
|
3 | include setupegg.py | |
5 |
|
4 | |||
@@ -9,7 +8,7 b' graft scripts' | |||||
9 | graft IPython/kernel |
|
8 | graft IPython/kernel | |
10 | graft IPython/config |
|
9 | graft IPython/config | |
11 | graft IPython/core |
|
10 | graft IPython/core | |
12 | graft IPython/deathrow |
|
11 | # graft IPython/deathrow | |
13 | graft IPython/external |
|
12 | graft IPython/external | |
14 | graft IPython/frontend |
|
13 | graft IPython/frontend | |
15 | graft IPython/gui |
|
14 | graft IPython/gui |
@@ -1,71 +1,148 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | """Run a Monte-Carlo options pricer in parallel.""" |
|
2 | """Run a Monte-Carlo options pricer in parallel.""" | |
3 |
|
3 | |||
|
4 | #----------------------------------------------------------------------------- | |||
|
5 | # Imports | |||
|
6 | #----------------------------------------------------------------------------- | |||
|
7 | ||||
|
8 | import sys | |||
|
9 | import time | |||
4 | from IPython.kernel import client |
|
10 | from IPython.kernel import client | |
5 | import numpy as np |
|
11 | import numpy as np | |
6 | from mcpricer import price_options |
|
12 | from mcpricer import price_options | |
|
13 | from matplotlib import pyplot as plt | |||
|
14 | ||||
|
15 | #----------------------------------------------------------------------------- | |||
|
16 | # Setup parameters for the run | |||
|
17 | #----------------------------------------------------------------------------- | |||
|
18 | ||||
|
19 | def ask_question(text, the_type, default): | |||
|
20 | s = '%s [%r]: ' % (text, the_type(default)) | |||
|
21 | result = raw_input(s) | |||
|
22 | if result: | |||
|
23 | return the_type(result) | |||
|
24 | else: | |||
|
25 | return the_type(default) | |||
|
26 | ||||
|
27 | cluster_profile = ask_question("Cluster profile", str, "default") | |||
|
28 | price = ask_question("Initial price", float, 100.0) | |||
|
29 | rate = ask_question("Interest rate", float, 0.05) | |||
|
30 | days = ask_question("Days to expiration", int, 260) | |||
|
31 | paths = ask_question("Number of MC paths", int, 10000) | |||
|
32 | n_strikes = ask_question("Number of strike values", int, 5) | |||
|
33 | min_strike = ask_question("Min strike price", float, 90.0) | |||
|
34 | max_strike = ask_question("Max strike price", float, 110.0) | |||
|
35 | n_sigmas = ask_question("Number of volatility values", int, 5) | |||
|
36 | min_sigma = ask_question("Min volatility", float, 0.1) | |||
|
37 | max_sigma = ask_question("Max volatility", float, 0.4) | |||
|
38 | ||||
|
39 | strike_vals = np.linspace(min_strike, max_strike, n_strikes) | |||
|
40 | sigma_vals = np.linspace(min_sigma, max_sigma, n_sigmas) | |||
|
41 | ||||
|
42 | #----------------------------------------------------------------------------- | |||
|
43 | # Setup for parallel calculation | |||
|
44 | #----------------------------------------------------------------------------- | |||
7 |
|
45 | |||
8 | # The MultiEngineClient is used to setup the calculation and works with all |
|
46 | # The MultiEngineClient is used to setup the calculation and works with all | |
9 | # engine. |
|
47 | # engine. | |
10 |
mec = client.MultiEngineClient(profile= |
|
48 | mec = client.MultiEngineClient(profile=cluster_profile) | |
11 |
|
49 | |||
12 | # The TaskClient is an interface to the engines that provides dynamic load |
|
50 | # The TaskClient is an interface to the engines that provides dynamic load | |
13 | # balancing at the expense of not knowing which engine will execute the code. |
|
51 | # balancing at the expense of not knowing which engine will execute the code. | |
14 |
tc = client.TaskClient(profile= |
|
52 | tc = client.TaskClient(profile=cluster_profile) | |
15 |
|
53 | |||
16 | # Initialize the common code on the engines. This Python module has the |
|
54 | # Initialize the common code on the engines. This Python module has the | |
17 | # price_options function that prices the options. |
|
55 | # price_options function that prices the options. | |
18 | mec.run('mcpricer.py') |
|
56 | mec.run('mcpricer.py') | |
19 |
|
57 | |||
20 | # Define the function that will make up our tasks. We basically want to |
|
58 | #----------------------------------------------------------------------------- | |
21 | # call the price_options function with all but two arguments (K, sigma) |
|
59 | # Perform parallel calculation | |
22 | # fixed. |
|
60 | #----------------------------------------------------------------------------- | |
23 | def my_prices(K, sigma): |
|
61 | ||
24 | S = 100.0 |
|
62 | print "Running parallel calculation over strike prices and volatilities..." | |
25 | r = 0.05 |
|
63 | print "Strike prices: ", strike_vals | |
26 | days = 260 |
|
64 | print "Volatilities: ", sigma_vals | |
27 | paths = 100000 |
|
65 | sys.stdout.flush() | |
28 | return price_options(S, K, sigma, r, days, paths) |
|
66 | ||
29 |
|
67 | # Submit tasks to the TaskClient for each (strike, sigma) pair as a MapTask. | ||
30 | # Create arrays of strike prices and volatilities |
|
68 | t1 = time.time() | |
31 | nK = 10 |
|
|||
32 | nsigma = 10 |
|
|||
33 | K_vals = np.linspace(90.0, 100.0, nK) |
|
|||
34 | sigma_vals = np.linspace(0.1, 0.4, nsigma) |
|
|||
35 |
|
||||
36 | # Submit tasks to the TaskClient for each (K, sigma) pair as a MapTask. |
|
|||
37 | # The MapTask simply applies a function (my_prices) to the arguments: |
|
|||
38 | # my_prices(K, sigma) and returns the result. |
|
|||
39 | taskids = [] |
|
69 | taskids = [] | |
40 |
for |
|
70 | for strike in strike_vals: | |
41 | for sigma in sigma_vals: |
|
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 | taskids.append(tc.run(t)) |
|
76 | taskids.append(tc.run(t)) | |
44 |
|
77 | |||
45 | print "Submitted tasks: ", len(taskids) |
|
78 | print "Submitted tasks: ", len(taskids) | |
|
79 | sys.stdout.flush() | |||
46 |
|
80 | |||
47 | # Block until all tasks are completed. |
|
81 | # Block until all tasks are completed. | |
48 | tc.barrier(taskids) |
|
82 | tc.barrier(taskids) | |
|
83 | t2 = time.time() | |||
|
84 | t = t2-t1 | |||
|
85 | ||||
|
86 | print "Parallel calculation completed, time = %s s" % t | |||
|
87 | print "Collecting results..." | |||
49 |
|
88 | |||
50 | # Get the results using TaskClient.get_task_result. |
|
89 | # Get the results using TaskClient.get_task_result. | |
51 | results = [tc.get_task_result(tid) for tid in taskids] |
|
90 | results = [tc.get_task_result(tid) for tid in taskids] | |
52 |
|
91 | |||
53 | # Assemble the result into a structured NumPy array. |
|
92 | # Assemble the result into a structured NumPy array. | |
54 |
prices = np.empty(n |
|
93 | prices = np.empty(n_strikes*n_sigmas, | |
55 | dtype=[('ecall',float),('eput',float),('acall',float),('aput',float)] |
|
94 | dtype=[('ecall',float),('eput',float),('acall',float),('aput',float)] | |
56 | ) |
|
95 | ) | |
|
96 | ||||
57 | for i, price_tuple in enumerate(results): |
|
97 | for i, price_tuple in enumerate(results): | |
58 | prices[i] = price_tuple |
|
98 | prices[i] = price_tuple | |
59 | prices.shape = (nK, nsigma) |
|
|||
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 |
|
114 | plt.figure(1) | |
67 | plt.contourf(sigma_vals, K_vals, prices) |
|
115 | ||
|
116 | plt.subplot(221) | |||
|
117 | plt.contourf(sigma_mesh, strike_mesh, prices['ecall']) | |||
|
118 | plt.axis('tight') | |||
|
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 | plt.colorbar() |
|
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 | plt.xlabel("Volatility") |
|
134 | plt.xlabel("Volatility") | |
71 | plt.ylabel("Strike Price") |
|
135 | plt.ylabel("Strike Price") | |
|
136 | ||||
|
137 | plt.subplot(224) | |||
|
138 | plt.contourf(sigma_mesh, strike_mesh, prices['aput']) | |||
|
139 | plt.axis('tight') | |||
|
140 | plt.colorbar() | |||
|
141 | plt.title("Asian Put") | |||
|
142 | plt.xlabel("Volatility") | |||
|
143 | ||||
|
144 | ||||
|
145 | ||||
|
146 | ||||
|
147 | ||||
|
148 |
@@ -18,7 +18,7 b' overhead of a single task is about 0.001-0.01 seconds.' | |||||
18 | import random, sys |
|
18 | import random, sys | |
19 | from optparse import OptionParser |
|
19 | from optparse import OptionParser | |
20 |
|
20 | |||
21 |
from IPython. |
|
21 | from IPython.utils.timing import time | |
22 | from IPython.kernel import client |
|
22 | from IPython.kernel import client | |
23 |
|
23 | |||
24 | def main(): |
|
24 | def main(): | |
@@ -51,7 +51,7 b' def main():' | |||||
51 | print tc.task_controller |
|
51 | print tc.task_controller | |
52 | rc.block=True |
|
52 | rc.block=True | |
53 | nengines = len(rc.get_ids()) |
|
53 | nengines = len(rc.get_ids()) | |
54 |
rc.execute('from IPython. |
|
54 | rc.execute('from IPython.utils.timing import time') | |
55 |
|
55 | |||
56 | # the jobs should take a random time within a range |
|
56 | # the jobs should take a random time within a range | |
57 | times = [random.random()*(opts.tmax-opts.tmin)+opts.tmin for i in range(opts.n)] |
|
57 | times = [random.random()*(opts.tmax-opts.tmin)+opts.tmin for i in range(opts.n)] |
@@ -245,7 +245,7 b' this directory is determined by the following algorithm:' | |||||
245 |
|
245 | |||
246 | * If the ``--ipython-dir`` command line flag is given, its value is used. |
|
246 | * If the ``--ipython-dir`` command line flag is given, its value is used. | |
247 |
|
247 | |||
248 |
* If not, the value returned by :func:`IPython.utils. |
|
248 | * If not, the value returned by :func:`IPython.utils.path.get_ipython_dir` | |
249 | is used. This function will first look at the :envvar:`IPYTHON_DIR` |
|
249 | is used. This function will first look at the :envvar:`IPYTHON_DIR` | |
250 | environment variable and then default to the directory |
|
250 | environment variable and then default to the directory | |
251 | :file:`$HOME/.ipython`. |
|
251 | :file:`$HOME/.ipython`. |
@@ -41,8 +41,8 b' A bit of Python code::' | |||||
41 |
|
41 | |||
42 | An interactive Python session:: |
|
42 | An interactive Python session:: | |
43 |
|
43 | |||
44 |
>>> from IPython import ge |
|
44 | >>> from IPython.utils.path import get_ipython_dir | |
45 |
>>> ge |
|
45 | >>> get_ipython_dir() | |
46 | '/home/fperez/.ipython' |
|
46 | '/home/fperez/.ipython' | |
47 |
|
47 | |||
48 | An IPython session: |
|
48 | An IPython session: |
@@ -57,3 +57,5 b' methods in :class:`InteractiveShell` that manage code execution::' | |||||
57 | nx.draw_spectral(g, node_size=100, alpha=0.6, node_color='r', |
|
57 | nx.draw_spectral(g, node_size=100, alpha=0.6, node_color='r', | |
58 | font_size=10, node_shape='o') |
|
58 | font_size=10, node_shape='o') | |
59 | plt.show() |
|
59 | plt.show() | |
|
60 | ||||
|
61 |
@@ -244,7 +244,7 b' and an example of ``# all-random``::' | |||||
244 |
|
244 | |||
245 | When writing docstrings, you can use the ``@skip_doctest`` decorator to |
|
245 | When writing docstrings, you can use the ``@skip_doctest`` decorator to | |
246 | indicate that a docstring should *not* be treated as a doctest at all. The |
|
246 | indicate that a docstring should *not* be treated as a doctest at all. The | |
247 | difference betwee ``# all-random`` and ``@skip_doctest`` is that the former |
|
247 | difference between ``# all-random`` and ``@skip_doctest`` is that the former | |
248 | executes the example but ignores output, while the latter doesn't execute any |
|
248 | executes the example but ignores output, while the latter doesn't execute any | |
249 | code. ``@skip_doctest`` should be used for docstrings whose examples are |
|
249 | code. ``@skip_doctest`` should be used for docstrings whose examples are | |
250 | purely informational. |
|
250 | purely informational. |
@@ -166,7 +166,7 b' ipy_user_conf.py.' | |||||
166 | String lists |
|
166 | String lists | |
167 | ============ |
|
167 | ============ | |
168 |
|
168 | |||
169 |
String lists (IPython. |
|
169 | String lists (IPython.utils.text.SList) are handy way to process output | |
170 | from system commands. They are produced by ``var = !cmd`` syntax. |
|
170 | from system commands. They are produced by ``var = !cmd`` syntax. | |
171 |
|
171 | |||
172 | First, we acquire the output of 'ls -l':: |
|
172 | First, we acquire the output of 'ls -l':: |
@@ -8,7 +8,7 b' import re' | |||||
8 | import pydoc |
|
8 | import pydoc | |
9 | from StringIO import StringIO |
|
9 | from StringIO import StringIO | |
10 | from warnings import warn |
|
10 | from warnings import warn | |
11 | 4 |
|
11 | ||
12 | class Reader(object): |
|
12 | class Reader(object): | |
13 | """A line-based string reader. |
|
13 | """A line-based string reader. | |
14 |
|
14 |
@@ -59,12 +59,9 b' Authors' | |||||
59 |
|
59 | |||
60 | # Stdlib |
|
60 | # Stdlib | |
61 | import cStringIO |
|
61 | import cStringIO | |
62 | import imp |
|
|||
63 | import os |
|
62 | import os | |
64 | import re |
|
63 | import re | |
65 | import shutil |
|
|||
66 | import sys |
|
64 | import sys | |
67 | import warnings |
|
|||
68 |
|
65 | |||
69 | # To keep compatibility with various python versions |
|
66 | # To keep compatibility with various python versions | |
70 | try: |
|
67 | try: | |
@@ -80,8 +77,8 b' from docutils.parsers.rst import directives' | |||||
80 | matplotlib.use('Agg') |
|
77 | matplotlib.use('Agg') | |
81 |
|
78 | |||
82 | # Our own |
|
79 | # Our own | |
83 |
from IPython import Config, I |
|
80 | from IPython import Config, InteractiveShell | |
84 |
from IPython.utils. |
|
81 | from IPython.utils.io import Term | |
85 |
|
82 | |||
86 | #----------------------------------------------------------------------------- |
|
83 | #----------------------------------------------------------------------------- | |
87 | # Globals |
|
84 | # Globals | |
@@ -208,8 +205,9 b' class EmbeddedSphinxShell(object):' | |||||
208 | Term.cerr = self.cout |
|
205 | Term.cerr = self.cout | |
209 |
|
206 | |||
210 | # For debugging, so we can see normal output, use this: |
|
207 | # For debugging, so we can see normal output, use this: | |
211 | #Term.cout = genutils.Tee(self.cout, channel='stdout') # dbg |
|
208 | # from IPython.utils.io import Tee | |
212 |
#Term.c |
|
209 | #Term.cout = Tee(self.cout, channel='stdout') # dbg | |
|
210 | #Term.cerr = Tee(self.cout, channel='stderr') # dbg | |||
213 |
|
211 | |||
214 | # Create config object for IPython |
|
212 | # Create config object for IPython | |
215 | config = Config() |
|
213 | config = Config() | |
@@ -221,15 +219,11 b' class EmbeddedSphinxShell(object):' | |||||
221 | config.InteractiveShell.autoindent = False |
|
219 | config.InteractiveShell.autoindent = False | |
222 | config.InteractiveShell.colors = 'NoColor' |
|
220 | config.InteractiveShell.colors = 'NoColor' | |
223 |
|
221 | |||
224 | # Merge global config which can be used to override. |
|
|||
225 | config._merge(CONFIG) |
|
|||
226 |
|
||||
227 | # Create and initialize ipython, but don't start its mainloop |
|
222 | # Create and initialize ipython, but don't start its mainloop | |
228 |
IP = I |
|
223 | IP = InteractiveShell(parent=None, config=config) | |
229 | IP.initialize() |
|
|||
230 |
|
224 | |||
231 | # Store a few parts of IPython we'll need. |
|
225 | # Store a few parts of IPython we'll need. | |
232 |
self.IP = IP |
|
226 | self.IP = IP | |
233 | self.user_ns = self.IP.user_ns |
|
227 | self.user_ns = self.IP.user_ns | |
234 | self.user_global_ns = self.IP.user_global_ns |
|
228 | self.user_global_ns = self.IP.user_global_ns | |
235 |
|
229 |
@@ -47,7 +47,7 b" if os.path.exists('MANIFEST'): os.remove('MANIFEST')" | |||||
47 | from distutils.core import setup |
|
47 | from distutils.core import setup | |
48 |
|
48 | |||
49 | # Our own imports |
|
49 | # Our own imports | |
50 |
from IPython.utils. |
|
50 | from IPython.utils.path import target_update | |
51 |
|
51 | |||
52 | from setupbase import ( |
|
52 | from setupbase import ( | |
53 | setup_args, |
|
53 | setup_args, |
@@ -1,62 +0,0 b'' | |||||
1 | from os.path import join |
|
|||
2 | pjoin = join |
|
|||
3 |
|
||||
4 | from IPython.utils.genutils import get_ipython_dir, get_security_dir |
|
|||
5 | security_dir = get_security_dir() |
|
|||
6 |
|
||||
7 |
|
||||
8 | ENGINE_LOGFILE = '' |
|
|||
9 |
|
||||
10 | ENGINE_FURL_FILE = 'ipcontroller-engine.furl' |
|
|||
11 |
|
||||
12 | MPI_CONFIG_MPI4PY = """from mpi4py import MPI as mpi |
|
|||
13 | mpi.size = mpi.COMM_WORLD.Get_size() |
|
|||
14 | mpi.rank = mpi.COMM_WORLD.Get_rank() |
|
|||
15 | """ |
|
|||
16 |
|
||||
17 | MPI_CONFIG_PYTRILINOS = """from PyTrilinos import Epetra |
|
|||
18 | class SimpleStruct: |
|
|||
19 | pass |
|
|||
20 | mpi = SimpleStruct() |
|
|||
21 | mpi.rank = 0 |
|
|||
22 | mpi.size = 0 |
|
|||
23 | """ |
|
|||
24 |
|
||||
25 | MPI_DEFAULT = '' |
|
|||
26 |
|
||||
27 | CONTROLLER_LOGFILE = '' |
|
|||
28 | CONTROLLER_IMPORT_STATEMENT = '' |
|
|||
29 | CONTROLLER_REUSE_FURLS = False |
|
|||
30 |
|
||||
31 | ENGINE_TUB_IP = '' |
|
|||
32 | ENGINE_TUB_PORT = 0 |
|
|||
33 | ENGINE_TUB_LOCATION = '' |
|
|||
34 | ENGINE_TUB_SECURE = True |
|
|||
35 | ENGINE_TUB_CERT_FILE = 'ipcontroller-engine.pem' |
|
|||
36 | ENGINE_FC_INTERFACE = 'IPython.kernel.enginefc.IFCControllerBase' |
|
|||
37 | ENGINE_FURL_FILE = 'ipcontroller-engine.furl' |
|
|||
38 |
|
||||
39 | CONTROLLER_INTERFACES = dict( |
|
|||
40 | TASK = dict( |
|
|||
41 | CONTROLLER_INTERFACE = 'IPython.kernel.task.ITaskController', |
|
|||
42 | FC_INTERFACE = 'IPython.kernel.taskfc.IFCTaskController', |
|
|||
43 | FURL_FILE = pjoin(security_dir, 'ipcontroller-tc.furl') |
|
|||
44 | ), |
|
|||
45 | MULTIENGINE = dict( |
|
|||
46 | CONTROLLER_INTERFACE = 'IPython.kernel.multiengine.IMultiEngine', |
|
|||
47 | FC_INTERFACE = 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine', |
|
|||
48 | FURL_FILE = pjoin(security_dir, 'ipcontroller-mec.furl') |
|
|||
49 | ) |
|
|||
50 | ) |
|
|||
51 |
|
||||
52 | CLIENT_TUB_IP = '' |
|
|||
53 | CLIENT_TUB_PORT = 0 |
|
|||
54 | CLIENT_TUB_LOCATION = '' |
|
|||
55 | CLIENT_TUB_SECURE = True |
|
|||
56 | CLIENT_TUB_CERT_FILE = 'ipcontroller-client.pem' |
|
|||
57 |
|
||||
58 | CLIENT_INTERFACES = dict( |
|
|||
59 | TASK = dict(FURL_FILE = 'ipcontroller-tc.furl'), |
|
|||
60 | MULTIENGINE = dict(FURLFILE='ipcontroller-mec.furl') |
|
|||
61 | ) |
|
|||
62 |
|
@@ -1,141 +0,0 b'' | |||||
1 | # encoding: utf-8 |
|
|||
2 | # -*- test-case-name: IPython.kernel.test.test_contexts -*- |
|
|||
3 | """Context managers for IPython. |
|
|||
4 |
|
||||
5 | Python 2.5 introduced the `with` statement, which is based on the context |
|
|||
6 | manager protocol. This module offers a few context managers for common cases, |
|
|||
7 | which can also be useful as templates for writing new, application-specific |
|
|||
8 | managers. |
|
|||
9 | """ |
|
|||
10 |
|
||||
11 | __docformat__ = "restructuredtext en" |
|
|||
12 |
|
||||
13 | #------------------------------------------------------------------------------- |
|
|||
14 | # Copyright (C) 2008 The IPython Development Team |
|
|||
15 | # |
|
|||
16 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
17 | # the file COPYING, distributed as part of this software. |
|
|||
18 | #------------------------------------------------------------------------------- |
|
|||
19 |
|
||||
20 | #------------------------------------------------------------------------------- |
|
|||
21 | # Imports |
|
|||
22 | #------------------------------------------------------------------------------- |
|
|||
23 |
|
||||
24 | import linecache |
|
|||
25 | import sys |
|
|||
26 |
|
||||
27 | from twisted.internet.error import ConnectionRefusedError |
|
|||
28 |
|
||||
29 | from IPython.core.ultratb import _fixed_getinnerframes, findsource |
|
|||
30 | from IPython.core import ipapi |
|
|||
31 |
|
||||
32 | from IPython.kernel import error |
|
|||
33 |
|
||||
34 | #--------------------------------------------------------------------------- |
|
|||
35 | # Utility functions needed by all context managers. |
|
|||
36 | #--------------------------------------------------------------------------- |
|
|||
37 |
|
||||
38 | def remote(): |
|
|||
39 | """Raises a special exception meant to be caught by context managers. |
|
|||
40 | """ |
|
|||
41 | m = 'Special exception to stop local execution of parallel code.' |
|
|||
42 | raise error.StopLocalExecution(m) |
|
|||
43 |
|
||||
44 |
|
||||
45 | def strip_whitespace(source,require_remote=True): |
|
|||
46 | """strip leading whitespace from input source. |
|
|||
47 |
|
||||
48 | :Parameters: |
|
|||
49 |
|
||||
50 | """ |
|
|||
51 | remote_mark = 'remote()' |
|
|||
52 | # Expand tabs to avoid any confusion. |
|
|||
53 | wsource = [l.expandtabs(4) for l in source] |
|
|||
54 | # Detect the indentation level |
|
|||
55 | done = False |
|
|||
56 | for line in wsource: |
|
|||
57 | if line.isspace(): |
|
|||
58 | continue |
|
|||
59 | for col,char in enumerate(line): |
|
|||
60 | if char != ' ': |
|
|||
61 | done = True |
|
|||
62 | break |
|
|||
63 | if done: |
|
|||
64 | break |
|
|||
65 | # Now we know how much leading space there is in the code. Next, we |
|
|||
66 | # extract up to the first line that has less indentation. |
|
|||
67 | # WARNINGS: we skip comments that may be misindented, but we do NOT yet |
|
|||
68 | # detect triple quoted strings that may have flush left text. |
|
|||
69 | for lno,line in enumerate(wsource): |
|
|||
70 | lead = line[:col] |
|
|||
71 | if lead.isspace(): |
|
|||
72 | continue |
|
|||
73 | else: |
|
|||
74 | if not lead.lstrip().startswith('#'): |
|
|||
75 | break |
|
|||
76 | # The real 'with' source is up to lno |
|
|||
77 | src_lines = [l[col:] for l in wsource[:lno+1]] |
|
|||
78 |
|
||||
79 | # Finally, check that the source's first non-comment line begins with the |
|
|||
80 | # special call 'remote()' |
|
|||
81 | if require_remote: |
|
|||
82 | for nline,line in enumerate(src_lines): |
|
|||
83 | if line.isspace() or line.startswith('#'): |
|
|||
84 | continue |
|
|||
85 | if line.startswith(remote_mark): |
|
|||
86 | break |
|
|||
87 | else: |
|
|||
88 | raise ValueError('%s call missing at the start of code' % |
|
|||
89 | remote_mark) |
|
|||
90 | out_lines = src_lines[nline+1:] |
|
|||
91 | else: |
|
|||
92 | # If the user specified that the remote() call wasn't mandatory |
|
|||
93 | out_lines = src_lines |
|
|||
94 |
|
||||
95 | # src = ''.join(out_lines) # dbg |
|
|||
96 | #print 'SRC:\n<<<<<<<>>>>>>>\n%s<<<<<>>>>>>' % src # dbg |
|
|||
97 | return ''.join(out_lines) |
|
|||
98 |
|
||||
99 | class RemoteContextBase(object): |
|
|||
100 | def __init__(self): |
|
|||
101 | self.ip = ipapi.get() |
|
|||
102 |
|
||||
103 | def _findsource_file(self,f): |
|
|||
104 | linecache.checkcache() |
|
|||
105 | s = findsource(f.f_code) |
|
|||
106 | lnum = f.f_lineno |
|
|||
107 | wsource = s[0][f.f_lineno:] |
|
|||
108 | return strip_whitespace(wsource) |
|
|||
109 |
|
||||
110 | def _findsource_ipython(self,f): |
|
|||
111 | from IPython.core import ipapi |
|
|||
112 | self.ip = ipapi.get() |
|
|||
113 | buf = self.ip.input_hist_raw[-1].splitlines()[1:] |
|
|||
114 | wsource = [l+'\n' for l in buf ] |
|
|||
115 |
|
||||
116 | return strip_whitespace(wsource) |
|
|||
117 |
|
||||
118 | def findsource(self,frame): |
|
|||
119 | local_ns = frame.f_locals |
|
|||
120 | global_ns = frame.f_globals |
|
|||
121 | if frame.f_code.co_filename == '<ipython console>': |
|
|||
122 | src = self._findsource_ipython(frame) |
|
|||
123 | else: |
|
|||
124 | src = self._findsource_file(frame) |
|
|||
125 | return src |
|
|||
126 |
|
||||
127 | def __enter__(self): |
|
|||
128 | raise NotImplementedError |
|
|||
129 |
|
||||
130 | def __exit__ (self, etype, value, tb): |
|
|||
131 | if issubclass(etype,error.StopLocalExecution): |
|
|||
132 | return True |
|
|||
133 |
|
||||
134 | class RemoteMultiEngine(RemoteContextBase): |
|
|||
135 | def __init__(self,mec): |
|
|||
136 | self.mec = mec |
|
|||
137 | RemoteContextBase.__init__(self) |
|
|||
138 |
|
||||
139 | def __enter__(self): |
|
|||
140 | src = self.findsource(sys._getframe(1)) |
|
|||
141 | return self.mec.execute(src) |
|
@@ -1,46 +0,0 b'' | |||||
1 | # Tell nose to skip this module |
|
|||
2 | __test__ = {} |
|
|||
3 |
|
||||
4 | #from __future__ import with_statement |
|
|||
5 |
|
||||
6 | # XXX This file is currently disabled to preserve 2.4 compatibility. |
|
|||
7 |
|
||||
8 | #def test_simple(): |
|
|||
9 | if 0: |
|
|||
10 |
|
||||
11 | # XXX - for now, we need a running cluster to be started separately. The |
|
|||
12 | # daemon work is almost finished, and will make much of this unnecessary. |
|
|||
13 | from IPython.kernel import client |
|
|||
14 | mec = client.MultiEngineClient(('127.0.0.1',10105)) |
|
|||
15 |
|
||||
16 | try: |
|
|||
17 | mec.get_ids() |
|
|||
18 | except ConnectionRefusedError: |
|
|||
19 | import os, time |
|
|||
20 | os.system('ipcluster -n 2 &') |
|
|||
21 | time.sleep(2) |
|
|||
22 | mec = client.MultiEngineClient(('127.0.0.1',10105)) |
|
|||
23 |
|
||||
24 | mec.block = False |
|
|||
25 |
|
||||
26 | import itertools |
|
|||
27 | c = itertools.count() |
|
|||
28 |
|
||||
29 | parallel = RemoteMultiEngine(mec) |
|
|||
30 |
|
||||
31 | mec.pushAll() |
|
|||
32 |
|
||||
33 | ## with parallel as pr: |
|
|||
34 | ## # A comment |
|
|||
35 | ## remote() # this means the code below only runs remotely |
|
|||
36 | ## print 'Hello remote world' |
|
|||
37 | ## x = range(10) |
|
|||
38 | ## # Comments are OK |
|
|||
39 | ## # Even misindented. |
|
|||
40 | ## y = x+1 |
|
|||
41 |
|
||||
42 |
|
||||
43 | ## with pfor('i',sequence) as pr: |
|
|||
44 | ## print x[i] |
|
|||
45 |
|
||||
46 | print pr.x + pr.y |
|
This diff has been collapsed as it changes many lines, (690 lines changed) Show them Hide them | |||||
@@ -1,690 +0,0 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
|||
2 | """DPyGetOpt -- Demiurge Python GetOptions Module |
|
|||
3 |
|
||||
4 | This module is modeled after perl's Getopt::Long module-- which |
|
|||
5 | is, in turn, modeled after GNU's extended getopt() function. |
|
|||
6 |
|
||||
7 | Upon instantiation, the option specification should be a sequence |
|
|||
8 | (list) of option definitions. |
|
|||
9 |
|
||||
10 | Options that take no arguments should simply contain the name of |
|
|||
11 | the option. If a ! is post-pended, the option can be negated by |
|
|||
12 | prepending 'no'; ie 'debug!' specifies that -debug and -nodebug |
|
|||
13 | should be accepted. |
|
|||
14 |
|
||||
15 | Mandatory arguments to options are specified using a postpended |
|
|||
16 | '=' + a type specifier. '=s' specifies a mandatory string |
|
|||
17 | argument, '=i' specifies a mandatory integer argument, and '=f' |
|
|||
18 | specifies a mandatory real number. In all cases, the '=' can be |
|
|||
19 | substituted with ':' to specify that the argument is optional. |
|
|||
20 |
|
||||
21 | Dashes '-' in option names are allowed. |
|
|||
22 |
|
||||
23 | If an option has the character '@' postpended (after the |
|
|||
24 | argumentation specification), it can appear multiple times within |
|
|||
25 | each argument list that is processed. The results will be stored |
|
|||
26 | in a list. |
|
|||
27 |
|
||||
28 | The option name can actually be a list of names separated by '|' |
|
|||
29 | characters; ie-- 'foo|bar|baz=f@' specifies that all -foo, -bar, |
|
|||
30 | and -baz options that appear on within the parsed argument list |
|
|||
31 | must have a real number argument and that the accumulated list |
|
|||
32 | of values will be available under the name 'foo' |
|
|||
33 | """ |
|
|||
34 |
|
||||
35 | #***************************************************************************** |
|
|||
36 | # |
|
|||
37 | # Copyright (c) 2001 Bill Bumgarner <bbum@friday.com> |
|
|||
38 | # |
|
|||
39 | # |
|
|||
40 | # Published under the terms of the MIT license, hereby reproduced: |
|
|||
41 | # |
|
|||
42 | # Permission is hereby granted, free of charge, to any person obtaining a copy |
|
|||
43 | # of this software and associated documentation files (the "Software"), to |
|
|||
44 | # deal in the Software without restriction, including without limitation the |
|
|||
45 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|
|||
46 | # sell copies of the Software, and to permit persons to whom the Software is |
|
|||
47 | # furnished to do so, subject to the following conditions: |
|
|||
48 | # |
|
|||
49 | # The above copyright notice and this permission notice shall be included in |
|
|||
50 | # all copies or substantial portions of the Software. |
|
|||
51 | # |
|
|||
52 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
|||
53 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
|||
54 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
|||
55 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
|||
56 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
|||
57 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|
|||
58 | # IN THE SOFTWARE. |
|
|||
59 | # |
|
|||
60 | #***************************************************************************** |
|
|||
61 |
|
||||
62 | __author__ = 'Bill Bumgarner <bbum@friday.com>' |
|
|||
63 | __license__ = 'MIT' |
|
|||
64 | __version__ = '1.2' |
|
|||
65 |
|
||||
66 | # Modified to use re instead of regex and regsub modules. |
|
|||
67 | # 2001/5/7, Jonathan Hogg <jonathan@onegoodidea.com> |
|
|||
68 |
|
||||
69 | import re |
|
|||
70 | import string |
|
|||
71 | import sys |
|
|||
72 | import types |
|
|||
73 |
|
||||
74 | class Error(Exception): |
|
|||
75 | """Base class for exceptions in the DPyGetOpt module.""" |
|
|||
76 |
|
||||
77 | class ArgumentError(Error): |
|
|||
78 | """Exception indicating an error in the arguments passed to |
|
|||
79 | DPyGetOpt.processArguments.""" |
|
|||
80 |
|
||||
81 | class SpecificationError(Error): |
|
|||
82 | """Exception indicating an error with an option specification.""" |
|
|||
83 |
|
||||
84 | class TerminationError(Error): |
|
|||
85 | """Exception indicating an error with an option processing terminator.""" |
|
|||
86 |
|
||||
87 | specificationExpr = re.compile('(?P<required>.)(?P<type>.)(?P<multi>@?)') |
|
|||
88 |
|
||||
89 | ArgRequired = 'Requires an Argument' |
|
|||
90 | ArgOptional = 'Argument Optional' |
|
|||
91 |
|
||||
92 | # The types modules is not used for these identifiers because there |
|
|||
93 | # is no identifier for 'boolean' or 'generic' |
|
|||
94 | StringArgType = 'String Argument Type' |
|
|||
95 | IntegerArgType = 'Integer Argument Type' |
|
|||
96 | RealArgType = 'Real Argument Type' |
|
|||
97 | BooleanArgType = 'Boolean Argument Type' |
|
|||
98 | GenericArgType = 'Generic Argument Type' |
|
|||
99 |
|
||||
100 | # dictionary of conversion functions-- boolean and generic options |
|
|||
101 | # do not accept arguments and do not need conversion functions; |
|
|||
102 | # the identity function is used purely for convenience. |
|
|||
103 | ConversionFunctions = { |
|
|||
104 | StringArgType : lambda x: x, |
|
|||
105 | IntegerArgType : string.atoi, |
|
|||
106 | RealArgType : string.atof, |
|
|||
107 | BooleanArgType : lambda x: x, |
|
|||
108 | GenericArgType : lambda x: x, |
|
|||
109 | } |
|
|||
110 |
|
||||
111 | class DPyGetOpt: |
|
|||
112 |
|
||||
113 | def __init__(self, spec = None, terminators = ['--']): |
|
|||
114 | """ |
|
|||
115 | Declare and intialize instance variables |
|
|||
116 |
|
||||
117 | Yes, declaration is not necessary... but one of the things |
|
|||
118 | I sorely miss from C/Obj-C is the concept of having an |
|
|||
119 | interface definition that clearly declares all instance |
|
|||
120 | variables and methods without providing any implementation |
|
|||
121 | details. it is a useful reference! |
|
|||
122 |
|
||||
123 | all instance variables are initialized to 0/Null/None of |
|
|||
124 | the appropriate type-- not even the default value... |
|
|||
125 | """ |
|
|||
126 |
|
||||
127 | # sys.stderr.write(string.join(spec) + "\n") |
|
|||
128 |
|
||||
129 | self.allowAbbreviations = 1 # boolean, 1 if abbreviations will |
|
|||
130 | # be expanded |
|
|||
131 | self.freeValues = [] # list, contains free values |
|
|||
132 | self.ignoreCase = 0 # boolean, YES if ignoring case |
|
|||
133 | self.needsParse = 0 # boolean, YES if need to reparse parameter spec |
|
|||
134 | self.optionNames = {} # dict, all option names-- value is index of tuple |
|
|||
135 | self.optionStartExpr = None # regexp defining the start of an option (ie; '-', '--') |
|
|||
136 | self.optionTuples = [] # list o' tuples containing defn of options AND aliases |
|
|||
137 | self.optionValues = {} # dict, option names (after alias expansion) -> option value(s) |
|
|||
138 | self.orderMixed = 0 # boolean, YES if options can be mixed with args |
|
|||
139 | self.posixCompliance = 0 # boolean, YES indicates posix like behaviour |
|
|||
140 | self.spec = [] # list, raw specs (in case it must be reparsed) |
|
|||
141 | self.terminators = terminators # list, strings that terminate argument processing |
|
|||
142 | self.termValues = [] # list, values after terminator |
|
|||
143 | self.terminator = None # full name of terminator that ended |
|
|||
144 | # option processing |
|
|||
145 |
|
||||
146 | # set up defaults |
|
|||
147 | self.setPosixCompliance() |
|
|||
148 | self.setIgnoreCase() |
|
|||
149 | self.setAllowAbbreviations() |
|
|||
150 |
|
||||
151 | # parse spec-- if present |
|
|||
152 | if spec: |
|
|||
153 | self.parseConfiguration(spec) |
|
|||
154 |
|
||||
155 | def setPosixCompliance(self, aFlag = 0): |
|
|||
156 | """ |
|
|||
157 | Enables and disables posix compliance. |
|
|||
158 |
|
||||
159 | When enabled, '+' can be used as an option prefix and free |
|
|||
160 | values can be mixed with options. |
|
|||
161 | """ |
|
|||
162 | self.posixCompliance = aFlag |
|
|||
163 | self.needsParse = 1 |
|
|||
164 |
|
||||
165 | if self.posixCompliance: |
|
|||
166 | self.optionStartExpr = re.compile('(--|-)(?P<option>[A-Za-z0-9_-]+)(?P<arg>=.*)?') |
|
|||
167 | self.orderMixed = 0 |
|
|||
168 | else: |
|
|||
169 | self.optionStartExpr = re.compile('(--|-|\+)(?P<option>[A-Za-z0-9_-]+)(?P<arg>=.*)?') |
|
|||
170 | self.orderMixed = 1 |
|
|||
171 |
|
||||
172 | def isPosixCompliant(self): |
|
|||
173 | """ |
|
|||
174 | Returns the value of the posix compliance flag. |
|
|||
175 | """ |
|
|||
176 | return self.posixCompliance |
|
|||
177 |
|
||||
178 | def setIgnoreCase(self, aFlag = 1): |
|
|||
179 | """ |
|
|||
180 | Enables and disables ignoring case during option processing. |
|
|||
181 | """ |
|
|||
182 | self.needsParse = 1 |
|
|||
183 | self.ignoreCase = aFlag |
|
|||
184 |
|
||||
185 | def ignoreCase(self): |
|
|||
186 | """ |
|
|||
187 | Returns 1 if the option processor will ignore case when |
|
|||
188 | processing options. |
|
|||
189 | """ |
|
|||
190 | return self.ignoreCase |
|
|||
191 |
|
||||
192 | def setAllowAbbreviations(self, aFlag = 1): |
|
|||
193 | """ |
|
|||
194 | Enables and disables the expansion of abbreviations during |
|
|||
195 | option processing. |
|
|||
196 | """ |
|
|||
197 | self.allowAbbreviations = aFlag |
|
|||
198 |
|
||||
199 | def willAllowAbbreviations(self): |
|
|||
200 | """ |
|
|||
201 | Returns 1 if abbreviated options will be automatically |
|
|||
202 | expanded to the non-abbreviated form (instead of causing an |
|
|||
203 | unrecognized option error). |
|
|||
204 | """ |
|
|||
205 | return self.allowAbbreviations |
|
|||
206 |
|
||||
207 | def addTerminator(self, newTerm): |
|
|||
208 | """ |
|
|||
209 | Adds newTerm as terminator of option processing. |
|
|||
210 |
|
||||
211 | Whenever the option processor encounters one of the terminators |
|
|||
212 | during option processing, the processing of options terminates |
|
|||
213 | immediately, all remaining options are stored in the termValues |
|
|||
214 | instance variable and the full name of the terminator is stored |
|
|||
215 | in the terminator instance variable. |
|
|||
216 | """ |
|
|||
217 | self.terminators = self.terminators + [newTerm] |
|
|||
218 |
|
||||
219 | def _addOption(self, oTuple): |
|
|||
220 | """ |
|
|||
221 | Adds the option described by oTuple (name, (type, mode, |
|
|||
222 | default), alias) to optionTuples. Adds index keyed under name |
|
|||
223 | to optionNames. Raises SpecificationError if name already in |
|
|||
224 | optionNames |
|
|||
225 | """ |
|
|||
226 | (name, (type, mode, default, multi), realName) = oTuple |
|
|||
227 |
|
||||
228 | # verify name and add to option names dictionary |
|
|||
229 | if self.optionNames.has_key(name): |
|
|||
230 | if realName: |
|
|||
231 | raise SpecificationError('Alias \'' + name + '\' for \'' + |
|
|||
232 | realName + |
|
|||
233 | '\' already used for another option or alias.') |
|
|||
234 | else: |
|
|||
235 | raise SpecificationError('Option named \'' + name + |
|
|||
236 | '\' specified more than once. Specification: ' |
|
|||
237 | + option) |
|
|||
238 |
|
||||
239 | # validated. add to optionNames |
|
|||
240 | self.optionNames[name] = self.tupleIndex |
|
|||
241 | self.tupleIndex = self.tupleIndex + 1 |
|
|||
242 |
|
||||
243 | # add to optionTuples |
|
|||
244 | self.optionTuples = self.optionTuples + [oTuple] |
|
|||
245 |
|
||||
246 | # if type is boolean, add negation |
|
|||
247 | if type == BooleanArgType: |
|
|||
248 | alias = 'no' + name |
|
|||
249 | specTuple = (type, mode, 0, multi) |
|
|||
250 | oTuple = (alias, specTuple, name) |
|
|||
251 |
|
||||
252 | # verify name and add to option names dictionary |
|
|||
253 | if self.optionNames.has_key(alias): |
|
|||
254 | if realName: |
|
|||
255 | raise SpecificationError('Negated alias \'' + name + |
|
|||
256 | '\' for \'' + realName + |
|
|||
257 | '\' already used for another option or alias.') |
|
|||
258 | else: |
|
|||
259 | raise SpecificationError('Negated option named \'' + name + |
|
|||
260 | '\' specified more than once. Specification: ' |
|
|||
261 | + option) |
|
|||
262 |
|
||||
263 | # validated. add to optionNames |
|
|||
264 | self.optionNames[alias] = self.tupleIndex |
|
|||
265 | self.tupleIndex = self.tupleIndex + 1 |
|
|||
266 |
|
||||
267 | # add to optionTuples |
|
|||
268 | self.optionTuples = self.optionTuples + [oTuple] |
|
|||
269 |
|
||||
270 | def addOptionConfigurationTuple(self, oTuple): |
|
|||
271 | (name, argSpec, realName) = oTuple |
|
|||
272 | if self.ignoreCase: |
|
|||
273 | name = string.lower(name) |
|
|||
274 | if realName: |
|
|||
275 | realName = string.lower(realName) |
|
|||
276 | else: |
|
|||
277 | realName = name |
|
|||
278 |
|
||||
279 | oTuple = (name, argSpec, realName) |
|
|||
280 |
|
||||
281 | # add option |
|
|||
282 | self._addOption(oTuple) |
|
|||
283 |
|
||||
284 | def addOptionConfigurationTuples(self, oTuple): |
|
|||
285 | if type(oTuple) is ListType: |
|
|||
286 | for t in oTuple: |
|
|||
287 | self.addOptionConfigurationTuple(t) |
|
|||
288 | else: |
|
|||
289 | self.addOptionConfigurationTuple(oTuple) |
|
|||
290 |
|
||||
291 | def parseConfiguration(self, spec): |
|
|||
292 | # destroy previous stored information + store raw spec |
|
|||
293 | self.spec = spec |
|
|||
294 | self.optionTuples = [] |
|
|||
295 | self.optionNames = {} |
|
|||
296 | self.tupleIndex = 0 |
|
|||
297 |
|
||||
298 | tupleIndex = 0 |
|
|||
299 |
|
||||
300 | # create some regex's for parsing each spec |
|
|||
301 | splitExpr = \ |
|
|||
302 | re.compile('(?P<names>\w+[-A-Za-z0-9|]*)?(?P<spec>!|[=:][infs]@?)?') |
|
|||
303 | for option in spec: |
|
|||
304 | # push to lower case (does not negatively affect |
|
|||
305 | # specification) |
|
|||
306 | if self.ignoreCase: |
|
|||
307 | option = string.lower(option) |
|
|||
308 |
|
||||
309 | # break into names, specification |
|
|||
310 | match = splitExpr.match(option) |
|
|||
311 | if match is None: |
|
|||
312 | raise SpecificationError('Invalid specification {' + option + |
|
|||
313 | '}') |
|
|||
314 |
|
||||
315 | names = match.group('names') |
|
|||
316 | specification = match.group('spec') |
|
|||
317 |
|
||||
318 | # break name into name, aliases |
|
|||
319 | nlist = string.split(names, '|') |
|
|||
320 |
|
||||
321 | # get name |
|
|||
322 | name = nlist[0] |
|
|||
323 | aliases = nlist[1:] |
|
|||
324 |
|
||||
325 | # specificationExpr = regex.symcomp('\(<required>.\)\(<type>.\)\(<multi>@?\)') |
|
|||
326 | if not specification: |
|
|||
327 | #spec tuple is ('type', 'arg mode', 'default value', 'multiple') |
|
|||
328 | argType = GenericArgType |
|
|||
329 | argMode = None |
|
|||
330 | argDefault = 1 |
|
|||
331 | argMultiple = 0 |
|
|||
332 | elif specification == '!': |
|
|||
333 | argType = BooleanArgType |
|
|||
334 | argMode = None |
|
|||
335 | argDefault = 1 |
|
|||
336 | argMultiple = 0 |
|
|||
337 | else: |
|
|||
338 | # parse |
|
|||
339 | match = specificationExpr.match(specification) |
|
|||
340 | if match is None: |
|
|||
341 | # failed to parse, die |
|
|||
342 | raise SpecificationError('Invalid configuration for option \'' |
|
|||
343 | + option + '\'') |
|
|||
344 |
|
||||
345 | # determine mode |
|
|||
346 | required = match.group('required') |
|
|||
347 | if required == '=': |
|
|||
348 | argMode = ArgRequired |
|
|||
349 | elif required == ':': |
|
|||
350 | argMode = ArgOptional |
|
|||
351 | else: |
|
|||
352 | raise SpecificationError('Unknown requirement configuration \'' |
|
|||
353 | + required + '\'') |
|
|||
354 |
|
||||
355 | # determine type |
|
|||
356 | type = match.group('type') |
|
|||
357 | if type == 's': |
|
|||
358 | argType = StringArgType |
|
|||
359 | argDefault = '' |
|
|||
360 | elif type == 'i': |
|
|||
361 | argType = IntegerArgType |
|
|||
362 | argDefault = 1 |
|
|||
363 | elif type == 'f' or type == 'n': |
|
|||
364 | argType = RealArgType |
|
|||
365 | argDefault = 1 |
|
|||
366 | else: |
|
|||
367 | raise SpecificationError('Unknown type specifier \'' + |
|
|||
368 | type + '\'') |
|
|||
369 |
|
||||
370 | # determine quantity |
|
|||
371 | if match.group('multi') == '@': |
|
|||
372 | argMultiple = 1 |
|
|||
373 | else: |
|
|||
374 | argMultiple = 0 |
|
|||
375 | ## end else (of not specification) |
|
|||
376 |
|
||||
377 | # construct specification tuple |
|
|||
378 | specTuple = (argType, argMode, argDefault, argMultiple) |
|
|||
379 |
|
||||
380 | # add the option-- option tuple is (name, specTuple, real name) |
|
|||
381 | oTuple = (name, specTuple, name) |
|
|||
382 | self._addOption(oTuple) |
|
|||
383 |
|
||||
384 | for alias in aliases: |
|
|||
385 | # drop to all lower (if configured to do so) |
|
|||
386 | if self.ignoreCase: |
|
|||
387 | alias = string.lower(alias) |
|
|||
388 | # create configuration tuple |
|
|||
389 | oTuple = (alias, specTuple, name) |
|
|||
390 | # add |
|
|||
391 | self._addOption(oTuple) |
|
|||
392 |
|
||||
393 | # successfully parsed.... |
|
|||
394 | self.needsParse = 0 |
|
|||
395 |
|
||||
396 | def _getArgTuple(self, argName): |
|
|||
397 | """ |
|
|||
398 | Returns a list containing all the specification tuples that |
|
|||
399 | match argName. If none match, None is returned. If one |
|
|||
400 | matches, a list with one tuple is returned. If more than one |
|
|||
401 | match, a list containing all the tuples that matched is |
|
|||
402 | returned. |
|
|||
403 |
|
||||
404 | In other words, this function does not pass judgement upon the |
|
|||
405 | validity of multiple matches. |
|
|||
406 | """ |
|
|||
407 | # is it in the optionNames dict? |
|
|||
408 |
|
||||
409 | try: |
|
|||
410 | # sys.stderr.write(argName + string.join(self.optionNames.keys()) + "\n") |
|
|||
411 |
|
||||
412 | # yes, get index |
|
|||
413 | tupleIndex = self.optionNames[argName] |
|
|||
414 | # and return tuple as element of list |
|
|||
415 | return [self.optionTuples[tupleIndex]] |
|
|||
416 | except KeyError: |
|
|||
417 | # are abbreviations allowed? |
|
|||
418 | if not self.allowAbbreviations: |
|
|||
419 | # No! terefore, this cannot be valid argument-- nothing found |
|
|||
420 | return None |
|
|||
421 |
|
||||
422 | # argName might be an abbreviation (and, abbreviations must |
|
|||
423 | # be allowed... or this would not have been reached!) |
|
|||
424 |
|
||||
425 | # create regex for argName |
|
|||
426 | argExpr = re.compile('^' + argName) |
|
|||
427 |
|
||||
428 | tuples = filter(lambda x, argExpr=argExpr: argExpr.search(x[0]) is not None, |
|
|||
429 | self.optionTuples) |
|
|||
430 |
|
||||
431 | if not len(tuples): |
|
|||
432 | return None |
|
|||
433 | else: |
|
|||
434 | return tuples |
|
|||
435 |
|
||||
436 | def _isTerminator(self, optionName): |
|
|||
437 | """ |
|
|||
438 | Returns the full name of the terminator if optionName is a valid |
|
|||
439 | terminator. If it is, sets self.terminator to the full name of |
|
|||
440 | the terminator. |
|
|||
441 |
|
||||
442 | If more than one terminator matched, raises a TerminationError with a |
|
|||
443 | string describing the ambiguity. |
|
|||
444 | """ |
|
|||
445 |
|
||||
446 | # sys.stderr.write(optionName + "\n") |
|
|||
447 | # sys.stderr.write(repr(self.terminators)) |
|
|||
448 |
|
||||
449 | if optionName in self.terminators: |
|
|||
450 | self.terminator = optionName |
|
|||
451 | elif not self.allowAbbreviations: |
|
|||
452 | return None |
|
|||
453 |
|
||||
454 | # regex thing in bogus |
|
|||
455 | # termExpr = regex.compile('^' + optionName) |
|
|||
456 |
|
||||
457 | terms = filter(lambda x, on=optionName: string.find(x,on) == 0, self.terminators) |
|
|||
458 |
|
||||
459 | if not len(terms): |
|
|||
460 | return None |
|
|||
461 | elif len(terms) > 1: |
|
|||
462 | raise TerminationError('Ambiguous terminator \'' + optionName + |
|
|||
463 | '\' matches ' + repr(terms)) |
|
|||
464 |
|
||||
465 | self.terminator = terms[0] |
|
|||
466 | return self.terminator |
|
|||
467 |
|
||||
468 | def processArguments(self, args = None): |
|
|||
469 | """ |
|
|||
470 | Processes args, a list of arguments (including options). |
|
|||
471 |
|
||||
472 | If args is the same as sys.argv, automatically trims the first |
|
|||
473 | argument (the executable name/path). |
|
|||
474 |
|
||||
475 | If an exception is not raised, the argument list was parsed |
|
|||
476 | correctly. |
|
|||
477 |
|
||||
478 | Upon successful completion, the freeValues instance variable |
|
|||
479 | will contain all the arguments that were not associated with an |
|
|||
480 | option in the order they were encountered. optionValues is a |
|
|||
481 | dictionary containing the value of each option-- the method |
|
|||
482 | valueForOption() can be used to query this dictionary. |
|
|||
483 | terminator will contain the argument encountered that terminated |
|
|||
484 | option processing (or None, if a terminator was never |
|
|||
485 | encountered) and termValues will contain all of the options that |
|
|||
486 | appeared after the Terminator (or an empty list). |
|
|||
487 | """ |
|
|||
488 |
|
||||
489 | if hasattr(sys, "argv") and args == sys.argv: |
|
|||
490 | args = sys.argv[1:] |
|
|||
491 |
|
||||
492 | max = len(args) # maximum index + 1 |
|
|||
493 | self.freeValues = [] # array to hold return values |
|
|||
494 | self.optionValues= {} |
|
|||
495 | index = 0 # initial index |
|
|||
496 | self.terminator = None |
|
|||
497 | self.termValues = [] |
|
|||
498 |
|
||||
499 | while index < max: |
|
|||
500 | # obtain argument |
|
|||
501 | arg = args[index] |
|
|||
502 | # increment index -- REMEMBER; it is NOW incremented |
|
|||
503 | index = index + 1 |
|
|||
504 |
|
||||
505 | # terminate immediately if option terminator encountered |
|
|||
506 | if self._isTerminator(arg): |
|
|||
507 | self.freeValues = self.freeValues + args[index:] |
|
|||
508 | self.termValues = args[index:] |
|
|||
509 | return |
|
|||
510 |
|
||||
511 | # is this possibly an option? |
|
|||
512 | match = self.optionStartExpr.match(arg) |
|
|||
513 | if match is None: |
|
|||
514 | # not an option-- add to freeValues |
|
|||
515 | self.freeValues = self.freeValues + [arg] |
|
|||
516 | if not self.orderMixed: |
|
|||
517 | # mixing not allowed; add rest of args as freeValues |
|
|||
518 | self.freeValues = self.freeValues + args[index:] |
|
|||
519 | # return to caller |
|
|||
520 | return |
|
|||
521 | else: |
|
|||
522 | continue |
|
|||
523 |
|
||||
524 | # grab name |
|
|||
525 | optName = match.group('option') |
|
|||
526 |
|
||||
527 | # obtain next argument-- index has already been incremented |
|
|||
528 | nextArg = match.group('arg') |
|
|||
529 | if nextArg: |
|
|||
530 | nextArg = nextArg[1:] |
|
|||
531 | index = index - 1 # put it back |
|
|||
532 | else: |
|
|||
533 | try: |
|
|||
534 | nextArg = args[index] |
|
|||
535 | except: |
|
|||
536 | nextArg = None |
|
|||
537 |
|
||||
538 | # transpose to lower case, if necessary |
|
|||
539 | if self.ignoreCase: |
|
|||
540 | optName = string.lower(optName) |
|
|||
541 |
|
||||
542 | # obtain defining tuple |
|
|||
543 | tuples = self._getArgTuple(optName) |
|
|||
544 |
|
||||
545 | if tuples == None: |
|
|||
546 | raise ArgumentError('Illegal option \'' + arg + '\'') |
|
|||
547 | elif len(tuples) > 1: |
|
|||
548 | raise ArgumentError('Ambiguous option \'' + arg + |
|
|||
549 | '\'; matches ' + |
|
|||
550 | repr(map(lambda x: x[0], tuples))) |
|
|||
551 | else: |
|
|||
552 | config = tuples[0] |
|
|||
553 |
|
||||
554 | # config is now set to the configuration tuple for the |
|
|||
555 | # argument |
|
|||
556 | (fullName, spec, realName) = config |
|
|||
557 | (optType, optMode, optDefault, optMultiple) = spec |
|
|||
558 |
|
||||
559 | # if opt mode required, but nextArg is none, raise an error |
|
|||
560 | if (optMode == ArgRequired): |
|
|||
561 | if (not nextArg) or self._isTerminator(nextArg): |
|
|||
562 | # print nextArg |
|
|||
563 | raise ArgumentError('Option \'' + arg + |
|
|||
564 | '\' requires an argument of type ' + |
|
|||
565 | optType) |
|
|||
566 |
|
||||
567 | if (not optMode == None) and nextArg and (not self._isTerminator(nextArg)): |
|
|||
568 | # nextArg defined, option configured to possibly consume arg |
|
|||
569 | try: |
|
|||
570 | # grab conversion function-- the try is more for internal diagnostics |
|
|||
571 | func = ConversionFunctions[optType] |
|
|||
572 | try: |
|
|||
573 | optionValue = func(nextArg) |
|
|||
574 | index = index + 1 |
|
|||
575 | except: |
|
|||
576 | # only raise conversion error if REQUIRED to consume argument |
|
|||
577 | if optMode == ArgRequired: |
|
|||
578 | raise ArgumentError('Invalid argument to option \'' |
|
|||
579 | + arg + '\'; should be \'' + |
|
|||
580 | optType + '\'') |
|
|||
581 | else: |
|
|||
582 | optionValue = optDefault |
|
|||
583 | except ArgumentError: |
|
|||
584 | raise |
|
|||
585 | except: |
|
|||
586 | raise ArgumentError('(' + arg + |
|
|||
587 | ') Conversion function for \'' + |
|
|||
588 | optType + '\' not found.') |
|
|||
589 | else: |
|
|||
590 | optionValue = optDefault |
|
|||
591 |
|
||||
592 | # add value to options dictionary |
|
|||
593 | if optMultiple: |
|
|||
594 | # can be multiple values |
|
|||
595 | try: |
|
|||
596 | # try to append element |
|
|||
597 | self.optionValues[realName] = self.optionValues[realName] + [optionValue] |
|
|||
598 | except: |
|
|||
599 | # failed-- must not exist; add it |
|
|||
600 | self.optionValues[realName] = [optionValue] |
|
|||
601 | else: |
|
|||
602 | # only one value per |
|
|||
603 | if self.isPosixCompliant and self.optionValues.has_key(realName): |
|
|||
604 | raise ArgumentError('Argument \'' + arg + |
|
|||
605 | '\' occurs multiple times.') |
|
|||
606 |
|
||||
607 | self.optionValues[realName] = optionValue |
|
|||
608 |
|
||||
609 | def valueForOption(self, optionName, defaultValue = None): |
|
|||
610 | """ |
|
|||
611 | Return the value associated with optionName. If optionName was |
|
|||
612 | not encountered during parsing of the arguments, returns the |
|
|||
613 | defaultValue (which defaults to None). |
|
|||
614 | """ |
|
|||
615 | try: |
|
|||
616 | optionValue = self.optionValues[optionName] |
|
|||
617 | except: |
|
|||
618 | optionValue = defaultValue |
|
|||
619 |
|
||||
620 | return optionValue |
|
|||
621 |
|
||||
622 | ## |
|
|||
623 | ## test/example section |
|
|||
624 | ## |
|
|||
625 | test_error = 'Test Run Amok!' |
|
|||
626 | def _test(): |
|
|||
627 | """ |
|
|||
628 | A relatively complete test suite. |
|
|||
629 | """ |
|
|||
630 | try: |
|
|||
631 | DPyGetOpt(['foo', 'bar=s', 'foo']) |
|
|||
632 | except Error, exc: |
|
|||
633 | print 'EXCEPTION (should be \'foo\' already used..): %s' % exc |
|
|||
634 |
|
||||
635 | try: |
|
|||
636 | DPyGetOpt(['foo|bar|apple=s@', 'baz|apple!']) |
|
|||
637 | except Error, exc: |
|
|||
638 | print 'EXCEPTION (should be duplicate alias/name error): %s' % exc |
|
|||
639 |
|
||||
640 | x = DPyGetOpt(['apple|atlas=i@', 'application|executable=f@']) |
|
|||
641 | try: |
|
|||
642 | x.processArguments(['-app', '29.3']) |
|
|||
643 | except Error, exc: |
|
|||
644 | print 'EXCEPTION (should be ambiguous argument): %s' % exc |
|
|||
645 |
|
||||
646 | x = DPyGetOpt(['foo'], ['antigravity', 'antithesis']) |
|
|||
647 | try: |
|
|||
648 | x.processArguments(['-foo', 'anti']) |
|
|||
649 | except Error, exc: |
|
|||
650 | print 'EXCEPTION (should be ambiguous terminator): %s' % exc |
|
|||
651 |
|
||||
652 | profile = ['plain-option', |
|
|||
653 | 'boolean-option!', |
|
|||
654 | 'list-of-integers=i@', |
|
|||
655 | 'list-real-option|list-real-alias|list-real-pseudonym=f@', |
|
|||
656 | 'optional-string-option:s', |
|
|||
657 | 'abbreviated-string-list=s@'] |
|
|||
658 |
|
||||
659 | terminators = ['terminator'] |
|
|||
660 |
|
||||
661 | args = ['-plain-option', |
|
|||
662 | '+noboolean-option', |
|
|||
663 | '--list-of-integers', '1', |
|
|||
664 | '+list-of-integers', '2', |
|
|||
665 | '-list-of-integers', '3', |
|
|||
666 | 'freeargone', |
|
|||
667 | '-list-real-option', '1.1', |
|
|||
668 | '+list-real-alias', '1.2', |
|
|||
669 | '--list-real-pseudonym', '1.3', |
|
|||
670 | 'freeargtwo', |
|
|||
671 | '-abbreviated-string-list', 'String1', |
|
|||
672 | '--abbreviated-s', 'String2', |
|
|||
673 | '-abbrev', 'String3', |
|
|||
674 | '-a', 'String4', |
|
|||
675 | '-optional-string-option', |
|
|||
676 | 'term', |
|
|||
677 | 'next option should look like an invalid arg', |
|
|||
678 | '-a'] |
|
|||
679 |
|
||||
680 |
|
||||
681 | print 'Using profile: ' + repr(profile) |
|
|||
682 | print 'With terminator: ' + repr(terminators) |
|
|||
683 | print 'Processing arguments: ' + repr(args) |
|
|||
684 |
|
||||
685 | go = DPyGetOpt(profile, terminators) |
|
|||
686 | go.processArguments(args) |
|
|||
687 |
|
||||
688 | print 'Options (and values): ' + repr(go.optionValues) |
|
|||
689 | print 'free args: ' + repr(go.freeValues) |
|
|||
690 | print 'term args: ' + repr(go.termValues) |
|
@@ -1,51 +0,0 b'' | |||||
1 | """Base utilities support for IPython. |
|
|||
2 |
|
||||
3 | Warning: this is a module that other utilities modules will import from, so it |
|
|||
4 | can ONLY depend on the standard library, and NOTHING ELSE. In particular, this |
|
|||
5 | module can NOT import anything from IPython, or circular dependencies will arise. |
|
|||
6 | """ |
|
|||
7 |
|
||||
8 | #----------------------------------------------------------------------------- |
|
|||
9 | # Imports |
|
|||
10 | #----------------------------------------------------------------------------- |
|
|||
11 |
|
||||
12 | import subprocess |
|
|||
13 |
|
||||
14 | #----------------------------------------------------------------------------- |
|
|||
15 | # Functions |
|
|||
16 | #----------------------------------------------------------------------------- |
|
|||
17 |
|
||||
18 | def getoutputerror(cmd,verbose=0,debug=0,header='',split=0): |
|
|||
19 | """Return (standard output,standard error) of executing cmd in a shell. |
|
|||
20 |
|
||||
21 | Accepts the same arguments as system(), plus: |
|
|||
22 |
|
||||
23 | - split(0): if true, each of stdout/err is returned as a list split on |
|
|||
24 | newlines. |
|
|||
25 |
|
||||
26 | Note: a stateful version of this function is available through the |
|
|||
27 | SystemExec class.""" |
|
|||
28 |
|
||||
29 | if verbose or debug: print header+cmd |
|
|||
30 | if not cmd: |
|
|||
31 | if split: |
|
|||
32 | return [],[] |
|
|||
33 | else: |
|
|||
34 | return '','' |
|
|||
35 | if not debug: |
|
|||
36 | p = subprocess.Popen(cmd, shell=True, |
|
|||
37 | stdin=subprocess.PIPE, |
|
|||
38 | stdout=subprocess.PIPE, |
|
|||
39 | stderr=subprocess.PIPE, |
|
|||
40 | close_fds=True) |
|
|||
41 | pin, pout, perr = (p.stdin, p.stdout, p.stderr) |
|
|||
42 |
|
||||
43 | tout = pout.read().rstrip() |
|
|||
44 | terr = perr.read().rstrip() |
|
|||
45 | pin.close() |
|
|||
46 | pout.close() |
|
|||
47 | perr.close() |
|
|||
48 | if split: |
|
|||
49 | return tout.split('\n'),terr.split('\n') |
|
|||
50 | else: |
|
|||
51 | return tout,terr |
|
This diff has been collapsed as it changes many lines, (1894 lines changed) Show them Hide them | |||||
@@ -1,1894 +0,0 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
|||
2 | """General purpose utilities. |
|
|||
3 |
|
||||
4 | This is a grab-bag of stuff I find useful in most programs I write. Some of |
|
|||
5 | these things are also convenient when working at the command line. |
|
|||
6 | """ |
|
|||
7 |
|
||||
8 | #***************************************************************************** |
|
|||
9 | # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu> |
|
|||
10 | # |
|
|||
11 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
12 | # the file COPYING, distributed as part of this software. |
|
|||
13 | #***************************************************************************** |
|
|||
14 | from __future__ import absolute_import |
|
|||
15 |
|
||||
16 | #**************************************************************************** |
|
|||
17 | # required modules from the Python standard library |
|
|||
18 | import __main__ |
|
|||
19 |
|
||||
20 | import os |
|
|||
21 | import platform |
|
|||
22 | import re |
|
|||
23 | import shlex |
|
|||
24 | import shutil |
|
|||
25 | import subprocess |
|
|||
26 | import sys |
|
|||
27 | import time |
|
|||
28 | import types |
|
|||
29 | import warnings |
|
|||
30 |
|
||||
31 | # Curses and termios are Unix-only modules |
|
|||
32 | try: |
|
|||
33 | import curses |
|
|||
34 | # We need termios as well, so if its import happens to raise, we bail on |
|
|||
35 | # using curses altogether. |
|
|||
36 | import termios |
|
|||
37 | except ImportError: |
|
|||
38 | USE_CURSES = False |
|
|||
39 | else: |
|
|||
40 | # Curses on Solaris may not be complete, so we can't use it there |
|
|||
41 | USE_CURSES = hasattr(curses,'initscr') |
|
|||
42 |
|
||||
43 | # Other IPython utilities |
|
|||
44 | import IPython |
|
|||
45 | from IPython.core import release |
|
|||
46 | from IPython.external.Itpl import itpl,printpl |
|
|||
47 | from IPython.utils import platutils |
|
|||
48 | from IPython.utils.generics import result_display |
|
|||
49 | from IPython.external.path import path |
|
|||
50 | from .baseutils import getoutputerror |
|
|||
51 |
|
||||
52 | #**************************************************************************** |
|
|||
53 | # Exceptions |
|
|||
54 | class Error(Exception): |
|
|||
55 | """Base class for exceptions in this module.""" |
|
|||
56 | pass |
|
|||
57 |
|
||||
58 | #---------------------------------------------------------------------------- |
|
|||
59 | class IOStream: |
|
|||
60 | def __init__(self,stream,fallback): |
|
|||
61 | if not hasattr(stream,'write') or not hasattr(stream,'flush'): |
|
|||
62 | stream = fallback |
|
|||
63 | self.stream = stream |
|
|||
64 | self._swrite = stream.write |
|
|||
65 | self.flush = stream.flush |
|
|||
66 |
|
||||
67 | def write(self,data): |
|
|||
68 | try: |
|
|||
69 | self._swrite(data) |
|
|||
70 | except: |
|
|||
71 | try: |
|
|||
72 | # print handles some unicode issues which may trip a plain |
|
|||
73 | # write() call. Attempt to emulate write() by using a |
|
|||
74 | # trailing comma |
|
|||
75 | print >> self.stream, data, |
|
|||
76 | except: |
|
|||
77 | # if we get here, something is seriously broken. |
|
|||
78 | print >> sys.stderr, \ |
|
|||
79 | 'ERROR - failed to write data to stream:', self.stream |
|
|||
80 |
|
||||
81 | def writeln(self, data): |
|
|||
82 | self.write(data) |
|
|||
83 | self.write('\n') |
|
|||
84 |
|
||||
85 | def close(self): |
|
|||
86 | pass |
|
|||
87 |
|
||||
88 |
|
||||
89 | class IOTerm: |
|
|||
90 | """ Term holds the file or file-like objects for handling I/O operations. |
|
|||
91 |
|
||||
92 | These are normally just sys.stdin, sys.stdout and sys.stderr but for |
|
|||
93 | Windows they can can replaced to allow editing the strings before they are |
|
|||
94 | displayed.""" |
|
|||
95 |
|
||||
96 | # In the future, having IPython channel all its I/O operations through |
|
|||
97 | # this class will make it easier to embed it into other environments which |
|
|||
98 | # are not a normal terminal (such as a GUI-based shell) |
|
|||
99 | def __init__(self,cin=None,cout=None,cerr=None): |
|
|||
100 | self.cin = IOStream(cin,sys.stdin) |
|
|||
101 | self.cout = IOStream(cout,sys.stdout) |
|
|||
102 | self.cerr = IOStream(cerr,sys.stderr) |
|
|||
103 |
|
||||
104 | # Global variable to be used for all I/O |
|
|||
105 | Term = IOTerm() |
|
|||
106 |
|
||||
107 | import IPython.utils.rlineimpl as readline |
|
|||
108 | # Remake Term to use the readline i/o facilities |
|
|||
109 | if sys.platform == 'win32' and readline.have_readline: |
|
|||
110 |
|
||||
111 | Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile) |
|
|||
112 |
|
||||
113 |
|
||||
114 | class Tee(object): |
|
|||
115 | """A class to duplicate an output stream to stdout/err. |
|
|||
116 |
|
||||
117 | This works in a manner very similar to the Unix 'tee' command. |
|
|||
118 |
|
||||
119 | When the object is closed or deleted, it closes the original file given to |
|
|||
120 | it for duplication. |
|
|||
121 | """ |
|
|||
122 | # Inspired by: |
|
|||
123 | # http://mail.python.org/pipermail/python-list/2007-May/442737.html |
|
|||
124 |
|
||||
125 | def __init__(self, file, mode=None, channel='stdout'): |
|
|||
126 | """Construct a new Tee object. |
|
|||
127 |
|
||||
128 | Parameters |
|
|||
129 | ---------- |
|
|||
130 | file : filename or open filehandle (writable) |
|
|||
131 | File that will be duplicated |
|
|||
132 |
|
||||
133 | mode : optional, valid mode for open(). |
|
|||
134 | If a filename was give, open with this mode. |
|
|||
135 |
|
||||
136 | channel : str, one of ['stdout', 'stderr'] |
|
|||
137 | """ |
|
|||
138 | if channel not in ['stdout', 'stderr']: |
|
|||
139 | raise ValueError('Invalid channel spec %s' % channel) |
|
|||
140 |
|
||||
141 | if hasattr(file, 'write') and hasattr(file, 'seek'): |
|
|||
142 | self.file = file |
|
|||
143 | else: |
|
|||
144 | self.file = open(name, mode) |
|
|||
145 | self.channel = channel |
|
|||
146 | self.ostream = getattr(sys, channel) |
|
|||
147 | setattr(sys, channel, self) |
|
|||
148 | self._closed = False |
|
|||
149 |
|
||||
150 | def close(self): |
|
|||
151 | """Close the file and restore the channel.""" |
|
|||
152 | self.flush() |
|
|||
153 | setattr(sys, self.channel, self.ostream) |
|
|||
154 | self.file.close() |
|
|||
155 | self._closed = True |
|
|||
156 |
|
||||
157 | def write(self, data): |
|
|||
158 | """Write data to both channels.""" |
|
|||
159 | self.file.write(data) |
|
|||
160 | self.ostream.write(data) |
|
|||
161 | self.ostream.flush() |
|
|||
162 |
|
||||
163 | def flush(self): |
|
|||
164 | """Flush both channels.""" |
|
|||
165 | self.file.flush() |
|
|||
166 | self.ostream.flush() |
|
|||
167 |
|
||||
168 | def __del__(self): |
|
|||
169 | if not self._closed: |
|
|||
170 | self.close() |
|
|||
171 |
|
||||
172 |
|
||||
173 | #**************************************************************************** |
|
|||
174 | # Generic warning/error printer, used by everything else |
|
|||
175 | def warn(msg,level=2,exit_val=1): |
|
|||
176 | """Standard warning printer. Gives formatting consistency. |
|
|||
177 |
|
||||
178 | Output is sent to Term.cerr (sys.stderr by default). |
|
|||
179 |
|
||||
180 | Options: |
|
|||
181 |
|
||||
182 | -level(2): allows finer control: |
|
|||
183 | 0 -> Do nothing, dummy function. |
|
|||
184 | 1 -> Print message. |
|
|||
185 | 2 -> Print 'WARNING:' + message. (Default level). |
|
|||
186 | 3 -> Print 'ERROR:' + message. |
|
|||
187 | 4 -> Print 'FATAL ERROR:' + message and trigger a sys.exit(exit_val). |
|
|||
188 |
|
||||
189 | -exit_val (1): exit value returned by sys.exit() for a level 4 |
|
|||
190 | warning. Ignored for all other levels.""" |
|
|||
191 |
|
||||
192 | if level>0: |
|
|||
193 | header = ['','','WARNING: ','ERROR: ','FATAL ERROR: '] |
|
|||
194 | print >> Term.cerr, '%s%s' % (header[level],msg) |
|
|||
195 | if level == 4: |
|
|||
196 | print >> Term.cerr,'Exiting.\n' |
|
|||
197 | sys.exit(exit_val) |
|
|||
198 |
|
||||
199 |
|
||||
200 | def info(msg): |
|
|||
201 | """Equivalent to warn(msg,level=1).""" |
|
|||
202 |
|
||||
203 | warn(msg,level=1) |
|
|||
204 |
|
||||
205 |
|
||||
206 | def error(msg): |
|
|||
207 | """Equivalent to warn(msg,level=3).""" |
|
|||
208 |
|
||||
209 | warn(msg,level=3) |
|
|||
210 |
|
||||
211 |
|
||||
212 | def fatal(msg,exit_val=1): |
|
|||
213 | """Equivalent to warn(msg,exit_val=exit_val,level=4).""" |
|
|||
214 |
|
||||
215 | warn(msg,exit_val=exit_val,level=4) |
|
|||
216 |
|
||||
217 | def sys_info(): |
|
|||
218 | """Return useful information about IPython and the system, as a string. |
|
|||
219 |
|
||||
220 | Examples |
|
|||
221 | -------- |
|
|||
222 | In [1]: print(sys_info()) |
|
|||
223 | IPython version: 0.11.bzr.r1340 # random |
|
|||
224 | BZR revision : 1340 |
|
|||
225 | Platform info : os.name -> posix, sys.platform -> linux2 |
|
|||
226 | : Linux-2.6.31-17-generic-i686-with-Ubuntu-9.10-karmic |
|
|||
227 | Python info : 2.6.4 (r264:75706, Dec 7 2009, 18:45:15) |
|
|||
228 | [GCC 4.4.1] |
|
|||
229 | """ |
|
|||
230 | import platform |
|
|||
231 | out = [] |
|
|||
232 | out.append('IPython version: %s' % release.version) |
|
|||
233 | out.append('BZR revision : %s' % release.revision) |
|
|||
234 | out.append('Platform info : os.name -> %s, sys.platform -> %s' % |
|
|||
235 | (os.name,sys.platform) ) |
|
|||
236 | out.append(' : %s' % platform.platform()) |
|
|||
237 | out.append('Python info : %s' % sys.version) |
|
|||
238 | out.append('') # ensure closing newline |
|
|||
239 | return '\n'.join(out) |
|
|||
240 |
|
||||
241 |
|
||||
242 | #--------------------------------------------------------------------------- |
|
|||
243 | # Debugging routines |
|
|||
244 | # |
|
|||
245 | def debugx(expr,pre_msg=''): |
|
|||
246 | """Print the value of an expression from the caller's frame. |
|
|||
247 |
|
||||
248 | Takes an expression, evaluates it in the caller's frame and prints both |
|
|||
249 | the given expression and the resulting value (as well as a debug mark |
|
|||
250 | indicating the name of the calling function. The input must be of a form |
|
|||
251 | suitable for eval(). |
|
|||
252 |
|
||||
253 | An optional message can be passed, which will be prepended to the printed |
|
|||
254 | expr->value pair.""" |
|
|||
255 |
|
||||
256 | cf = sys._getframe(1) |
|
|||
257 | print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr, |
|
|||
258 | eval(expr,cf.f_globals,cf.f_locals)) |
|
|||
259 |
|
||||
260 | # deactivate it by uncommenting the following line, which makes it a no-op |
|
|||
261 | #def debugx(expr,pre_msg=''): pass |
|
|||
262 |
|
||||
263 | #---------------------------------------------------------------------------- |
|
|||
264 | StringTypes = types.StringTypes |
|
|||
265 |
|
||||
266 | # Basic timing functionality |
|
|||
267 |
|
||||
268 | # If possible (Unix), use the resource module instead of time.clock() |
|
|||
269 | try: |
|
|||
270 | import resource |
|
|||
271 | def clocku(): |
|
|||
272 | """clocku() -> floating point number |
|
|||
273 |
|
||||
274 | Return the *USER* CPU time in seconds since the start of the process. |
|
|||
275 | This is done via a call to resource.getrusage, so it avoids the |
|
|||
276 | wraparound problems in time.clock().""" |
|
|||
277 |
|
||||
278 | return resource.getrusage(resource.RUSAGE_SELF)[0] |
|
|||
279 |
|
||||
280 | def clocks(): |
|
|||
281 | """clocks() -> floating point number |
|
|||
282 |
|
||||
283 | Return the *SYSTEM* CPU time in seconds since the start of the process. |
|
|||
284 | This is done via a call to resource.getrusage, so it avoids the |
|
|||
285 | wraparound problems in time.clock().""" |
|
|||
286 |
|
||||
287 | return resource.getrusage(resource.RUSAGE_SELF)[1] |
|
|||
288 |
|
||||
289 | def clock(): |
|
|||
290 | """clock() -> floating point number |
|
|||
291 |
|
||||
292 | Return the *TOTAL USER+SYSTEM* CPU time in seconds since the start of |
|
|||
293 | the process. This is done via a call to resource.getrusage, so it |
|
|||
294 | avoids the wraparound problems in time.clock().""" |
|
|||
295 |
|
||||
296 | u,s = resource.getrusage(resource.RUSAGE_SELF)[:2] |
|
|||
297 | return u+s |
|
|||
298 |
|
||||
299 | def clock2(): |
|
|||
300 | """clock2() -> (t_user,t_system) |
|
|||
301 |
|
||||
302 | Similar to clock(), but return a tuple of user/system times.""" |
|
|||
303 | return resource.getrusage(resource.RUSAGE_SELF)[:2] |
|
|||
304 |
|
||||
305 | except ImportError: |
|
|||
306 | # There is no distinction of user/system time under windows, so we just use |
|
|||
307 | # time.clock() for everything... |
|
|||
308 | clocku = clocks = clock = time.clock |
|
|||
309 | def clock2(): |
|
|||
310 | """Under windows, system CPU time can't be measured. |
|
|||
311 |
|
||||
312 | This just returns clock() and zero.""" |
|
|||
313 | return time.clock(),0.0 |
|
|||
314 |
|
||||
315 |
|
||||
316 | def timings_out(reps,func,*args,**kw): |
|
|||
317 | """timings_out(reps,func,*args,**kw) -> (t_total,t_per_call,output) |
|
|||
318 |
|
||||
319 | Execute a function reps times, return a tuple with the elapsed total |
|
|||
320 | CPU time in seconds, the time per call and the function's output. |
|
|||
321 |
|
||||
322 | Under Unix, the return value is the sum of user+system time consumed by |
|
|||
323 | the process, computed via the resource module. This prevents problems |
|
|||
324 | related to the wraparound effect which the time.clock() function has. |
|
|||
325 |
|
||||
326 | Under Windows the return value is in wall clock seconds. See the |
|
|||
327 | documentation for the time module for more details.""" |
|
|||
328 |
|
||||
329 | reps = int(reps) |
|
|||
330 | assert reps >=1, 'reps must be >= 1' |
|
|||
331 | if reps==1: |
|
|||
332 | start = clock() |
|
|||
333 | out = func(*args,**kw) |
|
|||
334 | tot_time = clock()-start |
|
|||
335 | else: |
|
|||
336 | rng = xrange(reps-1) # the last time is executed separately to store output |
|
|||
337 | start = clock() |
|
|||
338 | for dummy in rng: func(*args,**kw) |
|
|||
339 | out = func(*args,**kw) # one last time |
|
|||
340 | tot_time = clock()-start |
|
|||
341 | av_time = tot_time / reps |
|
|||
342 | return tot_time,av_time,out |
|
|||
343 |
|
||||
344 |
|
||||
345 | def timings(reps,func,*args,**kw): |
|
|||
346 | """timings(reps,func,*args,**kw) -> (t_total,t_per_call) |
|
|||
347 |
|
||||
348 | Execute a function reps times, return a tuple with the elapsed total CPU |
|
|||
349 | time in seconds and the time per call. These are just the first two values |
|
|||
350 | in timings_out().""" |
|
|||
351 |
|
||||
352 | return timings_out(reps,func,*args,**kw)[0:2] |
|
|||
353 |
|
||||
354 |
|
||||
355 | def timing(func,*args,**kw): |
|
|||
356 | """timing(func,*args,**kw) -> t_total |
|
|||
357 |
|
||||
358 | Execute a function once, return the elapsed total CPU time in |
|
|||
359 | seconds. This is just the first value in timings_out().""" |
|
|||
360 |
|
||||
361 | return timings_out(1,func,*args,**kw)[0] |
|
|||
362 |
|
||||
363 | #**************************************************************************** |
|
|||
364 | # file and system |
|
|||
365 |
|
||||
366 | def arg_split(s,posix=False): |
|
|||
367 | """Split a command line's arguments in a shell-like manner. |
|
|||
368 |
|
||||
369 | This is a modified version of the standard library's shlex.split() |
|
|||
370 | function, but with a default of posix=False for splitting, so that quotes |
|
|||
371 | in inputs are respected.""" |
|
|||
372 |
|
||||
373 | # XXX - there may be unicode-related problems here!!! I'm not sure that |
|
|||
374 | # shlex is truly unicode-safe, so it might be necessary to do |
|
|||
375 | # |
|
|||
376 | # s = s.encode(sys.stdin.encoding) |
|
|||
377 | # |
|
|||
378 | # first, to ensure that shlex gets a normal string. Input from anyone who |
|
|||
379 | # knows more about unicode and shlex than I would be good to have here... |
|
|||
380 | lex = shlex.shlex(s, posix=posix) |
|
|||
381 | lex.whitespace_split = True |
|
|||
382 | return list(lex) |
|
|||
383 |
|
||||
384 |
|
||||
385 | def system(cmd,verbose=0,debug=0,header=''): |
|
|||
386 | """Execute a system command, return its exit status. |
|
|||
387 |
|
||||
388 | Options: |
|
|||
389 |
|
||||
390 | - verbose (0): print the command to be executed. |
|
|||
391 |
|
||||
392 | - debug (0): only print, do not actually execute. |
|
|||
393 |
|
||||
394 | - header (''): Header to print on screen prior to the executed command (it |
|
|||
395 | is only prepended to the command, no newlines are added). |
|
|||
396 |
|
||||
397 | Note: a stateful version of this function is available through the |
|
|||
398 | SystemExec class.""" |
|
|||
399 |
|
||||
400 | stat = 0 |
|
|||
401 | if verbose or debug: print header+cmd |
|
|||
402 | sys.stdout.flush() |
|
|||
403 | if not debug: stat = os.system(cmd) |
|
|||
404 | return stat |
|
|||
405 |
|
||||
406 |
|
||||
407 | def abbrev_cwd(): |
|
|||
408 | """ Return abbreviated version of cwd, e.g. d:mydir """ |
|
|||
409 | cwd = os.getcwd().replace('\\','/') |
|
|||
410 | drivepart = '' |
|
|||
411 | tail = cwd |
|
|||
412 | if sys.platform == 'win32': |
|
|||
413 | if len(cwd) < 4: |
|
|||
414 | return cwd |
|
|||
415 | drivepart,tail = os.path.splitdrive(cwd) |
|
|||
416 |
|
||||
417 |
|
||||
418 | parts = tail.split('/') |
|
|||
419 | if len(parts) > 2: |
|
|||
420 | tail = '/'.join(parts[-2:]) |
|
|||
421 |
|
||||
422 | return (drivepart + ( |
|
|||
423 | cwd == '/' and '/' or tail)) |
|
|||
424 |
|
||||
425 |
|
||||
426 | # This function is used by ipython in a lot of places to make system calls. |
|
|||
427 | # We need it to be slightly different under win32, due to the vagaries of |
|
|||
428 | # 'network shares'. A win32 override is below. |
|
|||
429 |
|
||||
430 | def shell(cmd,verbose=0,debug=0,header=''): |
|
|||
431 | """Execute a command in the system shell, always return None. |
|
|||
432 |
|
||||
433 | Options: |
|
|||
434 |
|
||||
435 | - verbose (0): print the command to be executed. |
|
|||
436 |
|
||||
437 | - debug (0): only print, do not actually execute. |
|
|||
438 |
|
||||
439 | - header (''): Header to print on screen prior to the executed command (it |
|
|||
440 | is only prepended to the command, no newlines are added). |
|
|||
441 |
|
||||
442 | Note: this is similar to genutils.system(), but it returns None so it can |
|
|||
443 | be conveniently used in interactive loops without getting the return value |
|
|||
444 | (typically 0) printed many times.""" |
|
|||
445 |
|
||||
446 | stat = 0 |
|
|||
447 | if verbose or debug: print header+cmd |
|
|||
448 | # flush stdout so we don't mangle python's buffering |
|
|||
449 | sys.stdout.flush() |
|
|||
450 |
|
||||
451 | if not debug: |
|
|||
452 | platutils.set_term_title("IPy " + cmd) |
|
|||
453 | os.system(cmd) |
|
|||
454 | platutils.set_term_title("IPy " + abbrev_cwd()) |
|
|||
455 |
|
||||
456 | # override shell() for win32 to deal with network shares |
|
|||
457 | if os.name in ('nt','dos'): |
|
|||
458 |
|
||||
459 | shell_ori = shell |
|
|||
460 |
|
||||
461 | def shell(cmd,verbose=0,debug=0,header=''): |
|
|||
462 | if os.getcwd().startswith(r"\\"): |
|
|||
463 | path = os.getcwd() |
|
|||
464 | # change to c drive (cannot be on UNC-share when issuing os.system, |
|
|||
465 | # as cmd.exe cannot handle UNC addresses) |
|
|||
466 | os.chdir("c:") |
|
|||
467 | # issue pushd to the UNC-share and then run the command |
|
|||
468 | try: |
|
|||
469 | shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header) |
|
|||
470 | finally: |
|
|||
471 | os.chdir(path) |
|
|||
472 | else: |
|
|||
473 | shell_ori(cmd,verbose,debug,header) |
|
|||
474 |
|
||||
475 | shell.__doc__ = shell_ori.__doc__ |
|
|||
476 |
|
||||
477 |
|
||||
478 | def getoutput(cmd,verbose=0,debug=0,header='',split=0): |
|
|||
479 | """Dummy substitute for perl's backquotes. |
|
|||
480 |
|
||||
481 | Executes a command and returns the output. |
|
|||
482 |
|
||||
483 | Accepts the same arguments as system(), plus: |
|
|||
484 |
|
||||
485 | - split(0): if true, the output is returned as a list split on newlines. |
|
|||
486 |
|
||||
487 | Note: a stateful version of this function is available through the |
|
|||
488 | SystemExec class. |
|
|||
489 |
|
||||
490 | This is pretty much deprecated and rarely used, |
|
|||
491 | genutils.getoutputerror may be what you need. |
|
|||
492 |
|
||||
493 | """ |
|
|||
494 |
|
||||
495 | if verbose or debug: print header+cmd |
|
|||
496 | if not debug: |
|
|||
497 | pipe = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout |
|
|||
498 | output = pipe.read() |
|
|||
499 | # stipping last \n is here for backwards compat. |
|
|||
500 | if output.endswith('\n'): |
|
|||
501 | output = output[:-1] |
|
|||
502 | if split: |
|
|||
503 | return output.split('\n') |
|
|||
504 | else: |
|
|||
505 | return output |
|
|||
506 |
|
||||
507 | # for compatibility with older naming conventions |
|
|||
508 | xsys = system |
|
|||
509 | bq = getoutput |
|
|||
510 |
|
||||
511 |
|
||||
512 | class SystemExec: |
|
|||
513 | """Access the system and getoutput functions through a stateful interface. |
|
|||
514 |
|
||||
515 | Note: here we refer to the system and getoutput functions from this |
|
|||
516 | library, not the ones from the standard python library. |
|
|||
517 |
|
||||
518 | This class offers the system and getoutput functions as methods, but the |
|
|||
519 | verbose, debug and header parameters can be set for the instance (at |
|
|||
520 | creation time or later) so that they don't need to be specified on each |
|
|||
521 | call. |
|
|||
522 |
|
||||
523 | For efficiency reasons, there's no way to override the parameters on a |
|
|||
524 | per-call basis other than by setting instance attributes. If you need |
|
|||
525 | local overrides, it's best to directly call system() or getoutput(). |
|
|||
526 |
|
||||
527 | The following names are provided as alternate options: |
|
|||
528 | - xsys: alias to system |
|
|||
529 | - bq: alias to getoutput |
|
|||
530 |
|
||||
531 | An instance can then be created as: |
|
|||
532 | >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ') |
|
|||
533 | """ |
|
|||
534 |
|
||||
535 | def __init__(self,verbose=0,debug=0,header='',split=0): |
|
|||
536 | """Specify the instance's values for verbose, debug and header.""" |
|
|||
537 | setattr_list(self,'verbose debug header split') |
|
|||
538 |
|
||||
539 | def system(self,cmd): |
|
|||
540 | """Stateful interface to system(), with the same keyword parameters.""" |
|
|||
541 |
|
||||
542 | system(cmd,self.verbose,self.debug,self.header) |
|
|||
543 |
|
||||
544 | def shell(self,cmd): |
|
|||
545 | """Stateful interface to shell(), with the same keyword parameters.""" |
|
|||
546 |
|
||||
547 | shell(cmd,self.verbose,self.debug,self.header) |
|
|||
548 |
|
||||
549 | xsys = system # alias |
|
|||
550 |
|
||||
551 | def getoutput(self,cmd): |
|
|||
552 | """Stateful interface to getoutput().""" |
|
|||
553 |
|
||||
554 | return getoutput(cmd,self.verbose,self.debug,self.header,self.split) |
|
|||
555 |
|
||||
556 | def getoutputerror(self,cmd): |
|
|||
557 | """Stateful interface to getoutputerror().""" |
|
|||
558 |
|
||||
559 | return getoutputerror(cmd,self.verbose,self.debug,self.header,self.split) |
|
|||
560 |
|
||||
561 | bq = getoutput # alias |
|
|||
562 |
|
||||
563 | #----------------------------------------------------------------------------- |
|
|||
564 | def mutex_opts(dict,ex_op): |
|
|||
565 | """Check for presence of mutually exclusive keys in a dict. |
|
|||
566 |
|
||||
567 | Call: mutex_opts(dict,[[op1a,op1b],[op2a,op2b]...]""" |
|
|||
568 | for op1,op2 in ex_op: |
|
|||
569 | if op1 in dict and op2 in dict: |
|
|||
570 | raise ValueError,'\n*** ERROR in Arguments *** '\ |
|
|||
571 | 'Options '+op1+' and '+op2+' are mutually exclusive.' |
|
|||
572 |
|
||||
573 | #----------------------------------------------------------------------------- |
|
|||
574 | def get_py_filename(name): |
|
|||
575 | """Return a valid python filename in the current directory. |
|
|||
576 |
|
||||
577 | If the given name is not a file, it adds '.py' and searches again. |
|
|||
578 | Raises IOError with an informative message if the file isn't found.""" |
|
|||
579 |
|
||||
580 | name = os.path.expanduser(name) |
|
|||
581 | if not os.path.isfile(name) and not name.endswith('.py'): |
|
|||
582 | name += '.py' |
|
|||
583 | if os.path.isfile(name): |
|
|||
584 | return name |
|
|||
585 | else: |
|
|||
586 | raise IOError,'File `%s` not found.' % name |
|
|||
587 |
|
||||
588 | #----------------------------------------------------------------------------- |
|
|||
589 |
|
||||
590 |
|
||||
591 | def filefind(filename, path_dirs=None): |
|
|||
592 | """Find a file by looking through a sequence of paths. |
|
|||
593 |
|
||||
594 | This iterates through a sequence of paths looking for a file and returns |
|
|||
595 | the full, absolute path of the first occurence of the file. If no set of |
|
|||
596 | path dirs is given, the filename is tested as is, after running through |
|
|||
597 | :func:`expandvars` and :func:`expanduser`. Thus a simple call:: |
|
|||
598 |
|
||||
599 | filefind('myfile.txt') |
|
|||
600 |
|
||||
601 | will find the file in the current working dir, but:: |
|
|||
602 |
|
||||
603 | filefind('~/myfile.txt') |
|
|||
604 |
|
||||
605 | Will find the file in the users home directory. This function does not |
|
|||
606 | automatically try any paths, such as the cwd or the user's home directory. |
|
|||
607 |
|
||||
608 | Parameters |
|
|||
609 | ---------- |
|
|||
610 | filename : str |
|
|||
611 | The filename to look for. |
|
|||
612 | path_dirs : str, None or sequence of str |
|
|||
613 | The sequence of paths to look for the file in. If None, the filename |
|
|||
614 | need to be absolute or be in the cwd. If a string, the string is |
|
|||
615 | put into a sequence and the searched. If a sequence, walk through |
|
|||
616 | each element and join with ``filename``, calling :func:`expandvars` |
|
|||
617 | and :func:`expanduser` before testing for existence. |
|
|||
618 |
|
||||
619 | Returns |
|
|||
620 | ------- |
|
|||
621 | Raises :exc:`IOError` or returns absolute path to file. |
|
|||
622 | """ |
|
|||
623 |
|
||||
624 | # If paths are quoted, abspath gets confused, strip them... |
|
|||
625 | filename = filename.strip('"').strip("'") |
|
|||
626 | # If the input is an absolute path, just check it exists |
|
|||
627 | if os.path.isabs(filename) and os.path.isfile(filename): |
|
|||
628 | return filename |
|
|||
629 |
|
||||
630 | if path_dirs is None: |
|
|||
631 | path_dirs = ("",) |
|
|||
632 | elif isinstance(path_dirs, basestring): |
|
|||
633 | path_dirs = (path_dirs,) |
|
|||
634 |
|
||||
635 | for path in path_dirs: |
|
|||
636 | if path == '.': path = os.getcwd() |
|
|||
637 | testname = expand_path(os.path.join(path, filename)) |
|
|||
638 | if os.path.isfile(testname): |
|
|||
639 | return os.path.abspath(testname) |
|
|||
640 |
|
||||
641 | raise IOError("File %r does not exist in any of the search paths: %r" % |
|
|||
642 | (filename, path_dirs) ) |
|
|||
643 |
|
||||
644 |
|
||||
645 | #---------------------------------------------------------------------------- |
|
|||
646 | def file_read(filename): |
|
|||
647 | """Read a file and close it. Returns the file source.""" |
|
|||
648 | fobj = open(filename,'r'); |
|
|||
649 | source = fobj.read(); |
|
|||
650 | fobj.close() |
|
|||
651 | return source |
|
|||
652 |
|
||||
653 | def file_readlines(filename): |
|
|||
654 | """Read a file and close it. Returns the file source using readlines().""" |
|
|||
655 | fobj = open(filename,'r'); |
|
|||
656 | lines = fobj.readlines(); |
|
|||
657 | fobj.close() |
|
|||
658 | return lines |
|
|||
659 |
|
||||
660 | #---------------------------------------------------------------------------- |
|
|||
661 | def target_outdated(target,deps): |
|
|||
662 | """Determine whether a target is out of date. |
|
|||
663 |
|
||||
664 | target_outdated(target,deps) -> 1/0 |
|
|||
665 |
|
||||
666 | deps: list of filenames which MUST exist. |
|
|||
667 | target: single filename which may or may not exist. |
|
|||
668 |
|
||||
669 | If target doesn't exist or is older than any file listed in deps, return |
|
|||
670 | true, otherwise return false. |
|
|||
671 | """ |
|
|||
672 | try: |
|
|||
673 | target_time = os.path.getmtime(target) |
|
|||
674 | except os.error: |
|
|||
675 | return 1 |
|
|||
676 | for dep in deps: |
|
|||
677 | dep_time = os.path.getmtime(dep) |
|
|||
678 | if dep_time > target_time: |
|
|||
679 | #print "For target",target,"Dep failed:",dep # dbg |
|
|||
680 | #print "times (dep,tar):",dep_time,target_time # dbg |
|
|||
681 | return 1 |
|
|||
682 | return 0 |
|
|||
683 |
|
||||
684 | #----------------------------------------------------------------------------- |
|
|||
685 | def target_update(target,deps,cmd): |
|
|||
686 | """Update a target with a given command given a list of dependencies. |
|
|||
687 |
|
||||
688 | target_update(target,deps,cmd) -> runs cmd if target is outdated. |
|
|||
689 |
|
||||
690 | This is just a wrapper around target_outdated() which calls the given |
|
|||
691 | command if target is outdated.""" |
|
|||
692 |
|
||||
693 | if target_outdated(target,deps): |
|
|||
694 | xsys(cmd) |
|
|||
695 |
|
||||
696 | #---------------------------------------------------------------------------- |
|
|||
697 | def unquote_ends(istr): |
|
|||
698 | """Remove a single pair of quotes from the endpoints of a string.""" |
|
|||
699 |
|
||||
700 | if not istr: |
|
|||
701 | return istr |
|
|||
702 | if (istr[0]=="'" and istr[-1]=="'") or \ |
|
|||
703 | (istr[0]=='"' and istr[-1]=='"'): |
|
|||
704 | return istr[1:-1] |
|
|||
705 | else: |
|
|||
706 | return istr |
|
|||
707 |
|
||||
708 | #---------------------------------------------------------------------------- |
|
|||
709 | def flag_calls(func): |
|
|||
710 | """Wrap a function to detect and flag when it gets called. |
|
|||
711 |
|
||||
712 | This is a decorator which takes a function and wraps it in a function with |
|
|||
713 | a 'called' attribute. wrapper.called is initialized to False. |
|
|||
714 |
|
||||
715 | The wrapper.called attribute is set to False right before each call to the |
|
|||
716 | wrapped function, so if the call fails it remains False. After the call |
|
|||
717 | completes, wrapper.called is set to True and the output is returned. |
|
|||
718 |
|
||||
719 | Testing for truth in wrapper.called allows you to determine if a call to |
|
|||
720 | func() was attempted and succeeded.""" |
|
|||
721 |
|
||||
722 | def wrapper(*args,**kw): |
|
|||
723 | wrapper.called = False |
|
|||
724 | out = func(*args,**kw) |
|
|||
725 | wrapper.called = True |
|
|||
726 | return out |
|
|||
727 |
|
||||
728 | wrapper.called = False |
|
|||
729 | wrapper.__doc__ = func.__doc__ |
|
|||
730 | return wrapper |
|
|||
731 |
|
||||
732 | #---------------------------------------------------------------------------- |
|
|||
733 | def dhook_wrap(func,*a,**k): |
|
|||
734 | """Wrap a function call in a sys.displayhook controller. |
|
|||
735 |
|
||||
736 | Returns a wrapper around func which calls func, with all its arguments and |
|
|||
737 | keywords unmodified, using the default sys.displayhook. Since IPython |
|
|||
738 | modifies sys.displayhook, it breaks the behavior of certain systems that |
|
|||
739 | rely on the default behavior, notably doctest. |
|
|||
740 | """ |
|
|||
741 |
|
||||
742 | def f(*a,**k): |
|
|||
743 |
|
||||
744 | dhook_s = sys.displayhook |
|
|||
745 | sys.displayhook = sys.__displayhook__ |
|
|||
746 | try: |
|
|||
747 | out = func(*a,**k) |
|
|||
748 | finally: |
|
|||
749 | sys.displayhook = dhook_s |
|
|||
750 |
|
||||
751 | return out |
|
|||
752 |
|
||||
753 | f.__doc__ = func.__doc__ |
|
|||
754 | return f |
|
|||
755 |
|
||||
756 | #---------------------------------------------------------------------------- |
|
|||
757 | def doctest_reload(): |
|
|||
758 | """Properly reload doctest to reuse it interactively. |
|
|||
759 |
|
||||
760 | This routine: |
|
|||
761 |
|
||||
762 | - imports doctest but does NOT reload it (see below). |
|
|||
763 |
|
||||
764 | - resets its global 'master' attribute to None, so that multiple uses of |
|
|||
765 | the module interactively don't produce cumulative reports. |
|
|||
766 |
|
||||
767 | - Monkeypatches its core test runner method to protect it from IPython's |
|
|||
768 | modified displayhook. Doctest expects the default displayhook behavior |
|
|||
769 | deep down, so our modification breaks it completely. For this reason, a |
|
|||
770 | hard monkeypatch seems like a reasonable solution rather than asking |
|
|||
771 | users to manually use a different doctest runner when under IPython. |
|
|||
772 |
|
||||
773 | Notes |
|
|||
774 | ----- |
|
|||
775 |
|
||||
776 | This function *used to* reload doctest, but this has been disabled because |
|
|||
777 | reloading doctest unconditionally can cause massive breakage of other |
|
|||
778 | doctest-dependent modules already in memory, such as those for IPython's |
|
|||
779 | own testing system. The name wasn't changed to avoid breaking people's |
|
|||
780 | code, but the reload call isn't actually made anymore.""" |
|
|||
781 |
|
||||
782 | import doctest |
|
|||
783 | doctest.master = None |
|
|||
784 | doctest.DocTestRunner.run = dhook_wrap(doctest.DocTestRunner.run) |
|
|||
785 |
|
||||
786 | #---------------------------------------------------------------------------- |
|
|||
787 | class HomeDirError(Error): |
|
|||
788 | pass |
|
|||
789 |
|
||||
790 | def get_home_dir(): |
|
|||
791 | """Return the closest possible equivalent to a 'home' directory. |
|
|||
792 |
|
||||
793 | * On POSIX, we try $HOME. |
|
|||
794 | * On Windows we try: |
|
|||
795 | - %HOME%: rare, but some people with unix-like setups may have defined it |
|
|||
796 | - %HOMESHARE% |
|
|||
797 | - %HOMEDRIVE\%HOMEPATH% |
|
|||
798 | - %USERPROFILE% |
|
|||
799 | - Registry hack |
|
|||
800 | * On Dos C:\ |
|
|||
801 |
|
||||
802 | Currently only Posix and NT are implemented, a HomeDirError exception is |
|
|||
803 | raised for all other OSes. |
|
|||
804 | """ |
|
|||
805 |
|
||||
806 | isdir = os.path.isdir |
|
|||
807 | env = os.environ |
|
|||
808 |
|
||||
809 | # first, check py2exe distribution root directory for _ipython. |
|
|||
810 | # This overrides all. Normally does not exist. |
|
|||
811 |
|
||||
812 | if hasattr(sys, "frozen"): #Is frozen by py2exe |
|
|||
813 | if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file |
|
|||
814 | root, rest = IPython.__file__.lower().split('library.zip') |
|
|||
815 | else: |
|
|||
816 | root=os.path.join(os.path.split(IPython.__file__)[0],"../../") |
|
|||
817 | root=os.path.abspath(root).rstrip('\\') |
|
|||
818 | if isdir(os.path.join(root, '_ipython')): |
|
|||
819 | os.environ["IPYKITROOT"] = root |
|
|||
820 | return root.decode(sys.getfilesystemencoding()) |
|
|||
821 |
|
||||
822 | if os.name == 'posix': |
|
|||
823 | # Linux, Unix, AIX, OS X |
|
|||
824 | try: |
|
|||
825 | homedir = env['HOME'] |
|
|||
826 | except KeyError: |
|
|||
827 | raise HomeDirError('Undefined $HOME, IPython cannot proceed.') |
|
|||
828 | else: |
|
|||
829 | return homedir.decode(sys.getfilesystemencoding()) |
|
|||
830 | elif os.name == 'nt': |
|
|||
831 | # Now for win9x, XP, Vista, 7? |
|
|||
832 | # For some strange reason all of these return 'nt' for os.name. |
|
|||
833 | # First look for a network home directory. This will return the UNC |
|
|||
834 | # path (\\server\\Users\%username%) not the mapped path (Z:\). This |
|
|||
835 | # is needed when running IPython on cluster where all paths have to |
|
|||
836 | # be UNC. |
|
|||
837 | try: |
|
|||
838 | # A user with a lot of unix tools in win32 may have defined $HOME, |
|
|||
839 | # honor it if it exists, but otherwise let the more typical |
|
|||
840 | # %HOMESHARE% variable be used. |
|
|||
841 | homedir = env.get('HOME') |
|
|||
842 | if homedir is None: |
|
|||
843 | homedir = env['HOMESHARE'] |
|
|||
844 | except KeyError: |
|
|||
845 | pass |
|
|||
846 | else: |
|
|||
847 | if isdir(homedir): |
|
|||
848 | return homedir.decode(sys.getfilesystemencoding()) |
|
|||
849 |
|
||||
850 | # Now look for a local home directory |
|
|||
851 | try: |
|
|||
852 | homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH']) |
|
|||
853 | except KeyError: |
|
|||
854 | pass |
|
|||
855 | else: |
|
|||
856 | if isdir(homedir): |
|
|||
857 | return homedir.decode(sys.getfilesystemencoding()) |
|
|||
858 |
|
||||
859 | # Now the users profile directory |
|
|||
860 | try: |
|
|||
861 | homedir = os.path.join(env['USERPROFILE']) |
|
|||
862 | except KeyError: |
|
|||
863 | pass |
|
|||
864 | else: |
|
|||
865 | if isdir(homedir): |
|
|||
866 | return homedir.decode(sys.getfilesystemencoding()) |
|
|||
867 |
|
||||
868 | # Use the registry to get the 'My Documents' folder. |
|
|||
869 | try: |
|
|||
870 | import _winreg as wreg |
|
|||
871 | key = wreg.OpenKey( |
|
|||
872 | wreg.HKEY_CURRENT_USER, |
|
|||
873 | "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" |
|
|||
874 | ) |
|
|||
875 | homedir = wreg.QueryValueEx(key,'Personal')[0] |
|
|||
876 | key.Close() |
|
|||
877 | except: |
|
|||
878 | pass |
|
|||
879 | else: |
|
|||
880 | if isdir(homedir): |
|
|||
881 | return homedir.decode(sys.getfilesystemencoding()) |
|
|||
882 |
|
||||
883 | # If all else fails, raise HomeDirError |
|
|||
884 | raise HomeDirError('No valid home directory could be found') |
|
|||
885 | elif os.name == 'dos': |
|
|||
886 | # Desperate, may do absurd things in classic MacOS. May work under DOS. |
|
|||
887 | return 'C:\\'.decode(sys.getfilesystemencoding()) |
|
|||
888 | else: |
|
|||
889 | raise HomeDirError('No valid home directory could be found for your OS') |
|
|||
890 |
|
||||
891 |
|
||||
892 | def get_ipython_dir(): |
|
|||
893 | """Get the IPython directory for this platform and user. |
|
|||
894 |
|
||||
895 | This uses the logic in `get_home_dir` to find the home directory |
|
|||
896 | and the adds .ipython to the end of the path. |
|
|||
897 | """ |
|
|||
898 | ipdir_def = '.ipython' |
|
|||
899 | home_dir = get_home_dir() |
|
|||
900 | #import pdb; pdb.set_trace() # dbg |
|
|||
901 | ipdir = os.environ.get( |
|
|||
902 | 'IPYTHON_DIR', os.environ.get( |
|
|||
903 | 'IPYTHONDIR', os.path.join(home_dir, ipdir_def) |
|
|||
904 | ) |
|
|||
905 | ) |
|
|||
906 | return ipdir.decode(sys.getfilesystemencoding()) |
|
|||
907 |
|
||||
908 |
|
||||
909 | def get_ipython_package_dir(): |
|
|||
910 | """Get the base directory where IPython itself is installed.""" |
|
|||
911 | ipdir = os.path.dirname(IPython.__file__) |
|
|||
912 | return ipdir.decode(sys.getfilesystemencoding()) |
|
|||
913 |
|
||||
914 |
|
||||
915 | #**************************************************************************** |
|
|||
916 | # strings and text |
|
|||
917 |
|
||||
918 | class LSString(str): |
|
|||
919 | """String derivative with a special access attributes. |
|
|||
920 |
|
||||
921 | These are normal strings, but with the special attributes: |
|
|||
922 |
|
||||
923 | .l (or .list) : value as list (split on newlines). |
|
|||
924 | .n (or .nlstr): original value (the string itself). |
|
|||
925 | .s (or .spstr): value as whitespace-separated string. |
|
|||
926 | .p (or .paths): list of path objects |
|
|||
927 |
|
||||
928 | Any values which require transformations are computed only once and |
|
|||
929 | cached. |
|
|||
930 |
|
||||
931 | Such strings are very useful to efficiently interact with the shell, which |
|
|||
932 | typically only understands whitespace-separated options for commands.""" |
|
|||
933 |
|
||||
934 | def get_list(self): |
|
|||
935 | try: |
|
|||
936 | return self.__list |
|
|||
937 | except AttributeError: |
|
|||
938 | self.__list = self.split('\n') |
|
|||
939 | return self.__list |
|
|||
940 |
|
||||
941 | l = list = property(get_list) |
|
|||
942 |
|
||||
943 | def get_spstr(self): |
|
|||
944 | try: |
|
|||
945 | return self.__spstr |
|
|||
946 | except AttributeError: |
|
|||
947 | self.__spstr = self.replace('\n',' ') |
|
|||
948 | return self.__spstr |
|
|||
949 |
|
||||
950 | s = spstr = property(get_spstr) |
|
|||
951 |
|
||||
952 | def get_nlstr(self): |
|
|||
953 | return self |
|
|||
954 |
|
||||
955 | n = nlstr = property(get_nlstr) |
|
|||
956 |
|
||||
957 | def get_paths(self): |
|
|||
958 | try: |
|
|||
959 | return self.__paths |
|
|||
960 | except AttributeError: |
|
|||
961 | self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)] |
|
|||
962 | return self.__paths |
|
|||
963 |
|
||||
964 | p = paths = property(get_paths) |
|
|||
965 |
|
||||
966 | def print_lsstring(arg): |
|
|||
967 | """ Prettier (non-repr-like) and more informative printer for LSString """ |
|
|||
968 | print "LSString (.p, .n, .l, .s available). Value:" |
|
|||
969 | print arg |
|
|||
970 |
|
||||
971 | print_lsstring = result_display.when_type(LSString)(print_lsstring) |
|
|||
972 |
|
||||
973 | #---------------------------------------------------------------------------- |
|
|||
974 | class SList(list): |
|
|||
975 | """List derivative with a special access attributes. |
|
|||
976 |
|
||||
977 | These are normal lists, but with the special attributes: |
|
|||
978 |
|
||||
979 | .l (or .list) : value as list (the list itself). |
|
|||
980 | .n (or .nlstr): value as a string, joined on newlines. |
|
|||
981 | .s (or .spstr): value as a string, joined on spaces. |
|
|||
982 | .p (or .paths): list of path objects |
|
|||
983 |
|
||||
984 | Any values which require transformations are computed only once and |
|
|||
985 | cached.""" |
|
|||
986 |
|
||||
987 | def get_list(self): |
|
|||
988 | return self |
|
|||
989 |
|
||||
990 | l = list = property(get_list) |
|
|||
991 |
|
||||
992 | def get_spstr(self): |
|
|||
993 | try: |
|
|||
994 | return self.__spstr |
|
|||
995 | except AttributeError: |
|
|||
996 | self.__spstr = ' '.join(self) |
|
|||
997 | return self.__spstr |
|
|||
998 |
|
||||
999 | s = spstr = property(get_spstr) |
|
|||
1000 |
|
||||
1001 | def get_nlstr(self): |
|
|||
1002 | try: |
|
|||
1003 | return self.__nlstr |
|
|||
1004 | except AttributeError: |
|
|||
1005 | self.__nlstr = '\n'.join(self) |
|
|||
1006 | return self.__nlstr |
|
|||
1007 |
|
||||
1008 | n = nlstr = property(get_nlstr) |
|
|||
1009 |
|
||||
1010 | def get_paths(self): |
|
|||
1011 | try: |
|
|||
1012 | return self.__paths |
|
|||
1013 | except AttributeError: |
|
|||
1014 | self.__paths = [path(p) for p in self if os.path.exists(p)] |
|
|||
1015 | return self.__paths |
|
|||
1016 |
|
||||
1017 | p = paths = property(get_paths) |
|
|||
1018 |
|
||||
1019 | def grep(self, pattern, prune = False, field = None): |
|
|||
1020 | """ Return all strings matching 'pattern' (a regex or callable) |
|
|||
1021 |
|
||||
1022 | This is case-insensitive. If prune is true, return all items |
|
|||
1023 | NOT matching the pattern. |
|
|||
1024 |
|
||||
1025 | If field is specified, the match must occur in the specified |
|
|||
1026 | whitespace-separated field. |
|
|||
1027 |
|
||||
1028 | Examples:: |
|
|||
1029 |
|
||||
1030 | a.grep( lambda x: x.startswith('C') ) |
|
|||
1031 | a.grep('Cha.*log', prune=1) |
|
|||
1032 | a.grep('chm', field=-1) |
|
|||
1033 | """ |
|
|||
1034 |
|
||||
1035 | def match_target(s): |
|
|||
1036 | if field is None: |
|
|||
1037 | return s |
|
|||
1038 | parts = s.split() |
|
|||
1039 | try: |
|
|||
1040 | tgt = parts[field] |
|
|||
1041 | return tgt |
|
|||
1042 | except IndexError: |
|
|||
1043 | return "" |
|
|||
1044 |
|
||||
1045 | if isinstance(pattern, basestring): |
|
|||
1046 | pred = lambda x : re.search(pattern, x, re.IGNORECASE) |
|
|||
1047 | else: |
|
|||
1048 | pred = pattern |
|
|||
1049 | if not prune: |
|
|||
1050 | return SList([el for el in self if pred(match_target(el))]) |
|
|||
1051 | else: |
|
|||
1052 | return SList([el for el in self if not pred(match_target(el))]) |
|
|||
1053 | def fields(self, *fields): |
|
|||
1054 | """ Collect whitespace-separated fields from string list |
|
|||
1055 |
|
||||
1056 | Allows quick awk-like usage of string lists. |
|
|||
1057 |
|
||||
1058 | Example data (in var a, created by 'a = !ls -l'):: |
|
|||
1059 | -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog |
|
|||
1060 | drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython |
|
|||
1061 |
|
||||
1062 | a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+'] |
|
|||
1063 | a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+'] |
|
|||
1064 | (note the joining by space). |
|
|||
1065 | a.fields(-1) is ['ChangeLog', 'IPython'] |
|
|||
1066 |
|
||||
1067 | IndexErrors are ignored. |
|
|||
1068 |
|
||||
1069 | Without args, fields() just split()'s the strings. |
|
|||
1070 | """ |
|
|||
1071 | if len(fields) == 0: |
|
|||
1072 | return [el.split() for el in self] |
|
|||
1073 |
|
||||
1074 | res = SList() |
|
|||
1075 | for el in [f.split() for f in self]: |
|
|||
1076 | lineparts = [] |
|
|||
1077 |
|
||||
1078 | for fd in fields: |
|
|||
1079 | try: |
|
|||
1080 | lineparts.append(el[fd]) |
|
|||
1081 | except IndexError: |
|
|||
1082 | pass |
|
|||
1083 | if lineparts: |
|
|||
1084 | res.append(" ".join(lineparts)) |
|
|||
1085 |
|
||||
1086 | return res |
|
|||
1087 | def sort(self,field= None, nums = False): |
|
|||
1088 | """ sort by specified fields (see fields()) |
|
|||
1089 |
|
||||
1090 | Example:: |
|
|||
1091 | a.sort(1, nums = True) |
|
|||
1092 |
|
||||
1093 | Sorts a by second field, in numerical order (so that 21 > 3) |
|
|||
1094 |
|
||||
1095 | """ |
|
|||
1096 |
|
||||
1097 | #decorate, sort, undecorate |
|
|||
1098 | if field is not None: |
|
|||
1099 | dsu = [[SList([line]).fields(field), line] for line in self] |
|
|||
1100 | else: |
|
|||
1101 | dsu = [[line, line] for line in self] |
|
|||
1102 | if nums: |
|
|||
1103 | for i in range(len(dsu)): |
|
|||
1104 | numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()]) |
|
|||
1105 | try: |
|
|||
1106 | n = int(numstr) |
|
|||
1107 | except ValueError: |
|
|||
1108 | n = 0; |
|
|||
1109 | dsu[i][0] = n |
|
|||
1110 |
|
||||
1111 |
|
||||
1112 | dsu.sort() |
|
|||
1113 | return SList([t[1] for t in dsu]) |
|
|||
1114 |
|
||||
1115 | def print_slist(arg): |
|
|||
1116 | """ Prettier (non-repr-like) and more informative printer for SList """ |
|
|||
1117 | print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):" |
|
|||
1118 | if hasattr(arg, 'hideonce') and arg.hideonce: |
|
|||
1119 | arg.hideonce = False |
|
|||
1120 | return |
|
|||
1121 |
|
||||
1122 | nlprint(arg) |
|
|||
1123 |
|
||||
1124 | print_slist = result_display.when_type(SList)(print_slist) |
|
|||
1125 |
|
||||
1126 |
|
||||
1127 |
|
||||
1128 | #---------------------------------------------------------------------------- |
|
|||
1129 | def esc_quotes(strng): |
|
|||
1130 | """Return the input string with single and double quotes escaped out""" |
|
|||
1131 |
|
||||
1132 | return strng.replace('"','\\"').replace("'","\\'") |
|
|||
1133 |
|
||||
1134 | #---------------------------------------------------------------------------- |
|
|||
1135 | def make_quoted_expr(s): |
|
|||
1136 | """Return string s in appropriate quotes, using raw string if possible. |
|
|||
1137 |
|
||||
1138 | XXX - example removed because it caused encoding errors in documentation |
|
|||
1139 | generation. We need a new example that doesn't contain invalid chars. |
|
|||
1140 |
|
||||
1141 | Note the use of raw string and padding at the end to allow trailing |
|
|||
1142 | backslash. |
|
|||
1143 | """ |
|
|||
1144 |
|
||||
1145 | tail = '' |
|
|||
1146 | tailpadding = '' |
|
|||
1147 | raw = '' |
|
|||
1148 | if "\\" in s: |
|
|||
1149 | raw = 'r' |
|
|||
1150 | if s.endswith('\\'): |
|
|||
1151 | tail = '[:-1]' |
|
|||
1152 | tailpadding = '_' |
|
|||
1153 | if '"' not in s: |
|
|||
1154 | quote = '"' |
|
|||
1155 | elif "'" not in s: |
|
|||
1156 | quote = "'" |
|
|||
1157 | elif '"""' not in s and not s.endswith('"'): |
|
|||
1158 | quote = '"""' |
|
|||
1159 | elif "'''" not in s and not s.endswith("'"): |
|
|||
1160 | quote = "'''" |
|
|||
1161 | else: |
|
|||
1162 | # give up, backslash-escaped string will do |
|
|||
1163 | return '"%s"' % esc_quotes(s) |
|
|||
1164 | res = raw + quote + s + tailpadding + quote + tail |
|
|||
1165 | return res |
|
|||
1166 |
|
||||
1167 |
|
||||
1168 | #---------------------------------------------------------------------------- |
|
|||
1169 | def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'): |
|
|||
1170 | """Take multiple lines of input. |
|
|||
1171 |
|
||||
1172 | A list with each line of input as a separate element is returned when a |
|
|||
1173 | termination string is entered (defaults to a single '.'). Input can also |
|
|||
1174 | terminate via EOF (^D in Unix, ^Z-RET in Windows). |
|
|||
1175 |
|
||||
1176 | Lines of input which end in \\ are joined into single entries (and a |
|
|||
1177 | secondary continuation prompt is issued as long as the user terminates |
|
|||
1178 | lines with \\). This allows entering very long strings which are still |
|
|||
1179 | meant to be treated as single entities. |
|
|||
1180 | """ |
|
|||
1181 |
|
||||
1182 | try: |
|
|||
1183 | if header: |
|
|||
1184 | header += '\n' |
|
|||
1185 | lines = [raw_input(header + ps1)] |
|
|||
1186 | except EOFError: |
|
|||
1187 | return [] |
|
|||
1188 | terminate = [terminate_str] |
|
|||
1189 | try: |
|
|||
1190 | while lines[-1:] != terminate: |
|
|||
1191 | new_line = raw_input(ps1) |
|
|||
1192 | while new_line.endswith('\\'): |
|
|||
1193 | new_line = new_line[:-1] + raw_input(ps2) |
|
|||
1194 | lines.append(new_line) |
|
|||
1195 |
|
||||
1196 | return lines[:-1] # don't return the termination command |
|
|||
1197 | except EOFError: |
|
|||
1198 |
|
||||
1199 | return lines |
|
|||
1200 |
|
||||
1201 | #---------------------------------------------------------------------------- |
|
|||
1202 | def raw_input_ext(prompt='', ps2='... '): |
|
|||
1203 | """Similar to raw_input(), but accepts extended lines if input ends with \\.""" |
|
|||
1204 |
|
||||
1205 | line = raw_input(prompt) |
|
|||
1206 | while line.endswith('\\'): |
|
|||
1207 | line = line[:-1] + raw_input(ps2) |
|
|||
1208 | return line |
|
|||
1209 |
|
||||
1210 | #---------------------------------------------------------------------------- |
|
|||
1211 | def ask_yes_no(prompt,default=None): |
|
|||
1212 | """Asks a question and returns a boolean (y/n) answer. |
|
|||
1213 |
|
||||
1214 | If default is given (one of 'y','n'), it is used if the user input is |
|
|||
1215 | empty. Otherwise the question is repeated until an answer is given. |
|
|||
1216 |
|
||||
1217 | An EOF is treated as the default answer. If there is no default, an |
|
|||
1218 | exception is raised to prevent infinite loops. |
|
|||
1219 |
|
||||
1220 | Valid answers are: y/yes/n/no (match is not case sensitive).""" |
|
|||
1221 |
|
||||
1222 | answers = {'y':True,'n':False,'yes':True,'no':False} |
|
|||
1223 | ans = None |
|
|||
1224 | while ans not in answers.keys(): |
|
|||
1225 | try: |
|
|||
1226 | ans = raw_input(prompt+' ').lower() |
|
|||
1227 | if not ans: # response was an empty string |
|
|||
1228 | ans = default |
|
|||
1229 | except KeyboardInterrupt: |
|
|||
1230 | pass |
|
|||
1231 | except EOFError: |
|
|||
1232 | if default in answers.keys(): |
|
|||
1233 | ans = default |
|
|||
1234 |
|
||||
1235 | else: |
|
|||
1236 | raise |
|
|||
1237 |
|
||||
1238 | return answers[ans] |
|
|||
1239 |
|
||||
1240 | #---------------------------------------------------------------------------- |
|
|||
1241 | class EvalDict: |
|
|||
1242 | """ |
|
|||
1243 | Emulate a dict which evaluates its contents in the caller's frame. |
|
|||
1244 |
|
||||
1245 | Usage: |
|
|||
1246 | >>> number = 19 |
|
|||
1247 |
|
||||
1248 | >>> text = "python" |
|
|||
1249 |
|
||||
1250 | >>> print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict() |
|
|||
1251 | Python 2.1 rules! |
|
|||
1252 | """ |
|
|||
1253 |
|
||||
1254 | # This version is due to sismex01@hebmex.com on c.l.py, and is basically a |
|
|||
1255 | # modified (shorter) version of: |
|
|||
1256 | # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66018 by |
|
|||
1257 | # Skip Montanaro (skip@pobox.com). |
|
|||
1258 |
|
||||
1259 | def __getitem__(self, name): |
|
|||
1260 | frame = sys._getframe(1) |
|
|||
1261 | return eval(name, frame.f_globals, frame.f_locals) |
|
|||
1262 |
|
||||
1263 | EvalString = EvalDict # for backwards compatibility |
|
|||
1264 | #---------------------------------------------------------------------------- |
|
|||
1265 | def qw(words,flat=0,sep=None,maxsplit=-1): |
|
|||
1266 | """Similar to Perl's qw() operator, but with some more options. |
|
|||
1267 |
|
||||
1268 | qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit) |
|
|||
1269 |
|
||||
1270 | words can also be a list itself, and with flat=1, the output will be |
|
|||
1271 | recursively flattened. |
|
|||
1272 |
|
||||
1273 | Examples: |
|
|||
1274 |
|
||||
1275 | >>> qw('1 2') |
|
|||
1276 | ['1', '2'] |
|
|||
1277 |
|
||||
1278 | >>> qw(['a b','1 2',['m n','p q']]) |
|
|||
1279 | [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]] |
|
|||
1280 |
|
||||
1281 | >>> qw(['a b','1 2',['m n','p q']],flat=1) |
|
|||
1282 | ['a', 'b', '1', '2', 'm', 'n', 'p', 'q'] |
|
|||
1283 | """ |
|
|||
1284 |
|
||||
1285 | if type(words) in StringTypes: |
|
|||
1286 | return [word.strip() for word in words.split(sep,maxsplit) |
|
|||
1287 | if word and not word.isspace() ] |
|
|||
1288 | if flat: |
|
|||
1289 | return flatten(map(qw,words,[1]*len(words))) |
|
|||
1290 | return map(qw,words) |
|
|||
1291 |
|
||||
1292 | #---------------------------------------------------------------------------- |
|
|||
1293 | def qwflat(words,sep=None,maxsplit=-1): |
|
|||
1294 | """Calls qw(words) in flat mode. It's just a convenient shorthand.""" |
|
|||
1295 | return qw(words,1,sep,maxsplit) |
|
|||
1296 |
|
||||
1297 | #---------------------------------------------------------------------------- |
|
|||
1298 | def qw_lol(indata): |
|
|||
1299 | """qw_lol('a b') -> [['a','b']], |
|
|||
1300 | otherwise it's just a call to qw(). |
|
|||
1301 |
|
||||
1302 | We need this to make sure the modules_some keys *always* end up as a |
|
|||
1303 | list of lists.""" |
|
|||
1304 |
|
||||
1305 | if type(indata) in StringTypes: |
|
|||
1306 | return [qw(indata)] |
|
|||
1307 | else: |
|
|||
1308 | return qw(indata) |
|
|||
1309 |
|
||||
1310 | #---------------------------------------------------------------------------- |
|
|||
1311 | def grep(pat,list,case=1): |
|
|||
1312 | """Simple minded grep-like function. |
|
|||
1313 | grep(pat,list) returns occurrences of pat in list, None on failure. |
|
|||
1314 |
|
||||
1315 | It only does simple string matching, with no support for regexps. Use the |
|
|||
1316 | option case=0 for case-insensitive matching.""" |
|
|||
1317 |
|
||||
1318 | # This is pretty crude. At least it should implement copying only references |
|
|||
1319 | # to the original data in case it's big. Now it copies the data for output. |
|
|||
1320 | out=[] |
|
|||
1321 | if case: |
|
|||
1322 | for term in list: |
|
|||
1323 | if term.find(pat)>-1: out.append(term) |
|
|||
1324 | else: |
|
|||
1325 | lpat=pat.lower() |
|
|||
1326 | for term in list: |
|
|||
1327 | if term.lower().find(lpat)>-1: out.append(term) |
|
|||
1328 |
|
||||
1329 | if len(out): return out |
|
|||
1330 | else: return None |
|
|||
1331 |
|
||||
1332 | #---------------------------------------------------------------------------- |
|
|||
1333 | def dgrep(pat,*opts): |
|
|||
1334 | """Return grep() on dir()+dir(__builtins__). |
|
|||
1335 |
|
||||
1336 | A very common use of grep() when working interactively.""" |
|
|||
1337 |
|
||||
1338 | return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts) |
|
|||
1339 |
|
||||
1340 | #---------------------------------------------------------------------------- |
|
|||
1341 | def idgrep(pat): |
|
|||
1342 | """Case-insensitive dgrep()""" |
|
|||
1343 |
|
||||
1344 | return dgrep(pat,0) |
|
|||
1345 |
|
||||
1346 | #---------------------------------------------------------------------------- |
|
|||
1347 | def igrep(pat,list): |
|
|||
1348 | """Synonym for case-insensitive grep.""" |
|
|||
1349 |
|
||||
1350 | return grep(pat,list,case=0) |
|
|||
1351 |
|
||||
1352 | #---------------------------------------------------------------------------- |
|
|||
1353 | def indent(str,nspaces=4,ntabs=0): |
|
|||
1354 | """Indent a string a given number of spaces or tabstops. |
|
|||
1355 |
|
||||
1356 | indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces. |
|
|||
1357 | """ |
|
|||
1358 | if str is None: |
|
|||
1359 | return |
|
|||
1360 | ind = '\t'*ntabs+' '*nspaces |
|
|||
1361 | outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind)) |
|
|||
1362 | if outstr.endswith(os.linesep+ind): |
|
|||
1363 | return outstr[:-len(ind)] |
|
|||
1364 | else: |
|
|||
1365 | return outstr |
|
|||
1366 |
|
||||
1367 | #----------------------------------------------------------------------------- |
|
|||
1368 | def native_line_ends(filename,backup=1): |
|
|||
1369 | """Convert (in-place) a file to line-ends native to the current OS. |
|
|||
1370 |
|
||||
1371 | If the optional backup argument is given as false, no backup of the |
|
|||
1372 | original file is left. """ |
|
|||
1373 |
|
||||
1374 | backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'} |
|
|||
1375 |
|
||||
1376 | bak_filename = filename + backup_suffixes[os.name] |
|
|||
1377 |
|
||||
1378 | original = open(filename).read() |
|
|||
1379 | shutil.copy2(filename,bak_filename) |
|
|||
1380 | try: |
|
|||
1381 | new = open(filename,'wb') |
|
|||
1382 | new.write(os.linesep.join(original.splitlines())) |
|
|||
1383 | new.write(os.linesep) # ALWAYS put an eol at the end of the file |
|
|||
1384 | new.close() |
|
|||
1385 | except: |
|
|||
1386 | os.rename(bak_filename,filename) |
|
|||
1387 | if not backup: |
|
|||
1388 | try: |
|
|||
1389 | os.remove(bak_filename) |
|
|||
1390 | except: |
|
|||
1391 | pass |
|
|||
1392 |
|
||||
1393 | #**************************************************************************** |
|
|||
1394 | # lists, dicts and structures |
|
|||
1395 |
|
||||
1396 | def belong(candidates,checklist): |
|
|||
1397 | """Check whether a list of items appear in a given list of options. |
|
|||
1398 |
|
||||
1399 | Returns a list of 1 and 0, one for each candidate given.""" |
|
|||
1400 |
|
||||
1401 | return [x in checklist for x in candidates] |
|
|||
1402 |
|
||||
1403 | #---------------------------------------------------------------------------- |
|
|||
1404 | def uniq_stable(elems): |
|
|||
1405 | """uniq_stable(elems) -> list |
|
|||
1406 |
|
||||
1407 | Return from an iterable, a list of all the unique elements in the input, |
|
|||
1408 | but maintaining the order in which they first appear. |
|
|||
1409 |
|
||||
1410 | A naive solution to this problem which just makes a dictionary with the |
|
|||
1411 | elements as keys fails to respect the stability condition, since |
|
|||
1412 | dictionaries are unsorted by nature. |
|
|||
1413 |
|
||||
1414 | Note: All elements in the input must be valid dictionary keys for this |
|
|||
1415 | routine to work, as it internally uses a dictionary for efficiency |
|
|||
1416 | reasons.""" |
|
|||
1417 |
|
||||
1418 | unique = [] |
|
|||
1419 | unique_dict = {} |
|
|||
1420 | for nn in elems: |
|
|||
1421 | if nn not in unique_dict: |
|
|||
1422 | unique.append(nn) |
|
|||
1423 | unique_dict[nn] = None |
|
|||
1424 | return unique |
|
|||
1425 |
|
||||
1426 | #---------------------------------------------------------------------------- |
|
|||
1427 | class NLprinter: |
|
|||
1428 | """Print an arbitrarily nested list, indicating index numbers. |
|
|||
1429 |
|
||||
1430 | An instance of this class called nlprint is available and callable as a |
|
|||
1431 | function. |
|
|||
1432 |
|
||||
1433 | nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent' |
|
|||
1434 | and using 'sep' to separate the index from the value. """ |
|
|||
1435 |
|
||||
1436 | def __init__(self): |
|
|||
1437 | self.depth = 0 |
|
|||
1438 |
|
||||
1439 | def __call__(self,lst,pos='',**kw): |
|
|||
1440 | """Prints the nested list numbering levels.""" |
|
|||
1441 | kw.setdefault('indent',' ') |
|
|||
1442 | kw.setdefault('sep',': ') |
|
|||
1443 | kw.setdefault('start',0) |
|
|||
1444 | kw.setdefault('stop',len(lst)) |
|
|||
1445 | # we need to remove start and stop from kw so they don't propagate |
|
|||
1446 | # into a recursive call for a nested list. |
|
|||
1447 | start = kw['start']; del kw['start'] |
|
|||
1448 | stop = kw['stop']; del kw['stop'] |
|
|||
1449 | if self.depth == 0 and 'header' in kw.keys(): |
|
|||
1450 | print kw['header'] |
|
|||
1451 |
|
||||
1452 | for idx in range(start,stop): |
|
|||
1453 | elem = lst[idx] |
|
|||
1454 | if type(elem)==type([]): |
|
|||
1455 | self.depth += 1 |
|
|||
1456 | self.__call__(elem,itpl('$pos$idx,'),**kw) |
|
|||
1457 | self.depth -= 1 |
|
|||
1458 | else: |
|
|||
1459 | printpl(kw['indent']*self.depth+'$pos$idx$kw["sep"]$elem') |
|
|||
1460 |
|
||||
1461 | nlprint = NLprinter() |
|
|||
1462 | #---------------------------------------------------------------------------- |
|
|||
1463 | def all_belong(candidates,checklist): |
|
|||
1464 | """Check whether a list of items ALL appear in a given list of options. |
|
|||
1465 |
|
||||
1466 | Returns a single 1 or 0 value.""" |
|
|||
1467 |
|
||||
1468 | return 1-(0 in [x in checklist for x in candidates]) |
|
|||
1469 |
|
||||
1470 | #---------------------------------------------------------------------------- |
|
|||
1471 | def sort_compare(lst1,lst2,inplace = 1): |
|
|||
1472 | """Sort and compare two lists. |
|
|||
1473 |
|
||||
1474 | By default it does it in place, thus modifying the lists. Use inplace = 0 |
|
|||
1475 | to avoid that (at the cost of temporary copy creation).""" |
|
|||
1476 | if not inplace: |
|
|||
1477 | lst1 = lst1[:] |
|
|||
1478 | lst2 = lst2[:] |
|
|||
1479 | lst1.sort(); lst2.sort() |
|
|||
1480 | return lst1 == lst2 |
|
|||
1481 |
|
||||
1482 | #---------------------------------------------------------------------------- |
|
|||
1483 | def list2dict(lst): |
|
|||
1484 | """Takes a list of (key,value) pairs and turns it into a dict.""" |
|
|||
1485 |
|
||||
1486 | dic = {} |
|
|||
1487 | for k,v in lst: dic[k] = v |
|
|||
1488 | return dic |
|
|||
1489 |
|
||||
1490 | #---------------------------------------------------------------------------- |
|
|||
1491 | def list2dict2(lst,default=''): |
|
|||
1492 | """Takes a list and turns it into a dict. |
|
|||
1493 | Much slower than list2dict, but more versatile. This version can take |
|
|||
1494 | lists with sublists of arbitrary length (including sclars).""" |
|
|||
1495 |
|
||||
1496 | dic = {} |
|
|||
1497 | for elem in lst: |
|
|||
1498 | if type(elem) in (types.ListType,types.TupleType): |
|
|||
1499 | size = len(elem) |
|
|||
1500 | if size == 0: |
|
|||
1501 | pass |
|
|||
1502 | elif size == 1: |
|
|||
1503 | dic[elem] = default |
|
|||
1504 | else: |
|
|||
1505 | k,v = elem[0], elem[1:] |
|
|||
1506 | if len(v) == 1: v = v[0] |
|
|||
1507 | dic[k] = v |
|
|||
1508 | else: |
|
|||
1509 | dic[elem] = default |
|
|||
1510 | return dic |
|
|||
1511 |
|
||||
1512 | #---------------------------------------------------------------------------- |
|
|||
1513 | def flatten(seq): |
|
|||
1514 | """Flatten a list of lists (NOT recursive, only works for 2d lists).""" |
|
|||
1515 |
|
||||
1516 | return [x for subseq in seq for x in subseq] |
|
|||
1517 |
|
||||
1518 | #---------------------------------------------------------------------------- |
|
|||
1519 | def get_slice(seq,start=0,stop=None,step=1): |
|
|||
1520 | """Get a slice of a sequence with variable step. Specify start,stop,step.""" |
|
|||
1521 | if stop == None: |
|
|||
1522 | stop = len(seq) |
|
|||
1523 | item = lambda i: seq[i] |
|
|||
1524 | return map(item,xrange(start,stop,step)) |
|
|||
1525 |
|
||||
1526 | #---------------------------------------------------------------------------- |
|
|||
1527 | def chop(seq,size): |
|
|||
1528 | """Chop a sequence into chunks of the given size.""" |
|
|||
1529 | chunk = lambda i: seq[i:i+size] |
|
|||
1530 | return map(chunk,xrange(0,len(seq),size)) |
|
|||
1531 |
|
||||
1532 | #---------------------------------------------------------------------------- |
|
|||
1533 | # with is a keyword as of python 2.5, so this function is renamed to withobj |
|
|||
1534 | # from its old 'with' name. |
|
|||
1535 | def with_obj(object, **args): |
|
|||
1536 | """Set multiple attributes for an object, similar to Pascal's with. |
|
|||
1537 |
|
||||
1538 | Example: |
|
|||
1539 | with_obj(jim, |
|
|||
1540 | born = 1960, |
|
|||
1541 | haircolour = 'Brown', |
|
|||
1542 | eyecolour = 'Green') |
|
|||
1543 |
|
||||
1544 | Credit: Greg Ewing, in |
|
|||
1545 | http://mail.python.org/pipermail/python-list/2001-May/040703.html. |
|
|||
1546 |
|
||||
1547 | NOTE: up until IPython 0.7.2, this was called simply 'with', but 'with' |
|
|||
1548 | has become a keyword for Python 2.5, so we had to rename it.""" |
|
|||
1549 |
|
||||
1550 | object.__dict__.update(args) |
|
|||
1551 |
|
||||
1552 | #---------------------------------------------------------------------------- |
|
|||
1553 | def setattr_list(obj,alist,nspace = None): |
|
|||
1554 | """Set a list of attributes for an object taken from a namespace. |
|
|||
1555 |
|
||||
1556 | setattr_list(obj,alist,nspace) -> sets in obj all the attributes listed in |
|
|||
1557 | alist with their values taken from nspace, which must be a dict (something |
|
|||
1558 | like locals() will often do) If nspace isn't given, locals() of the |
|
|||
1559 | *caller* is used, so in most cases you can omit it. |
|
|||
1560 |
|
||||
1561 | Note that alist can be given as a string, which will be automatically |
|
|||
1562 | split into a list on whitespace. If given as a list, it must be a list of |
|
|||
1563 | *strings* (the variable names themselves), not of variables.""" |
|
|||
1564 |
|
||||
1565 | # this grabs the local variables from the *previous* call frame -- that is |
|
|||
1566 | # the locals from the function that called setattr_list(). |
|
|||
1567 | # - snipped from weave.inline() |
|
|||
1568 | if nspace is None: |
|
|||
1569 | call_frame = sys._getframe().f_back |
|
|||
1570 | nspace = call_frame.f_locals |
|
|||
1571 |
|
||||
1572 | if type(alist) in StringTypes: |
|
|||
1573 | alist = alist.split() |
|
|||
1574 | for attr in alist: |
|
|||
1575 | val = eval(attr,nspace) |
|
|||
1576 | setattr(obj,attr,val) |
|
|||
1577 |
|
||||
1578 | #---------------------------------------------------------------------------- |
|
|||
1579 | def getattr_list(obj,alist,*args): |
|
|||
1580 | """getattr_list(obj,alist[, default]) -> attribute list. |
|
|||
1581 |
|
||||
1582 | Get a list of named attributes for an object. When a default argument is |
|
|||
1583 | given, it is returned when the attribute doesn't exist; without it, an |
|
|||
1584 | exception is raised in that case. |
|
|||
1585 |
|
||||
1586 | Note that alist can be given as a string, which will be automatically |
|
|||
1587 | split into a list on whitespace. If given as a list, it must be a list of |
|
|||
1588 | *strings* (the variable names themselves), not of variables.""" |
|
|||
1589 |
|
||||
1590 | if type(alist) in StringTypes: |
|
|||
1591 | alist = alist.split() |
|
|||
1592 | if args: |
|
|||
1593 | if len(args)==1: |
|
|||
1594 | default = args[0] |
|
|||
1595 | return map(lambda attr: getattr(obj,attr,default),alist) |
|
|||
1596 | else: |
|
|||
1597 | raise ValueError,'getattr_list() takes only one optional argument' |
|
|||
1598 | else: |
|
|||
1599 | return map(lambda attr: getattr(obj,attr),alist) |
|
|||
1600 |
|
||||
1601 | #---------------------------------------------------------------------------- |
|
|||
1602 | def map_method(method,object_list,*argseq,**kw): |
|
|||
1603 | """map_method(method,object_list,*args,**kw) -> list |
|
|||
1604 |
|
||||
1605 | Return a list of the results of applying the methods to the items of the |
|
|||
1606 | argument sequence(s). If more than one sequence is given, the method is |
|
|||
1607 | called with an argument list consisting of the corresponding item of each |
|
|||
1608 | sequence. All sequences must be of the same length. |
|
|||
1609 |
|
||||
1610 | Keyword arguments are passed verbatim to all objects called. |
|
|||
1611 |
|
||||
1612 | This is Python code, so it's not nearly as fast as the builtin map().""" |
|
|||
1613 |
|
||||
1614 | out_list = [] |
|
|||
1615 | idx = 0 |
|
|||
1616 | for object in object_list: |
|
|||
1617 | try: |
|
|||
1618 | handler = getattr(object, method) |
|
|||
1619 | except AttributeError: |
|
|||
1620 | out_list.append(None) |
|
|||
1621 | else: |
|
|||
1622 | if argseq: |
|
|||
1623 | args = map(lambda lst:lst[idx],argseq) |
|
|||
1624 | #print 'ob',object,'hand',handler,'ar',args # dbg |
|
|||
1625 | out_list.append(handler(args,**kw)) |
|
|||
1626 | else: |
|
|||
1627 | out_list.append(handler(**kw)) |
|
|||
1628 | idx += 1 |
|
|||
1629 | return out_list |
|
|||
1630 |
|
||||
1631 | #---------------------------------------------------------------------------- |
|
|||
1632 | def get_class_members(cls): |
|
|||
1633 | ret = dir(cls) |
|
|||
1634 | if hasattr(cls,'__bases__'): |
|
|||
1635 | for base in cls.__bases__: |
|
|||
1636 | ret.extend(get_class_members(base)) |
|
|||
1637 | return ret |
|
|||
1638 |
|
||||
1639 | #---------------------------------------------------------------------------- |
|
|||
1640 | def dir2(obj): |
|
|||
1641 | """dir2(obj) -> list of strings |
|
|||
1642 |
|
||||
1643 | Extended version of the Python builtin dir(), which does a few extra |
|
|||
1644 | checks, and supports common objects with unusual internals that confuse |
|
|||
1645 | dir(), such as Traits and PyCrust. |
|
|||
1646 |
|
||||
1647 | This version is guaranteed to return only a list of true strings, whereas |
|
|||
1648 | dir() returns anything that objects inject into themselves, even if they |
|
|||
1649 | are later not really valid for attribute access (many extension libraries |
|
|||
1650 | have such bugs). |
|
|||
1651 | """ |
|
|||
1652 |
|
||||
1653 | # Start building the attribute list via dir(), and then complete it |
|
|||
1654 | # with a few extra special-purpose calls. |
|
|||
1655 | words = dir(obj) |
|
|||
1656 |
|
||||
1657 | if hasattr(obj,'__class__'): |
|
|||
1658 | words.append('__class__') |
|
|||
1659 | words.extend(get_class_members(obj.__class__)) |
|
|||
1660 | #if '__base__' in words: 1/0 |
|
|||
1661 |
|
||||
1662 | # Some libraries (such as traits) may introduce duplicates, we want to |
|
|||
1663 | # track and clean this up if it happens |
|
|||
1664 | may_have_dupes = False |
|
|||
1665 |
|
||||
1666 | # this is the 'dir' function for objects with Enthought's traits |
|
|||
1667 | if hasattr(obj, 'trait_names'): |
|
|||
1668 | try: |
|
|||
1669 | words.extend(obj.trait_names()) |
|
|||
1670 | may_have_dupes = True |
|
|||
1671 | except TypeError: |
|
|||
1672 | # This will happen if `obj` is a class and not an instance. |
|
|||
1673 | pass |
|
|||
1674 |
|
||||
1675 | # Support for PyCrust-style _getAttributeNames magic method. |
|
|||
1676 | if hasattr(obj, '_getAttributeNames'): |
|
|||
1677 | try: |
|
|||
1678 | words.extend(obj._getAttributeNames()) |
|
|||
1679 | may_have_dupes = True |
|
|||
1680 | except TypeError: |
|
|||
1681 | # `obj` is a class and not an instance. Ignore |
|
|||
1682 | # this error. |
|
|||
1683 | pass |
|
|||
1684 |
|
||||
1685 | if may_have_dupes: |
|
|||
1686 | # eliminate possible duplicates, as some traits may also |
|
|||
1687 | # appear as normal attributes in the dir() call. |
|
|||
1688 | words = list(set(words)) |
|
|||
1689 | words.sort() |
|
|||
1690 |
|
||||
1691 | # filter out non-string attributes which may be stuffed by dir() calls |
|
|||
1692 | # and poor coding in third-party modules |
|
|||
1693 | return [w for w in words if isinstance(w, basestring)] |
|
|||
1694 |
|
||||
1695 | #---------------------------------------------------------------------------- |
|
|||
1696 | def import_fail_info(mod_name,fns=None): |
|
|||
1697 | """Inform load failure for a module.""" |
|
|||
1698 |
|
||||
1699 | if fns == None: |
|
|||
1700 | warn("Loading of %s failed.\n" % (mod_name,)) |
|
|||
1701 | else: |
|
|||
1702 | warn("Loading of %s from %s failed.\n" % (fns,mod_name)) |
|
|||
1703 |
|
||||
1704 | #---------------------------------------------------------------------------- |
|
|||
1705 | # Proposed popitem() extension, written as a method |
|
|||
1706 |
|
||||
1707 |
|
||||
1708 | class NotGiven: pass |
|
|||
1709 |
|
||||
1710 | def popkey(dct,key,default=NotGiven): |
|
|||
1711 | """Return dct[key] and delete dct[key]. |
|
|||
1712 |
|
||||
1713 | If default is given, return it if dct[key] doesn't exist, otherwise raise |
|
|||
1714 | KeyError. """ |
|
|||
1715 |
|
||||
1716 | try: |
|
|||
1717 | val = dct[key] |
|
|||
1718 | except KeyError: |
|
|||
1719 | if default is NotGiven: |
|
|||
1720 | raise |
|
|||
1721 | else: |
|
|||
1722 | return default |
|
|||
1723 | else: |
|
|||
1724 | del dct[key] |
|
|||
1725 | return val |
|
|||
1726 |
|
||||
1727 | def wrap_deprecated(func, suggest = '<nothing>'): |
|
|||
1728 | def newFunc(*args, **kwargs): |
|
|||
1729 | warnings.warn("Call to deprecated function %s, use %s instead" % |
|
|||
1730 | ( func.__name__, suggest), |
|
|||
1731 | category=DeprecationWarning, |
|
|||
1732 | stacklevel = 2) |
|
|||
1733 | return func(*args, **kwargs) |
|
|||
1734 | return newFunc |
|
|||
1735 |
|
||||
1736 |
|
||||
1737 | def _num_cpus_unix(): |
|
|||
1738 | """Return the number of active CPUs on a Unix system.""" |
|
|||
1739 | return os.sysconf("SC_NPROCESSORS_ONLN") |
|
|||
1740 |
|
||||
1741 |
|
||||
1742 | def _num_cpus_darwin(): |
|
|||
1743 | """Return the number of active CPUs on a Darwin system.""" |
|
|||
1744 | p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE) |
|
|||
1745 | return p.stdout.read() |
|
|||
1746 |
|
||||
1747 |
|
||||
1748 | def _num_cpus_windows(): |
|
|||
1749 | """Return the number of active CPUs on a Windows system.""" |
|
|||
1750 | return os.environ.get("NUMBER_OF_PROCESSORS") |
|
|||
1751 |
|
||||
1752 |
|
||||
1753 | def num_cpus(): |
|
|||
1754 | """Return the effective number of CPUs in the system as an integer. |
|
|||
1755 |
|
||||
1756 | This cross-platform function makes an attempt at finding the total number of |
|
|||
1757 | available CPUs in the system, as returned by various underlying system and |
|
|||
1758 | python calls. |
|
|||
1759 |
|
||||
1760 | If it can't find a sensible answer, it returns 1 (though an error *may* make |
|
|||
1761 | it return a large positive number that's actually incorrect). |
|
|||
1762 | """ |
|
|||
1763 |
|
||||
1764 | # Many thanks to the Parallel Python project (http://www.parallelpython.com) |
|
|||
1765 | # for the names of the keys we needed to look up for this function. This |
|
|||
1766 | # code was inspired by their equivalent function. |
|
|||
1767 |
|
||||
1768 | ncpufuncs = {'Linux':_num_cpus_unix, |
|
|||
1769 | 'Darwin':_num_cpus_darwin, |
|
|||
1770 | 'Windows':_num_cpus_windows, |
|
|||
1771 | # On Vista, python < 2.5.2 has a bug and returns 'Microsoft' |
|
|||
1772 | # See http://bugs.python.org/issue1082 for details. |
|
|||
1773 | 'Microsoft':_num_cpus_windows, |
|
|||
1774 | } |
|
|||
1775 |
|
||||
1776 | ncpufunc = ncpufuncs.get(platform.system(), |
|
|||
1777 | # default to unix version (Solaris, AIX, etc) |
|
|||
1778 | _num_cpus_unix) |
|
|||
1779 |
|
||||
1780 | try: |
|
|||
1781 | ncpus = max(1,int(ncpufunc())) |
|
|||
1782 | except: |
|
|||
1783 | ncpus = 1 |
|
|||
1784 | return ncpus |
|
|||
1785 |
|
||||
1786 | def extract_vars(*names,**kw): |
|
|||
1787 | """Extract a set of variables by name from another frame. |
|
|||
1788 |
|
||||
1789 | :Parameters: |
|
|||
1790 | - `*names`: strings |
|
|||
1791 | One or more variable names which will be extracted from the caller's |
|
|||
1792 | frame. |
|
|||
1793 |
|
||||
1794 | :Keywords: |
|
|||
1795 | - `depth`: integer (0) |
|
|||
1796 | How many frames in the stack to walk when looking for your variables. |
|
|||
1797 |
|
||||
1798 |
|
||||
1799 | Examples: |
|
|||
1800 |
|
||||
1801 | In [2]: def func(x): |
|
|||
1802 | ...: y = 1 |
|
|||
1803 | ...: print extract_vars('x','y') |
|
|||
1804 | ...: |
|
|||
1805 |
|
||||
1806 | In [3]: func('hello') |
|
|||
1807 | {'y': 1, 'x': 'hello'} |
|
|||
1808 | """ |
|
|||
1809 |
|
||||
1810 | depth = kw.get('depth',0) |
|
|||
1811 |
|
||||
1812 | callerNS = sys._getframe(depth+1).f_locals |
|
|||
1813 | return dict((k,callerNS[k]) for k in names) |
|
|||
1814 |
|
||||
1815 |
|
||||
1816 | def extract_vars_above(*names): |
|
|||
1817 | """Extract a set of variables by name from another frame. |
|
|||
1818 |
|
||||
1819 | Similar to extractVars(), but with a specified depth of 1, so that names |
|
|||
1820 | are exctracted exactly from above the caller. |
|
|||
1821 |
|
||||
1822 | This is simply a convenience function so that the very common case (for us) |
|
|||
1823 | of skipping exactly 1 frame doesn't have to construct a special dict for |
|
|||
1824 | keyword passing.""" |
|
|||
1825 |
|
||||
1826 | callerNS = sys._getframe(2).f_locals |
|
|||
1827 | return dict((k,callerNS[k]) for k in names) |
|
|||
1828 |
|
||||
1829 | def expand_path(s): |
|
|||
1830 | """Expand $VARS and ~names in a string, like a shell |
|
|||
1831 |
|
||||
1832 | :Examples: |
|
|||
1833 |
|
||||
1834 | In [2]: os.environ['FOO']='test' |
|
|||
1835 |
|
||||
1836 | In [3]: expand_path('variable FOO is $FOO') |
|
|||
1837 | Out[3]: 'variable FOO is test' |
|
|||
1838 | """ |
|
|||
1839 | # This is a pretty subtle hack. When expand user is given a UNC path |
|
|||
1840 | # on Windows (\\server\share$\%username%), os.path.expandvars, removes |
|
|||
1841 | # the $ to get (\\server\share\%username%). I think it considered $ |
|
|||
1842 | # alone an empty var. But, we need the $ to remains there (it indicates |
|
|||
1843 | # a hidden share). |
|
|||
1844 | if os.name=='nt': |
|
|||
1845 | s = s.replace('$\\', 'IPYTHON_TEMP') |
|
|||
1846 | s = os.path.expandvars(os.path.expanduser(s)) |
|
|||
1847 | if os.name=='nt': |
|
|||
1848 | s = s.replace('IPYTHON_TEMP', '$\\') |
|
|||
1849 | return s |
|
|||
1850 |
|
||||
1851 | def list_strings(arg): |
|
|||
1852 | """Always return a list of strings, given a string or list of strings |
|
|||
1853 | as input. |
|
|||
1854 |
|
||||
1855 | :Examples: |
|
|||
1856 |
|
||||
1857 | In [7]: list_strings('A single string') |
|
|||
1858 | Out[7]: ['A single string'] |
|
|||
1859 |
|
||||
1860 | In [8]: list_strings(['A single string in a list']) |
|
|||
1861 | Out[8]: ['A single string in a list'] |
|
|||
1862 |
|
||||
1863 | In [9]: list_strings(['A','list','of','strings']) |
|
|||
1864 | Out[9]: ['A', 'list', 'of', 'strings'] |
|
|||
1865 | """ |
|
|||
1866 |
|
||||
1867 | if isinstance(arg,basestring): return [arg] |
|
|||
1868 | else: return arg |
|
|||
1869 |
|
||||
1870 |
|
||||
1871 | #---------------------------------------------------------------------------- |
|
|||
1872 | def marquee(txt='',width=78,mark='*'): |
|
|||
1873 | """Return the input string centered in a 'marquee'. |
|
|||
1874 |
|
||||
1875 | :Examples: |
|
|||
1876 |
|
||||
1877 | In [16]: marquee('A test',40) |
|
|||
1878 | Out[16]: '**************** A test ****************' |
|
|||
1879 |
|
||||
1880 | In [17]: marquee('A test',40,'-') |
|
|||
1881 | Out[17]: '---------------- A test ----------------' |
|
|||
1882 |
|
||||
1883 | In [18]: marquee('A test',40,' ') |
|
|||
1884 | Out[18]: ' A test ' |
|
|||
1885 |
|
||||
1886 | """ |
|
|||
1887 | if not txt: |
|
|||
1888 | return (mark*width)[:width] |
|
|||
1889 | nmark = (width-len(txt)-2)/len(mark)/2 |
|
|||
1890 | if nmark < 0: nmark =0 |
|
|||
1891 | marks = mark*nmark |
|
|||
1892 | return '%s %s %s' % (marks,txt,marks) |
|
|||
1893 |
|
||||
1894 | #*************************** end of file <genutils.py> ********************** |
|
@@ -1,102 +0,0 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
|||
2 | """ Proxy module for accessing platform specific utility functions. |
|
|||
3 |
|
||||
4 | Importing this module should give you the implementations that are correct |
|
|||
5 | for your operation system, from platutils_PLATFORMNAME module. |
|
|||
6 | """ |
|
|||
7 |
|
||||
8 | #***************************************************************************** |
|
|||
9 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> |
|
|||
10 | # |
|
|||
11 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
12 | # the file COPYING, distributed as part of this software. |
|
|||
13 | #***************************************************************************** |
|
|||
14 |
|
||||
15 | import os |
|
|||
16 | import sys |
|
|||
17 | import warnings |
|
|||
18 |
|
||||
19 | # Import the platform-specific implementations |
|
|||
20 | if os.name == 'posix': |
|
|||
21 | import platutils_posix as _platutils |
|
|||
22 | elif sys.platform == 'win32': |
|
|||
23 | import platutils_win32 as _platutils |
|
|||
24 | else: |
|
|||
25 | import platutils_dummy as _platutils |
|
|||
26 |
|
||||
27 | # Functionality that's logically common to all platforms goes here, each |
|
|||
28 | # platform-specific module only provides the bits that are OS-dependent. |
|
|||
29 |
|
||||
30 | # XXX - I'm still not happy with a module global for this, but at least now |
|
|||
31 | # there is a public, cross-platform way of toggling the term title control on |
|
|||
32 | # and off. We should make this a stateful object later on so that each user |
|
|||
33 | # can have its own instance if needed. |
|
|||
34 | def term_clear(): |
|
|||
35 | _platutils.term_clear() |
|
|||
36 |
|
||||
37 | def toggle_set_term_title(val): |
|
|||
38 | """Control whether set_term_title is active or not. |
|
|||
39 |
|
||||
40 | set_term_title() allows writing to the console titlebar. In embedded |
|
|||
41 | widgets this can cause problems, so this call can be used to toggle it on |
|
|||
42 | or off as needed. |
|
|||
43 |
|
||||
44 | The default state of the module is for the function to be disabled. |
|
|||
45 |
|
||||
46 | Parameters |
|
|||
47 | ---------- |
|
|||
48 | val : bool |
|
|||
49 | If True, set_term_title() actually writes to the terminal (using the |
|
|||
50 | appropriate platform-specific module). If False, it is a no-op. |
|
|||
51 | """ |
|
|||
52 | _platutils.ignore_termtitle = not(val) |
|
|||
53 |
|
||||
54 |
|
||||
55 | def set_term_title(title): |
|
|||
56 | """Set terminal title using the necessary platform-dependent calls.""" |
|
|||
57 | if _platutils.ignore_termtitle: |
|
|||
58 | return |
|
|||
59 | _platutils.set_term_title(title) |
|
|||
60 |
|
||||
61 |
|
||||
62 | class FindCmdError(Exception): |
|
|||
63 | pass |
|
|||
64 |
|
||||
65 | def find_cmd(cmd): |
|
|||
66 | """Find full path to executable cmd in a cross platform manner. |
|
|||
67 |
|
||||
68 | This function tries to determine the full path to a command line program |
|
|||
69 | using `which` on Unix/Linux/OS X and `win32api` on Windows. Most of the |
|
|||
70 | time it will use the version that is first on the users `PATH`. If |
|
|||
71 | cmd is `python` return `sys.executable`. |
|
|||
72 |
|
||||
73 | Parameters |
|
|||
74 | ---------- |
|
|||
75 | cmd : str |
|
|||
76 | The command line program to look for. |
|
|||
77 | """ |
|
|||
78 | if cmd == 'python': |
|
|||
79 | return sys.executable |
|
|||
80 | try: |
|
|||
81 | path = _platutils.find_cmd(cmd) |
|
|||
82 | except OSError: |
|
|||
83 | raise FindCmdError('command could not be found: %s' % cmd) |
|
|||
84 | # which returns empty if not found |
|
|||
85 | if path == '': |
|
|||
86 | raise FindCmdError('command could not be found: %s' % cmd) |
|
|||
87 | return path |
|
|||
88 |
|
||||
89 | def get_long_path_name(path): |
|
|||
90 | """Expand a path into its long form. |
|
|||
91 |
|
||||
92 | On Windows this expands any ~ in the paths. On other platforms, it is |
|
|||
93 | a null operation. |
|
|||
94 | """ |
|
|||
95 | return _platutils.get_long_path_name(path) |
|
|||
96 |
|
||||
97 | #----------------------------------------------------------------------------- |
|
|||
98 | # Deprecated functions |
|
|||
99 | #----------------------------------------------------------------------------- |
|
|||
100 | def freeze_term_title(): |
|
|||
101 | warnings.warn("This function is deprecated, use toggle_set_term_title()") |
|
|||
102 | _platutils.ignore_termtitle = True |
|
@@ -1,33 +0,0 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
|||
2 | """ Platform specific utility functions, dummy version |
|
|||
3 |
|
||||
4 | This has empty implementation of the platutils functions, used for |
|
|||
5 | unsupported operating systems. |
|
|||
6 |
|
||||
7 | Authors |
|
|||
8 | ------- |
|
|||
9 | - Ville Vainio <vivainio@gmail.com> |
|
|||
10 | """ |
|
|||
11 |
|
||||
12 | #***************************************************************************** |
|
|||
13 | # Copyright (C) 2008-2009 The IPython Development Team |
|
|||
14 | # Copyright (C) 2001-2007 Fernando Perez <fperez@colorado.edu> |
|
|||
15 | # |
|
|||
16 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
17 | # the file COPYING, distributed as part of this software. |
|
|||
18 | #***************************************************************************** |
|
|||
19 |
|
||||
20 | # This variable is part of the expected API of the module: |
|
|||
21 | ignore_termtitle = True |
|
|||
22 |
|
||||
23 | def set_term_title(*args,**kw): |
|
|||
24 | """Dummy no-op.""" |
|
|||
25 | pass |
|
|||
26 |
|
||||
27 | def find_cmd(cmd): |
|
|||
28 | """Find the full path to a command using which.""" |
|
|||
29 | return os.popen('which %s' % cmd).read().strip() |
|
|||
30 |
|
||||
31 | def get_long_path_name(path): |
|
|||
32 | """Dummy no-op.""" |
|
|||
33 | return path |
|
@@ -1,61 +0,0 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
|||
2 | """ Platform specific utility functions, posix version |
|
|||
3 |
|
||||
4 | Importing this module directly is not portable - rather, import platutils |
|
|||
5 | to use these functions in platform agnostic fashion. |
|
|||
6 | """ |
|
|||
7 |
|
||||
8 | #***************************************************************************** |
|
|||
9 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> |
|
|||
10 | # |
|
|||
11 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
12 | # the file COPYING, distributed as part of this software. |
|
|||
13 | #***************************************************************************** |
|
|||
14 | #----------------------------------------------------------------------------- |
|
|||
15 | # Imports |
|
|||
16 | #----------------------------------------------------------------------------- |
|
|||
17 | from __future__ import absolute_import |
|
|||
18 |
|
||||
19 | import sys |
|
|||
20 | import os |
|
|||
21 |
|
||||
22 | from .baseutils import getoutputerror |
|
|||
23 |
|
||||
24 | #----------------------------------------------------------------------------- |
|
|||
25 | # Globals |
|
|||
26 | #----------------------------------------------------------------------------- |
|
|||
27 |
|
||||
28 | ignore_termtitle = True |
|
|||
29 |
|
||||
30 | #----------------------------------------------------------------------------- |
|
|||
31 | # Functions |
|
|||
32 | #----------------------------------------------------------------------------- |
|
|||
33 |
|
||||
34 | def _dummy_op(*a, **b): |
|
|||
35 | """ A no-op function """ |
|
|||
36 |
|
||||
37 |
|
||||
38 | def _set_term_title_xterm(title): |
|
|||
39 | """ Change virtual terminal title in xterm-workalikes """ |
|
|||
40 | sys.stdout.write('\033]0;%s\007' % title) |
|
|||
41 |
|
||||
42 | TERM = os.environ.get('TERM','') |
|
|||
43 |
|
||||
44 | if (TERM == 'xterm') or (TERM == 'xterm-color'): |
|
|||
45 | set_term_title = _set_term_title_xterm |
|
|||
46 | else: |
|
|||
47 | set_term_title = _dummy_op |
|
|||
48 |
|
||||
49 |
|
||||
50 | def find_cmd(cmd): |
|
|||
51 | """Find the full path to a command using which.""" |
|
|||
52 | return getoutputerror('/usr/bin/env which %s' % cmd)[0] |
|
|||
53 |
|
||||
54 |
|
||||
55 | def get_long_path_name(path): |
|
|||
56 | """Dummy no-op.""" |
|
|||
57 | return path |
|
|||
58 |
|
||||
59 |
|
||||
60 | def term_clear(): |
|
|||
61 | os.system('clear') |
|
@@ -1,95 +0,0 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
|||
2 | """ Platform specific utility functions, win32 version |
|
|||
3 |
|
||||
4 | Importing this module directly is not portable - rather, import platutils |
|
|||
5 | to use these functions in platform agnostic fashion. |
|
|||
6 | """ |
|
|||
7 |
|
||||
8 | #***************************************************************************** |
|
|||
9 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> |
|
|||
10 | # |
|
|||
11 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
12 | # the file COPYING, distributed as part of this software. |
|
|||
13 | #***************************************************************************** |
|
|||
14 |
|
||||
15 | import os |
|
|||
16 |
|
||||
17 | ignore_termtitle = True |
|
|||
18 |
|
||||
19 | try: |
|
|||
20 | import ctypes |
|
|||
21 |
|
||||
22 | SetConsoleTitleW = ctypes.windll.kernel32.SetConsoleTitleW |
|
|||
23 | SetConsoleTitleW.argtypes = [ctypes.c_wchar_p] |
|
|||
24 |
|
||||
25 | def set_term_title(title): |
|
|||
26 | """Set terminal title using ctypes to access the Win32 APIs.""" |
|
|||
27 | SetConsoleTitleW(title) |
|
|||
28 |
|
||||
29 |
|
||||
30 | except ImportError: |
|
|||
31 | def set_term_title(title): |
|
|||
32 | """Set terminal title using the 'title' command.""" |
|
|||
33 | global ignore_termtitle |
|
|||
34 |
|
||||
35 | try: |
|
|||
36 | # Cannot be on network share when issuing system commands |
|
|||
37 | curr = os.getcwd() |
|
|||
38 | os.chdir("C:") |
|
|||
39 | ret = os.system("title " + title) |
|
|||
40 | finally: |
|
|||
41 | os.chdir(curr) |
|
|||
42 | if ret: |
|
|||
43 | # non-zero return code signals error, don't try again |
|
|||
44 | ignore_termtitle = True |
|
|||
45 |
|
||||
46 |
|
||||
47 | def find_cmd(cmd): |
|
|||
48 | """Find the full path to a .bat or .exe using the win32api module.""" |
|
|||
49 | try: |
|
|||
50 | from win32api import SearchPath |
|
|||
51 | except ImportError: |
|
|||
52 | raise ImportError('you need to have pywin32 installed for this to work') |
|
|||
53 | else: |
|
|||
54 | PATH = os.environ['PATH'] |
|
|||
55 | extensions = ['.exe', '.com', '.bat', '.py'] |
|
|||
56 | path = None |
|
|||
57 | for ext in extensions: |
|
|||
58 | try: |
|
|||
59 | path = SearchPath(PATH,cmd + ext)[0] |
|
|||
60 | except: |
|
|||
61 | pass |
|
|||
62 | if path is None: |
|
|||
63 | raise OSError("command %r not found" % cmd) |
|
|||
64 | else: |
|
|||
65 | return path |
|
|||
66 |
|
||||
67 |
|
||||
68 | def get_long_path_name(path): |
|
|||
69 | """Get a long path name (expand ~) on Windows using ctypes. |
|
|||
70 |
|
||||
71 | Examples |
|
|||
72 | -------- |
|
|||
73 |
|
||||
74 | >>> get_long_path_name('c:\\docume~1') |
|
|||
75 | u'c:\\\\Documents and Settings' |
|
|||
76 |
|
||||
77 | """ |
|
|||
78 | try: |
|
|||
79 | import ctypes |
|
|||
80 | except ImportError: |
|
|||
81 | raise ImportError('you need to have ctypes installed for this to work') |
|
|||
82 | _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW |
|
|||
83 | _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p, |
|
|||
84 | ctypes.c_uint ] |
|
|||
85 |
|
||||
86 | buf = ctypes.create_unicode_buffer(260) |
|
|||
87 | rv = _GetLongPathName(path, buf, 260) |
|
|||
88 | if rv == 0 or rv > 260: |
|
|||
89 | return path |
|
|||
90 | else: |
|
|||
91 | return buf.value |
|
|||
92 |
|
||||
93 |
|
||||
94 | def term_clear(): |
|
|||
95 | os.system('cls') |
|
@@ -1,46 +0,0 b'' | |||||
1 | """Set of functions to work with console on Windows. |
|
|||
2 | """ |
|
|||
3 |
|
||||
4 | #***************************************************************************** |
|
|||
5 | # Copyright (C) 2005 Alexander Belchenko <bialix@ukr.net> |
|
|||
6 | # |
|
|||
7 | # This file is placed in the public domain. |
|
|||
8 | # |
|
|||
9 | #***************************************************************************** |
|
|||
10 |
|
||||
11 | __author__ = 'Alexander Belchenko (e-mail: bialix AT ukr.net)' |
|
|||
12 | __license__ = 'Public domain' |
|
|||
13 |
|
||||
14 | import struct |
|
|||
15 |
|
||||
16 | try: |
|
|||
17 | import ctypes |
|
|||
18 | except ImportError: |
|
|||
19 | ctypes = None |
|
|||
20 |
|
||||
21 | def get_console_size(defaultx=80, defaulty=25): |
|
|||
22 | """ Return size of current console. |
|
|||
23 |
|
||||
24 | This function try to determine actual size of current working |
|
|||
25 | console window and return tuple (sizex, sizey) if success, |
|
|||
26 | or default size (defaultx, defaulty) otherwise. |
|
|||
27 |
|
||||
28 | Dependencies: ctypes should be installed. |
|
|||
29 | """ |
|
|||
30 | if ctypes is None: |
|
|||
31 | # no ctypes is found |
|
|||
32 | return (defaultx, defaulty) |
|
|||
33 |
|
||||
34 | h = ctypes.windll.kernel32.GetStdHandle(-11) |
|
|||
35 | csbi = ctypes.create_string_buffer(22) |
|
|||
36 | res = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) |
|
|||
37 |
|
||||
38 | if res: |
|
|||
39 | (bufx, bufy, curx, cury, wattr, |
|
|||
40 | left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", |
|
|||
41 | csbi.raw) |
|
|||
42 | sizex = right - left + 1 |
|
|||
43 | sizey = bottom - top + 1 |
|
|||
44 | return (sizex, sizey) |
|
|||
45 | else: |
|
|||
46 | return (defaultx, defaulty) |
|
@@ -1,26 +0,0 b'' | |||||
1 | #!/usr/bin/env python |
|
|||
2 | """Test script for IPython. |
|
|||
3 |
|
||||
4 | The actual ipython test script to be installed with 'python setup.py install' |
|
|||
5 | is in './scripts' directory, and will test IPython from an importable |
|
|||
6 | location. |
|
|||
7 |
|
||||
8 | This file is here (ipython source root directory) to facilitate non-root |
|
|||
9 | 'zero-installation testing and development' (just copy the source tree |
|
|||
10 | somewhere and run iptest.py). |
|
|||
11 |
|
||||
12 | You can run this script directly, type -h to see all options.""" |
|
|||
13 |
|
||||
14 | # Ensure that the imported IPython packages come from *THIS* IPython, not some |
|
|||
15 | # other one that may exist system-wide |
|
|||
16 | import os, sys |
|
|||
17 | this_dir = os.path.dirname(os.path.abspath(__file__)) |
|
|||
18 | sys.path.insert(0, this_dir) |
|
|||
19 |
|
||||
20 | import IPython.testing.tools as t |
|
|||
21 | import IPython.testing.iptest as ipt |
|
|||
22 | t.INSTALLED = False |
|
|||
23 | ipt.INSTALLED = False |
|
|||
24 |
|
||||
25 | # Now proceed with execution |
|
|||
26 | execfile(os.path.join(this_dir, 'IPython', 'scripts', 'iptest')) |
|
General Comments 0
You need to be logged in to leave comments.
Login now