##// END OF EJS Templates
Fixing small bugs in ipcluster stuff....
Brian Granger -
Show More
@@ -1,79 +1,79 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 A class for creating a Twisted service that is configured using IPython's
5 5 configuration system.
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2008-2009 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 import zope.interface as zi
20 20
21 21 from IPython.config.configurable import Configurable
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Code
25 25 #-----------------------------------------------------------------------------
26 26
27 27
28 28 class IConfiguredObjectFactory(zi.Interface):
29 29 """I am a component that creates a configured object.
30 30
31 31 This class is useful if you want to configure a class that is not a
32 32 subclass of :class:`IPython.config.configurable.Configurable`.
33 33 """
34 34
35 def __init__(config):
35 def __init__(config=None):
36 36 """Get ready to configure the object using config."""
37 37
38 38 def create():
39 39 """Return an instance of the configured object."""
40 40
41 41
42 42 class ConfiguredObjectFactory(Configurable):
43 43
44 44 zi.implements(IConfiguredObjectFactory)
45 45
46 46 def __init__(self, config=None):
47 47 super(ConfiguredObjectFactory, self).__init__(config=config)
48 48
49 49 def create(self):
50 50 raise NotImplementedError('create must be implemented in a subclass')
51 51
52 52
53 53 class IAdaptedConfiguredObjectFactory(zi.Interface):
54 54 """I am a component that adapts and configures an object.
55 55
56 56 This class is useful if you have the adapt an instance and configure it.
57 57 """
58 58
59 59 def __init__(config=None, adaptee=None):
60 60 """Get ready to adapt adaptee and then configure it using config."""
61 61
62 62 def create():
63 63 """Return an instance of the adapted and configured object."""
64 64
65 65
66 66 class AdaptedConfiguredObjectFactory(Configurable):
67 67
68 68 # zi.implements(IAdaptedConfiguredObjectFactory)
69 69
70 70 def __init__(self, config=None, adaptee=None):
71 71 # print
72 72 # print "config pre:", config
73 73 super(AdaptedConfiguredObjectFactory, self).__init__(config=config)
74 74 # print
75 75 # print "config post:", config
76 76 self.adaptee = adaptee
77 77
78 78 def create(self):
79 79 raise NotImplementedError('create must be implemented in a subclass')
@@ -1,296 +1,296 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 Foolscap related utilities.
5 5 """
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008-2009 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17
18 18 from __future__ import with_statement
19 19
20 20 import os
21 21 import tempfile
22 22
23 23 from twisted.internet import reactor, defer
24 24 from twisted.python import log
25 25
26 26 import foolscap
27 27 try:
28 28 from foolscap.api import Tub, UnauthenticatedTub
29 29 except ImportError:
30 30 from foolscap import Tub, UnauthenticatedTub
31 31
32 32 from IPython.config.loader import Config
33 33 from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory
34 34 from IPython.kernel.error import SecurityError
35 35
36 36 from IPython.utils.importstring import import_item
37 37 from IPython.utils.path import expand_path
38 38 from IPython.utils.traitlets import Int, Str, Bool, Instance
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Code
42 42 #-----------------------------------------------------------------------------
43 43
44 44
45 45 # We do this so if a user doesn't have OpenSSL installed, it will try to use
46 46 # an UnauthenticatedTub. But, they will still run into problems if they
47 47 # try to use encrypted furls.
48 48 try:
49 49 import OpenSSL
50 50 except:
51 51 Tub = UnauthenticatedTub
52 52 have_crypto = False
53 53 else:
54 54 have_crypto = True
55 55
56 56
57 57 class FURLError(Exception):
58 58 pass
59 59
60 60
61 61 def check_furl_file_security(furl_file, secure):
62 62 """Remove the old furl_file if changing security modes."""
63 63 furl_file = expand_path(furl_file)
64 64 if os.path.isfile(furl_file):
65 65 with open(furl_file, 'r') as f:
66 66 oldfurl = f.read().strip()
67 67 if (oldfurl.startswith('pb://') and not secure) or (oldfurl.startswith('pbu://') and secure):
68 68 os.remove(furl_file)
69 69
70 70
71 71 def is_secure(furl):
72 72 """Is the given FURL secure or not."""
73 73 if is_valid_furl(furl):
74 74 if furl.startswith("pb://"):
75 75 return True
76 76 elif furl.startswith("pbu://"):
77 77 return False
78 78 else:
79 79 raise FURLError("invalid FURL: %s" % furl)
80 80
81 81
82 82 def is_valid_furl(furl):
83 83 """Is the str a valid FURL or not."""
84 84 if isinstance(furl, str):
85 85 if furl.startswith("pb://") or furl.startswith("pbu://"):
86 86 return True
87 87 else:
88 88 return False
89 89 else:
90 90 return False
91 91
92 92
93 93 def is_valid_furl_file(furl_or_file):
94 94 """See if furl_or_file exists and contains a valid FURL.
95 95
96 96 This doesn't try to read the contents because often we have to validate
97 97 FURL files that are created, but don't yet have a FURL written to them.
98 98 """
99 99 if isinstance(furl_or_file, (str, unicode)):
100 100 path, furl_filename = os.path.split(furl_or_file)
101 101 if os.path.isdir(path) and furl_filename.endswith('.furl'):
102 102 return True
103 103 return False
104 104
105 105
106 106 def find_furl(furl_or_file):
107 107 """Find, validate and return a FURL in a string or file.
108 108
109 109 This calls :func:`IPython.utils.path.expand_path` on the argument to
110 110 properly handle ``~`` and ``$`` variables in the path.
111 111 """
112 112 if is_valid_furl(furl_or_file):
113 113 return furl_or_file
114 114 furl_or_file = expand_path(furl_or_file)
115 115 if is_valid_furl_file(furl_or_file):
116 116 with open(furl_or_file, 'r') as f:
117 117 furl = f.read().strip()
118 118 if is_valid_furl(furl):
119 119 return furl
120 120 raise FURLError("Not a valid FURL or FURL file: %r" % furl_or_file)
121 121
122 122
123 123 def is_valid_furl_or_file(furl_or_file):
124 124 """Validate a FURL or a FURL file.
125 125
126 126 If ``furl_or_file`` looks like a file, we simply make sure its directory
127 127 exists and that it has a ``.furl`` file extension. We don't try to see
128 128 if the FURL file exists or to read its contents. This is useful for
129 129 cases where auto re-connection is being used.
130 130 """
131 131 if is_valid_furl(furl_or_file) or is_valid_furl_file(furl_or_file):
132 132 return True
133 133 else:
134 134 return False
135 135
136 136
137 137 def validate_furl_or_file(furl_or_file):
138 138 """Like :func:`is_valid_furl_or_file`, but raises an error."""
139 139 if not is_valid_furl_or_file(furl_or_file):
140 140 raise FURLError('Not a valid FURL or FURL file: %r' % furl_or_file)
141 141
142 142
143 143 def get_temp_furlfile(filename):
144 144 """Return a temporary FURL file."""
145 145 return tempfile.mktemp(dir=os.path.dirname(filename),
146 146 prefix=os.path.basename(filename))
147 147
148 148
149 149 def make_tub(ip, port, secure, cert_file):
150 150 """Create a listening tub given an ip, port, and cert_file location.
151 151
152 152 Parameters
153 153 ----------
154 154 ip : str
155 155 The ip address or hostname that the tub should listen on.
156 156 Empty means all interfaces.
157 157 port : int
158 158 The port that the tub should listen on. A value of 0 means
159 159 pick a random port
160 160 secure: bool
161 161 Will the connection be secure (in the Foolscap sense).
162 162 cert_file: str
163 163 A filename of a file to be used for theSSL certificate.
164 164
165 165 Returns
166 166 -------
167 167 A tub, listener tuple.
168 168 """
169 169 if secure:
170 170 if have_crypto:
171 171 tub = Tub(certFile=cert_file)
172 172 else:
173 173 raise SecurityError("OpenSSL/pyOpenSSL is not available, so we "
174 174 "can't run in secure mode. Try running without "
175 175 "security using 'ipcontroller -xy'.")
176 176 else:
177 177 tub = UnauthenticatedTub()
178 178
179 179 # Set the strport based on the ip and port and start listening
180 180 if ip == '':
181 181 strport = "tcp:%i" % port
182 182 else:
183 183 strport = "tcp:%i:interface=%s" % (port, ip)
184 184 log.msg("Starting listener with [secure=%r] on: %s" % (secure, strport))
185 185 listener = tub.listenOn(strport)
186 186
187 187 return tub, listener
188 188
189 189
190 190 class FCServiceFactory(AdaptedConfiguredObjectFactory):
191 191 """This class creates a tub with various services running in it.
192 192
193 193 The basic idea is that :meth:`create` returns a running :class:`Tub`
194 194 instance that has a number of Foolscap references registered in it. This
195 195 class is a subclass of :class:`IPython.config.configurable.Configurable`
196 196 so the IPython configuration system is used.
197 197
198 198 Attributes
199 199 ----------
200 200 interfaces : Config
201 201 A Config instance whose values are sub-Config objects having two
202 202 keys: furl_file and interface_chain.
203 203
204 204 The other attributes are the standard ones for Foolscap.
205 205 """
206 206
207 207 ip = Str('', config=True)
208 208 port = Int(0, config=True)
209 209 secure = Bool(True, config=True)
210 210 cert_file = Str('', config=True)
211 211 location = Str('', config=True)
212 212 reuse_furls = Bool(False, config=True)
213 213 interfaces = Instance(klass=Config, kw={}, allow_none=False, config=True)
214 214
215 def __init__(self, config, adaptee):
216 super(FCServiceFactory, self).__init__(config, adaptee)
215 def __init__(self, config=None, adaptee=None):
216 super(FCServiceFactory, self).__init__(config=config, adaptee=adaptee)
217 217 self._check_reuse_furls()
218 218
219 219 def _ip_changed(self, name, old, new):
220 220 if new == 'localhost' or new == '127.0.0.1':
221 221 self.location = '127.0.0.1'
222 222
223 223 def _check_reuse_furls(self):
224 224 furl_files = [i.furl_file for i in self.interfaces.values()]
225 225 for ff in furl_files:
226 226 fullfile = self._get_security_file(ff)
227 227 if self.reuse_furls:
228 228 if self.port==0:
229 229 raise FURLError("You are trying to reuse the FURL file "
230 230 "for this connection, but the port for this connection "
231 231 "is set to 0 (autoselect). To reuse the FURL file "
232 232 "you need to specify specific port to listen on."
233 233 )
234 234 else:
235 235 log.msg("Reusing FURL file: %s" % fullfile)
236 236 else:
237 237 if os.path.isfile(fullfile):
238 238 log.msg("Removing old FURL file: %s" % fullfile)
239 239 os.remove(fullfile)
240 240
241 241 def _get_security_file(self, filename):
242 242 return os.path.join(self.config.Global.security_dir, filename)
243 243
244 244 def create(self):
245 245 """Create and return the Foolscap tub with everything running."""
246 246
247 247 self.tub, self.listener = make_tub(
248 248 self.ip, self.port, self.secure,
249 249 self._get_security_file(self.cert_file)
250 250 )
251 251 # log.msg("Interfaces to register [%r]: %r" % \
252 252 # (self.__class__, self.interfaces))
253 253 if not self.secure:
254 254 log.msg("WARNING: running with no security: %s" % \
255 255 self.__class__.__name__)
256 256 reactor.callWhenRunning(self.set_location_and_register)
257 257 return self.tub
258 258
259 259 def set_location_and_register(self):
260 260 """Set the location for the tub and return a deferred."""
261 261
262 262 if self.location == '':
263 263 d = self.tub.setLocationAutomatically()
264 264 else:
265 265 d = defer.maybeDeferred(self.tub.setLocation,
266 266 "%s:%i" % (self.location, self.listener.getPortnum()))
267 267 self.adapt_to_interfaces(d)
268 268
269 269 def adapt_to_interfaces(self, d):
270 270 """Run through the interfaces, adapt and register."""
271 271
272 272 for ifname, ifconfig in self.interfaces.iteritems():
273 273 ff = self._get_security_file(ifconfig.furl_file)
274 274 log.msg("Adapting [%s] to interface: %s" % \
275 275 (self.adaptee.__class__.__name__, ifname))
276 276 log.msg("Saving FURL for interface [%s] to file: %s" % (ifname, ff))
277 277 check_furl_file_security(ff, self.secure)
278 278 adaptee = self.adaptee
279 279 for i in ifconfig.interface_chain:
280 280 adaptee = import_item(i)(adaptee)
281 281 d.addCallback(self.register, adaptee, furl_file=ff)
282 282
283 283 def register(self, empty, ref, furl_file):
284 284 """Register the reference with the FURL file.
285 285
286 286 The FURL file is created and then moved to make sure that when the
287 287 file appears, the buffer has been flushed and the file closed. This
288 288 is not done if we are re-using FURLS however.
289 289 """
290 290 if self.reuse_furls:
291 291 self.tub.registerReference(ref, furlFile=furl_file)
292 292 else:
293 293 temp_furl_file = get_temp_furlfile(furl_file)
294 294 self.tub.registerReference(ref, furlFile=temp_furl_file)
295 295 os.rename(temp_furl_file, furl_file)
296 296
@@ -1,271 +1,271 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 The IPython controller application.
5 5 """
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008-2009 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17
18 18 from __future__ import with_statement
19 19
20 20 import copy
21 21 import sys
22 22
23 23 from twisted.application import service
24 24 from twisted.internet import reactor
25 25 from twisted.python import log
26 26
27 27 from IPython.config.loader import Config
28 28 from IPython.kernel import controllerservice
29 29 from IPython.kernel.clusterdir import (
30 30 ApplicationWithClusterDir,
31 31 ClusterDirConfigLoader
32 32 )
33 33 from IPython.kernel.fcutil import FCServiceFactory, FURLError
34 34 from IPython.utils.traitlets import Instance, Unicode
35 35
36 36
37 37 #-----------------------------------------------------------------------------
38 38 # Module level variables
39 39 #-----------------------------------------------------------------------------
40 40
41 41
42 42 #: The default config file name for this application
43 43 default_config_file_name = u'ipcontroller_config.py'
44 44
45 45
46 46 _description = """Start the IPython controller for parallel computing.
47 47
48 48 The IPython controller provides a gateway between the IPython engines and
49 49 clients. The controller needs to be started before the engines and can be
50 50 configured using command line options or using a cluster directory. Cluster
51 51 directories contain config, log and security files and are usually located in
52 52 your .ipython directory and named as "cluster_<profile>". See the --profile
53 53 and --cluster-dir options for details.
54 54 """
55 55
56 56 #-----------------------------------------------------------------------------
57 57 # Default interfaces
58 58 #-----------------------------------------------------------------------------
59 59
60 60 # The default client interfaces for FCClientServiceFactory.interfaces
61 61 default_client_interfaces = Config()
62 62 default_client_interfaces.Task.interface_chain = [
63 63 'IPython.kernel.task.ITaskController',
64 64 'IPython.kernel.taskfc.IFCTaskController'
65 65 ]
66 66
67 67 default_client_interfaces.Task.furl_file = 'ipcontroller-tc.furl'
68 68
69 69 default_client_interfaces.MultiEngine.interface_chain = [
70 70 'IPython.kernel.multiengine.IMultiEngine',
71 71 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
72 72 ]
73 73
74 74 default_client_interfaces.MultiEngine.furl_file = u'ipcontroller-mec.furl'
75 75
76 76 # Make this a dict we can pass to Config.__init__ for the default
77 77 default_client_interfaces = dict(copy.deepcopy(default_client_interfaces.items()))
78 78
79 79
80 80
81 81 # The default engine interfaces for FCEngineServiceFactory.interfaces
82 82 default_engine_interfaces = Config()
83 83 default_engine_interfaces.Default.interface_chain = [
84 84 'IPython.kernel.enginefc.IFCControllerBase'
85 85 ]
86 86
87 87 default_engine_interfaces.Default.furl_file = u'ipcontroller-engine.furl'
88 88
89 89 # Make this a dict we can pass to Config.__init__ for the default
90 90 default_engine_interfaces = dict(copy.deepcopy(default_engine_interfaces.items()))
91 91
92 92
93 93 #-----------------------------------------------------------------------------
94 94 # Service factories
95 95 #-----------------------------------------------------------------------------
96 96
97 97
98 98 class FCClientServiceFactory(FCServiceFactory):
99 99 """A Foolscap implementation of the client services."""
100 100
101 101 cert_file = Unicode(u'ipcontroller-client.pem', config=True)
102 102 interfaces = Instance(klass=Config, kw=default_client_interfaces,
103 103 allow_none=False, config=True)
104 104
105 105
106 106 class FCEngineServiceFactory(FCServiceFactory):
107 107 """A Foolscap implementation of the engine services."""
108 108
109 109 cert_file = Unicode(u'ipcontroller-engine.pem', config=True)
110 110 interfaces = Instance(klass=dict, kw=default_engine_interfaces,
111 111 allow_none=False, config=True)
112 112
113 113
114 114 #-----------------------------------------------------------------------------
115 115 # Command line options
116 116 #-----------------------------------------------------------------------------
117 117
118 118
119 119 class IPControllerAppConfigLoader(ClusterDirConfigLoader):
120 120
121 121 def _add_arguments(self):
122 122 super(IPControllerAppConfigLoader, self)._add_arguments()
123 123 paa = self.parser.add_argument
124 124 # Client config
125 125 paa('--client-ip',
126 126 type=str, dest='FCClientServiceFactory.ip',
127 127 help='The IP address or hostname the controller will listen on for '
128 128 'client connections.',
129 129 metavar='FCClientServiceFactory.ip')
130 130 paa('--client-port',
131 131 type=int, dest='FCClientServiceFactory.port',
132 132 help='The port the controller will listen on for client connections. '
133 133 'The default is to use 0, which will autoselect an open port.',
134 134 metavar='FCClientServiceFactory.port')
135 135 paa('--client-location',), dict(
136 136 type=str, dest='FCClientServiceFactory.location',
137 137 help='The hostname or IP that clients should connect to. This does '
138 138 'not control which interface the controller listens on. Instead, this '
139 139 'determines the hostname/IP that is listed in the FURL, which is how '
140 140 'clients know where to connect. Useful if the controller is listening '
141 141 'on multiple interfaces.',
142 142 metavar='FCClientServiceFactory.location')
143 143 # Engine config
144 144 paa('--engine-ip',
145 145 type=str, dest='FCEngineServiceFactory.ip',
146 146 help='The IP address or hostname the controller will listen on for '
147 147 'engine connections.',
148 148 metavar='FCEngineServiceFactory.ip')
149 149 paa('--engine-port',
150 150 type=int, dest='FCEngineServiceFactory.port',
151 151 help='The port the controller will listen on for engine connections. '
152 152 'The default is to use 0, which will autoselect an open port.',
153 153 metavar='FCEngineServiceFactory.port')
154 154 paa('--engine-location',
155 155 type=str, dest='FCEngineServiceFactory.location',
156 156 help='The hostname or IP that engines should connect to. This does '
157 157 'not control which interface the controller listens on. Instead, this '
158 158 'determines the hostname/IP that is listed in the FURL, which is how '
159 159 'engines know where to connect. Useful if the controller is listening '
160 160 'on multiple interfaces.',
161 161 metavar='FCEngineServiceFactory.location')
162 162 # Global config
163 163 paa('--log-to-file',
164 164 action='store_true', dest='Global.log_to_file',
165 165 help='Log to a file in the log directory (default is stdout)')
166 166 paa('-r','--reuse-furls',
167 167 action='store_true', dest='Global.reuse_furls',
168 168 help='Try to reuse all FURL files. If this is not set all FURL files '
169 169 'are deleted before the controller starts. This must be set if '
170 170 'specific ports are specified by --engine-port or --client-port.')
171 171 paa('--no-secure',
172 172 action='store_false', dest='Global.secure',
173 173 help='Turn off SSL encryption for all connections.')
174 174 paa('--secure',
175 175 action='store_true', dest='Global.secure',
176 176 help='Turn off SSL encryption for all connections.')
177 177
178 178
179 179 #-----------------------------------------------------------------------------
180 180 # The main application
181 181 #-----------------------------------------------------------------------------
182 182
183 183
184 184 class IPControllerApp(ApplicationWithClusterDir):
185 185
186 186 name = u'ipcontroller'
187 187 description = _description
188 188 command_line_loader = IPControllerAppConfigLoader
189 189 default_config_file_name = default_config_file_name
190 190 auto_create_cluster_dir = True
191 191
192 192 def create_default_config(self):
193 193 super(IPControllerApp, self).create_default_config()
194 194 # Don't set defaults for Global.secure or Global.reuse_furls
195 195 # as those are set in a component.
196 196 self.default_config.Global.import_statements = []
197 197 self.default_config.Global.clean_logs = True
198 198
199 199 def pre_construct(self):
200 200 super(IPControllerApp, self).pre_construct()
201 201 c = self.master_config
202 202 # The defaults for these are set in FCClientServiceFactory and
203 203 # FCEngineServiceFactory, so we only set them here if the global
204 204 # options have be set to override the class level defaults.
205 205 if hasattr(c.Global, 'reuse_furls'):
206 206 c.FCClientServiceFactory.reuse_furls = c.Global.reuse_furls
207 207 c.FCEngineServiceFactory.reuse_furls = c.Global.reuse_furls
208 208 del c.Global.reuse_furls
209 209 if hasattr(c.Global, 'secure'):
210 210 c.FCClientServiceFactory.secure = c.Global.secure
211 211 c.FCEngineServiceFactory.secure = c.Global.secure
212 212 del c.Global.secure
213 213
214 214 def construct(self):
215 215 # This is the working dir by now.
216 216 sys.path.insert(0, '')
217 217
218 218 self.start_logging()
219 219 self.import_statements()
220 220
221 221 # Create the service hierarchy
222 222 self.main_service = service.MultiService()
223 223 # The controller service
224 224 controller_service = controllerservice.ControllerService()
225 225 controller_service.setServiceParent(self.main_service)
226 226 # The client tub and all its refereceables
227 227 try:
228 csfactory = FCClientServiceFactory(self.master_config, controller_service)
228 csfactory = FCClientServiceFactory(config=self.master_config, adaptee=controller_service)
229 229 except FURLError, e:
230 230 log.err(e)
231 231 self.exit(0)
232 232 client_service = csfactory.create()
233 233 client_service.setServiceParent(self.main_service)
234 234 # The engine tub
235 235 try:
236 esfactory = FCEngineServiceFactory(self.master_config, controller_service)
236 esfactory = FCEngineServiceFactory(config=self.master_config, adaptee=controller_service)
237 237 except FURLError, e:
238 238 log.err(e)
239 239 self.exit(0)
240 240 engine_service = esfactory.create()
241 241 engine_service.setServiceParent(self.main_service)
242 242
243 243 def import_statements(self):
244 244 statements = self.master_config.Global.import_statements
245 245 for s in statements:
246 246 try:
247 247 log.msg("Executing statement: '%s'" % s)
248 248 exec s in globals(), locals()
249 249 except:
250 250 log.msg("Error running statement: %s" % s)
251 251
252 252 def start_app(self):
253 253 # Start the controller service.
254 254 self.main_service.startService()
255 255 # Write the .pid file overwriting old ones. This allow multiple
256 256 # controllers to clober each other. But Windows is not cleaning
257 257 # these up properly.
258 258 self.write_pid_file(overwrite=True)
259 259 # Add a trigger to delete the .pid file upon shutting down.
260 260 reactor.addSystemEventTrigger('during','shutdown', self.remove_pid_file)
261 261 reactor.run()
262 262
263 263
264 264 def launch_new_instance():
265 265 """Create and run the IPython controller"""
266 266 app = IPControllerApp()
267 267 app.start()
268 268
269 269
270 270 if __name__ == '__main__':
271 271 launch_new_instance()
General Comments 0
You need to be logged in to leave comments. Login now