##// END OF EJS Templates
Backport PR #5805: fix engine startup files...
Thomas Kluyver -
Show More
@@ -1,394 +1,397 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The IPython engine application
4 The IPython engine application
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * MinRK
9 * MinRK
10
10
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Copyright (C) 2008-2011 The IPython Development Team
14 # Copyright (C) 2008-2011 The IPython Development Team
15 #
15 #
16 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 import json
24 import json
25 import os
25 import os
26 import sys
26 import sys
27 import time
27 import time
28
28
29 import zmq
29 import zmq
30 from zmq.eventloop import ioloop
30 from zmq.eventloop import ioloop
31
31
32 from IPython.core.profiledir import ProfileDir
32 from IPython.core.profiledir import ProfileDir
33 from IPython.parallel.apps.baseapp import (
33 from IPython.parallel.apps.baseapp import (
34 BaseParallelApplication,
34 BaseParallelApplication,
35 base_aliases,
35 base_aliases,
36 base_flags,
36 base_flags,
37 catch_config_error,
37 catch_config_error,
38 )
38 )
39 from IPython.kernel.zmq.log import EnginePUBHandler
39 from IPython.kernel.zmq.log import EnginePUBHandler
40 from IPython.kernel.zmq.ipkernel import Kernel
40 from IPython.kernel.zmq.ipkernel import Kernel
41 from IPython.kernel.zmq.kernelapp import IPKernelApp
41 from IPython.kernel.zmq.kernelapp import IPKernelApp
42 from IPython.kernel.zmq.session import (
42 from IPython.kernel.zmq.session import (
43 Session, session_aliases, session_flags
43 Session, session_aliases, session_flags
44 )
44 )
45 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
45 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
46
46
47 from IPython.config.configurable import Configurable
47 from IPython.config.configurable import Configurable
48
48
49 from IPython.parallel.engine.engine import EngineFactory
49 from IPython.parallel.engine.engine import EngineFactory
50 from IPython.parallel.util import disambiguate_ip_address
50 from IPython.parallel.util import disambiguate_ip_address
51
51
52 from IPython.utils.importstring import import_item
52 from IPython.utils.importstring import import_item
53 from IPython.utils.py3compat import cast_bytes
53 from IPython.utils.py3compat import cast_bytes
54 from IPython.utils.traitlets import Bool, Unicode, Dict, List, Float, Instance
54 from IPython.utils.traitlets import Bool, Unicode, Dict, List, Float, Instance
55
55
56
56
57 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
58 # Module level variables
58 # Module level variables
59 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
60
60
61 _description = """Start an IPython engine for parallel computing.
61 _description = """Start an IPython engine for parallel computing.
62
62
63 IPython engines run in parallel and perform computations on behalf of a client
63 IPython engines run in parallel and perform computations on behalf of a client
64 and controller. A controller needs to be started before the engines. The
64 and controller. A controller needs to be started before the engines. The
65 engine can be configured using command line options or using a cluster
65 engine can be configured using command line options or using a cluster
66 directory. Cluster directories contain config, log and security files and are
66 directory. Cluster directories contain config, log and security files and are
67 usually located in your ipython directory and named as "profile_name".
67 usually located in your ipython directory and named as "profile_name".
68 See the `profile` and `profile-dir` options for details.
68 See the `profile` and `profile-dir` options for details.
69 """
69 """
70
70
71 _examples = """
71 _examples = """
72 ipengine --ip=192.168.0.1 --port=1000 # connect to hub at ip and port
72 ipengine --ip=192.168.0.1 --port=1000 # connect to hub at ip and port
73 ipengine --log-to-file --log-level=DEBUG # log to a file with DEBUG verbosity
73 ipengine --log-to-file --log-level=DEBUG # log to a file with DEBUG verbosity
74 """
74 """
75
75
76 #-----------------------------------------------------------------------------
76 #-----------------------------------------------------------------------------
77 # MPI configuration
77 # MPI configuration
78 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
79
79
80 mpi4py_init = """from mpi4py import MPI as mpi
80 mpi4py_init = """from mpi4py import MPI as mpi
81 mpi.size = mpi.COMM_WORLD.Get_size()
81 mpi.size = mpi.COMM_WORLD.Get_size()
82 mpi.rank = mpi.COMM_WORLD.Get_rank()
82 mpi.rank = mpi.COMM_WORLD.Get_rank()
83 """
83 """
84
84
85
85
86 pytrilinos_init = """from PyTrilinos import Epetra
86 pytrilinos_init = """from PyTrilinos import Epetra
87 class SimpleStruct:
87 class SimpleStruct:
88 pass
88 pass
89 mpi = SimpleStruct()
89 mpi = SimpleStruct()
90 mpi.rank = 0
90 mpi.rank = 0
91 mpi.size = 0
91 mpi.size = 0
92 """
92 """
93
93
94 class MPI(Configurable):
94 class MPI(Configurable):
95 """Configurable for MPI initialization"""
95 """Configurable for MPI initialization"""
96 use = Unicode('', config=True,
96 use = Unicode('', config=True,
97 help='How to enable MPI (mpi4py, pytrilinos, or empty string to disable).'
97 help='How to enable MPI (mpi4py, pytrilinos, or empty string to disable).'
98 )
98 )
99
99
100 def _use_changed(self, name, old, new):
100 def _use_changed(self, name, old, new):
101 # load default init script if it's not set
101 # load default init script if it's not set
102 if not self.init_script:
102 if not self.init_script:
103 self.init_script = self.default_inits.get(new, '')
103 self.init_script = self.default_inits.get(new, '')
104
104
105 init_script = Unicode('', config=True,
105 init_script = Unicode('', config=True,
106 help="Initialization code for MPI")
106 help="Initialization code for MPI")
107
107
108 default_inits = Dict({'mpi4py' : mpi4py_init, 'pytrilinos':pytrilinos_init},
108 default_inits = Dict({'mpi4py' : mpi4py_init, 'pytrilinos':pytrilinos_init},
109 config=True)
109 config=True)
110
110
111
111
112 #-----------------------------------------------------------------------------
112 #-----------------------------------------------------------------------------
113 # Main application
113 # Main application
114 #-----------------------------------------------------------------------------
114 #-----------------------------------------------------------------------------
115 aliases = dict(
115 aliases = dict(
116 file = 'IPEngineApp.url_file',
116 file = 'IPEngineApp.url_file',
117 c = 'IPEngineApp.startup_command',
117 c = 'IPEngineApp.startup_command',
118 s = 'IPEngineApp.startup_script',
118 s = 'IPEngineApp.startup_script',
119
119
120 url = 'EngineFactory.url',
120 url = 'EngineFactory.url',
121 ssh = 'EngineFactory.sshserver',
121 ssh = 'EngineFactory.sshserver',
122 sshkey = 'EngineFactory.sshkey',
122 sshkey = 'EngineFactory.sshkey',
123 ip = 'EngineFactory.ip',
123 ip = 'EngineFactory.ip',
124 transport = 'EngineFactory.transport',
124 transport = 'EngineFactory.transport',
125 port = 'EngineFactory.regport',
125 port = 'EngineFactory.regport',
126 location = 'EngineFactory.location',
126 location = 'EngineFactory.location',
127
127
128 timeout = 'EngineFactory.timeout',
128 timeout = 'EngineFactory.timeout',
129
129
130 mpi = 'MPI.use',
130 mpi = 'MPI.use',
131
131
132 )
132 )
133 aliases.update(base_aliases)
133 aliases.update(base_aliases)
134 aliases.update(session_aliases)
134 aliases.update(session_aliases)
135 flags = {}
135 flags = {}
136 flags.update(base_flags)
136 flags.update(base_flags)
137 flags.update(session_flags)
137 flags.update(session_flags)
138
138
139 class IPEngineApp(BaseParallelApplication):
139 class IPEngineApp(BaseParallelApplication):
140
140
141 name = 'ipengine'
141 name = 'ipengine'
142 description = _description
142 description = _description
143 examples = _examples
143 examples = _examples
144 classes = List([ZMQInteractiveShell, ProfileDir, Session, EngineFactory, Kernel, MPI])
144 classes = List([ZMQInteractiveShell, ProfileDir, Session, EngineFactory, Kernel, MPI])
145
145
146 startup_script = Unicode(u'', config=True,
146 startup_script = Unicode(u'', config=True,
147 help='specify a script to be run at startup')
147 help='specify a script to be run at startup')
148 startup_command = Unicode('', config=True,
148 startup_command = Unicode('', config=True,
149 help='specify a command to be run at startup')
149 help='specify a command to be run at startup')
150
150
151 url_file = Unicode(u'', config=True,
151 url_file = Unicode(u'', config=True,
152 help="""The full location of the file containing the connection information for
152 help="""The full location of the file containing the connection information for
153 the controller. If this is not given, the file must be in the
153 the controller. If this is not given, the file must be in the
154 security directory of the cluster directory. This location is
154 security directory of the cluster directory. This location is
155 resolved using the `profile` or `profile_dir` options.""",
155 resolved using the `profile` or `profile_dir` options.""",
156 )
156 )
157 wait_for_url_file = Float(5, config=True,
157 wait_for_url_file = Float(5, config=True,
158 help="""The maximum number of seconds to wait for url_file to exist.
158 help="""The maximum number of seconds to wait for url_file to exist.
159 This is useful for batch-systems and shared-filesystems where the
159 This is useful for batch-systems and shared-filesystems where the
160 controller and engine are started at the same time and it
160 controller and engine are started at the same time and it
161 may take a moment for the controller to write the connector files.""")
161 may take a moment for the controller to write the connector files.""")
162
162
163 url_file_name = Unicode(u'ipcontroller-engine.json', config=True)
163 url_file_name = Unicode(u'ipcontroller-engine.json', config=True)
164
164
165 def _cluster_id_changed(self, name, old, new):
165 def _cluster_id_changed(self, name, old, new):
166 if new:
166 if new:
167 base = 'ipcontroller-%s' % new
167 base = 'ipcontroller-%s' % new
168 else:
168 else:
169 base = 'ipcontroller'
169 base = 'ipcontroller'
170 self.url_file_name = "%s-engine.json" % base
170 self.url_file_name = "%s-engine.json" % base
171
171
172 log_url = Unicode('', config=True,
172 log_url = Unicode('', config=True,
173 help="""The URL for the iploggerapp instance, for forwarding
173 help="""The URL for the iploggerapp instance, for forwarding
174 logging to a central location.""")
174 logging to a central location.""")
175
175
176 # an IPKernelApp instance, used to setup listening for shell frontends
176 # an IPKernelApp instance, used to setup listening for shell frontends
177 kernel_app = Instance(IPKernelApp)
177 kernel_app = Instance(IPKernelApp)
178
178
179 aliases = Dict(aliases)
179 aliases = Dict(aliases)
180 flags = Dict(flags)
180 flags = Dict(flags)
181
181
182 @property
182 @property
183 def kernel(self):
183 def kernel(self):
184 """allow access to the Kernel object, so I look like IPKernelApp"""
184 """allow access to the Kernel object, so I look like IPKernelApp"""
185 return self.engine.kernel
185 return self.engine.kernel
186
186
187 def find_url_file(self):
187 def find_url_file(self):
188 """Set the url file.
188 """Set the url file.
189
189
190 Here we don't try to actually see if it exists for is valid as that
190 Here we don't try to actually see if it exists for is valid as that
191 is hadled by the connection logic.
191 is hadled by the connection logic.
192 """
192 """
193 config = self.config
193 config = self.config
194 # Find the actual controller key file
194 # Find the actual controller key file
195 if not self.url_file:
195 if not self.url_file:
196 self.url_file = os.path.join(
196 self.url_file = os.path.join(
197 self.profile_dir.security_dir,
197 self.profile_dir.security_dir,
198 self.url_file_name
198 self.url_file_name
199 )
199 )
200
200
201 def load_connector_file(self):
201 def load_connector_file(self):
202 """load config from a JSON connector file,
202 """load config from a JSON connector file,
203 at a *lower* priority than command-line/config files.
203 at a *lower* priority than command-line/config files.
204 """
204 """
205
205
206 self.log.info("Loading url_file %r", self.url_file)
206 self.log.info("Loading url_file %r", self.url_file)
207 config = self.config
207 config = self.config
208
208
209 with open(self.url_file) as f:
209 with open(self.url_file) as f:
210 num_tries = 0
210 num_tries = 0
211 max_tries = 5
211 max_tries = 5
212 d = ""
212 d = ""
213 while not d:
213 while not d:
214 try:
214 try:
215 d = json.loads(f.read())
215 d = json.loads(f.read())
216 except ValueError:
216 except ValueError:
217 if num_tries > max_tries:
217 if num_tries > max_tries:
218 raise
218 raise
219 num_tries += 1
219 num_tries += 1
220 time.sleep(0.5)
220 time.sleep(0.5)
221
221
222 # allow hand-override of location for disambiguation
222 # allow hand-override of location for disambiguation
223 # and ssh-server
223 # and ssh-server
224 if 'EngineFactory.location' not in config:
224 if 'EngineFactory.location' not in config:
225 config.EngineFactory.location = d['location']
225 config.EngineFactory.location = d['location']
226 if 'EngineFactory.sshserver' not in config:
226 if 'EngineFactory.sshserver' not in config:
227 config.EngineFactory.sshserver = d.get('ssh')
227 config.EngineFactory.sshserver = d.get('ssh')
228
228
229 location = config.EngineFactory.location
229 location = config.EngineFactory.location
230
230
231 proto, ip = d['interface'].split('://')
231 proto, ip = d['interface'].split('://')
232 ip = disambiguate_ip_address(ip, location)
232 ip = disambiguate_ip_address(ip, location)
233 d['interface'] = '%s://%s' % (proto, ip)
233 d['interface'] = '%s://%s' % (proto, ip)
234
234
235 # DO NOT allow override of basic URLs, serialization, or key
235 # DO NOT allow override of basic URLs, serialization, or key
236 # JSON file takes top priority there
236 # JSON file takes top priority there
237 config.Session.key = cast_bytes(d['key'])
237 config.Session.key = cast_bytes(d['key'])
238 config.Session.signature_scheme = d['signature_scheme']
238 config.Session.signature_scheme = d['signature_scheme']
239
239
240 config.EngineFactory.url = d['interface'] + ':%i' % d['registration']
240 config.EngineFactory.url = d['interface'] + ':%i' % d['registration']
241
241
242 config.Session.packer = d['pack']
242 config.Session.packer = d['pack']
243 config.Session.unpacker = d['unpack']
243 config.Session.unpacker = d['unpack']
244
244
245 self.log.debug("Config changed:")
245 self.log.debug("Config changed:")
246 self.log.debug("%r", config)
246 self.log.debug("%r", config)
247 self.connection_info = d
247 self.connection_info = d
248
248
249 def bind_kernel(self, **kwargs):
249 def bind_kernel(self, **kwargs):
250 """Promote engine to listening kernel, accessible to frontends."""
250 """Promote engine to listening kernel, accessible to frontends."""
251 if self.kernel_app is not None:
251 if self.kernel_app is not None:
252 return
252 return
253
253
254 self.log.info("Opening ports for direct connections as an IPython kernel")
254 self.log.info("Opening ports for direct connections as an IPython kernel")
255
255
256 kernel = self.kernel
256 kernel = self.kernel
257
257
258 kwargs.setdefault('config', self.config)
258 kwargs.setdefault('config', self.config)
259 kwargs.setdefault('log', self.log)
259 kwargs.setdefault('log', self.log)
260 kwargs.setdefault('profile_dir', self.profile_dir)
260 kwargs.setdefault('profile_dir', self.profile_dir)
261 kwargs.setdefault('session', self.engine.session)
261 kwargs.setdefault('session', self.engine.session)
262
262
263 app = self.kernel_app = IPKernelApp(**kwargs)
263 app = self.kernel_app = IPKernelApp(**kwargs)
264
264
265 # allow IPKernelApp.instance():
265 # allow IPKernelApp.instance():
266 IPKernelApp._instance = app
266 IPKernelApp._instance = app
267
267
268 app.init_connection_file()
268 app.init_connection_file()
269 # relevant contents of init_sockets:
269 # relevant contents of init_sockets:
270
270
271 app.shell_port = app._bind_socket(kernel.shell_streams[0], app.shell_port)
271 app.shell_port = app._bind_socket(kernel.shell_streams[0], app.shell_port)
272 app.log.debug("shell ROUTER Channel on port: %i", app.shell_port)
272 app.log.debug("shell ROUTER Channel on port: %i", app.shell_port)
273
273
274 app.iopub_port = app._bind_socket(kernel.iopub_socket, app.iopub_port)
274 app.iopub_port = app._bind_socket(kernel.iopub_socket, app.iopub_port)
275 app.log.debug("iopub PUB Channel on port: %i", app.iopub_port)
275 app.log.debug("iopub PUB Channel on port: %i", app.iopub_port)
276
276
277 kernel.stdin_socket = self.engine.context.socket(zmq.ROUTER)
277 kernel.stdin_socket = self.engine.context.socket(zmq.ROUTER)
278 app.stdin_port = app._bind_socket(kernel.stdin_socket, app.stdin_port)
278 app.stdin_port = app._bind_socket(kernel.stdin_socket, app.stdin_port)
279 app.log.debug("stdin ROUTER Channel on port: %i", app.stdin_port)
279 app.log.debug("stdin ROUTER Channel on port: %i", app.stdin_port)
280
280
281 # start the heartbeat, and log connection info:
281 # start the heartbeat, and log connection info:
282
282
283 app.init_heartbeat()
283 app.init_heartbeat()
284
284
285 app.log_connection_info()
285 app.log_connection_info()
286 app.write_connection_file()
286 app.write_connection_file()
287
287
288
288
289 def init_engine(self):
289 def init_engine(self):
290 # This is the working dir by now.
290 # This is the working dir by now.
291 sys.path.insert(0, '')
291 sys.path.insert(0, '')
292 config = self.config
292 config = self.config
293 # print config
293 # print config
294 self.find_url_file()
294 self.find_url_file()
295
295
296 # was the url manually specified?
296 # was the url manually specified?
297 keys = set(self.config.EngineFactory.keys())
297 keys = set(self.config.EngineFactory.keys())
298 keys = keys.union(set(self.config.RegistrationFactory.keys()))
298 keys = keys.union(set(self.config.RegistrationFactory.keys()))
299
299
300 if keys.intersection(set(['ip', 'url', 'port'])):
300 if keys.intersection(set(['ip', 'url', 'port'])):
301 # Connection info was specified, don't wait for the file
301 # Connection info was specified, don't wait for the file
302 url_specified = True
302 url_specified = True
303 self.wait_for_url_file = 0
303 self.wait_for_url_file = 0
304 else:
304 else:
305 url_specified = False
305 url_specified = False
306
306
307 if self.wait_for_url_file and not os.path.exists(self.url_file):
307 if self.wait_for_url_file and not os.path.exists(self.url_file):
308 self.log.warn("url_file %r not found", self.url_file)
308 self.log.warn("url_file %r not found", self.url_file)
309 self.log.warn("Waiting up to %.1f seconds for it to arrive.", self.wait_for_url_file)
309 self.log.warn("Waiting up to %.1f seconds for it to arrive.", self.wait_for_url_file)
310 tic = time.time()
310 tic = time.time()
311 while not os.path.exists(self.url_file) and (time.time()-tic < self.wait_for_url_file):
311 while not os.path.exists(self.url_file) and (time.time()-tic < self.wait_for_url_file):
312 # wait for url_file to exist, or until time limit
312 # wait for url_file to exist, or until time limit
313 time.sleep(0.1)
313 time.sleep(0.1)
314
314
315 if os.path.exists(self.url_file):
315 if os.path.exists(self.url_file):
316 self.load_connector_file()
316 self.load_connector_file()
317 elif not url_specified:
317 elif not url_specified:
318 self.log.fatal("Fatal: url file never arrived: %s", self.url_file)
318 self.log.fatal("Fatal: url file never arrived: %s", self.url_file)
319 self.exit(1)
319 self.exit(1)
320
320
321 exec_lines = []
321 exec_lines = []
322 for app in ('IPKernelApp', 'InteractiveShellApp'):
322 for app in ('IPKernelApp', 'InteractiveShellApp'):
323 if '%s.exec_lines' in config:
323 if '%s.exec_lines' % app in config:
324 exec_lines = config.IPKernelApp.exec_lines = config[app].exec_lines
324 exec_lines = config[app].exec_lines
325 break
325 break
326
326
327 exec_files = []
327 exec_files = []
328 for app in ('IPKernelApp', 'InteractiveShellApp'):
328 for app in ('IPKernelApp', 'InteractiveShellApp'):
329 if '%s.exec_files' in config:
329 if '%s.exec_files' % app in config:
330 exec_files = config.IPKernelApp.exec_files = config[app].exec_files
330 exec_files = config[app].exec_files
331 break
331 break
332
332
333 config.IPKernelApp.exec_lines = exec_lines
334 config.IPKernelApp.exec_files = exec_files
335
333 if self.startup_script:
336 if self.startup_script:
334 exec_files.append(self.startup_script)
337 exec_files.append(self.startup_script)
335 if self.startup_command:
338 if self.startup_command:
336 exec_lines.append(self.startup_command)
339 exec_lines.append(self.startup_command)
337
340
338 # Create the underlying shell class and Engine
341 # Create the underlying shell class and Engine
339 # shell_class = import_item(self.master_config.Global.shell_class)
342 # shell_class = import_item(self.master_config.Global.shell_class)
340 # print self.config
343 # print self.config
341 try:
344 try:
342 self.engine = EngineFactory(config=config, log=self.log,
345 self.engine = EngineFactory(config=config, log=self.log,
343 connection_info=self.connection_info,
346 connection_info=self.connection_info,
344 )
347 )
345 except:
348 except:
346 self.log.error("Couldn't start the Engine", exc_info=True)
349 self.log.error("Couldn't start the Engine", exc_info=True)
347 self.exit(1)
350 self.exit(1)
348
351
349 def forward_logging(self):
352 def forward_logging(self):
350 if self.log_url:
353 if self.log_url:
351 self.log.info("Forwarding logging to %s", self.log_url)
354 self.log.info("Forwarding logging to %s", self.log_url)
352 context = self.engine.context
355 context = self.engine.context
353 lsock = context.socket(zmq.PUB)
356 lsock = context.socket(zmq.PUB)
354 lsock.connect(self.log_url)
357 lsock.connect(self.log_url)
355 handler = EnginePUBHandler(self.engine, lsock)
358 handler = EnginePUBHandler(self.engine, lsock)
356 handler.setLevel(self.log_level)
359 handler.setLevel(self.log_level)
357 self.log.addHandler(handler)
360 self.log.addHandler(handler)
358
361
359 def init_mpi(self):
362 def init_mpi(self):
360 global mpi
363 global mpi
361 self.mpi = MPI(parent=self)
364 self.mpi = MPI(parent=self)
362
365
363 mpi_import_statement = self.mpi.init_script
366 mpi_import_statement = self.mpi.init_script
364 if mpi_import_statement:
367 if mpi_import_statement:
365 try:
368 try:
366 self.log.info("Initializing MPI:")
369 self.log.info("Initializing MPI:")
367 self.log.info(mpi_import_statement)
370 self.log.info(mpi_import_statement)
368 exec(mpi_import_statement, globals())
371 exec(mpi_import_statement, globals())
369 except:
372 except:
370 mpi = None
373 mpi = None
371 else:
374 else:
372 mpi = None
375 mpi = None
373
376
374 @catch_config_error
377 @catch_config_error
375 def initialize(self, argv=None):
378 def initialize(self, argv=None):
376 super(IPEngineApp, self).initialize(argv)
379 super(IPEngineApp, self).initialize(argv)
377 self.init_mpi()
380 self.init_mpi()
378 self.init_engine()
381 self.init_engine()
379 self.forward_logging()
382 self.forward_logging()
380
383
381 def start(self):
384 def start(self):
382 self.engine.start()
385 self.engine.start()
383 try:
386 try:
384 self.engine.loop.start()
387 self.engine.loop.start()
385 except KeyboardInterrupt:
388 except KeyboardInterrupt:
386 self.log.critical("Engine Interrupted, shutting down...\n")
389 self.log.critical("Engine Interrupted, shutting down...\n")
387
390
388
391
389 launch_new_instance = IPEngineApp.launch_instance
392 launch_new_instance = IPEngineApp.launch_instance
390
393
391
394
392 if __name__ == '__main__':
395 if __name__ == '__main__':
393 launch_new_instance()
396 launch_new_instance()
394
397
General Comments 0
You need to be logged in to leave comments. Login now