Show More
@@ -1,178 +1,180 b'' | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 | # -*- test-case-name: IPython.kernel.test.test_contexts -*- |
|
2 | # -*- test-case-name: IPython.kernel.test.test_contexts -*- | |
3 | """Context managers for IPython. |
|
3 | """Context managers for IPython. | |
4 |
|
4 | |||
5 | Python 2.5 introduced the `with` statement, which is based on the context |
|
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, |
|
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 |
|
7 | which can also be useful as templates for writing new, application-specific | |
8 | managers. |
|
8 | managers. | |
9 | """ |
|
9 | """ | |
10 |
|
10 | |||
11 | from __future__ import with_statement |
|
11 | from __future__ import with_statement | |
12 |
|
12 | |||
13 | __docformat__ = "restructuredtext en" |
|
13 | __docformat__ = "restructuredtext en" | |
14 |
|
14 | |||
15 | #------------------------------------------------------------------------------- |
|
15 | #------------------------------------------------------------------------------- | |
16 | # Copyright (C) 2008 The IPython Development Team |
|
16 | # Copyright (C) 2008 The IPython Development Team | |
17 | # |
|
17 | # | |
18 | # Distributed under the terms of the BSD License. The full license is in |
|
18 | # Distributed under the terms of the BSD License. The full license is in | |
19 | # the file COPYING, distributed as part of this software. |
|
19 | # the file COPYING, distributed as part of this software. | |
20 | #------------------------------------------------------------------------------- |
|
20 | #------------------------------------------------------------------------------- | |
21 |
|
21 | |||
22 | #------------------------------------------------------------------------------- |
|
22 | #------------------------------------------------------------------------------- | |
23 | # Imports |
|
23 | # Imports | |
24 | #------------------------------------------------------------------------------- |
|
24 | #------------------------------------------------------------------------------- | |
25 |
|
25 | |||
26 | import linecache |
|
26 | import linecache | |
27 | import sys |
|
27 | import sys | |
28 |
|
28 | |||
29 | from twisted.internet.error import ConnectionRefusedError |
|
29 | from twisted.internet.error import ConnectionRefusedError | |
30 |
|
30 | |||
31 | from IPython.ultraTB import _fixed_getinnerframes, findsource |
|
31 | from IPython.ultraTB import _fixed_getinnerframes, findsource | |
32 | from IPython import ipapi |
|
32 | from IPython import ipapi | |
33 |
|
33 | |||
34 | from IPython.kernel import error |
|
34 | from IPython.kernel import error | |
35 |
|
35 | |||
36 | #--------------------------------------------------------------------------- |
|
36 | #--------------------------------------------------------------------------- | |
37 | # Utility functions needed by all context managers. |
|
37 | # Utility functions needed by all context managers. | |
38 | #--------------------------------------------------------------------------- |
|
38 | #--------------------------------------------------------------------------- | |
39 |
|
39 | |||
40 | def remote(): |
|
40 | def remote(): | |
41 | """Raises a special exception meant to be caught by context managers. |
|
41 | """Raises a special exception meant to be caught by context managers. | |
42 | """ |
|
42 | """ | |
43 | m = 'Special exception to stop local execution of parallel code.' |
|
43 | m = 'Special exception to stop local execution of parallel code.' | |
44 | raise error.StopLocalExecution(m) |
|
44 | raise error.StopLocalExecution(m) | |
45 |
|
45 | |||
46 |
|
46 | |||
47 | def strip_whitespace(source,require_remote=True): |
|
47 | def strip_whitespace(source,require_remote=True): | |
48 | """strip leading whitespace from input source. |
|
48 | """strip leading whitespace from input source. | |
49 |
|
49 | |||
50 | :Parameters: |
|
50 | :Parameters: | |
51 |
|
51 | |||
52 | """ |
|
52 | """ | |
53 | remote_mark = 'remote()' |
|
53 | remote_mark = 'remote()' | |
54 | # Expand tabs to avoid any confusion. |
|
54 | # Expand tabs to avoid any confusion. | |
55 | wsource = [l.expandtabs(4) for l in source] |
|
55 | wsource = [l.expandtabs(4) for l in source] | |
56 | # Detect the indentation level |
|
56 | # Detect the indentation level | |
57 | done = False |
|
57 | done = False | |
58 | for line in wsource: |
|
58 | for line in wsource: | |
59 | if line.isspace(): |
|
59 | if line.isspace(): | |
60 | continue |
|
60 | continue | |
61 | for col,char in enumerate(line): |
|
61 | for col,char in enumerate(line): | |
62 | if char != ' ': |
|
62 | if char != ' ': | |
63 | done = True |
|
63 | done = True | |
64 | break |
|
64 | break | |
65 | if done: |
|
65 | if done: | |
66 | break |
|
66 | break | |
67 | # Now we know how much leading space there is in the code. Next, we |
|
67 | # Now we know how much leading space there is in the code. Next, we | |
68 | # extract up to the first line that has less indentation. |
|
68 | # extract up to the first line that has less indentation. | |
69 | # WARNINGS: we skip comments that may be misindented, but we do NOT yet |
|
69 | # WARNINGS: we skip comments that may be misindented, but we do NOT yet | |
70 | # detect triple quoted strings that may have flush left text. |
|
70 | # detect triple quoted strings that may have flush left text. | |
71 | for lno,line in enumerate(wsource): |
|
71 | for lno,line in enumerate(wsource): | |
72 | lead = line[:col] |
|
72 | lead = line[:col] | |
73 | if lead.isspace(): |
|
73 | if lead.isspace(): | |
74 | continue |
|
74 | continue | |
75 | else: |
|
75 | else: | |
76 | if not lead.lstrip().startswith('#'): |
|
76 | if not lead.lstrip().startswith('#'): | |
77 | break |
|
77 | break | |
78 | # The real 'with' source is up to lno |
|
78 | # The real 'with' source is up to lno | |
79 | src_lines = [l[col:] for l in wsource[:lno+1]] |
|
79 | src_lines = [l[col:] for l in wsource[:lno+1]] | |
80 |
|
80 | |||
81 | # Finally, check that the source's first non-comment line begins with the |
|
81 | # Finally, check that the source's first non-comment line begins with the | |
82 | # special call 'remote()' |
|
82 | # special call 'remote()' | |
83 | if require_remote: |
|
83 | if require_remote: | |
84 | for nline,line in enumerate(src_lines): |
|
84 | for nline,line in enumerate(src_lines): | |
85 | if line.isspace() or line.startswith('#'): |
|
85 | if line.isspace() or line.startswith('#'): | |
86 | continue |
|
86 | continue | |
87 | if line.startswith(remote_mark): |
|
87 | if line.startswith(remote_mark): | |
88 | break |
|
88 | break | |
89 | else: |
|
89 | else: | |
90 | raise ValueError('%s call missing at the start of code' % |
|
90 | raise ValueError('%s call missing at the start of code' % | |
91 | remote_mark) |
|
91 | remote_mark) | |
92 | out_lines = src_lines[nline+1:] |
|
92 | out_lines = src_lines[nline+1:] | |
93 | else: |
|
93 | else: | |
94 | # If the user specified that the remote() call wasn't mandatory |
|
94 | # If the user specified that the remote() call wasn't mandatory | |
95 | out_lines = src_lines |
|
95 | out_lines = src_lines | |
96 |
|
96 | |||
97 | # src = ''.join(out_lines) # dbg |
|
97 | # src = ''.join(out_lines) # dbg | |
98 | #print 'SRC:\n<<<<<<<>>>>>>>\n%s<<<<<>>>>>>' % src # dbg |
|
98 | #print 'SRC:\n<<<<<<<>>>>>>>\n%s<<<<<>>>>>>' % src # dbg | |
99 | return ''.join(out_lines) |
|
99 | return ''.join(out_lines) | |
100 |
|
100 | |||
101 | class RemoteContextBase(object): |
|
101 | class RemoteContextBase(object): | |
102 | def __init__(self): |
|
102 | def __init__(self): | |
103 | self.ip = ipapi.get() |
|
103 | self.ip = ipapi.get() | |
104 |
|
104 | |||
105 | def _findsource_file(self,f): |
|
105 | def _findsource_file(self,f): | |
106 | linecache.checkcache() |
|
106 | linecache.checkcache() | |
107 | s = findsource(f.f_code) |
|
107 | s = findsource(f.f_code) | |
108 | lnum = f.f_lineno |
|
108 | lnum = f.f_lineno | |
109 | wsource = s[0][f.f_lineno:] |
|
109 | wsource = s[0][f.f_lineno:] | |
110 | return strip_whitespace(wsource) |
|
110 | return strip_whitespace(wsource) | |
111 |
|
111 | |||
112 | def _findsource_ipython(self,f): |
|
112 | def _findsource_ipython(self,f): | |
113 | from IPython import ipapi |
|
113 | from IPython import ipapi | |
114 | self.ip = ipapi.get() |
|
114 | self.ip = ipapi.get() | |
115 | buf = self.ip.IP.input_hist_raw[-1].splitlines()[1:] |
|
115 | buf = self.ip.IP.input_hist_raw[-1].splitlines()[1:] | |
116 | wsource = [l+'\n' for l in buf ] |
|
116 | wsource = [l+'\n' for l in buf ] | |
117 |
|
117 | |||
118 | return strip_whitespace(wsource) |
|
118 | return strip_whitespace(wsource) | |
119 |
|
119 | |||
120 | def findsource(self,frame): |
|
120 | def findsource(self,frame): | |
121 | local_ns = frame.f_locals |
|
121 | local_ns = frame.f_locals | |
122 | global_ns = frame.f_globals |
|
122 | global_ns = frame.f_globals | |
123 | if frame.f_code.co_filename == '<ipython console>': |
|
123 | if frame.f_code.co_filename == '<ipython console>': | |
124 | src = self._findsource_ipython(frame) |
|
124 | src = self._findsource_ipython(frame) | |
125 | else: |
|
125 | else: | |
126 | src = self._findsource_file(frame) |
|
126 | src = self._findsource_file(frame) | |
127 | return src |
|
127 | return src | |
128 |
|
128 | |||
129 | def __enter__(self): |
|
129 | def __enter__(self): | |
130 | raise NotImplementedError |
|
130 | raise NotImplementedError | |
131 |
|
131 | |||
132 | def __exit__ (self, etype, value, tb): |
|
132 | def __exit__ (self, etype, value, tb): | |
133 | if issubclass(etype,error.StopLocalExecution): |
|
133 | if issubclass(etype,error.StopLocalExecution): | |
134 | return True |
|
134 | return True | |
135 |
|
135 | |||
136 | class RemoteMultiEngine(RemoteContextBase): |
|
136 | class RemoteMultiEngine(RemoteContextBase): | |
137 | def __init__(self,mec): |
|
137 | def __init__(self,mec): | |
138 | self.mec = mec |
|
138 | self.mec = mec | |
139 | RemoteContextBase.__init__(self) |
|
139 | RemoteContextBase.__init__(self) | |
140 |
|
140 | |||
141 | def __enter__(self): |
|
141 | def __enter__(self): | |
142 | src = self.findsource(sys._getframe(1)) |
|
142 | src = self.findsource(sys._getframe(1)) | |
143 | return self.mec.execute(src) |
|
143 | return self.mec.execute(src) | |
144 |
|
144 | |||
145 |
|
145 | |||
146 | # XXX - Temporary hackish testing, we'll move this into proper tests right |
|
146 | # XXX - Temporary hackish testing, we'll move this into proper tests right | |
147 | # away |
|
147 | # away. This has been commented out as it doesn't run under Python 2.4 | |
148 |
|
148 | # because of the usage of the with statement below. We need to protect | ||
149 | if __name__ == '__main__': |
|
149 | # such things with a try:except. | |
150 |
|
150 | |||
151 | # XXX - for now, we need a running cluster to be started separately. The |
|
151 | # if __name__ == '__main__': | |
152 | # daemon work is almost finished, and will make much of this unnecessary. |
|
152 | # | |
153 | from IPython.kernel import client |
|
153 | # # XXX - for now, we need a running cluster to be started separately. The | |
154 | mec = client.MultiEngineClient(('127.0.0.1',10105)) |
|
154 | # # daemon work is almost finished, and will make much of this unnecessary. | |
155 |
|
155 | # from IPython.kernel import client | ||
156 | try: |
|
156 | # mec = client.MultiEngineClient(('127.0.0.1',10105)) | |
157 | mec.get_ids() |
|
157 | # | |
158 | except ConnectionRefusedError: |
|
158 | # try: | |
159 | import os, time |
|
159 | # mec.get_ids() | |
160 | os.system('ipcluster -n 2 &') |
|
160 | # except ConnectionRefusedError: | |
161 | time.sleep(2) |
|
161 | # import os, time | |
162 | mec = client.MultiEngineClient(('127.0.0.1',10105)) |
|
162 | # os.system('ipcluster -n 2 &') | |
163 |
|
163 | # time.sleep(2) | ||
164 | mec.block = False |
|
164 | # mec = client.MultiEngineClient(('127.0.0.1',10105)) | |
165 |
|
165 | # | ||
166 | import itertools |
|
166 | # mec.block = False | |
167 | c = itertools.count() |
|
167 | # | |
168 |
|
168 | # import itertools | ||
169 | parallel = RemoteMultiEngine(mec) |
|
169 | # c = itertools.count() | |
170 |
|
170 | # | ||
171 | with parallel as pr: |
|
171 | # parallel = RemoteMultiEngine(mec) | |
172 | # A comment |
|
172 | # | |
173 | remote() # this means the code below only runs remotely |
|
173 | # with parallel as pr: | |
174 | print 'Hello remote world' |
|
174 | # # A comment | |
175 | x = 3.14 |
|
175 | # remote() # this means the code below only runs remotely | |
176 | # Comments are OK |
|
176 | # print 'Hello remote world' | |
177 | # Even misindented. |
|
177 | # x = 3.14 | |
178 | y = x+1 |
|
178 | # # Comments are OK | |
|
179 | # # Even misindented. | |||
|
180 | # y = x+1 |
General Comments 0
You need to be logged in to leave comments.
Login now