##// END OF EJS Templates
Fix all remaining imports that used `IPython.frontend`.
Fernando Perez -
Show More
@@ -1,14 +1,14 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Terminal-based IPython entry point.
2 """Terminal-based IPython entry point.
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2012, IPython Development Team.
5 # Copyright (c) 2012, IPython Development Team.
6 #
6 #
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8 #
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 from IPython.frontend.terminal.ipapp import launch_new_instance
12 from IPython.terminal.ipapp import launch_new_instance
13
13
14 launch_new_instance()
14 launch_new_instance()
@@ -1,391 +1,391 b''
1 """ A minimal application base mixin for all ZMQ based IPython frontends.
1 """ A minimal application base mixin for all ZMQ based IPython frontends.
2
2
3 This is not a complete console app, as subprocess will not be able to receive
3 This is not a complete console app, as subprocess will not be able to receive
4 input, there is no real readline support, among other limitations. This is a
4 input, there is no real readline support, among other limitations. This is a
5 refactoring of what used to be the IPython/frontend/qt/console/qtconsoleapp.py
5 refactoring of what used to be the IPython/qt/console/qtconsoleapp.py
6
6
7 Authors:
7 Authors:
8
8
9 * Evan Patterson
9 * Evan Patterson
10 * Min RK
10 * Min RK
11 * Erik Tollerud
11 * Erik Tollerud
12 * Fernando Perez
12 * Fernando Perez
13 * Bussonnier Matthias
13 * Bussonnier Matthias
14 * Thomas Kluyver
14 * Thomas Kluyver
15 * Paul Ivanov
15 * Paul Ivanov
16
16
17 """
17 """
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 # stdlib imports
23 # stdlib imports
24 import atexit
24 import atexit
25 import json
25 import json
26 import os
26 import os
27 import shutil
27 import shutil
28 import signal
28 import signal
29 import sys
29 import sys
30 import uuid
30 import uuid
31
31
32
32
33 # Local imports
33 # Local imports
34 from IPython.config.application import boolean_flag
34 from IPython.config.application import boolean_flag
35 from IPython.config.configurable import Configurable
35 from IPython.config.configurable import Configurable
36 from IPython.core.profiledir import ProfileDir
36 from IPython.core.profiledir import ProfileDir
37 from IPython.kernel.blocking import BlockingKernelClient
37 from IPython.kernel.blocking import BlockingKernelClient
38 from IPython.kernel import KernelManager
38 from IPython.kernel import KernelManager
39 from IPython.kernel import tunnel_to_kernel, find_connection_file, swallow_argv
39 from IPython.kernel import tunnel_to_kernel, find_connection_file, swallow_argv
40 from IPython.utils.path import filefind
40 from IPython.utils.path import filefind
41 from IPython.utils.py3compat import str_to_bytes
41 from IPython.utils.py3compat import str_to_bytes
42 from IPython.utils.traitlets import (
42 from IPython.utils.traitlets import (
43 Dict, List, Unicode, CUnicode, Int, CBool, Any, CaselessStrEnum
43 Dict, List, Unicode, CUnicode, Int, CBool, Any, CaselessStrEnum
44 )
44 )
45 from IPython.kernel.zmq.kernelapp import (
45 from IPython.kernel.zmq.kernelapp import (
46 kernel_flags,
46 kernel_flags,
47 kernel_aliases,
47 kernel_aliases,
48 IPKernelApp
48 IPKernelApp
49 )
49 )
50 from IPython.kernel.zmq.session import Session, default_secure
50 from IPython.kernel.zmq.session import Session, default_secure
51 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
51 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
52
52
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 # Network Constants
54 # Network Constants
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56
57 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
57 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
58
58
59 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
60 # Globals
60 # Globals
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62
62
63
63
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65 # Aliases and Flags
65 # Aliases and Flags
66 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
67
67
68 flags = dict(kernel_flags)
68 flags = dict(kernel_flags)
69
69
70 # the flags that are specific to the frontend
70 # the flags that are specific to the frontend
71 # these must be scrubbed before being passed to the kernel,
71 # these must be scrubbed before being passed to the kernel,
72 # or it will raise an error on unrecognized flags
72 # or it will raise an error on unrecognized flags
73 app_flags = {
73 app_flags = {
74 'existing' : ({'IPythonConsoleApp' : {'existing' : 'kernel*.json'}},
74 'existing' : ({'IPythonConsoleApp' : {'existing' : 'kernel*.json'}},
75 "Connect to an existing kernel. If no argument specified, guess most recent"),
75 "Connect to an existing kernel. If no argument specified, guess most recent"),
76 }
76 }
77 app_flags.update(boolean_flag(
77 app_flags.update(boolean_flag(
78 'confirm-exit', 'IPythonConsoleApp.confirm_exit',
78 'confirm-exit', 'IPythonConsoleApp.confirm_exit',
79 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
79 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
80 to force a direct exit without any confirmation.
80 to force a direct exit without any confirmation.
81 """,
81 """,
82 """Don't prompt the user when exiting. This will terminate the kernel
82 """Don't prompt the user when exiting. This will terminate the kernel
83 if it is owned by the frontend, and leave it alive if it is external.
83 if it is owned by the frontend, and leave it alive if it is external.
84 """
84 """
85 ))
85 ))
86 flags.update(app_flags)
86 flags.update(app_flags)
87
87
88 aliases = dict(kernel_aliases)
88 aliases = dict(kernel_aliases)
89
89
90 # also scrub aliases from the frontend
90 # also scrub aliases from the frontend
91 app_aliases = dict(
91 app_aliases = dict(
92 ip = 'KernelManager.ip',
92 ip = 'KernelManager.ip',
93 transport = 'KernelManager.transport',
93 transport = 'KernelManager.transport',
94 hb = 'IPythonConsoleApp.hb_port',
94 hb = 'IPythonConsoleApp.hb_port',
95 shell = 'IPythonConsoleApp.shell_port',
95 shell = 'IPythonConsoleApp.shell_port',
96 iopub = 'IPythonConsoleApp.iopub_port',
96 iopub = 'IPythonConsoleApp.iopub_port',
97 stdin = 'IPythonConsoleApp.stdin_port',
97 stdin = 'IPythonConsoleApp.stdin_port',
98 existing = 'IPythonConsoleApp.existing',
98 existing = 'IPythonConsoleApp.existing',
99 f = 'IPythonConsoleApp.connection_file',
99 f = 'IPythonConsoleApp.connection_file',
100
100
101
101
102 ssh = 'IPythonConsoleApp.sshserver',
102 ssh = 'IPythonConsoleApp.sshserver',
103 )
103 )
104 aliases.update(app_aliases)
104 aliases.update(app_aliases)
105
105
106 #-----------------------------------------------------------------------------
106 #-----------------------------------------------------------------------------
107 # Classes
107 # Classes
108 #-----------------------------------------------------------------------------
108 #-----------------------------------------------------------------------------
109
109
110 #-----------------------------------------------------------------------------
110 #-----------------------------------------------------------------------------
111 # IPythonConsole
111 # IPythonConsole
112 #-----------------------------------------------------------------------------
112 #-----------------------------------------------------------------------------
113
113
114 classes = [IPKernelApp, ZMQInteractiveShell, KernelManager, ProfileDir, Session]
114 classes = [IPKernelApp, ZMQInteractiveShell, KernelManager, ProfileDir, Session]
115
115
116 try:
116 try:
117 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
117 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
118 except ImportError:
118 except ImportError:
119 pass
119 pass
120 else:
120 else:
121 classes.append(InlineBackend)
121 classes.append(InlineBackend)
122
122
123 class IPythonConsoleApp(Configurable):
123 class IPythonConsoleApp(Configurable):
124 name = 'ipython-console-mixin'
124 name = 'ipython-console-mixin'
125 default_config_file_name='ipython_config.py'
125 default_config_file_name='ipython_config.py'
126
126
127 description = """
127 description = """
128 The IPython Mixin Console.
128 The IPython Mixin Console.
129
129
130 This class contains the common portions of console client (QtConsole,
130 This class contains the common portions of console client (QtConsole,
131 ZMQ-based terminal console, etc). It is not a full console, in that
131 ZMQ-based terminal console, etc). It is not a full console, in that
132 launched terminal subprocesses will not be able to accept input.
132 launched terminal subprocesses will not be able to accept input.
133
133
134 The Console using this mixing supports various extra features beyond
134 The Console using this mixing supports various extra features beyond
135 the single-process Terminal IPython shell, such as connecting to
135 the single-process Terminal IPython shell, such as connecting to
136 existing kernel, via:
136 existing kernel, via:
137
137
138 ipython <appname> --existing
138 ipython <appname> --existing
139
139
140 as well as tunnel via SSH
140 as well as tunnel via SSH
141
141
142 """
142 """
143
143
144 classes = classes
144 classes = classes
145 flags = Dict(flags)
145 flags = Dict(flags)
146 aliases = Dict(aliases)
146 aliases = Dict(aliases)
147 kernel_manager_class = KernelManager
147 kernel_manager_class = KernelManager
148 kernel_client_class = BlockingKernelClient
148 kernel_client_class = BlockingKernelClient
149
149
150 kernel_argv = List(Unicode)
150 kernel_argv = List(Unicode)
151 # frontend flags&aliases to be stripped when building kernel_argv
151 # frontend flags&aliases to be stripped when building kernel_argv
152 frontend_flags = Any(app_flags)
152 frontend_flags = Any(app_flags)
153 frontend_aliases = Any(app_aliases)
153 frontend_aliases = Any(app_aliases)
154
154
155 # create requested profiles by default, if they don't exist:
155 # create requested profiles by default, if they don't exist:
156 auto_create = CBool(True)
156 auto_create = CBool(True)
157 # connection info:
157 # connection info:
158
158
159 sshserver = Unicode('', config=True,
159 sshserver = Unicode('', config=True,
160 help="""The SSH server to use to connect to the kernel.""")
160 help="""The SSH server to use to connect to the kernel.""")
161 sshkey = Unicode('', config=True,
161 sshkey = Unicode('', config=True,
162 help="""Path to the ssh key to use for logging in to the ssh server.""")
162 help="""Path to the ssh key to use for logging in to the ssh server.""")
163
163
164 hb_port = Int(0, config=True,
164 hb_port = Int(0, config=True,
165 help="set the heartbeat port [default: random]")
165 help="set the heartbeat port [default: random]")
166 shell_port = Int(0, config=True,
166 shell_port = Int(0, config=True,
167 help="set the shell (ROUTER) port [default: random]")
167 help="set the shell (ROUTER) port [default: random]")
168 iopub_port = Int(0, config=True,
168 iopub_port = Int(0, config=True,
169 help="set the iopub (PUB) port [default: random]")
169 help="set the iopub (PUB) port [default: random]")
170 stdin_port = Int(0, config=True,
170 stdin_port = Int(0, config=True,
171 help="set the stdin (DEALER) port [default: random]")
171 help="set the stdin (DEALER) port [default: random]")
172 connection_file = Unicode('', config=True,
172 connection_file = Unicode('', config=True,
173 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
173 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
174
174
175 This file will contain the IP, ports, and authentication key needed to connect
175 This file will contain the IP, ports, and authentication key needed to connect
176 clients to this kernel. By default, this file will be created in the security-dir
176 clients to this kernel. By default, this file will be created in the security-dir
177 of the current profile, but can be specified by absolute path.
177 of the current profile, but can be specified by absolute path.
178 """)
178 """)
179 def _connection_file_default(self):
179 def _connection_file_default(self):
180 return 'kernel-%i.json' % os.getpid()
180 return 'kernel-%i.json' % os.getpid()
181
181
182 existing = CUnicode('', config=True,
182 existing = CUnicode('', config=True,
183 help="""Connect to an already running kernel""")
183 help="""Connect to an already running kernel""")
184
184
185 confirm_exit = CBool(True, config=True,
185 confirm_exit = CBool(True, config=True,
186 help="""
186 help="""
187 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
187 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
188 to force a direct exit without any confirmation.""",
188 to force a direct exit without any confirmation.""",
189 )
189 )
190
190
191
191
192 def build_kernel_argv(self, argv=None):
192 def build_kernel_argv(self, argv=None):
193 """build argv to be passed to kernel subprocess"""
193 """build argv to be passed to kernel subprocess"""
194 if argv is None:
194 if argv is None:
195 argv = sys.argv[1:]
195 argv = sys.argv[1:]
196 self.kernel_argv = swallow_argv(argv, self.frontend_aliases, self.frontend_flags)
196 self.kernel_argv = swallow_argv(argv, self.frontend_aliases, self.frontend_flags)
197 # kernel should inherit default config file from frontend
197 # kernel should inherit default config file from frontend
198 self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name)
198 self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name)
199
199
200 def init_connection_file(self):
200 def init_connection_file(self):
201 """find the connection file, and load the info if found.
201 """find the connection file, and load the info if found.
202
202
203 The current working directory and the current profile's security
203 The current working directory and the current profile's security
204 directory will be searched for the file if it is not given by
204 directory will be searched for the file if it is not given by
205 absolute path.
205 absolute path.
206
206
207 When attempting to connect to an existing kernel and the `--existing`
207 When attempting to connect to an existing kernel and the `--existing`
208 argument does not match an existing file, it will be interpreted as a
208 argument does not match an existing file, it will be interpreted as a
209 fileglob, and the matching file in the current profile's security dir
209 fileglob, and the matching file in the current profile's security dir
210 with the latest access time will be used.
210 with the latest access time will be used.
211
211
212 After this method is called, self.connection_file contains the *full path*
212 After this method is called, self.connection_file contains the *full path*
213 to the connection file, never just its name.
213 to the connection file, never just its name.
214 """
214 """
215 if self.existing:
215 if self.existing:
216 try:
216 try:
217 cf = find_connection_file(self.existing)
217 cf = find_connection_file(self.existing)
218 except Exception:
218 except Exception:
219 self.log.critical("Could not find existing kernel connection file %s", self.existing)
219 self.log.critical("Could not find existing kernel connection file %s", self.existing)
220 self.exit(1)
220 self.exit(1)
221 self.log.info("Connecting to existing kernel: %s" % cf)
221 self.log.info("Connecting to existing kernel: %s" % cf)
222 self.connection_file = cf
222 self.connection_file = cf
223 else:
223 else:
224 # not existing, check if we are going to write the file
224 # not existing, check if we are going to write the file
225 # and ensure that self.connection_file is a full path, not just the shortname
225 # and ensure that self.connection_file is a full path, not just the shortname
226 try:
226 try:
227 cf = find_connection_file(self.connection_file)
227 cf = find_connection_file(self.connection_file)
228 except Exception:
228 except Exception:
229 # file might not exist
229 # file might not exist
230 if self.connection_file == os.path.basename(self.connection_file):
230 if self.connection_file == os.path.basename(self.connection_file):
231 # just shortname, put it in security dir
231 # just shortname, put it in security dir
232 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
232 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
233 else:
233 else:
234 cf = self.connection_file
234 cf = self.connection_file
235 self.connection_file = cf
235 self.connection_file = cf
236
236
237 # should load_connection_file only be used for existing?
237 # should load_connection_file only be used for existing?
238 # as it is now, this allows reusing ports if an existing
238 # as it is now, this allows reusing ports if an existing
239 # file is requested
239 # file is requested
240 try:
240 try:
241 self.load_connection_file()
241 self.load_connection_file()
242 except Exception:
242 except Exception:
243 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
243 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
244 self.exit(1)
244 self.exit(1)
245
245
246 def load_connection_file(self):
246 def load_connection_file(self):
247 """load ip/port/hmac config from JSON connection file"""
247 """load ip/port/hmac config from JSON connection file"""
248 # this is identical to IPKernelApp.load_connection_file
248 # this is identical to IPKernelApp.load_connection_file
249 # perhaps it can be centralized somewhere?
249 # perhaps it can be centralized somewhere?
250 try:
250 try:
251 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
251 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
252 except IOError:
252 except IOError:
253 self.log.debug("Connection File not found: %s", self.connection_file)
253 self.log.debug("Connection File not found: %s", self.connection_file)
254 return
254 return
255 self.log.debug(u"Loading connection file %s", fname)
255 self.log.debug(u"Loading connection file %s", fname)
256 with open(fname) as f:
256 with open(fname) as f:
257 cfg = json.load(f)
257 cfg = json.load(f)
258
258
259 self.config.KernelManager.transport = cfg.get('transport', 'tcp')
259 self.config.KernelManager.transport = cfg.get('transport', 'tcp')
260 self.config.KernelManager.ip = cfg.get('ip', LOCALHOST)
260 self.config.KernelManager.ip = cfg.get('ip', LOCALHOST)
261
261
262 for channel in ('hb', 'shell', 'iopub', 'stdin'):
262 for channel in ('hb', 'shell', 'iopub', 'stdin'):
263 name = channel + '_port'
263 name = channel + '_port'
264 if getattr(self, name) == 0 and name in cfg:
264 if getattr(self, name) == 0 and name in cfg:
265 # not overridden by config or cl_args
265 # not overridden by config or cl_args
266 setattr(self, name, cfg[name])
266 setattr(self, name, cfg[name])
267 if 'key' in cfg:
267 if 'key' in cfg:
268 self.config.Session.key = str_to_bytes(cfg['key'])
268 self.config.Session.key = str_to_bytes(cfg['key'])
269
269
270 def init_ssh(self):
270 def init_ssh(self):
271 """set up ssh tunnels, if needed."""
271 """set up ssh tunnels, if needed."""
272 if not self.existing or (not self.sshserver and not self.sshkey):
272 if not self.existing or (not self.sshserver and not self.sshkey):
273 return
273 return
274
274
275 self.load_connection_file()
275 self.load_connection_file()
276
276
277 transport = self.config.KernelManager.transport
277 transport = self.config.KernelManager.transport
278 ip = self.config.KernelManager.ip
278 ip = self.config.KernelManager.ip
279
279
280 if transport != 'tcp':
280 if transport != 'tcp':
281 self.log.error("Can only use ssh tunnels with TCP sockets, not %s", transport)
281 self.log.error("Can only use ssh tunnels with TCP sockets, not %s", transport)
282 sys.exit(-1)
282 sys.exit(-1)
283
283
284 if self.sshkey and not self.sshserver:
284 if self.sshkey and not self.sshserver:
285 # specifying just the key implies that we are connecting directly
285 # specifying just the key implies that we are connecting directly
286 self.sshserver = ip
286 self.sshserver = ip
287 ip = LOCALHOST
287 ip = LOCALHOST
288
288
289 # build connection dict for tunnels:
289 # build connection dict for tunnels:
290 info = dict(ip=ip,
290 info = dict(ip=ip,
291 shell_port=self.shell_port,
291 shell_port=self.shell_port,
292 iopub_port=self.iopub_port,
292 iopub_port=self.iopub_port,
293 stdin_port=self.stdin_port,
293 stdin_port=self.stdin_port,
294 hb_port=self.hb_port
294 hb_port=self.hb_port
295 )
295 )
296
296
297 self.log.info("Forwarding connections to %s via %s"%(ip, self.sshserver))
297 self.log.info("Forwarding connections to %s via %s"%(ip, self.sshserver))
298
298
299 # tunnels return a new set of ports, which will be on localhost:
299 # tunnels return a new set of ports, which will be on localhost:
300 self.config.KernelManager.ip = LOCALHOST
300 self.config.KernelManager.ip = LOCALHOST
301 try:
301 try:
302 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
302 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
303 except:
303 except:
304 # even catch KeyboardInterrupt
304 # even catch KeyboardInterrupt
305 self.log.error("Could not setup tunnels", exc_info=True)
305 self.log.error("Could not setup tunnels", exc_info=True)
306 self.exit(1)
306 self.exit(1)
307
307
308 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports
308 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports
309
309
310 cf = self.connection_file
310 cf = self.connection_file
311 base,ext = os.path.splitext(cf)
311 base,ext = os.path.splitext(cf)
312 base = os.path.basename(base)
312 base = os.path.basename(base)
313 self.connection_file = os.path.basename(base)+'-ssh'+ext
313 self.connection_file = os.path.basename(base)+'-ssh'+ext
314 self.log.critical("To connect another client via this tunnel, use:")
314 self.log.critical("To connect another client via this tunnel, use:")
315 self.log.critical("--existing %s" % self.connection_file)
315 self.log.critical("--existing %s" % self.connection_file)
316
316
317 def _new_connection_file(self):
317 def _new_connection_file(self):
318 cf = ''
318 cf = ''
319 while not cf:
319 while not cf:
320 # we don't need a 128b id to distinguish kernels, use more readable
320 # we don't need a 128b id to distinguish kernels, use more readable
321 # 48b node segment (12 hex chars). Users running more than 32k simultaneous
321 # 48b node segment (12 hex chars). Users running more than 32k simultaneous
322 # kernels can subclass.
322 # kernels can subclass.
323 ident = str(uuid.uuid4()).split('-')[-1]
323 ident = str(uuid.uuid4()).split('-')[-1]
324 cf = os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % ident)
324 cf = os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % ident)
325 # only keep if it's actually new. Protect against unlikely collision
325 # only keep if it's actually new. Protect against unlikely collision
326 # in 48b random search space
326 # in 48b random search space
327 cf = cf if not os.path.exists(cf) else ''
327 cf = cf if not os.path.exists(cf) else ''
328 return cf
328 return cf
329
329
330 def init_kernel_manager(self):
330 def init_kernel_manager(self):
331 # Don't let Qt or ZMQ swallow KeyboardInterupts.
331 # Don't let Qt or ZMQ swallow KeyboardInterupts.
332 if self.existing:
332 if self.existing:
333 self.kernel_manager = None
333 self.kernel_manager = None
334 return
334 return
335 signal.signal(signal.SIGINT, signal.SIG_DFL)
335 signal.signal(signal.SIGINT, signal.SIG_DFL)
336
336
337 # Create a KernelManager and start a kernel.
337 # Create a KernelManager and start a kernel.
338 self.kernel_manager = self.kernel_manager_class(
338 self.kernel_manager = self.kernel_manager_class(
339 shell_port=self.shell_port,
339 shell_port=self.shell_port,
340 iopub_port=self.iopub_port,
340 iopub_port=self.iopub_port,
341 stdin_port=self.stdin_port,
341 stdin_port=self.stdin_port,
342 hb_port=self.hb_port,
342 hb_port=self.hb_port,
343 connection_file=self.connection_file,
343 connection_file=self.connection_file,
344 config=self.config,
344 config=self.config,
345 )
345 )
346 self.kernel_manager.client_factory = self.kernel_client_class
346 self.kernel_manager.client_factory = self.kernel_client_class
347 self.kernel_manager.start_kernel(extra_arguments=self.kernel_argv)
347 self.kernel_manager.start_kernel(extra_arguments=self.kernel_argv)
348 atexit.register(self.kernel_manager.cleanup_ipc_files)
348 atexit.register(self.kernel_manager.cleanup_ipc_files)
349
349
350 if self.sshserver:
350 if self.sshserver:
351 # ssh, write new connection file
351 # ssh, write new connection file
352 self.kernel_manager.write_connection_file()
352 self.kernel_manager.write_connection_file()
353
353
354 # in case KM defaults / ssh writing changes things:
354 # in case KM defaults / ssh writing changes things:
355 km = self.kernel_manager
355 km = self.kernel_manager
356 self.shell_port=km.shell_port
356 self.shell_port=km.shell_port
357 self.iopub_port=km.iopub_port
357 self.iopub_port=km.iopub_port
358 self.stdin_port=km.stdin_port
358 self.stdin_port=km.stdin_port
359 self.hb_port=km.hb_port
359 self.hb_port=km.hb_port
360 self.connection_file = km.connection_file
360 self.connection_file = km.connection_file
361
361
362 atexit.register(self.kernel_manager.cleanup_connection_file)
362 atexit.register(self.kernel_manager.cleanup_connection_file)
363
363
364 def init_kernel_client(self):
364 def init_kernel_client(self):
365 if self.kernel_manager is not None:
365 if self.kernel_manager is not None:
366 self.kernel_client = self.kernel_manager.client()
366 self.kernel_client = self.kernel_manager.client()
367 else:
367 else:
368 self.kernel_client = self.kernel_client_class(
368 self.kernel_client = self.kernel_client_class(
369 shell_port=self.shell_port,
369 shell_port=self.shell_port,
370 iopub_port=self.iopub_port,
370 iopub_port=self.iopub_port,
371 stdin_port=self.stdin_port,
371 stdin_port=self.stdin_port,
372 hb_port=self.hb_port,
372 hb_port=self.hb_port,
373 connection_file=self.connection_file,
373 connection_file=self.connection_file,
374 config=self.config,
374 config=self.config,
375 )
375 )
376
376
377 self.kernel_client.start_channels()
377 self.kernel_client.start_channels()
378
378
379
379
380
380
381 def initialize(self, argv=None):
381 def initialize(self, argv=None):
382 """
382 """
383 Classes which mix this class in should call:
383 Classes which mix this class in should call:
384 IPythonConsoleApp.initialize(self,argv)
384 IPythonConsoleApp.initialize(self,argv)
385 """
385 """
386 self.init_connection_file()
386 self.init_connection_file()
387 default_secure(self.config)
387 default_secure(self.config)
388 self.init_ssh()
388 self.init_ssh()
389 self.init_kernel_manager()
389 self.init_kernel_manager()
390 self.init_kernel_client()
390 self.init_kernel_client()
391
391
@@ -1,306 +1,306 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 An application for managing IPython profiles.
3 An application for managing IPython profiles.
4
4
5 To be invoked as the `ipython profile` subcommand.
5 To be invoked as the `ipython profile` subcommand.
6
6
7 Authors:
7 Authors:
8
8
9 * Min RK
9 * Min RK
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 os
24 import os
25
25
26 from IPython.config.application import Application
26 from IPython.config.application import Application
27 from IPython.core.application import (
27 from IPython.core.application import (
28 BaseIPythonApplication, base_flags
28 BaseIPythonApplication, base_flags
29 )
29 )
30 from IPython.core.profiledir import ProfileDir
30 from IPython.core.profiledir import ProfileDir
31 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
31 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
32 from IPython.utils.traitlets import Unicode, Bool, Dict
32 from IPython.utils.traitlets import Unicode, Bool, Dict
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Constants
35 # Constants
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 create_help = """Create an IPython profile by name
38 create_help = """Create an IPython profile by name
39
39
40 Create an ipython profile directory by its name or
40 Create an ipython profile directory by its name or
41 profile directory path. Profile directories contain
41 profile directory path. Profile directories contain
42 configuration, log and security related files and are named
42 configuration, log and security related files and are named
43 using the convention 'profile_<name>'. By default they are
43 using the convention 'profile_<name>'. By default they are
44 located in your ipython directory. Once created, you will
44 located in your ipython directory. Once created, you will
45 can edit the configuration files in the profile
45 can edit the configuration files in the profile
46 directory to configure IPython. Most users will create a
46 directory to configure IPython. Most users will create a
47 profile directory by name,
47 profile directory by name,
48 `ipython profile create myprofile`, which will put the directory
48 `ipython profile create myprofile`, which will put the directory
49 in `<ipython_dir>/profile_myprofile`.
49 in `<ipython_dir>/profile_myprofile`.
50 """
50 """
51 list_help = """List available IPython profiles
51 list_help = """List available IPython profiles
52
52
53 List all available profiles, by profile location, that can
53 List all available profiles, by profile location, that can
54 be found in the current working directly or in the ipython
54 be found in the current working directly or in the ipython
55 directory. Profile directories are named using the convention
55 directory. Profile directories are named using the convention
56 'profile_<profile>'.
56 'profile_<profile>'.
57 """
57 """
58 profile_help = """Manage IPython profiles
58 profile_help = """Manage IPython profiles
59
59
60 Profile directories contain
60 Profile directories contain
61 configuration, log and security related files and are named
61 configuration, log and security related files and are named
62 using the convention 'profile_<name>'. By default they are
62 using the convention 'profile_<name>'. By default they are
63 located in your ipython directory. You can create profiles
63 located in your ipython directory. You can create profiles
64 with `ipython profile create <name>`, or see the profiles you
64 with `ipython profile create <name>`, or see the profiles you
65 already have with `ipython profile list`
65 already have with `ipython profile list`
66
66
67 To get started configuring IPython, simply do:
67 To get started configuring IPython, simply do:
68
68
69 $> ipython profile create
69 $> ipython profile create
70
70
71 and IPython will create the default profile in <ipython_dir>/profile_default,
71 and IPython will create the default profile in <ipython_dir>/profile_default,
72 where you can edit ipython_config.py to start configuring IPython.
72 where you can edit ipython_config.py to start configuring IPython.
73
73
74 """
74 """
75
75
76 _list_examples = "ipython profile list # list all profiles"
76 _list_examples = "ipython profile list # list all profiles"
77
77
78 _create_examples = """
78 _create_examples = """
79 ipython profile create foo # create profile foo w/ default config files
79 ipython profile create foo # create profile foo w/ default config files
80 ipython profile create foo --reset # restage default config files over current
80 ipython profile create foo --reset # restage default config files over current
81 ipython profile create foo --parallel # also stage parallel config files
81 ipython profile create foo --parallel # also stage parallel config files
82 """
82 """
83
83
84 _main_examples = """
84 _main_examples = """
85 ipython profile create -h # show the help string for the create subcommand
85 ipython profile create -h # show the help string for the create subcommand
86 ipython profile list -h # show the help string for the list subcommand
86 ipython profile list -h # show the help string for the list subcommand
87
87
88 ipython locate profile foo # print the path to the directory for profile 'foo'
88 ipython locate profile foo # print the path to the directory for profile 'foo'
89 """
89 """
90
90
91 #-----------------------------------------------------------------------------
91 #-----------------------------------------------------------------------------
92 # Profile Application Class (for `ipython profile` subcommand)
92 # Profile Application Class (for `ipython profile` subcommand)
93 #-----------------------------------------------------------------------------
93 #-----------------------------------------------------------------------------
94
94
95
95
96 def list_profiles_in(path):
96 def list_profiles_in(path):
97 """list profiles in a given root directory"""
97 """list profiles in a given root directory"""
98 files = os.listdir(path)
98 files = os.listdir(path)
99 profiles = []
99 profiles = []
100 for f in files:
100 for f in files:
101 full_path = os.path.join(path, f)
101 full_path = os.path.join(path, f)
102 if os.path.isdir(full_path) and f.startswith('profile_'):
102 if os.path.isdir(full_path) and f.startswith('profile_'):
103 profiles.append(f.split('_',1)[-1])
103 profiles.append(f.split('_',1)[-1])
104 return profiles
104 return profiles
105
105
106
106
107 def list_bundled_profiles():
107 def list_bundled_profiles():
108 """list profiles that are bundled with IPython."""
108 """list profiles that are bundled with IPython."""
109 path = os.path.join(get_ipython_package_dir(), u'config', u'profile')
109 path = os.path.join(get_ipython_package_dir(), u'config', u'profile')
110 files = os.listdir(path)
110 files = os.listdir(path)
111 profiles = []
111 profiles = []
112 for profile in files:
112 for profile in files:
113 full_path = os.path.join(path, profile)
113 full_path = os.path.join(path, profile)
114 if os.path.isdir(full_path) and profile != "__pycache__":
114 if os.path.isdir(full_path) and profile != "__pycache__":
115 profiles.append(profile)
115 profiles.append(profile)
116 return profiles
116 return profiles
117
117
118
118
119 class ProfileLocate(BaseIPythonApplication):
119 class ProfileLocate(BaseIPythonApplication):
120 description = """print the path an IPython profile dir"""
120 description = """print the path an IPython profile dir"""
121
121
122 def parse_command_line(self, argv=None):
122 def parse_command_line(self, argv=None):
123 super(ProfileLocate, self).parse_command_line(argv)
123 super(ProfileLocate, self).parse_command_line(argv)
124 if self.extra_args:
124 if self.extra_args:
125 self.profile = self.extra_args[0]
125 self.profile = self.extra_args[0]
126
126
127 def start(self):
127 def start(self):
128 print self.profile_dir.location
128 print self.profile_dir.location
129
129
130
130
131 class ProfileList(Application):
131 class ProfileList(Application):
132 name = u'ipython-profile'
132 name = u'ipython-profile'
133 description = list_help
133 description = list_help
134 examples = _list_examples
134 examples = _list_examples
135
135
136 aliases = Dict({
136 aliases = Dict({
137 'ipython-dir' : 'ProfileList.ipython_dir',
137 'ipython-dir' : 'ProfileList.ipython_dir',
138 'log-level' : 'Application.log_level',
138 'log-level' : 'Application.log_level',
139 })
139 })
140 flags = Dict(dict(
140 flags = Dict(dict(
141 debug = ({'Application' : {'log_level' : 0}},
141 debug = ({'Application' : {'log_level' : 0}},
142 "Set Application.log_level to 0, maximizing log output."
142 "Set Application.log_level to 0, maximizing log output."
143 )
143 )
144 ))
144 ))
145
145
146 ipython_dir = Unicode(get_ipython_dir(), config=True,
146 ipython_dir = Unicode(get_ipython_dir(), config=True,
147 help="""
147 help="""
148 The name of the IPython directory. This directory is used for logging
148 The name of the IPython directory. This directory is used for logging
149 configuration (through profiles), history storage, etc. The default
149 configuration (through profiles), history storage, etc. The default
150 is usually $HOME/.ipython. This options can also be specified through
150 is usually $HOME/.ipython. This options can also be specified through
151 the environment variable IPYTHONDIR.
151 the environment variable IPYTHONDIR.
152 """
152 """
153 )
153 )
154
154
155
155
156 def _print_profiles(self, profiles):
156 def _print_profiles(self, profiles):
157 """print list of profiles, indented."""
157 """print list of profiles, indented."""
158 for profile in profiles:
158 for profile in profiles:
159 print ' %s' % profile
159 print ' %s' % profile
160
160
161 def list_profile_dirs(self):
161 def list_profile_dirs(self):
162 profiles = list_bundled_profiles()
162 profiles = list_bundled_profiles()
163 if profiles:
163 if profiles:
164 print
164 print
165 print "Available profiles in IPython:"
165 print "Available profiles in IPython:"
166 self._print_profiles(profiles)
166 self._print_profiles(profiles)
167 print
167 print
168 print " The first request for a bundled profile will copy it"
168 print " The first request for a bundled profile will copy it"
169 print " into your IPython directory (%s)," % self.ipython_dir
169 print " into your IPython directory (%s)," % self.ipython_dir
170 print " where you can customize it."
170 print " where you can customize it."
171
171
172 profiles = list_profiles_in(self.ipython_dir)
172 profiles = list_profiles_in(self.ipython_dir)
173 if profiles:
173 if profiles:
174 print
174 print
175 print "Available profiles in %s:" % self.ipython_dir
175 print "Available profiles in %s:" % self.ipython_dir
176 self._print_profiles(profiles)
176 self._print_profiles(profiles)
177
177
178 profiles = list_profiles_in(os.getcwdu())
178 profiles = list_profiles_in(os.getcwdu())
179 if profiles:
179 if profiles:
180 print
180 print
181 print "Available profiles in current directory (%s):" % os.getcwdu()
181 print "Available profiles in current directory (%s):" % os.getcwdu()
182 self._print_profiles(profiles)
182 self._print_profiles(profiles)
183
183
184 print
184 print
185 print "To use any of the above profiles, start IPython with:"
185 print "To use any of the above profiles, start IPython with:"
186 print " ipython --profile=<name>"
186 print " ipython --profile=<name>"
187 print
187 print
188
188
189 def start(self):
189 def start(self):
190 self.list_profile_dirs()
190 self.list_profile_dirs()
191
191
192
192
193 create_flags = {}
193 create_flags = {}
194 create_flags.update(base_flags)
194 create_flags.update(base_flags)
195 # don't include '--init' flag, which implies running profile create in other apps
195 # don't include '--init' flag, which implies running profile create in other apps
196 create_flags.pop('init')
196 create_flags.pop('init')
197 create_flags['reset'] = ({'ProfileCreate': {'overwrite' : True}},
197 create_flags['reset'] = ({'ProfileCreate': {'overwrite' : True}},
198 "reset config files in this profile to the defaults.")
198 "reset config files in this profile to the defaults.")
199 create_flags['parallel'] = ({'ProfileCreate': {'parallel' : True}},
199 create_flags['parallel'] = ({'ProfileCreate': {'parallel' : True}},
200 "Include the config files for parallel "
200 "Include the config files for parallel "
201 "computing apps (ipengine, ipcontroller, etc.)")
201 "computing apps (ipengine, ipcontroller, etc.)")
202
202
203
203
204 class ProfileCreate(BaseIPythonApplication):
204 class ProfileCreate(BaseIPythonApplication):
205 name = u'ipython-profile'
205 name = u'ipython-profile'
206 description = create_help
206 description = create_help
207 examples = _create_examples
207 examples = _create_examples
208 auto_create = Bool(True, config=False)
208 auto_create = Bool(True, config=False)
209
209
210 def _copy_config_files_default(self):
210 def _copy_config_files_default(self):
211 return True
211 return True
212
212
213 parallel = Bool(False, config=True,
213 parallel = Bool(False, config=True,
214 help="whether to include parallel computing config files")
214 help="whether to include parallel computing config files")
215 def _parallel_changed(self, name, old, new):
215 def _parallel_changed(self, name, old, new):
216 parallel_files = [ 'ipcontroller_config.py',
216 parallel_files = [ 'ipcontroller_config.py',
217 'ipengine_config.py',
217 'ipengine_config.py',
218 'ipcluster_config.py'
218 'ipcluster_config.py'
219 ]
219 ]
220 if new:
220 if new:
221 for cf in parallel_files:
221 for cf in parallel_files:
222 self.config_files.append(cf)
222 self.config_files.append(cf)
223 else:
223 else:
224 for cf in parallel_files:
224 for cf in parallel_files:
225 if cf in self.config_files:
225 if cf in self.config_files:
226 self.config_files.remove(cf)
226 self.config_files.remove(cf)
227
227
228 def parse_command_line(self, argv):
228 def parse_command_line(self, argv):
229 super(ProfileCreate, self).parse_command_line(argv)
229 super(ProfileCreate, self).parse_command_line(argv)
230 # accept positional arg as profile name
230 # accept positional arg as profile name
231 if self.extra_args:
231 if self.extra_args:
232 self.profile = self.extra_args[0]
232 self.profile = self.extra_args[0]
233
233
234 flags = Dict(create_flags)
234 flags = Dict(create_flags)
235
235
236 classes = [ProfileDir]
236 classes = [ProfileDir]
237
237
238 def init_config_files(self):
238 def init_config_files(self):
239 super(ProfileCreate, self).init_config_files()
239 super(ProfileCreate, self).init_config_files()
240 # use local imports, since these classes may import from here
240 # use local imports, since these classes may import from here
241 from IPython.frontend.terminal.ipapp import TerminalIPythonApp
241 from IPython.terminal.ipapp import TerminalIPythonApp
242 apps = [TerminalIPythonApp]
242 apps = [TerminalIPythonApp]
243 try:
243 try:
244 from IPython.frontend.qt.console.qtconsoleapp import IPythonQtConsoleApp
244 from IPython.qt.console.qtconsoleapp import IPythonQtConsoleApp
245 except Exception:
245 except Exception:
246 # this should be ImportError, but under weird circumstances
246 # this should be ImportError, but under weird circumstances
247 # this might be an AttributeError, or possibly others
247 # this might be an AttributeError, or possibly others
248 # in any case, nothing should cause the profile creation to crash.
248 # in any case, nothing should cause the profile creation to crash.
249 pass
249 pass
250 else:
250 else:
251 apps.append(IPythonQtConsoleApp)
251 apps.append(IPythonQtConsoleApp)
252 try:
252 try:
253 from IPython.frontend.html.notebook.notebookapp import NotebookApp
253 from IPython.html.notebook.notebookapp import NotebookApp
254 except ImportError:
254 except ImportError:
255 pass
255 pass
256 except Exception:
256 except Exception:
257 self.log.debug('Unexpected error when importing NotebookApp',
257 self.log.debug('Unexpected error when importing NotebookApp',
258 exc_info=True
258 exc_info=True
259 )
259 )
260 else:
260 else:
261 apps.append(NotebookApp)
261 apps.append(NotebookApp)
262 if self.parallel:
262 if self.parallel:
263 from IPython.parallel.apps.ipcontrollerapp import IPControllerApp
263 from IPython.parallel.apps.ipcontrollerapp import IPControllerApp
264 from IPython.parallel.apps.ipengineapp import IPEngineApp
264 from IPython.parallel.apps.ipengineapp import IPEngineApp
265 from IPython.parallel.apps.ipclusterapp import IPClusterStart
265 from IPython.parallel.apps.ipclusterapp import IPClusterStart
266 from IPython.parallel.apps.iploggerapp import IPLoggerApp
266 from IPython.parallel.apps.iploggerapp import IPLoggerApp
267 apps.extend([
267 apps.extend([
268 IPControllerApp,
268 IPControllerApp,
269 IPEngineApp,
269 IPEngineApp,
270 IPClusterStart,
270 IPClusterStart,
271 IPLoggerApp,
271 IPLoggerApp,
272 ])
272 ])
273 for App in apps:
273 for App in apps:
274 app = App()
274 app = App()
275 app.config.update(self.config)
275 app.config.update(self.config)
276 app.log = self.log
276 app.log = self.log
277 app.overwrite = self.overwrite
277 app.overwrite = self.overwrite
278 app.copy_config_files=True
278 app.copy_config_files=True
279 app.profile = self.profile
279 app.profile = self.profile
280 app.init_profile_dir()
280 app.init_profile_dir()
281 app.init_config_files()
281 app.init_config_files()
282
282
283 def stage_default_config_file(self):
283 def stage_default_config_file(self):
284 pass
284 pass
285
285
286
286
287 class ProfileApp(Application):
287 class ProfileApp(Application):
288 name = u'ipython-profile'
288 name = u'ipython-profile'
289 description = profile_help
289 description = profile_help
290 examples = _main_examples
290 examples = _main_examples
291
291
292 subcommands = Dict(dict(
292 subcommands = Dict(dict(
293 create = (ProfileCreate, ProfileCreate.description.splitlines()[0]),
293 create = (ProfileCreate, ProfileCreate.description.splitlines()[0]),
294 list = (ProfileList, ProfileList.description.splitlines()[0]),
294 list = (ProfileList, ProfileList.description.splitlines()[0]),
295 ))
295 ))
296
296
297 def start(self):
297 def start(self):
298 if self.subapp is None:
298 if self.subapp is None:
299 print "No subcommand specified. Must specify one of: %s"%(self.subcommands.keys())
299 print "No subcommand specified. Must specify one of: %s"%(self.subcommands.keys())
300 print
300 print
301 self.print_description()
301 self.print_description()
302 self.print_subcommands()
302 self.print_subcommands()
303 self.exit(1)
303 self.exit(1)
304 else:
304 else:
305 return self.subapp.start()
305 return self.subapp.start()
306
306
@@ -1,73 +1,73 b''
1 """
1 """
2 Shim to maintain backwards compatibility with old frontend imports.
2 Shim to maintain backwards compatibility with old frontend imports.
3
3
4 We have moved all contents of the old `frontend` subpackage into top-level
4 We have moved all contents of the old `frontend` subpackage into top-level
5 subpackages (`html`, `qt` and `terminal`). This will let code that was making
5 subpackages (`html`, `qt` and `terminal`).
6 `from IPython.frontend...` calls continue working, though a warning will be
6
7 printed.
7 This will let code that was making `from IPython.frontend...` calls continue
8 working, though a warning will be printed.
8 """
9 """
9
10
10 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
11 # Copyright (c) 2013, IPython Development Team.
12 # Copyright (c) 2013, IPython Development Team.
12 #
13 #
13 # Distributed under the terms of the Modified BSD License.
14 # Distributed under the terms of the Modified BSD License.
14 #
15 #
15 # The full license is in the file COPYING.txt, distributed with this software.
16 # The full license is in the file COPYING.txt, distributed with this software.
16 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
17
18
18 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
19 # Imports
20 # Imports
20 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
21 from __future__ import print_function
22 from __future__ import print_function
22
23
23 # Stdlib
24 # Stdlib
24 import sys
25 import sys
25 import types
26 import types
26 from warnings import warn
27 from warnings import warn
27
28
28 warn("The top-level `frontend` package has been deprecated. "
29 warn("The top-level `frontend` package has been deprecated. "
29 "All its subpackages have been moved to the top `IPython` level.")
30 "All its subpackages have been moved to the top `IPython` level.")
30
31
31 1/0
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Class declarations
33 # Class declarations
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 class ShimModule(types.ModuleType):
36 class ShimModule(types.ModuleType):
37
37
38 def __getattribute__(self, key):
38 def __getattribute__(self, key):
39 # Use the equivalent of import_item(name), see below
39 # Use the equivalent of import_item(name), see below
40 name = 'IPython.' + key
40 name = 'IPython.' + key
41
41
42 # NOTE: the code below is copied *verbatim* from
42 # NOTE: the code below is copied *verbatim* from
43 # importstring.import_item. For some very strange reason that makes no
43 # importstring.import_item. For some very strange reason that makes no
44 # sense to me, if we call it *as a function*, it doesn't work. This
44 # sense to me, if we call it *as a function*, it doesn't work. This
45 # has something to do with the deep bowels of the import machinery and
45 # has something to do with the deep bowels of the import machinery and
46 # I couldn't find a way to make the code work as a standard function
46 # I couldn't find a way to make the code work as a standard function
47 # call. But at least since it's an unmodified copy of import_item,
47 # call. But at least since it's an unmodified copy of import_item,
48 # which is used extensively and has a test suite, we can be reasonably
48 # which is used extensively and has a test suite, we can be reasonably
49 # confident this is OK. If anyone finds how to call the function, all
49 # confident this is OK. If anyone finds how to call the function, all
50 # the below could be replaced simply with:
50 # the below could be replaced simply with:
51 #
51 #
52 # from IPython.utils.importstring import import_item
52 # from IPython.utils.importstring import import_item
53 # return import_item('IPython.' + key)
53 # return import_item('IPython.' + key)
54
54
55 parts = name.rsplit('.', 1)
55 parts = name.rsplit('.', 1)
56 if len(parts) == 2:
56 if len(parts) == 2:
57 # called with 'foo.bar....'
57 # called with 'foo.bar....'
58 package, obj = parts
58 package, obj = parts
59 module = __import__(package, fromlist=[obj])
59 module = __import__(package, fromlist=[obj])
60 try:
60 try:
61 pak = module.__dict__[obj]
61 pak = module.__dict__[obj]
62 except KeyError:
62 except KeyError:
63 raise ImportError('No module named %s' % obj)
63 raise ImportError('No module named %s' % obj)
64 return pak
64 return pak
65 else:
65 else:
66 # called with un-dotted string
66 # called with un-dotted string
67 return __import__(parts[0])
67 return __import__(parts[0])
68
68
69
69
70 # Unconditionally insert the shim into sys.modules so that further import calls
70 # Unconditionally insert the shim into sys.modules so that further import calls
71 # trigger the custom attribute access above
71 # trigger the custom attribute access above
72
72
73 sys.modules['IPython.frontend'] = ShimModule('frontend')
73 sys.modules['IPython.frontend'] = ShimModule('frontend')
@@ -1,73 +1,73 b''
1 # IPython Notebook development
1 # IPython Notebook development
2
2
3 ## Development dependencies
3 ## Development dependencies
4
4
5 Developers of the IPython Notebook will need to install the following tools:
5 Developers of the IPython Notebook will need to install the following tools:
6
6
7 * fabric
7 * fabric
8 * node.js
8 * node.js
9 * less (`npm install -g less`)
9 * less (`npm install -g less`)
10 * bower (`npm install -g bower`)
10 * bower (`npm install -g bower`)
11
11
12 ## Components
12 ## Components
13
13
14 We are moving to a model where our JavaScript dependencies are managed using
14 We are moving to a model where our JavaScript dependencies are managed using
15 [bower](http://bower.io/). These packages are installed in `static/components`
15 [bower](http://bower.io/). These packages are installed in `static/components`
16 and commited into our git repo. Our dependencies are described in the file
16 and commited into our git repo. Our dependencies are described in the file
17 `static/bower.json`. To update our bower packages, run `fab components` in this
17 `static/bower.json`. To update our bower packages, run `fab components` in this
18 directory.
18 directory.
19
19
20 Because CodeMirror does not use proper semantic versioning for its GitHub tags,
20 Because CodeMirror does not use proper semantic versioning for its GitHub tags,
21 we maintain our own fork of CodeMirror that is used with bower. This fork should
21 we maintain our own fork of CodeMirror that is used with bower. This fork should
22 track the upstream CodeMirror exactly; the only difference is that we are adding
22 track the upstream CodeMirror exactly; the only difference is that we are adding
23 semantic versioned tags to our repo.
23 semantic versioned tags to our repo.
24
24
25 ## less
25 ## less
26
26
27 If you edit our `.less` files you will need to run the less compiler to build
27 If you edit our `.less` files you will need to run the less compiler to build
28 our minified css files. This can be done by running `fab css` from this directory.
28 our minified css files. This can be done by running `fab css` from this directory.
29
29
30 ## JavaScript Documentation
30 ## JavaScript Documentation
31
31
32
32
33 How to Build/ view the doc for JavaScript. JavaScript documentation should follow a
33 How to Build/ view the doc for JavaScript. JavaScript documentation should follow a
34 style close to JSDoc one, so you should be able to build them with your favorite
34 style close to JSDoc one, so you should be able to build them with your favorite
35 documentation builder. Still the documentation comment are mainly written to be read
35 documentation builder. Still the documentation comment are mainly written to be read
36 with YUI doc. You can either build a static version, or start a YUIdoc server that
36 with YUI doc. You can either build a static version, or start a YUIdoc server that
37 will live update the doc at every page request.
37 will live update the doc at every page request.
38
38
39
39
40
40
41 To do so, you will need to install YUIdoc.
41 To do so, you will need to install YUIdoc.
42
42
43 ### Install NodeJS
43 ### Install NodeJS
44
44
45 Node is a browser less javascript interpreter. To install it please refer to
45 Node is a browser less javascript interpreter. To install it please refer to
46 the documentation for your platform. Install also NPM (node package manager) if
46 the documentation for your platform. Install also NPM (node package manager) if
47 it does not come bundled with it.
47 it does not come bundled with it.
48
48
49 ### Get YUIdoc
49 ### Get YUIdoc
50
50
51 npm does by default install package in `./node_modules` instead of doing a
51 npm does by default install package in `./node_modules` instead of doing a
52 system wide install. I'll leave you to yuidoc docs if you want to make a system
52 system wide install. I'll leave you to yuidoc docs if you want to make a system
53 wide install.
53 wide install.
54
54
55 First, cd into js directory :
55 First, cd into js directory :
56 ```bash
56 ```bash
57 cd IPython/frontend/html/notebook/static/js/
57 cd IPython/html/notebook/static/js/
58 # install yuidoc
58 # install yuidoc
59 npm install yuidocjs
59 npm install yuidocjs
60 ```
60 ```
61
61
62
62
63 ### Run YUIdoc server
63 ### Run YUIdoc server
64
64
65 From IPython/frontend/html/notebook/static/js/
65 From IPython/html/notebook/static/js/
66 ```bash
66 ```bash
67 # run yuidoc for install dir
67 # run yuidoc for install dir
68 ./node_modules/yuidocjs/lib/cli.js --server .
68 ./node_modules/yuidocjs/lib/cli.js --server .
69 ```
69 ```
70
70
71 Follow the instruction and the documentation should be available on localhost:3000
71 Follow the instruction and the documentation should be available on localhost:3000
72
72
73 Omitting `--server` will build a static version in the `out` folder by default. No newline at end of file
73 Omitting `--server` will build a static version in the `out` folder by default.
@@ -1,540 +1,540 b''
1 """Utilities for connecting to kernels
1 """Utilities for connecting to kernels
2
2
3 Authors:
3 Authors:
4
4
5 * Min Ragan-Kelley
5 * Min Ragan-Kelley
6
6
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2013 The IPython Development Team
10 # Copyright (C) 2013 The IPython Development Team
11 #
11 #
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 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 from __future__ import absolute_import
20 from __future__ import absolute_import
21
21
22 import glob
22 import glob
23 import json
23 import json
24 import os
24 import os
25 import socket
25 import socket
26 import sys
26 import sys
27 from getpass import getpass
27 from getpass import getpass
28 from subprocess import Popen, PIPE
28 from subprocess import Popen, PIPE
29 import tempfile
29 import tempfile
30
30
31 import zmq
31 import zmq
32
32
33 # external imports
33 # external imports
34 from IPython.external.ssh import tunnel
34 from IPython.external.ssh import tunnel
35
35
36 # IPython imports
36 # IPython imports
37 # from IPython.config import Configurable
37 # from IPython.config import Configurable
38 from IPython.core.profiledir import ProfileDir
38 from IPython.core.profiledir import ProfileDir
39 from IPython.utils.localinterfaces import LOCALHOST
39 from IPython.utils.localinterfaces import LOCALHOST
40 from IPython.utils.path import filefind, get_ipython_dir
40 from IPython.utils.path import filefind, get_ipython_dir
41 from IPython.utils.py3compat import str_to_bytes, bytes_to_str
41 from IPython.utils.py3compat import str_to_bytes, bytes_to_str
42 from IPython.utils.traitlets import (
42 from IPython.utils.traitlets import (
43 Bool, Integer, Unicode, CaselessStrEnum,
43 Bool, Integer, Unicode, CaselessStrEnum,
44 HasTraits,
44 HasTraits,
45 )
45 )
46
46
47
47
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49 # Working with Connection Files
49 # Working with Connection Files
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51
51
52 def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
52 def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
53 control_port=0, ip=LOCALHOST, key=b'', transport='tcp'):
53 control_port=0, ip=LOCALHOST, key=b'', transport='tcp'):
54 """Generates a JSON config file, including the selection of random ports.
54 """Generates a JSON config file, including the selection of random ports.
55
55
56 Parameters
56 Parameters
57 ----------
57 ----------
58
58
59 fname : unicode
59 fname : unicode
60 The path to the file to write
60 The path to the file to write
61
61
62 shell_port : int, optional
62 shell_port : int, optional
63 The port to use for ROUTER (shell) channel.
63 The port to use for ROUTER (shell) channel.
64
64
65 iopub_port : int, optional
65 iopub_port : int, optional
66 The port to use for the SUB channel.
66 The port to use for the SUB channel.
67
67
68 stdin_port : int, optional
68 stdin_port : int, optional
69 The port to use for the ROUTER (raw input) channel.
69 The port to use for the ROUTER (raw input) channel.
70
70
71 control_port : int, optional
71 control_port : int, optional
72 The port to use for the ROUTER (control) channel.
72 The port to use for the ROUTER (control) channel.
73
73
74 hb_port : int, optional
74 hb_port : int, optional
75 The port to use for the heartbeat REP channel.
75 The port to use for the heartbeat REP channel.
76
76
77 ip : str, optional
77 ip : str, optional
78 The ip address the kernel will bind to.
78 The ip address the kernel will bind to.
79
79
80 key : str, optional
80 key : str, optional
81 The Session key used for HMAC authentication.
81 The Session key used for HMAC authentication.
82
82
83 """
83 """
84 # default to temporary connector file
84 # default to temporary connector file
85 if not fname:
85 if not fname:
86 fname = tempfile.mktemp('.json')
86 fname = tempfile.mktemp('.json')
87
87
88 # Find open ports as necessary.
88 # Find open ports as necessary.
89
89
90 ports = []
90 ports = []
91 ports_needed = int(shell_port <= 0) + \
91 ports_needed = int(shell_port <= 0) + \
92 int(iopub_port <= 0) + \
92 int(iopub_port <= 0) + \
93 int(stdin_port <= 0) + \
93 int(stdin_port <= 0) + \
94 int(control_port <= 0) + \
94 int(control_port <= 0) + \
95 int(hb_port <= 0)
95 int(hb_port <= 0)
96 if transport == 'tcp':
96 if transport == 'tcp':
97 for i in range(ports_needed):
97 for i in range(ports_needed):
98 sock = socket.socket()
98 sock = socket.socket()
99 sock.bind(('', 0))
99 sock.bind(('', 0))
100 ports.append(sock)
100 ports.append(sock)
101 for i, sock in enumerate(ports):
101 for i, sock in enumerate(ports):
102 port = sock.getsockname()[1]
102 port = sock.getsockname()[1]
103 sock.close()
103 sock.close()
104 ports[i] = port
104 ports[i] = port
105 else:
105 else:
106 N = 1
106 N = 1
107 for i in range(ports_needed):
107 for i in range(ports_needed):
108 while os.path.exists("%s-%s" % (ip, str(N))):
108 while os.path.exists("%s-%s" % (ip, str(N))):
109 N += 1
109 N += 1
110 ports.append(N)
110 ports.append(N)
111 N += 1
111 N += 1
112 if shell_port <= 0:
112 if shell_port <= 0:
113 shell_port = ports.pop(0)
113 shell_port = ports.pop(0)
114 if iopub_port <= 0:
114 if iopub_port <= 0:
115 iopub_port = ports.pop(0)
115 iopub_port = ports.pop(0)
116 if stdin_port <= 0:
116 if stdin_port <= 0:
117 stdin_port = ports.pop(0)
117 stdin_port = ports.pop(0)
118 if control_port <= 0:
118 if control_port <= 0:
119 control_port = ports.pop(0)
119 control_port = ports.pop(0)
120 if hb_port <= 0:
120 if hb_port <= 0:
121 hb_port = ports.pop(0)
121 hb_port = ports.pop(0)
122
122
123 cfg = dict( shell_port=shell_port,
123 cfg = dict( shell_port=shell_port,
124 iopub_port=iopub_port,
124 iopub_port=iopub_port,
125 stdin_port=stdin_port,
125 stdin_port=stdin_port,
126 control_port=control_port,
126 control_port=control_port,
127 hb_port=hb_port,
127 hb_port=hb_port,
128 )
128 )
129 cfg['ip'] = ip
129 cfg['ip'] = ip
130 cfg['key'] = bytes_to_str(key)
130 cfg['key'] = bytes_to_str(key)
131 cfg['transport'] = transport
131 cfg['transport'] = transport
132
132
133 with open(fname, 'w') as f:
133 with open(fname, 'w') as f:
134 f.write(json.dumps(cfg, indent=2))
134 f.write(json.dumps(cfg, indent=2))
135
135
136 return fname, cfg
136 return fname, cfg
137
137
138
138
139 def get_connection_file(app=None):
139 def get_connection_file(app=None):
140 """Return the path to the connection file of an app
140 """Return the path to the connection file of an app
141
141
142 Parameters
142 Parameters
143 ----------
143 ----------
144 app : IPKernelApp instance [optional]
144 app : IPKernelApp instance [optional]
145 If unspecified, the currently running app will be used
145 If unspecified, the currently running app will be used
146 """
146 """
147 if app is None:
147 if app is None:
148 from IPython.kernel.zmq.kernelapp import IPKernelApp
148 from IPython.kernel.zmq.kernelapp import IPKernelApp
149 if not IPKernelApp.initialized():
149 if not IPKernelApp.initialized():
150 raise RuntimeError("app not specified, and not in a running Kernel")
150 raise RuntimeError("app not specified, and not in a running Kernel")
151
151
152 app = IPKernelApp.instance()
152 app = IPKernelApp.instance()
153 return filefind(app.connection_file, ['.', app.profile_dir.security_dir])
153 return filefind(app.connection_file, ['.', app.profile_dir.security_dir])
154
154
155
155
156 def find_connection_file(filename, profile=None):
156 def find_connection_file(filename, profile=None):
157 """find a connection file, and return its absolute path.
157 """find a connection file, and return its absolute path.
158
158
159 The current working directory and the profile's security
159 The current working directory and the profile's security
160 directory will be searched for the file if it is not given by
160 directory will be searched for the file if it is not given by
161 absolute path.
161 absolute path.
162
162
163 If profile is unspecified, then the current running application's
163 If profile is unspecified, then the current running application's
164 profile will be used, or 'default', if not run from IPython.
164 profile will be used, or 'default', if not run from IPython.
165
165
166 If the argument does not match an existing file, it will be interpreted as a
166 If the argument does not match an existing file, it will be interpreted as a
167 fileglob, and the matching file in the profile's security dir with
167 fileglob, and the matching file in the profile's security dir with
168 the latest access time will be used.
168 the latest access time will be used.
169
169
170 Parameters
170 Parameters
171 ----------
171 ----------
172 filename : str
172 filename : str
173 The connection file or fileglob to search for.
173 The connection file or fileglob to search for.
174 profile : str [optional]
174 profile : str [optional]
175 The name of the profile to use when searching for the connection file,
175 The name of the profile to use when searching for the connection file,
176 if different from the current IPython session or 'default'.
176 if different from the current IPython session or 'default'.
177
177
178 Returns
178 Returns
179 -------
179 -------
180 str : The absolute path of the connection file.
180 str : The absolute path of the connection file.
181 """
181 """
182 from IPython.core.application import BaseIPythonApplication as IPApp
182 from IPython.core.application import BaseIPythonApplication as IPApp
183 try:
183 try:
184 # quick check for absolute path, before going through logic
184 # quick check for absolute path, before going through logic
185 return filefind(filename)
185 return filefind(filename)
186 except IOError:
186 except IOError:
187 pass
187 pass
188
188
189 if profile is None:
189 if profile is None:
190 # profile unspecified, check if running from an IPython app
190 # profile unspecified, check if running from an IPython app
191 if IPApp.initialized():
191 if IPApp.initialized():
192 app = IPApp.instance()
192 app = IPApp.instance()
193 profile_dir = app.profile_dir
193 profile_dir = app.profile_dir
194 else:
194 else:
195 # not running in IPython, use default profile
195 # not running in IPython, use default profile
196 profile_dir = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), 'default')
196 profile_dir = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), 'default')
197 else:
197 else:
198 # find profiledir by profile name:
198 # find profiledir by profile name:
199 profile_dir = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
199 profile_dir = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
200 security_dir = profile_dir.security_dir
200 security_dir = profile_dir.security_dir
201
201
202 try:
202 try:
203 # first, try explicit name
203 # first, try explicit name
204 return filefind(filename, ['.', security_dir])
204 return filefind(filename, ['.', security_dir])
205 except IOError:
205 except IOError:
206 pass
206 pass
207
207
208 # not found by full name
208 # not found by full name
209
209
210 if '*' in filename:
210 if '*' in filename:
211 # given as a glob already
211 # given as a glob already
212 pat = filename
212 pat = filename
213 else:
213 else:
214 # accept any substring match
214 # accept any substring match
215 pat = '*%s*' % filename
215 pat = '*%s*' % filename
216 matches = glob.glob( os.path.join(security_dir, pat) )
216 matches = glob.glob( os.path.join(security_dir, pat) )
217 if not matches:
217 if not matches:
218 raise IOError("Could not find %r in %r" % (filename, security_dir))
218 raise IOError("Could not find %r in %r" % (filename, security_dir))
219 elif len(matches) == 1:
219 elif len(matches) == 1:
220 return matches[0]
220 return matches[0]
221 else:
221 else:
222 # get most recent match, by access time:
222 # get most recent match, by access time:
223 return sorted(matches, key=lambda f: os.stat(f).st_atime)[-1]
223 return sorted(matches, key=lambda f: os.stat(f).st_atime)[-1]
224
224
225
225
226 def get_connection_info(connection_file=None, unpack=False, profile=None):
226 def get_connection_info(connection_file=None, unpack=False, profile=None):
227 """Return the connection information for the current Kernel.
227 """Return the connection information for the current Kernel.
228
228
229 Parameters
229 Parameters
230 ----------
230 ----------
231 connection_file : str [optional]
231 connection_file : str [optional]
232 The connection file to be used. Can be given by absolute path, or
232 The connection file to be used. Can be given by absolute path, or
233 IPython will search in the security directory of a given profile.
233 IPython will search in the security directory of a given profile.
234 If run from IPython,
234 If run from IPython,
235
235
236 If unspecified, the connection file for the currently running
236 If unspecified, the connection file for the currently running
237 IPython Kernel will be used, which is only allowed from inside a kernel.
237 IPython Kernel will be used, which is only allowed from inside a kernel.
238 unpack : bool [default: False]
238 unpack : bool [default: False]
239 if True, return the unpacked dict, otherwise just the string contents
239 if True, return the unpacked dict, otherwise just the string contents
240 of the file.
240 of the file.
241 profile : str [optional]
241 profile : str [optional]
242 The name of the profile to use when searching for the connection file,
242 The name of the profile to use when searching for the connection file,
243 if different from the current IPython session or 'default'.
243 if different from the current IPython session or 'default'.
244
244
245
245
246 Returns
246 Returns
247 -------
247 -------
248 The connection dictionary of the current kernel, as string or dict,
248 The connection dictionary of the current kernel, as string or dict,
249 depending on `unpack`.
249 depending on `unpack`.
250 """
250 """
251 if connection_file is None:
251 if connection_file is None:
252 # get connection file from current kernel
252 # get connection file from current kernel
253 cf = get_connection_file()
253 cf = get_connection_file()
254 else:
254 else:
255 # connection file specified, allow shortnames:
255 # connection file specified, allow shortnames:
256 cf = find_connection_file(connection_file, profile=profile)
256 cf = find_connection_file(connection_file, profile=profile)
257
257
258 with open(cf) as f:
258 with open(cf) as f:
259 info = f.read()
259 info = f.read()
260
260
261 if unpack:
261 if unpack:
262 info = json.loads(info)
262 info = json.loads(info)
263 # ensure key is bytes:
263 # ensure key is bytes:
264 info['key'] = str_to_bytes(info.get('key', ''))
264 info['key'] = str_to_bytes(info.get('key', ''))
265 return info
265 return info
266
266
267
267
268 def connect_qtconsole(connection_file=None, argv=None, profile=None):
268 def connect_qtconsole(connection_file=None, argv=None, profile=None):
269 """Connect a qtconsole to the current kernel.
269 """Connect a qtconsole to the current kernel.
270
270
271 This is useful for connecting a second qtconsole to a kernel, or to a
271 This is useful for connecting a second qtconsole to a kernel, or to a
272 local notebook.
272 local notebook.
273
273
274 Parameters
274 Parameters
275 ----------
275 ----------
276 connection_file : str [optional]
276 connection_file : str [optional]
277 The connection file to be used. Can be given by absolute path, or
277 The connection file to be used. Can be given by absolute path, or
278 IPython will search in the security directory of a given profile.
278 IPython will search in the security directory of a given profile.
279 If run from IPython,
279 If run from IPython,
280
280
281 If unspecified, the connection file for the currently running
281 If unspecified, the connection file for the currently running
282 IPython Kernel will be used, which is only allowed from inside a kernel.
282 IPython Kernel will be used, which is only allowed from inside a kernel.
283 argv : list [optional]
283 argv : list [optional]
284 Any extra args to be passed to the console.
284 Any extra args to be passed to the console.
285 profile : str [optional]
285 profile : str [optional]
286 The name of the profile to use when searching for the connection file,
286 The name of the profile to use when searching for the connection file,
287 if different from the current IPython session or 'default'.
287 if different from the current IPython session or 'default'.
288
288
289
289
290 Returns
290 Returns
291 -------
291 -------
292 subprocess.Popen instance running the qtconsole frontend
292 subprocess.Popen instance running the qtconsole frontend
293 """
293 """
294 argv = [] if argv is None else argv
294 argv = [] if argv is None else argv
295
295
296 if connection_file is None:
296 if connection_file is None:
297 # get connection file from current kernel
297 # get connection file from current kernel
298 cf = get_connection_file()
298 cf = get_connection_file()
299 else:
299 else:
300 cf = find_connection_file(connection_file, profile=profile)
300 cf = find_connection_file(connection_file, profile=profile)
301
301
302 cmd = ';'.join([
302 cmd = ';'.join([
303 "from IPython.frontend.qt.console import qtconsoleapp",
303 "from IPython.qt.console import qtconsoleapp",
304 "qtconsoleapp.main()"
304 "qtconsoleapp.main()"
305 ])
305 ])
306
306
307 return Popen([sys.executable, '-c', cmd, '--existing', cf] + argv,
307 return Popen([sys.executable, '-c', cmd, '--existing', cf] + argv,
308 stdout=PIPE, stderr=PIPE, close_fds=True,
308 stdout=PIPE, stderr=PIPE, close_fds=True,
309 )
309 )
310
310
311
311
312 def tunnel_to_kernel(connection_info, sshserver, sshkey=None):
312 def tunnel_to_kernel(connection_info, sshserver, sshkey=None):
313 """tunnel connections to a kernel via ssh
313 """tunnel connections to a kernel via ssh
314
314
315 This will open four SSH tunnels from localhost on this machine to the
315 This will open four SSH tunnels from localhost on this machine to the
316 ports associated with the kernel. They can be either direct
316 ports associated with the kernel. They can be either direct
317 localhost-localhost tunnels, or if an intermediate server is necessary,
317 localhost-localhost tunnels, or if an intermediate server is necessary,
318 the kernel must be listening on a public IP.
318 the kernel must be listening on a public IP.
319
319
320 Parameters
320 Parameters
321 ----------
321 ----------
322 connection_info : dict or str (path)
322 connection_info : dict or str (path)
323 Either a connection dict, or the path to a JSON connection file
323 Either a connection dict, or the path to a JSON connection file
324 sshserver : str
324 sshserver : str
325 The ssh sever to use to tunnel to the kernel. Can be a full
325 The ssh sever to use to tunnel to the kernel. Can be a full
326 `user@server:port` string. ssh config aliases are respected.
326 `user@server:port` string. ssh config aliases are respected.
327 sshkey : str [optional]
327 sshkey : str [optional]
328 Path to file containing ssh key to use for authentication.
328 Path to file containing ssh key to use for authentication.
329 Only necessary if your ssh config does not already associate
329 Only necessary if your ssh config does not already associate
330 a keyfile with the host.
330 a keyfile with the host.
331
331
332 Returns
332 Returns
333 -------
333 -------
334
334
335 (shell, iopub, stdin, hb) : ints
335 (shell, iopub, stdin, hb) : ints
336 The four ports on localhost that have been forwarded to the kernel.
336 The four ports on localhost that have been forwarded to the kernel.
337 """
337 """
338 if isinstance(connection_info, basestring):
338 if isinstance(connection_info, basestring):
339 # it's a path, unpack it
339 # it's a path, unpack it
340 with open(connection_info) as f:
340 with open(connection_info) as f:
341 connection_info = json.loads(f.read())
341 connection_info = json.loads(f.read())
342
342
343 cf = connection_info
343 cf = connection_info
344
344
345 lports = tunnel.select_random_ports(4)
345 lports = tunnel.select_random_ports(4)
346 rports = cf['shell_port'], cf['iopub_port'], cf['stdin_port'], cf['hb_port']
346 rports = cf['shell_port'], cf['iopub_port'], cf['stdin_port'], cf['hb_port']
347
347
348 remote_ip = cf['ip']
348 remote_ip = cf['ip']
349
349
350 if tunnel.try_passwordless_ssh(sshserver, sshkey):
350 if tunnel.try_passwordless_ssh(sshserver, sshkey):
351 password=False
351 password=False
352 else:
352 else:
353 password = getpass("SSH Password for %s: "%sshserver)
353 password = getpass("SSH Password for %s: "%sshserver)
354
354
355 for lp,rp in zip(lports, rports):
355 for lp,rp in zip(lports, rports):
356 tunnel.ssh_tunnel(lp, rp, sshserver, remote_ip, sshkey, password)
356 tunnel.ssh_tunnel(lp, rp, sshserver, remote_ip, sshkey, password)
357
357
358 return tuple(lports)
358 return tuple(lports)
359
359
360
360
361 #-----------------------------------------------------------------------------
361 #-----------------------------------------------------------------------------
362 # Mixin for classes that work with connection files
362 # Mixin for classes that work with connection files
363 #-----------------------------------------------------------------------------
363 #-----------------------------------------------------------------------------
364
364
365 channel_socket_types = {
365 channel_socket_types = {
366 'hb' : zmq.REQ,
366 'hb' : zmq.REQ,
367 'shell' : zmq.DEALER,
367 'shell' : zmq.DEALER,
368 'iopub' : zmq.SUB,
368 'iopub' : zmq.SUB,
369 'stdin' : zmq.DEALER,
369 'stdin' : zmq.DEALER,
370 'control': zmq.DEALER,
370 'control': zmq.DEALER,
371 }
371 }
372
372
373 port_names = [ "%s_port" % channel for channel in ('shell', 'stdin', 'iopub', 'hb', 'control')]
373 port_names = [ "%s_port" % channel for channel in ('shell', 'stdin', 'iopub', 'hb', 'control')]
374
374
375 class ConnectionFileMixin(HasTraits):
375 class ConnectionFileMixin(HasTraits):
376 """Mixin for configurable classes that work with connection files"""
376 """Mixin for configurable classes that work with connection files"""
377
377
378 # The addresses for the communication channels
378 # The addresses for the communication channels
379 connection_file = Unicode('')
379 connection_file = Unicode('')
380 _connection_file_written = Bool(False)
380 _connection_file_written = Bool(False)
381
381
382 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
382 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
383
383
384 ip = Unicode(LOCALHOST, config=True,
384 ip = Unicode(LOCALHOST, config=True,
385 help="""Set the kernel\'s IP address [default localhost].
385 help="""Set the kernel\'s IP address [default localhost].
386 If the IP address is something other than localhost, then
386 If the IP address is something other than localhost, then
387 Consoles on other machines will be able to connect
387 Consoles on other machines will be able to connect
388 to the Kernel, so be careful!"""
388 to the Kernel, so be careful!"""
389 )
389 )
390
390
391 def _ip_default(self):
391 def _ip_default(self):
392 if self.transport == 'ipc':
392 if self.transport == 'ipc':
393 if self.connection_file:
393 if self.connection_file:
394 return os.path.splitext(self.connection_file)[0] + '-ipc'
394 return os.path.splitext(self.connection_file)[0] + '-ipc'
395 else:
395 else:
396 return 'kernel-ipc'
396 return 'kernel-ipc'
397 else:
397 else:
398 return LOCALHOST
398 return LOCALHOST
399
399
400 def _ip_changed(self, name, old, new):
400 def _ip_changed(self, name, old, new):
401 if new == '*':
401 if new == '*':
402 self.ip = '0.0.0.0'
402 self.ip = '0.0.0.0'
403
403
404 # protected traits
404 # protected traits
405
405
406 shell_port = Integer(0)
406 shell_port = Integer(0)
407 iopub_port = Integer(0)
407 iopub_port = Integer(0)
408 stdin_port = Integer(0)
408 stdin_port = Integer(0)
409 control_port = Integer(0)
409 control_port = Integer(0)
410 hb_port = Integer(0)
410 hb_port = Integer(0)
411
411
412 @property
412 @property
413 def ports(self):
413 def ports(self):
414 return [ getattr(self, name) for name in port_names ]
414 return [ getattr(self, name) for name in port_names ]
415
415
416 #--------------------------------------------------------------------------
416 #--------------------------------------------------------------------------
417 # Connection and ipc file management
417 # Connection and ipc file management
418 #--------------------------------------------------------------------------
418 #--------------------------------------------------------------------------
419
419
420 def get_connection_info(self):
420 def get_connection_info(self):
421 """return the connection info as a dict"""
421 """return the connection info as a dict"""
422 return dict(
422 return dict(
423 transport=self.transport,
423 transport=self.transport,
424 ip=self.ip,
424 ip=self.ip,
425 shell_port=self.shell_port,
425 shell_port=self.shell_port,
426 iopub_port=self.iopub_port,
426 iopub_port=self.iopub_port,
427 stdin_port=self.stdin_port,
427 stdin_port=self.stdin_port,
428 hb_port=self.hb_port,
428 hb_port=self.hb_port,
429 control_port=self.control_port,
429 control_port=self.control_port,
430 )
430 )
431
431
432 def cleanup_connection_file(self):
432 def cleanup_connection_file(self):
433 """Cleanup connection file *if we wrote it*
433 """Cleanup connection file *if we wrote it*
434
434
435 Will not raise if the connection file was already removed somehow.
435 Will not raise if the connection file was already removed somehow.
436 """
436 """
437 if self._connection_file_written:
437 if self._connection_file_written:
438 # cleanup connection files on full shutdown of kernel we started
438 # cleanup connection files on full shutdown of kernel we started
439 self._connection_file_written = False
439 self._connection_file_written = False
440 try:
440 try:
441 os.remove(self.connection_file)
441 os.remove(self.connection_file)
442 except (IOError, OSError, AttributeError):
442 except (IOError, OSError, AttributeError):
443 pass
443 pass
444
444
445 def cleanup_ipc_files(self):
445 def cleanup_ipc_files(self):
446 """Cleanup ipc files if we wrote them."""
446 """Cleanup ipc files if we wrote them."""
447 if self.transport != 'ipc':
447 if self.transport != 'ipc':
448 return
448 return
449 for port in self.ports:
449 for port in self.ports:
450 ipcfile = "%s-%i" % (self.ip, port)
450 ipcfile = "%s-%i" % (self.ip, port)
451 try:
451 try:
452 os.remove(ipcfile)
452 os.remove(ipcfile)
453 except (IOError, OSError):
453 except (IOError, OSError):
454 pass
454 pass
455
455
456 def write_connection_file(self):
456 def write_connection_file(self):
457 """Write connection info to JSON dict in self.connection_file."""
457 """Write connection info to JSON dict in self.connection_file."""
458 if self._connection_file_written:
458 if self._connection_file_written:
459 return
459 return
460
460
461 self.connection_file, cfg = write_connection_file(self.connection_file,
461 self.connection_file, cfg = write_connection_file(self.connection_file,
462 transport=self.transport, ip=self.ip, key=self.session.key,
462 transport=self.transport, ip=self.ip, key=self.session.key,
463 stdin_port=self.stdin_port, iopub_port=self.iopub_port,
463 stdin_port=self.stdin_port, iopub_port=self.iopub_port,
464 shell_port=self.shell_port, hb_port=self.hb_port,
464 shell_port=self.shell_port, hb_port=self.hb_port,
465 control_port=self.control_port,
465 control_port=self.control_port,
466 )
466 )
467 # write_connection_file also sets default ports:
467 # write_connection_file also sets default ports:
468 for name in port_names:
468 for name in port_names:
469 setattr(self, name, cfg[name])
469 setattr(self, name, cfg[name])
470
470
471 self._connection_file_written = True
471 self._connection_file_written = True
472
472
473 def load_connection_file(self):
473 def load_connection_file(self):
474 """Load connection info from JSON dict in self.connection_file."""
474 """Load connection info from JSON dict in self.connection_file."""
475 with open(self.connection_file) as f:
475 with open(self.connection_file) as f:
476 cfg = json.loads(f.read())
476 cfg = json.loads(f.read())
477
477
478 self.transport = cfg.get('transport', 'tcp')
478 self.transport = cfg.get('transport', 'tcp')
479 self.ip = cfg['ip']
479 self.ip = cfg['ip']
480 for name in port_names:
480 for name in port_names:
481 setattr(self, name, cfg[name])
481 setattr(self, name, cfg[name])
482 self.session.key = str_to_bytes(cfg['key'])
482 self.session.key = str_to_bytes(cfg['key'])
483
483
484 #--------------------------------------------------------------------------
484 #--------------------------------------------------------------------------
485 # Creating connected sockets
485 # Creating connected sockets
486 #--------------------------------------------------------------------------
486 #--------------------------------------------------------------------------
487
487
488 def _make_url(self, channel):
488 def _make_url(self, channel):
489 """Make a ZeroMQ URL for a given channel."""
489 """Make a ZeroMQ URL for a given channel."""
490 transport = self.transport
490 transport = self.transport
491 ip = self.ip
491 ip = self.ip
492 port = getattr(self, '%s_port' % channel)
492 port = getattr(self, '%s_port' % channel)
493
493
494 if transport == 'tcp':
494 if transport == 'tcp':
495 return "tcp://%s:%i" % (ip, port)
495 return "tcp://%s:%i" % (ip, port)
496 else:
496 else:
497 return "%s://%s-%s" % (transport, ip, port)
497 return "%s://%s-%s" % (transport, ip, port)
498
498
499 def _create_connected_socket(self, channel, identity=None):
499 def _create_connected_socket(self, channel, identity=None):
500 """Create a zmq Socket and connect it to the kernel."""
500 """Create a zmq Socket and connect it to the kernel."""
501 url = self._make_url(channel)
501 url = self._make_url(channel)
502 socket_type = channel_socket_types[channel]
502 socket_type = channel_socket_types[channel]
503 self.log.info("Connecting to: %s" % url)
503 self.log.info("Connecting to: %s" % url)
504 sock = self.context.socket(socket_type)
504 sock = self.context.socket(socket_type)
505 if identity:
505 if identity:
506 sock.identity = identity
506 sock.identity = identity
507 sock.connect(url)
507 sock.connect(url)
508 return sock
508 return sock
509
509
510 def connect_iopub(self, identity=None):
510 def connect_iopub(self, identity=None):
511 """return zmq Socket connected to the IOPub channel"""
511 """return zmq Socket connected to the IOPub channel"""
512 sock = self._create_connected_socket('iopub', identity=identity)
512 sock = self._create_connected_socket('iopub', identity=identity)
513 sock.setsockopt(zmq.SUBSCRIBE, b'')
513 sock.setsockopt(zmq.SUBSCRIBE, b'')
514 return sock
514 return sock
515
515
516 def connect_shell(self, identity=None):
516 def connect_shell(self, identity=None):
517 """return zmq Socket connected to the Shell channel"""
517 """return zmq Socket connected to the Shell channel"""
518 return self._create_connected_socket('shell', identity=identity)
518 return self._create_connected_socket('shell', identity=identity)
519
519
520 def connect_stdin(self, identity=None):
520 def connect_stdin(self, identity=None):
521 """return zmq Socket connected to the StdIn channel"""
521 """return zmq Socket connected to the StdIn channel"""
522 return self._create_connected_socket('stdin', identity=identity)
522 return self._create_connected_socket('stdin', identity=identity)
523
523
524 def connect_hb(self, identity=None):
524 def connect_hb(self, identity=None):
525 """return zmq Socket connected to the Heartbeat channel"""
525 """return zmq Socket connected to the Heartbeat channel"""
526 return self._create_connected_socket('hb', identity=identity)
526 return self._create_connected_socket('hb', identity=identity)
527
527
528 def connect_control(self, identity=None):
528 def connect_control(self, identity=None):
529 """return zmq Socket connected to the Heartbeat channel"""
529 """return zmq Socket connected to the Heartbeat channel"""
530 return self._create_connected_socket('control', identity=identity)
530 return self._create_connected_socket('control', identity=identity)
531
531
532
532
533 __all__ = [
533 __all__ = [
534 'write_connection_file',
534 'write_connection_file',
535 'get_connection_file',
535 'get_connection_file',
536 'find_connection_file',
536 'find_connection_file',
537 'get_connection_info',
537 'get_connection_info',
538 'connect_qtconsole',
538 'connect_qtconsole',
539 'tunnel_to_kernel',
539 'tunnel_to_kernel',
540 ]
540 ]
@@ -1,171 +1,171 b''
1 # Standard library imports
1 # Standard library imports
2 import unittest
2 import unittest
3
3
4 # Local imports
4 # Local imports
5 from IPython.frontend.qt.console.ansi_code_processor import AnsiCodeProcessor
5 from IPython.qt.console.ansi_code_processor import AnsiCodeProcessor
6
6
7
7
8 class TestAnsiCodeProcessor(unittest.TestCase):
8 class TestAnsiCodeProcessor(unittest.TestCase):
9
9
10 def setUp(self):
10 def setUp(self):
11 self.processor = AnsiCodeProcessor()
11 self.processor = AnsiCodeProcessor()
12
12
13 def test_clear(self):
13 def test_clear(self):
14 """ Do control sequences for clearing the console work?
14 """ Do control sequences for clearing the console work?
15 """
15 """
16 string = '\x1b[2J\x1b[K'
16 string = '\x1b[2J\x1b[K'
17 i = -1
17 i = -1
18 for i, substring in enumerate(self.processor.split_string(string)):
18 for i, substring in enumerate(self.processor.split_string(string)):
19 if i == 0:
19 if i == 0:
20 self.assertEqual(len(self.processor.actions), 1)
20 self.assertEqual(len(self.processor.actions), 1)
21 action = self.processor.actions[0]
21 action = self.processor.actions[0]
22 self.assertEqual(action.action, 'erase')
22 self.assertEqual(action.action, 'erase')
23 self.assertEqual(action.area, 'screen')
23 self.assertEqual(action.area, 'screen')
24 self.assertEqual(action.erase_to, 'all')
24 self.assertEqual(action.erase_to, 'all')
25 elif i == 1:
25 elif i == 1:
26 self.assertEqual(len(self.processor.actions), 1)
26 self.assertEqual(len(self.processor.actions), 1)
27 action = self.processor.actions[0]
27 action = self.processor.actions[0]
28 self.assertEqual(action.action, 'erase')
28 self.assertEqual(action.action, 'erase')
29 self.assertEqual(action.area, 'line')
29 self.assertEqual(action.area, 'line')
30 self.assertEqual(action.erase_to, 'end')
30 self.assertEqual(action.erase_to, 'end')
31 else:
31 else:
32 self.fail('Too many substrings.')
32 self.fail('Too many substrings.')
33 self.assertEqual(i, 1, 'Too few substrings.')
33 self.assertEqual(i, 1, 'Too few substrings.')
34
34
35 def test_colors(self):
35 def test_colors(self):
36 """ Do basic controls sequences for colors work?
36 """ Do basic controls sequences for colors work?
37 """
37 """
38 string = 'first\x1b[34mblue\x1b[0mlast'
38 string = 'first\x1b[34mblue\x1b[0mlast'
39 i = -1
39 i = -1
40 for i, substring in enumerate(self.processor.split_string(string)):
40 for i, substring in enumerate(self.processor.split_string(string)):
41 if i == 0:
41 if i == 0:
42 self.assertEqual(substring, 'first')
42 self.assertEqual(substring, 'first')
43 self.assertEqual(self.processor.foreground_color, None)
43 self.assertEqual(self.processor.foreground_color, None)
44 elif i == 1:
44 elif i == 1:
45 self.assertEqual(substring, 'blue')
45 self.assertEqual(substring, 'blue')
46 self.assertEqual(self.processor.foreground_color, 4)
46 self.assertEqual(self.processor.foreground_color, 4)
47 elif i == 2:
47 elif i == 2:
48 self.assertEqual(substring, 'last')
48 self.assertEqual(substring, 'last')
49 self.assertEqual(self.processor.foreground_color, None)
49 self.assertEqual(self.processor.foreground_color, None)
50 else:
50 else:
51 self.fail('Too many substrings.')
51 self.fail('Too many substrings.')
52 self.assertEqual(i, 2, 'Too few substrings.')
52 self.assertEqual(i, 2, 'Too few substrings.')
53
53
54 def test_colors_xterm(self):
54 def test_colors_xterm(self):
55 """ Do xterm-specific control sequences for colors work?
55 """ Do xterm-specific control sequences for colors work?
56 """
56 """
57 string = '\x1b]4;20;rgb:ff/ff/ff\x1b' \
57 string = '\x1b]4;20;rgb:ff/ff/ff\x1b' \
58 '\x1b]4;25;rgbi:1.0/1.0/1.0\x1b'
58 '\x1b]4;25;rgbi:1.0/1.0/1.0\x1b'
59 substrings = list(self.processor.split_string(string))
59 substrings = list(self.processor.split_string(string))
60 desired = { 20 : (255, 255, 255),
60 desired = { 20 : (255, 255, 255),
61 25 : (255, 255, 255) }
61 25 : (255, 255, 255) }
62 self.assertEqual(self.processor.color_map, desired)
62 self.assertEqual(self.processor.color_map, desired)
63
63
64 string = '\x1b[38;5;20m\x1b[48;5;25m'
64 string = '\x1b[38;5;20m\x1b[48;5;25m'
65 substrings = list(self.processor.split_string(string))
65 substrings = list(self.processor.split_string(string))
66 self.assertEqual(self.processor.foreground_color, 20)
66 self.assertEqual(self.processor.foreground_color, 20)
67 self.assertEqual(self.processor.background_color, 25)
67 self.assertEqual(self.processor.background_color, 25)
68
68
69 def test_scroll(self):
69 def test_scroll(self):
70 """ Do control sequences for scrolling the buffer work?
70 """ Do control sequences for scrolling the buffer work?
71 """
71 """
72 string = '\x1b[5S\x1b[T'
72 string = '\x1b[5S\x1b[T'
73 i = -1
73 i = -1
74 for i, substring in enumerate(self.processor.split_string(string)):
74 for i, substring in enumerate(self.processor.split_string(string)):
75 if i == 0:
75 if i == 0:
76 self.assertEqual(len(self.processor.actions), 1)
76 self.assertEqual(len(self.processor.actions), 1)
77 action = self.processor.actions[0]
77 action = self.processor.actions[0]
78 self.assertEqual(action.action, 'scroll')
78 self.assertEqual(action.action, 'scroll')
79 self.assertEqual(action.dir, 'up')
79 self.assertEqual(action.dir, 'up')
80 self.assertEqual(action.unit, 'line')
80 self.assertEqual(action.unit, 'line')
81 self.assertEqual(action.count, 5)
81 self.assertEqual(action.count, 5)
82 elif i == 1:
82 elif i == 1:
83 self.assertEqual(len(self.processor.actions), 1)
83 self.assertEqual(len(self.processor.actions), 1)
84 action = self.processor.actions[0]
84 action = self.processor.actions[0]
85 self.assertEqual(action.action, 'scroll')
85 self.assertEqual(action.action, 'scroll')
86 self.assertEqual(action.dir, 'down')
86 self.assertEqual(action.dir, 'down')
87 self.assertEqual(action.unit, 'line')
87 self.assertEqual(action.unit, 'line')
88 self.assertEqual(action.count, 1)
88 self.assertEqual(action.count, 1)
89 else:
89 else:
90 self.fail('Too many substrings.')
90 self.fail('Too many substrings.')
91 self.assertEqual(i, 1, 'Too few substrings.')
91 self.assertEqual(i, 1, 'Too few substrings.')
92
92
93 def test_formfeed(self):
93 def test_formfeed(self):
94 """ Are formfeed characters processed correctly?
94 """ Are formfeed characters processed correctly?
95 """
95 """
96 string = '\f' # form feed
96 string = '\f' # form feed
97 self.assertEqual(list(self.processor.split_string(string)), [''])
97 self.assertEqual(list(self.processor.split_string(string)), [''])
98 self.assertEqual(len(self.processor.actions), 1)
98 self.assertEqual(len(self.processor.actions), 1)
99 action = self.processor.actions[0]
99 action = self.processor.actions[0]
100 self.assertEqual(action.action, 'scroll')
100 self.assertEqual(action.action, 'scroll')
101 self.assertEqual(action.dir, 'down')
101 self.assertEqual(action.dir, 'down')
102 self.assertEqual(action.unit, 'page')
102 self.assertEqual(action.unit, 'page')
103 self.assertEqual(action.count, 1)
103 self.assertEqual(action.count, 1)
104
104
105 def test_carriage_return(self):
105 def test_carriage_return(self):
106 """ Are carriage return characters processed correctly?
106 """ Are carriage return characters processed correctly?
107 """
107 """
108 string = 'foo\rbar' # carriage return
108 string = 'foo\rbar' # carriage return
109 splits = []
109 splits = []
110 actions = []
110 actions = []
111 for split in self.processor.split_string(string):
111 for split in self.processor.split_string(string):
112 splits.append(split)
112 splits.append(split)
113 actions.append([action.action for action in self.processor.actions])
113 actions.append([action.action for action in self.processor.actions])
114 self.assertEqual(splits, ['foo', None, 'bar'])
114 self.assertEqual(splits, ['foo', None, 'bar'])
115 self.assertEqual(actions, [[], ['carriage-return'], []])
115 self.assertEqual(actions, [[], ['carriage-return'], []])
116
116
117 def test_carriage_return_newline(self):
117 def test_carriage_return_newline(self):
118 """transform CRLF to LF"""
118 """transform CRLF to LF"""
119 string = 'foo\rbar\r\ncat\r\n\n' # carriage return and newline
119 string = 'foo\rbar\r\ncat\r\n\n' # carriage return and newline
120 # only one CR action should occur, and '\r\n' should transform to '\n'
120 # only one CR action should occur, and '\r\n' should transform to '\n'
121 splits = []
121 splits = []
122 actions = []
122 actions = []
123 for split in self.processor.split_string(string):
123 for split in self.processor.split_string(string):
124 splits.append(split)
124 splits.append(split)
125 actions.append([action.action for action in self.processor.actions])
125 actions.append([action.action for action in self.processor.actions])
126 self.assertEqual(splits, ['foo', None, 'bar', '\r\n', 'cat', '\r\n', '\n'])
126 self.assertEqual(splits, ['foo', None, 'bar', '\r\n', 'cat', '\r\n', '\n'])
127 self.assertEqual(actions, [[], ['carriage-return'], [], ['newline'], [], ['newline'], ['newline']])
127 self.assertEqual(actions, [[], ['carriage-return'], [], ['newline'], [], ['newline'], ['newline']])
128
128
129 def test_beep(self):
129 def test_beep(self):
130 """ Are beep characters processed correctly?
130 """ Are beep characters processed correctly?
131 """
131 """
132 string = 'foo\abar' # bell
132 string = 'foo\abar' # bell
133 splits = []
133 splits = []
134 actions = []
134 actions = []
135 for split in self.processor.split_string(string):
135 for split in self.processor.split_string(string):
136 splits.append(split)
136 splits.append(split)
137 actions.append([action.action for action in self.processor.actions])
137 actions.append([action.action for action in self.processor.actions])
138 self.assertEqual(splits, ['foo', None, 'bar'])
138 self.assertEqual(splits, ['foo', None, 'bar'])
139 self.assertEqual(actions, [[], ['beep'], []])
139 self.assertEqual(actions, [[], ['beep'], []])
140
140
141 def test_backspace(self):
141 def test_backspace(self):
142 """ Are backspace characters processed correctly?
142 """ Are backspace characters processed correctly?
143 """
143 """
144 string = 'foo\bbar' # backspace
144 string = 'foo\bbar' # backspace
145 splits = []
145 splits = []
146 actions = []
146 actions = []
147 for split in self.processor.split_string(string):
147 for split in self.processor.split_string(string):
148 splits.append(split)
148 splits.append(split)
149 actions.append([action.action for action in self.processor.actions])
149 actions.append([action.action for action in self.processor.actions])
150 self.assertEqual(splits, ['foo', None, 'bar'])
150 self.assertEqual(splits, ['foo', None, 'bar'])
151 self.assertEqual(actions, [[], ['backspace'], []])
151 self.assertEqual(actions, [[], ['backspace'], []])
152
152
153 def test_combined(self):
153 def test_combined(self):
154 """ Are CR and BS characters processed correctly in combination?
154 """ Are CR and BS characters processed correctly in combination?
155
155
156 BS is treated as a change in print position, rather than a
156 BS is treated as a change in print position, rather than a
157 backwards character deletion. Therefore a BS at EOL is
157 backwards character deletion. Therefore a BS at EOL is
158 effectively ignored.
158 effectively ignored.
159 """
159 """
160 string = 'abc\rdef\b' # CR and backspace
160 string = 'abc\rdef\b' # CR and backspace
161 splits = []
161 splits = []
162 actions = []
162 actions = []
163 for split in self.processor.split_string(string):
163 for split in self.processor.split_string(string):
164 splits.append(split)
164 splits.append(split)
165 actions.append([action.action for action in self.processor.actions])
165 actions.append([action.action for action in self.processor.actions])
166 self.assertEqual(splits, ['abc', None, 'def', None])
166 self.assertEqual(splits, ['abc', None, 'def', None])
167 self.assertEqual(actions, [[], ['carriage-return'], [], ['backspace']])
167 self.assertEqual(actions, [[], ['carriage-return'], [], ['backspace']])
168
168
169
169
170 if __name__ == '__main__':
170 if __name__ == '__main__':
171 unittest.main()
171 unittest.main()
@@ -1,47 +1,47 b''
1 # Standard library imports
1 # Standard library imports
2 import unittest
2 import unittest
3
3
4 # System library imports
4 # System library imports
5 from pygments.lexers import CLexer, CppLexer, PythonLexer
5 from pygments.lexers import CLexer, CppLexer, PythonLexer
6
6
7 # Local imports
7 # Local imports
8 from IPython.frontend.qt.console.completion_lexer import CompletionLexer
8 from IPython.qt.console.completion_lexer import CompletionLexer
9
9
10
10
11 class TestCompletionLexer(unittest.TestCase):
11 class TestCompletionLexer(unittest.TestCase):
12
12
13 def testPython(self):
13 def testPython(self):
14 """ Does the CompletionLexer work for Python?
14 """ Does the CompletionLexer work for Python?
15 """
15 """
16 lexer = CompletionLexer(PythonLexer())
16 lexer = CompletionLexer(PythonLexer())
17
17
18 # Test simplest case.
18 # Test simplest case.
19 self.assertEqual(lexer.get_context("foo.bar.baz"),
19 self.assertEqual(lexer.get_context("foo.bar.baz"),
20 [ "foo", "bar", "baz" ])
20 [ "foo", "bar", "baz" ])
21
21
22 # Test trailing period.
22 # Test trailing period.
23 self.assertEqual(lexer.get_context("foo.bar."), [ "foo", "bar", "" ])
23 self.assertEqual(lexer.get_context("foo.bar."), [ "foo", "bar", "" ])
24
24
25 # Test with prompt present.
25 # Test with prompt present.
26 self.assertEqual(lexer.get_context(">>> foo.bar.baz"),
26 self.assertEqual(lexer.get_context(">>> foo.bar.baz"),
27 [ "foo", "bar", "baz" ])
27 [ "foo", "bar", "baz" ])
28
28
29 # Test spacing in name.
29 # Test spacing in name.
30 self.assertEqual(lexer.get_context("foo.bar. baz"), [ "baz" ])
30 self.assertEqual(lexer.get_context("foo.bar. baz"), [ "baz" ])
31
31
32 # Test parenthesis.
32 # Test parenthesis.
33 self.assertEqual(lexer.get_context("foo("), [])
33 self.assertEqual(lexer.get_context("foo("), [])
34
34
35 def testC(self):
35 def testC(self):
36 """ Does the CompletionLexer work for C/C++?
36 """ Does the CompletionLexer work for C/C++?
37 """
37 """
38 lexer = CompletionLexer(CLexer())
38 lexer = CompletionLexer(CLexer())
39 self.assertEqual(lexer.get_context("foo.bar"), [ "foo", "bar" ])
39 self.assertEqual(lexer.get_context("foo.bar"), [ "foo", "bar" ])
40 self.assertEqual(lexer.get_context("foo->bar"), [ "foo", "bar" ])
40 self.assertEqual(lexer.get_context("foo->bar"), [ "foo", "bar" ])
41
41
42 lexer = CompletionLexer(CppLexer())
42 lexer = CompletionLexer(CppLexer())
43 self.assertEqual(lexer.get_context("Foo::Bar"), [ "Foo", "Bar" ])
43 self.assertEqual(lexer.get_context("Foo::Bar"), [ "Foo", "Bar" ])
44
44
45
45
46 if __name__ == '__main__':
46 if __name__ == '__main__':
47 unittest.main()
47 unittest.main()
@@ -1,80 +1,80 b''
1 # Standard library imports
1 # Standard library imports
2 import unittest
2 import unittest
3
3
4 # System library imports
4 # System library imports
5 from IPython.external.qt import QtCore, QtGui
5 from IPython.external.qt import QtCore, QtGui
6
6
7 # Local imports
7 # Local imports
8 from IPython.frontend.qt.console.console_widget import ConsoleWidget
8 from IPython.qt.console.console_widget import ConsoleWidget
9
9
10
10
11 class TestConsoleWidget(unittest.TestCase):
11 class TestConsoleWidget(unittest.TestCase):
12
12
13 @classmethod
13 @classmethod
14 def setUpClass(cls):
14 def setUpClass(cls):
15 """ Create the application for the test case.
15 """ Create the application for the test case.
16 """
16 """
17 cls._app = QtGui.QApplication.instance()
17 cls._app = QtGui.QApplication.instance()
18 if cls._app is None:
18 if cls._app is None:
19 cls._app = QtGui.QApplication([])
19 cls._app = QtGui.QApplication([])
20 cls._app.setQuitOnLastWindowClosed(False)
20 cls._app.setQuitOnLastWindowClosed(False)
21
21
22 @classmethod
22 @classmethod
23 def tearDownClass(cls):
23 def tearDownClass(cls):
24 """ Exit the application.
24 """ Exit the application.
25 """
25 """
26 QtGui.QApplication.quit()
26 QtGui.QApplication.quit()
27
27
28 def test_special_characters(self):
28 def test_special_characters(self):
29 """ Are special characters displayed correctly?
29 """ Are special characters displayed correctly?
30 """
30 """
31 w = ConsoleWidget()
31 w = ConsoleWidget()
32 cursor = w._get_prompt_cursor()
32 cursor = w._get_prompt_cursor()
33
33
34 test_inputs = ['xyz\b\b=\n', 'foo\b\nbar\n', 'foo\b\nbar\r\n', 'abc\rxyz\b\b=']
34 test_inputs = ['xyz\b\b=\n', 'foo\b\nbar\n', 'foo\b\nbar\r\n', 'abc\rxyz\b\b=']
35 expected_outputs = [u'x=z\u2029', u'foo\u2029bar\u2029', u'foo\u2029bar\u2029', 'x=z']
35 expected_outputs = [u'x=z\u2029', u'foo\u2029bar\u2029', u'foo\u2029bar\u2029', 'x=z']
36 for i, text in enumerate(test_inputs):
36 for i, text in enumerate(test_inputs):
37 w._insert_plain_text(cursor, text)
37 w._insert_plain_text(cursor, text)
38 cursor.select(cursor.Document)
38 cursor.select(cursor.Document)
39 selection = cursor.selectedText()
39 selection = cursor.selectedText()
40 self.assertEqual(expected_outputs[i], selection)
40 self.assertEqual(expected_outputs[i], selection)
41 # clear all the text
41 # clear all the text
42 cursor.insertText('')
42 cursor.insertText('')
43
43
44 def test_link_handling(self):
44 def test_link_handling(self):
45 noKeys = QtCore.Qt
45 noKeys = QtCore.Qt
46 noButton = QtCore.Qt.MouseButton(0)
46 noButton = QtCore.Qt.MouseButton(0)
47 noButtons = QtCore.Qt.MouseButtons(0)
47 noButtons = QtCore.Qt.MouseButtons(0)
48 noModifiers = QtCore.Qt.KeyboardModifiers(0)
48 noModifiers = QtCore.Qt.KeyboardModifiers(0)
49 MouseMove = QtCore.QEvent.MouseMove
49 MouseMove = QtCore.QEvent.MouseMove
50 QMouseEvent = QtGui.QMouseEvent
50 QMouseEvent = QtGui.QMouseEvent
51
51
52 w = ConsoleWidget()
52 w = ConsoleWidget()
53 cursor = w._get_prompt_cursor()
53 cursor = w._get_prompt_cursor()
54 w._insert_html(cursor, '<a href="http://python.org">written in</a>')
54 w._insert_html(cursor, '<a href="http://python.org">written in</a>')
55 obj = w._control
55 obj = w._control
56 tip = QtGui.QToolTip
56 tip = QtGui.QToolTip
57 self.assertEqual(tip.text(), u'')
57 self.assertEqual(tip.text(), u'')
58
58
59 # should be somewhere else
59 # should be somewhere else
60 elsewhereEvent = QMouseEvent(MouseMove, QtCore.QPoint(50,50),
60 elsewhereEvent = QMouseEvent(MouseMove, QtCore.QPoint(50,50),
61 noButton, noButtons, noModifiers)
61 noButton, noButtons, noModifiers)
62 w.eventFilter(obj, elsewhereEvent)
62 w.eventFilter(obj, elsewhereEvent)
63 self.assertEqual(tip.isVisible(), False)
63 self.assertEqual(tip.isVisible(), False)
64 self.assertEqual(tip.text(), u'')
64 self.assertEqual(tip.text(), u'')
65
65
66 #self.assertEqual(tip.text(), u'')
66 #self.assertEqual(tip.text(), u'')
67 # should be over text
67 # should be over text
68 overTextEvent = QMouseEvent(MouseMove, QtCore.QPoint(1,5),
68 overTextEvent = QMouseEvent(MouseMove, QtCore.QPoint(1,5),
69 noButton, noButtons, noModifiers)
69 noButton, noButtons, noModifiers)
70 w.eventFilter(obj, overTextEvent)
70 w.eventFilter(obj, overTextEvent)
71 self.assertEqual(tip.isVisible(), True)
71 self.assertEqual(tip.isVisible(), True)
72 self.assertEqual(tip.text(), "http://python.org")
72 self.assertEqual(tip.text(), "http://python.org")
73
73
74 # should still be over text
74 # should still be over text
75 stillOverTextEvent = QMouseEvent(MouseMove, QtCore.QPoint(1,5),
75 stillOverTextEvent = QMouseEvent(MouseMove, QtCore.QPoint(1,5),
76 noButton, noButtons, noModifiers)
76 noButton, noButtons, noModifiers)
77 w.eventFilter(obj, stillOverTextEvent)
77 w.eventFilter(obj, stillOverTextEvent)
78 self.assertEqual(tip.isVisible(), True)
78 self.assertEqual(tip.isVisible(), True)
79 self.assertEqual(tip.text(), "http://python.org")
79 self.assertEqual(tip.text(), "http://python.org")
80
80
@@ -1,85 +1,85 b''
1 # Standard library imports
1 # Standard library imports
2 import unittest
2 import unittest
3
3
4 # System library imports
4 # System library imports
5 from IPython.external.qt import QtCore, QtGui
5 from IPython.external.qt import QtCore, QtGui
6
6
7 # Local imports
7 # Local imports
8 from IPython.frontend.qt.console.kill_ring import KillRing, QtKillRing
8 from IPython.qt.console.kill_ring import KillRing, QtKillRing
9
9
10
10
11 class TestKillRing(unittest.TestCase):
11 class TestKillRing(unittest.TestCase):
12
12
13 @classmethod
13 @classmethod
14 def setUpClass(cls):
14 def setUpClass(cls):
15 """ Create the application for the test case.
15 """ Create the application for the test case.
16 """
16 """
17 cls._app = QtGui.QApplication.instance()
17 cls._app = QtGui.QApplication.instance()
18 if cls._app is None:
18 if cls._app is None:
19 cls._app = QtGui.QApplication([])
19 cls._app = QtGui.QApplication([])
20 cls._app.setQuitOnLastWindowClosed(False)
20 cls._app.setQuitOnLastWindowClosed(False)
21
21
22 @classmethod
22 @classmethod
23 def tearDownClass(cls):
23 def tearDownClass(cls):
24 """ Exit the application.
24 """ Exit the application.
25 """
25 """
26 QtGui.QApplication.quit()
26 QtGui.QApplication.quit()
27
27
28 def test_generic(self):
28 def test_generic(self):
29 """ Does the generic kill ring work?
29 """ Does the generic kill ring work?
30 """
30 """
31 ring = KillRing()
31 ring = KillRing()
32 self.assertTrue(ring.yank() is None)
32 self.assertTrue(ring.yank() is None)
33 self.assertTrue(ring.rotate() is None)
33 self.assertTrue(ring.rotate() is None)
34
34
35 ring.kill('foo')
35 ring.kill('foo')
36 self.assertEqual(ring.yank(), 'foo')
36 self.assertEqual(ring.yank(), 'foo')
37 self.assertTrue(ring.rotate() is None)
37 self.assertTrue(ring.rotate() is None)
38 self.assertEqual(ring.yank(), 'foo')
38 self.assertEqual(ring.yank(), 'foo')
39
39
40 ring.kill('bar')
40 ring.kill('bar')
41 self.assertEqual(ring.yank(), 'bar')
41 self.assertEqual(ring.yank(), 'bar')
42 self.assertEqual(ring.rotate(), 'foo')
42 self.assertEqual(ring.rotate(), 'foo')
43
43
44 ring.clear()
44 ring.clear()
45 self.assertTrue(ring.yank() is None)
45 self.assertTrue(ring.yank() is None)
46 self.assertTrue(ring.rotate() is None)
46 self.assertTrue(ring.rotate() is None)
47
47
48 def test_qt_basic(self):
48 def test_qt_basic(self):
49 """ Does the Qt kill ring work?
49 """ Does the Qt kill ring work?
50 """
50 """
51 text_edit = QtGui.QPlainTextEdit()
51 text_edit = QtGui.QPlainTextEdit()
52 ring = QtKillRing(text_edit)
52 ring = QtKillRing(text_edit)
53
53
54 ring.kill('foo')
54 ring.kill('foo')
55 ring.kill('bar')
55 ring.kill('bar')
56 ring.yank()
56 ring.yank()
57 ring.rotate()
57 ring.rotate()
58 ring.yank()
58 ring.yank()
59 self.assertEqual(text_edit.toPlainText(), 'foobar')
59 self.assertEqual(text_edit.toPlainText(), 'foobar')
60
60
61 text_edit.clear()
61 text_edit.clear()
62 ring.kill('baz')
62 ring.kill('baz')
63 ring.yank()
63 ring.yank()
64 ring.rotate()
64 ring.rotate()
65 ring.rotate()
65 ring.rotate()
66 ring.rotate()
66 ring.rotate()
67 self.assertEqual(text_edit.toPlainText(), 'foo')
67 self.assertEqual(text_edit.toPlainText(), 'foo')
68
68
69 def test_qt_cursor(self):
69 def test_qt_cursor(self):
70 """ Does the Qt kill ring maintain state with cursor movement?
70 """ Does the Qt kill ring maintain state with cursor movement?
71 """
71 """
72 text_edit = QtGui.QPlainTextEdit()
72 text_edit = QtGui.QPlainTextEdit()
73 ring = QtKillRing(text_edit)
73 ring = QtKillRing(text_edit)
74
74
75 ring.kill('foo')
75 ring.kill('foo')
76 ring.kill('bar')
76 ring.kill('bar')
77 ring.yank()
77 ring.yank()
78 text_edit.moveCursor(QtGui.QTextCursor.Left)
78 text_edit.moveCursor(QtGui.QTextCursor.Left)
79 ring.rotate()
79 ring.rotate()
80 self.assertEqual(text_edit.toPlainText(), 'bar')
80 self.assertEqual(text_edit.toPlainText(), 'bar')
81
81
82
82
83 if __name__ == '__main__':
83 if __name__ == '__main__':
84 import nose
84 import nose
85 nose.main()
85 nose.main()
@@ -1,95 +1,94 b''
1 #-----------------------------------------------------------------------------
1 #-----------------------------------------------------------------------------
2 # Copyright (C) 2012 The IPython Development Team
2 # Copyright (C) 2012 The IPython Development Team
3 #
3 #
4 # Distributed under the terms of the BSD License. The full license is in
4 # Distributed under the terms of the BSD License. The full license is in
5 # the file COPYING, distributed as part of this software.
5 # the file COPYING, distributed as part of this software.
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7
7
8 import os
8 import os
9 import sys
9 import sys
10 import unittest
10 import unittest
11 import base64
11 import base64
12
12
13 from IPython.kernel import KernelClient
13 from IPython.kernel import KernelClient
14 from IPython.frontend.terminal.console.interactiveshell \
14 from IPython.terminal.console.interactiveshell import ZMQTerminalInteractiveShell
15 import ZMQTerminalInteractiveShell
16 from IPython.utils.tempdir import TemporaryDirectory
15 from IPython.utils.tempdir import TemporaryDirectory
17 from IPython.testing.tools import monkeypatch
16 from IPython.testing.tools import monkeypatch
18 from IPython.testing.decorators import skip_without
17 from IPython.testing.decorators import skip_without
19 from IPython.utils.ipstruct import Struct
18 from IPython.utils.ipstruct import Struct
20
19
21
20
22 SCRIPT_PATH = os.path.join(
21 SCRIPT_PATH = os.path.join(
23 os.path.abspath(os.path.dirname(__file__)), 'writetofile.py')
22 os.path.abspath(os.path.dirname(__file__)), 'writetofile.py')
24
23
25
24
26 class ZMQTerminalInteractiveShellTestCase(unittest.TestCase):
25 class ZMQTerminalInteractiveShellTestCase(unittest.TestCase):
27
26
28 def setUp(self):
27 def setUp(self):
29 client = KernelClient()
28 client = KernelClient()
30 self.shell = ZMQTerminalInteractiveShell(kernel_client=client)
29 self.shell = ZMQTerminalInteractiveShell(kernel_client=client)
31 self.raw = b'dummy data'
30 self.raw = b'dummy data'
32 self.mime = 'image/png'
31 self.mime = 'image/png'
33 self.data = {self.mime: base64.encodestring(self.raw).decode('ascii')}
32 self.data = {self.mime: base64.encodestring(self.raw).decode('ascii')}
34
33
35 def test_no_call_by_default(self):
34 def test_no_call_by_default(self):
36 def raise_if_called(*args, **kwds):
35 def raise_if_called(*args, **kwds):
37 assert False
36 assert False
38
37
39 shell = self.shell
38 shell = self.shell
40 shell.handle_image_PIL = raise_if_called
39 shell.handle_image_PIL = raise_if_called
41 shell.handle_image_stream = raise_if_called
40 shell.handle_image_stream = raise_if_called
42 shell.handle_image_tempfile = raise_if_called
41 shell.handle_image_tempfile = raise_if_called
43 shell.handle_image_callable = raise_if_called
42 shell.handle_image_callable = raise_if_called
44
43
45 shell.handle_image(None, None) # arguments are dummy
44 shell.handle_image(None, None) # arguments are dummy
46
45
47 @skip_without('PIL')
46 @skip_without('PIL')
48 def test_handle_image_PIL(self):
47 def test_handle_image_PIL(self):
49 import PIL.Image
48 import PIL.Image
50
49
51 open_called_with = []
50 open_called_with = []
52 show_called_with = []
51 show_called_with = []
53
52
54 def fake_open(arg):
53 def fake_open(arg):
55 open_called_with.append(arg)
54 open_called_with.append(arg)
56 return Struct(show=lambda: show_called_with.append(None))
55 return Struct(show=lambda: show_called_with.append(None))
57
56
58 with monkeypatch(PIL.Image, 'open', fake_open):
57 with monkeypatch(PIL.Image, 'open', fake_open):
59 self.shell.handle_image_PIL(self.data, self.mime)
58 self.shell.handle_image_PIL(self.data, self.mime)
60
59
61 self.assertEqual(len(open_called_with), 1)
60 self.assertEqual(len(open_called_with), 1)
62 self.assertEqual(len(show_called_with), 1)
61 self.assertEqual(len(show_called_with), 1)
63 self.assertEqual(open_called_with[0].getvalue(), self.raw)
62 self.assertEqual(open_called_with[0].getvalue(), self.raw)
64
63
65 def check_handler_with_file(self, inpath, handler):
64 def check_handler_with_file(self, inpath, handler):
66 shell = self.shell
65 shell = self.shell
67 configname = '{0}_image_handler'.format(handler)
66 configname = '{0}_image_handler'.format(handler)
68 funcname = 'handle_image_{0}'.format(handler)
67 funcname = 'handle_image_{0}'.format(handler)
69
68
70 assert hasattr(shell, configname)
69 assert hasattr(shell, configname)
71 assert hasattr(shell, funcname)
70 assert hasattr(shell, funcname)
72
71
73 with TemporaryDirectory() as tmpdir:
72 with TemporaryDirectory() as tmpdir:
74 outpath = os.path.join(tmpdir, 'data')
73 outpath = os.path.join(tmpdir, 'data')
75 cmd = [sys.executable, SCRIPT_PATH, inpath, outpath]
74 cmd = [sys.executable, SCRIPT_PATH, inpath, outpath]
76 setattr(shell, configname, cmd)
75 setattr(shell, configname, cmd)
77 getattr(shell, funcname)(self.data, self.mime)
76 getattr(shell, funcname)(self.data, self.mime)
78 # cmd is called and file is closed. So it's safe to open now.
77 # cmd is called and file is closed. So it's safe to open now.
79 with open(outpath, 'rb') as file:
78 with open(outpath, 'rb') as file:
80 transferred = file.read()
79 transferred = file.read()
81
80
82 self.assertEqual(transferred, self.raw)
81 self.assertEqual(transferred, self.raw)
83
82
84 def test_handle_image_stream(self):
83 def test_handle_image_stream(self):
85 self.check_handler_with_file('-', 'stream')
84 self.check_handler_with_file('-', 'stream')
86
85
87 def test_handle_image_tempfile(self):
86 def test_handle_image_tempfile(self):
88 self.check_handler_with_file('{file}', 'tempfile')
87 self.check_handler_with_file('{file}', 'tempfile')
89
88
90 def test_handle_image_callable(self):
89 def test_handle_image_callable(self):
91 called_with = []
90 called_with = []
92 self.shell.callable_image_handler = called_with.append
91 self.shell.callable_image_handler = called_with.append
93 self.shell.handle_image_callable(self.data, self.mime)
92 self.shell.handle_image_callable(self.data, self.mime)
94 self.assertEqual(len(called_with), 1)
93 self.assertEqual(len(called_with), 1)
95 assert called_with[0] is self.data
94 assert called_with[0] is self.data
@@ -1,176 +1,176 b''
1 """Global IPython app to support test running.
1 """Global IPython app to support test running.
2
2
3 We must start our own ipython object and heavily muck with it so that all the
3 We must start our own ipython object and heavily muck with it so that all the
4 modifications IPython makes to system behavior don't send the doctest machinery
4 modifications IPython makes to system behavior don't send the doctest machinery
5 into a fit. This code should be considered a gross hack, but it gets the job
5 into a fit. This code should be considered a gross hack, but it gets the job
6 done.
6 done.
7 """
7 """
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9 from __future__ import print_function
9 from __future__ import print_function
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2009-2011 The IPython Development Team
12 # Copyright (C) 2009-2011 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 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 # stdlib
22 # stdlib
23 import __builtin__ as builtin_mod
23 import __builtin__ as builtin_mod
24 import os
24 import os
25 import sys
25 import sys
26
26
27 # our own
27 # our own
28 from . import tools
28 from . import tools
29
29
30 from IPython.core import page
30 from IPython.core import page
31 from IPython.utils import io
31 from IPython.utils import io
32 from IPython.utils import py3compat
32 from IPython.utils import py3compat
33 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
33 from IPython.terminal.interactiveshell import TerminalInteractiveShell
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Functions
36 # Functions
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38
38
39 class StreamProxy(io.IOStream):
39 class StreamProxy(io.IOStream):
40 """Proxy for sys.stdout/err. This will request the stream *at call time*
40 """Proxy for sys.stdout/err. This will request the stream *at call time*
41 allowing for nose's Capture plugin's redirection of sys.stdout/err.
41 allowing for nose's Capture plugin's redirection of sys.stdout/err.
42
42
43 Parameters
43 Parameters
44 ----------
44 ----------
45 name : str
45 name : str
46 The name of the stream. This will be requested anew at every call
46 The name of the stream. This will be requested anew at every call
47 """
47 """
48
48
49 def __init__(self, name):
49 def __init__(self, name):
50 self.name=name
50 self.name=name
51
51
52 @property
52 @property
53 def stream(self):
53 def stream(self):
54 return getattr(sys, self.name)
54 return getattr(sys, self.name)
55
55
56 def flush(self):
56 def flush(self):
57 self.stream.flush()
57 self.stream.flush()
58
58
59 # Hack to modify the %run command so we can sync the user's namespace with the
59 # Hack to modify the %run command so we can sync the user's namespace with the
60 # test globals. Once we move over to a clean magic system, this will be done
60 # test globals. Once we move over to a clean magic system, this will be done
61 # with much less ugliness.
61 # with much less ugliness.
62
62
63 class py_file_finder(object):
63 class py_file_finder(object):
64 def __init__(self,test_filename):
64 def __init__(self,test_filename):
65 self.test_filename = test_filename
65 self.test_filename = test_filename
66
66
67 def __call__(self,name,win32=False):
67 def __call__(self,name,win32=False):
68 from IPython.utils.path import get_py_filename
68 from IPython.utils.path import get_py_filename
69 try:
69 try:
70 return get_py_filename(name,win32=win32)
70 return get_py_filename(name,win32=win32)
71 except IOError:
71 except IOError:
72 test_dir = os.path.dirname(self.test_filename)
72 test_dir = os.path.dirname(self.test_filename)
73 new_path = os.path.join(test_dir,name)
73 new_path = os.path.join(test_dir,name)
74 return get_py_filename(new_path,win32=win32)
74 return get_py_filename(new_path,win32=win32)
75
75
76
76
77 def _run_ns_sync(self,arg_s,runner=None):
77 def _run_ns_sync(self,arg_s,runner=None):
78 """Modified version of %run that syncs testing namespaces.
78 """Modified version of %run that syncs testing namespaces.
79
79
80 This is strictly needed for running doctests that call %run.
80 This is strictly needed for running doctests that call %run.
81 """
81 """
82 #print('in run_ns_sync', arg_s, file=sys.stderr) # dbg
82 #print('in run_ns_sync', arg_s, file=sys.stderr) # dbg
83 finder = py_file_finder(arg_s)
83 finder = py_file_finder(arg_s)
84 return get_ipython().magic_run_ori(arg_s, runner, finder)
84 return get_ipython().magic_run_ori(arg_s, runner, finder)
85
85
86
86
87 def get_ipython():
87 def get_ipython():
88 # This will get replaced by the real thing once we start IPython below
88 # This will get replaced by the real thing once we start IPython below
89 return start_ipython()
89 return start_ipython()
90
90
91
91
92 # A couple of methods to override those in the running IPython to interact
92 # A couple of methods to override those in the running IPython to interact
93 # better with doctest (doctest captures on raw stdout, so we need to direct
93 # better with doctest (doctest captures on raw stdout, so we need to direct
94 # various types of output there otherwise it will miss them).
94 # various types of output there otherwise it will miss them).
95
95
96 def xsys(self, cmd):
96 def xsys(self, cmd):
97 """Replace the default system call with a capturing one for doctest.
97 """Replace the default system call with a capturing one for doctest.
98 """
98 """
99 # We use getoutput, but we need to strip it because pexpect captures
99 # We use getoutput, but we need to strip it because pexpect captures
100 # the trailing newline differently from commands.getoutput
100 # the trailing newline differently from commands.getoutput
101 print(self.getoutput(cmd, split=False, depth=1).rstrip(), end='', file=sys.stdout)
101 print(self.getoutput(cmd, split=False, depth=1).rstrip(), end='', file=sys.stdout)
102 sys.stdout.flush()
102 sys.stdout.flush()
103
103
104
104
105 def _showtraceback(self, etype, evalue, stb):
105 def _showtraceback(self, etype, evalue, stb):
106 """Print the traceback purely on stdout for doctest to capture it.
106 """Print the traceback purely on stdout for doctest to capture it.
107 """
107 """
108 print(self.InteractiveTB.stb2text(stb), file=sys.stdout)
108 print(self.InteractiveTB.stb2text(stb), file=sys.stdout)
109
109
110
110
111 def start_ipython():
111 def start_ipython():
112 """Start a global IPython shell, which we need for IPython-specific syntax.
112 """Start a global IPython shell, which we need for IPython-specific syntax.
113 """
113 """
114 global get_ipython
114 global get_ipython
115
115
116 # This function should only ever run once!
116 # This function should only ever run once!
117 if hasattr(start_ipython, 'already_called'):
117 if hasattr(start_ipython, 'already_called'):
118 return
118 return
119 start_ipython.already_called = True
119 start_ipython.already_called = True
120
120
121 # Store certain global objects that IPython modifies
121 # Store certain global objects that IPython modifies
122 _displayhook = sys.displayhook
122 _displayhook = sys.displayhook
123 _excepthook = sys.excepthook
123 _excepthook = sys.excepthook
124 _main = sys.modules.get('__main__')
124 _main = sys.modules.get('__main__')
125
125
126 # Create custom argv and namespaces for our IPython to be test-friendly
126 # Create custom argv and namespaces for our IPython to be test-friendly
127 config = tools.default_config()
127 config = tools.default_config()
128
128
129 # Create and initialize our test-friendly IPython instance.
129 # Create and initialize our test-friendly IPython instance.
130 shell = TerminalInteractiveShell.instance(config=config,
130 shell = TerminalInteractiveShell.instance(config=config,
131 )
131 )
132
132
133 # A few more tweaks needed for playing nicely with doctests...
133 # A few more tweaks needed for playing nicely with doctests...
134
134
135 # remove history file
135 # remove history file
136 shell.tempfiles.append(config.HistoryManager.hist_file)
136 shell.tempfiles.append(config.HistoryManager.hist_file)
137
137
138 # These traps are normally only active for interactive use, set them
138 # These traps are normally only active for interactive use, set them
139 # permanently since we'll be mocking interactive sessions.
139 # permanently since we'll be mocking interactive sessions.
140 shell.builtin_trap.activate()
140 shell.builtin_trap.activate()
141
141
142 # Modify the IPython system call with one that uses getoutput, so that we
142 # Modify the IPython system call with one that uses getoutput, so that we
143 # can capture subcommands and print them to Python's stdout, otherwise the
143 # can capture subcommands and print them to Python's stdout, otherwise the
144 # doctest machinery would miss them.
144 # doctest machinery would miss them.
145 shell.system = py3compat.MethodType(xsys, shell)
145 shell.system = py3compat.MethodType(xsys, shell)
146
146
147 shell._showtraceback = py3compat.MethodType(_showtraceback, shell)
147 shell._showtraceback = py3compat.MethodType(_showtraceback, shell)
148
148
149 # IPython is ready, now clean up some global state...
149 # IPython is ready, now clean up some global state...
150
150
151 # Deactivate the various python system hooks added by ipython for
151 # Deactivate the various python system hooks added by ipython for
152 # interactive convenience so we don't confuse the doctest system
152 # interactive convenience so we don't confuse the doctest system
153 sys.modules['__main__'] = _main
153 sys.modules['__main__'] = _main
154 sys.displayhook = _displayhook
154 sys.displayhook = _displayhook
155 sys.excepthook = _excepthook
155 sys.excepthook = _excepthook
156
156
157 # So that ipython magics and aliases can be doctested (they work by making
157 # So that ipython magics and aliases can be doctested (they work by making
158 # a call into a global _ip object). Also make the top-level get_ipython
158 # a call into a global _ip object). Also make the top-level get_ipython
159 # now return this without recursively calling here again.
159 # now return this without recursively calling here again.
160 _ip = shell
160 _ip = shell
161 get_ipython = _ip.get_ipython
161 get_ipython = _ip.get_ipython
162 builtin_mod._ip = _ip
162 builtin_mod._ip = _ip
163 builtin_mod.get_ipython = get_ipython
163 builtin_mod.get_ipython = get_ipython
164
164
165 # To avoid extra IPython messages during testing, suppress io.stdout/stderr
165 # To avoid extra IPython messages during testing, suppress io.stdout/stderr
166 io.stdout = StreamProxy('stdout')
166 io.stdout = StreamProxy('stdout')
167 io.stderr = StreamProxy('stderr')
167 io.stderr = StreamProxy('stderr')
168
168
169 # Override paging, so we don't require user interaction during the tests.
169 # Override paging, so we don't require user interaction during the tests.
170 def nopage(strng, start=0, screen_lines=0, pager_cmd=None):
170 def nopage(strng, start=0, screen_lines=0, pager_cmd=None):
171 print(strng)
171 print(strng)
172
172
173 page.orig_page = page.page
173 page.orig_page = page.page
174 page.page = nopage
174 page.page = nopage
175
175
176 return _ip
176 return _ip
@@ -1,122 +1,122 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Utilities for working with external processes.
3 Utilities for working with external processes.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2011 The IPython Development Team
7 # Copyright (C) 2008-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
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 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Stdlib
18 # Stdlib
19 import os
19 import os
20 import sys
20 import sys
21 import shlex
21 import shlex
22
22
23 # Our own
23 # Our own
24 if sys.platform == 'win32':
24 if sys.platform == 'win32':
25 from ._process_win32 import _find_cmd, system, getoutput, AvoidUNCPath, arg_split
25 from ._process_win32 import _find_cmd, system, getoutput, AvoidUNCPath, arg_split
26 else:
26 else:
27 from ._process_posix import _find_cmd, system, getoutput, arg_split
27 from ._process_posix import _find_cmd, system, getoutput, arg_split
28
28
29
29
30 from ._process_common import getoutputerror
30 from ._process_common import getoutputerror
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Code
33 # Code
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36
36
37 class FindCmdError(Exception):
37 class FindCmdError(Exception):
38 pass
38 pass
39
39
40
40
41 def find_cmd(cmd):
41 def find_cmd(cmd):
42 """Find absolute path to executable cmd in a cross platform manner.
42 """Find absolute path to executable cmd in a cross platform manner.
43
43
44 This function tries to determine the full path to a command line program
44 This function tries to determine the full path to a command line program
45 using `which` on Unix/Linux/OS X and `win32api` on Windows. Most of the
45 using `which` on Unix/Linux/OS X and `win32api` on Windows. Most of the
46 time it will use the version that is first on the users `PATH`.
46 time it will use the version that is first on the users `PATH`.
47
47
48 Warning, don't use this to find IPython command line programs as there
48 Warning, don't use this to find IPython command line programs as there
49 is a risk you will find the wrong one. Instead find those using the
49 is a risk you will find the wrong one. Instead find those using the
50 following code and looking for the application itself::
50 following code and looking for the application itself::
51
51
52 from IPython.utils.path import get_ipython_module_path
52 from IPython.utils.path import get_ipython_module_path
53 from IPython.utils.process import pycmd2argv
53 from IPython.utils.process import pycmd2argv
54 argv = pycmd2argv(get_ipython_module_path('IPython.frontend.terminal.ipapp'))
54 argv = pycmd2argv(get_ipython_module_path('IPython.terminal.ipapp'))
55
55
56 Parameters
56 Parameters
57 ----------
57 ----------
58 cmd : str
58 cmd : str
59 The command line program to look for.
59 The command line program to look for.
60 """
60 """
61 try:
61 try:
62 path = _find_cmd(cmd).rstrip()
62 path = _find_cmd(cmd).rstrip()
63 except OSError:
63 except OSError:
64 raise FindCmdError('command could not be found: %s' % cmd)
64 raise FindCmdError('command could not be found: %s' % cmd)
65 # which returns empty if not found
65 # which returns empty if not found
66 if path == '':
66 if path == '':
67 raise FindCmdError('command could not be found: %s' % cmd)
67 raise FindCmdError('command could not be found: %s' % cmd)
68 return os.path.abspath(path)
68 return os.path.abspath(path)
69
69
70
70
71 def is_cmd_found(cmd):
71 def is_cmd_found(cmd):
72 """Check whether executable `cmd` exists or not and return a bool."""
72 """Check whether executable `cmd` exists or not and return a bool."""
73 try:
73 try:
74 find_cmd(cmd)
74 find_cmd(cmd)
75 return True
75 return True
76 except FindCmdError:
76 except FindCmdError:
77 return False
77 return False
78
78
79
79
80 def pycmd2argv(cmd):
80 def pycmd2argv(cmd):
81 r"""Take the path of a python command and return a list (argv-style).
81 r"""Take the path of a python command and return a list (argv-style).
82
82
83 This only works on Python based command line programs and will find the
83 This only works on Python based command line programs and will find the
84 location of the ``python`` executable using ``sys.executable`` to make
84 location of the ``python`` executable using ``sys.executable`` to make
85 sure the right version is used.
85 sure the right version is used.
86
86
87 For a given path ``cmd``, this returns [cmd] if cmd's extension is .exe,
87 For a given path ``cmd``, this returns [cmd] if cmd's extension is .exe,
88 .com or .bat, and [, cmd] otherwise.
88 .com or .bat, and [, cmd] otherwise.
89
89
90 Parameters
90 Parameters
91 ----------
91 ----------
92 cmd : string
92 cmd : string
93 The path of the command.
93 The path of the command.
94
94
95 Returns
95 Returns
96 -------
96 -------
97 argv-style list.
97 argv-style list.
98 """
98 """
99 ext = os.path.splitext(cmd)[1]
99 ext = os.path.splitext(cmd)[1]
100 if ext in ['.exe', '.com', '.bat']:
100 if ext in ['.exe', '.com', '.bat']:
101 return [cmd]
101 return [cmd]
102 else:
102 else:
103 return [sys.executable, cmd]
103 return [sys.executable, cmd]
104
104
105
105
106 def abbrev_cwd():
106 def abbrev_cwd():
107 """ Return abbreviated version of cwd, e.g. d:mydir """
107 """ Return abbreviated version of cwd, e.g. d:mydir """
108 cwd = os.getcwdu().replace('\\','/')
108 cwd = os.getcwdu().replace('\\','/')
109 drivepart = ''
109 drivepart = ''
110 tail = cwd
110 tail = cwd
111 if sys.platform == 'win32':
111 if sys.platform == 'win32':
112 if len(cwd) < 4:
112 if len(cwd) < 4:
113 return cwd
113 return cwd
114 drivepart,tail = os.path.splitdrive(cwd)
114 drivepart,tail = os.path.splitdrive(cwd)
115
115
116
116
117 parts = tail.split('/')
117 parts = tail.split('/')
118 if len(parts) > 2:
118 if len(parts) > 2:
119 tail = '/'.join(parts[-2:])
119 tail = '/'.join(parts[-2:])
120
120
121 return (drivepart + (
121 return (drivepart + (
122 cwd == '/' and '/' or tail))
122 cwd == '/' and '/' or tail))
@@ -1,560 +1,560 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.utils.path.py"""
2 """Tests for IPython.utils.path.py"""
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2008-2011 The IPython Development Team
5 # Copyright (C) 2008-2011 The IPython Development Team
6 #
6 #
7 # 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
8 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 from __future__ import with_statement
15 from __future__ import with_statement
16
16
17 import os
17 import os
18 import shutil
18 import shutil
19 import sys
19 import sys
20 import tempfile
20 import tempfile
21 from io import StringIO
21 from io import StringIO
22 from contextlib import contextmanager
22 from contextlib import contextmanager
23
23
24 from os.path import join, abspath, split
24 from os.path import join, abspath, split
25
25
26 import nose.tools as nt
26 import nose.tools as nt
27
27
28 from nose import with_setup
28 from nose import with_setup
29
29
30 import IPython
30 import IPython
31 from IPython.testing import decorators as dec
31 from IPython.testing import decorators as dec
32 from IPython.testing.decorators import skip_if_not_win32, skip_win32
32 from IPython.testing.decorators import skip_if_not_win32, skip_win32
33 from IPython.testing.tools import make_tempfile, AssertPrints
33 from IPython.testing.tools import make_tempfile, AssertPrints
34 from IPython.utils import path, io
34 from IPython.utils import path, io
35 from IPython.utils import py3compat
35 from IPython.utils import py3compat
36 from IPython.utils.tempdir import TemporaryDirectory
36 from IPython.utils.tempdir import TemporaryDirectory
37
37
38 # Platform-dependent imports
38 # Platform-dependent imports
39 try:
39 try:
40 import _winreg as wreg
40 import _winreg as wreg
41 except ImportError:
41 except ImportError:
42 #Fake _winreg module on none windows platforms
42 #Fake _winreg module on none windows platforms
43 import types
43 import types
44 wr_name = "winreg" if py3compat.PY3 else "_winreg"
44 wr_name = "winreg" if py3compat.PY3 else "_winreg"
45 sys.modules[wr_name] = types.ModuleType(wr_name)
45 sys.modules[wr_name] = types.ModuleType(wr_name)
46 import _winreg as wreg
46 import _winreg as wreg
47 #Add entries that needs to be stubbed by the testing code
47 #Add entries that needs to be stubbed by the testing code
48 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
48 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
49
49
50 try:
50 try:
51 reload
51 reload
52 except NameError: # Python 3
52 except NameError: # Python 3
53 from imp import reload
53 from imp import reload
54
54
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56 # Globals
56 # Globals
57 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
58 env = os.environ
58 env = os.environ
59 TEST_FILE_PATH = split(abspath(__file__))[0]
59 TEST_FILE_PATH = split(abspath(__file__))[0]
60 TMP_TEST_DIR = tempfile.mkdtemp()
60 TMP_TEST_DIR = tempfile.mkdtemp()
61 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
61 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
62 XDG_TEST_DIR = join(HOME_TEST_DIR, "xdg_test_dir")
62 XDG_TEST_DIR = join(HOME_TEST_DIR, "xdg_test_dir")
63 XDG_CACHE_DIR = join(HOME_TEST_DIR, "xdg_cache_dir")
63 XDG_CACHE_DIR = join(HOME_TEST_DIR, "xdg_cache_dir")
64 IP_TEST_DIR = join(HOME_TEST_DIR,'.ipython')
64 IP_TEST_DIR = join(HOME_TEST_DIR,'.ipython')
65 #
65 #
66 # Setup/teardown functions/decorators
66 # Setup/teardown functions/decorators
67 #
67 #
68
68
69 def setup():
69 def setup():
70 """Setup testenvironment for the module:
70 """Setup testenvironment for the module:
71
71
72 - Adds dummy home dir tree
72 - Adds dummy home dir tree
73 """
73 """
74 # Do not mask exceptions here. In particular, catching WindowsError is a
74 # Do not mask exceptions here. In particular, catching WindowsError is a
75 # problem because that exception is only defined on Windows...
75 # problem because that exception is only defined on Windows...
76 os.makedirs(IP_TEST_DIR)
76 os.makedirs(IP_TEST_DIR)
77 os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython'))
77 os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython'))
78 os.makedirs(os.path.join(XDG_CACHE_DIR, 'ipython'))
78 os.makedirs(os.path.join(XDG_CACHE_DIR, 'ipython'))
79
79
80
80
81 def teardown():
81 def teardown():
82 """Teardown testenvironment for the module:
82 """Teardown testenvironment for the module:
83
83
84 - Remove dummy home dir tree
84 - Remove dummy home dir tree
85 """
85 """
86 # Note: we remove the parent test dir, which is the root of all test
86 # Note: we remove the parent test dir, which is the root of all test
87 # subdirs we may have created. Use shutil instead of os.removedirs, so
87 # subdirs we may have created. Use shutil instead of os.removedirs, so
88 # that non-empty directories are all recursively removed.
88 # that non-empty directories are all recursively removed.
89 shutil.rmtree(TMP_TEST_DIR)
89 shutil.rmtree(TMP_TEST_DIR)
90
90
91
91
92 def setup_environment():
92 def setup_environment():
93 """Setup testenvironment for some functions that are tested
93 """Setup testenvironment for some functions that are tested
94 in this module. In particular this functions stores attributes
94 in this module. In particular this functions stores attributes
95 and other things that we need to stub in some test functions.
95 and other things that we need to stub in some test functions.
96 This needs to be done on a function level and not module level because
96 This needs to be done on a function level and not module level because
97 each testfunction needs a pristine environment.
97 each testfunction needs a pristine environment.
98 """
98 """
99 global oldstuff, platformstuff
99 global oldstuff, platformstuff
100 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
100 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
101
101
102 if os.name == 'nt':
102 if os.name == 'nt':
103 platformstuff = (wreg.OpenKey, wreg.QueryValueEx,)
103 platformstuff = (wreg.OpenKey, wreg.QueryValueEx,)
104
104
105
105
106 def teardown_environment():
106 def teardown_environment():
107 """Restore things that were remebered by the setup_environment function
107 """Restore things that were remebered by the setup_environment function
108 """
108 """
109 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
109 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
110 os.chdir(old_wd)
110 os.chdir(old_wd)
111 reload(path)
111 reload(path)
112
112
113 for key in env.keys():
113 for key in env.keys():
114 if key not in oldenv:
114 if key not in oldenv:
115 del env[key]
115 del env[key]
116 env.update(oldenv)
116 env.update(oldenv)
117 if hasattr(sys, 'frozen'):
117 if hasattr(sys, 'frozen'):
118 del sys.frozen
118 del sys.frozen
119 if os.name == 'nt':
119 if os.name == 'nt':
120 (wreg.OpenKey, wreg.QueryValueEx,) = platformstuff
120 (wreg.OpenKey, wreg.QueryValueEx,) = platformstuff
121
121
122 # Build decorator that uses the setup_environment/setup_environment
122 # Build decorator that uses the setup_environment/setup_environment
123 with_environment = with_setup(setup_environment, teardown_environment)
123 with_environment = with_setup(setup_environment, teardown_environment)
124
124
125 @skip_if_not_win32
125 @skip_if_not_win32
126 @with_environment
126 @with_environment
127 def test_get_home_dir_1():
127 def test_get_home_dir_1():
128 """Testcase for py2exe logic, un-compressed lib
128 """Testcase for py2exe logic, un-compressed lib
129 """
129 """
130 sys.frozen = True
130 sys.frozen = True
131
131
132 #fake filename for IPython.__init__
132 #fake filename for IPython.__init__
133 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
133 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
134
134
135 home_dir = path.get_home_dir()
135 home_dir = path.get_home_dir()
136 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
136 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
137
137
138
138
139 @skip_if_not_win32
139 @skip_if_not_win32
140 @with_environment
140 @with_environment
141 def test_get_home_dir_2():
141 def test_get_home_dir_2():
142 """Testcase for py2exe logic, compressed lib
142 """Testcase for py2exe logic, compressed lib
143 """
143 """
144 sys.frozen = True
144 sys.frozen = True
145 #fake filename for IPython.__init__
145 #fake filename for IPython.__init__
146 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
146 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
147
147
148 home_dir = path.get_home_dir(True)
148 home_dir = path.get_home_dir(True)
149 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower())
149 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower())
150
150
151
151
152 @with_environment
152 @with_environment
153 def test_get_home_dir_3():
153 def test_get_home_dir_3():
154 """get_home_dir() uses $HOME if set"""
154 """get_home_dir() uses $HOME if set"""
155 env["HOME"] = HOME_TEST_DIR
155 env["HOME"] = HOME_TEST_DIR
156 home_dir = path.get_home_dir(True)
156 home_dir = path.get_home_dir(True)
157 # get_home_dir expands symlinks
157 # get_home_dir expands symlinks
158 nt.assert_equal(home_dir, os.path.realpath(env["HOME"]))
158 nt.assert_equal(home_dir, os.path.realpath(env["HOME"]))
159
159
160
160
161 @with_environment
161 @with_environment
162 def test_get_home_dir_4():
162 def test_get_home_dir_4():
163 """get_home_dir() still works if $HOME is not set"""
163 """get_home_dir() still works if $HOME is not set"""
164
164
165 if 'HOME' in env: del env['HOME']
165 if 'HOME' in env: del env['HOME']
166 # this should still succeed, but we don't care what the answer is
166 # this should still succeed, but we don't care what the answer is
167 home = path.get_home_dir(False)
167 home = path.get_home_dir(False)
168
168
169 @with_environment
169 @with_environment
170 def test_get_home_dir_5():
170 def test_get_home_dir_5():
171 """raise HomeDirError if $HOME is specified, but not a writable dir"""
171 """raise HomeDirError if $HOME is specified, but not a writable dir"""
172 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
172 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
173 # set os.name = posix, to prevent My Documents fallback on Windows
173 # set os.name = posix, to prevent My Documents fallback on Windows
174 os.name = 'posix'
174 os.name = 'posix'
175 nt.assert_raises(path.HomeDirError, path.get_home_dir, True)
175 nt.assert_raises(path.HomeDirError, path.get_home_dir, True)
176
176
177
177
178 # Should we stub wreg fully so we can run the test on all platforms?
178 # Should we stub wreg fully so we can run the test on all platforms?
179 @skip_if_not_win32
179 @skip_if_not_win32
180 @with_environment
180 @with_environment
181 def test_get_home_dir_8():
181 def test_get_home_dir_8():
182 """Using registry hack for 'My Documents', os=='nt'
182 """Using registry hack for 'My Documents', os=='nt'
183
183
184 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
184 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
185 """
185 """
186 os.name = 'nt'
186 os.name = 'nt'
187 # Remove from stub environment all keys that may be set
187 # Remove from stub environment all keys that may be set
188 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
188 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
189 env.pop(key, None)
189 env.pop(key, None)
190
190
191 #Stub windows registry functions
191 #Stub windows registry functions
192 def OpenKey(x, y):
192 def OpenKey(x, y):
193 class key:
193 class key:
194 def Close(self):
194 def Close(self):
195 pass
195 pass
196 return key()
196 return key()
197 def QueryValueEx(x, y):
197 def QueryValueEx(x, y):
198 return [abspath(HOME_TEST_DIR)]
198 return [abspath(HOME_TEST_DIR)]
199
199
200 wreg.OpenKey = OpenKey
200 wreg.OpenKey = OpenKey
201 wreg.QueryValueEx = QueryValueEx
201 wreg.QueryValueEx = QueryValueEx
202
202
203 home_dir = path.get_home_dir()
203 home_dir = path.get_home_dir()
204 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
204 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
205
205
206
206
207 @with_environment
207 @with_environment
208 def test_get_ipython_dir_1():
208 def test_get_ipython_dir_1():
209 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
209 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
210 env_ipdir = os.path.join("someplace", ".ipython")
210 env_ipdir = os.path.join("someplace", ".ipython")
211 path._writable_dir = lambda path: True
211 path._writable_dir = lambda path: True
212 env['IPYTHONDIR'] = env_ipdir
212 env['IPYTHONDIR'] = env_ipdir
213 ipdir = path.get_ipython_dir()
213 ipdir = path.get_ipython_dir()
214 nt.assert_equal(ipdir, env_ipdir)
214 nt.assert_equal(ipdir, env_ipdir)
215
215
216
216
217 @with_environment
217 @with_environment
218 def test_get_ipython_dir_2():
218 def test_get_ipython_dir_2():
219 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
219 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
220 path.get_home_dir = lambda : "someplace"
220 path.get_home_dir = lambda : "someplace"
221 path.get_xdg_dir = lambda : None
221 path.get_xdg_dir = lambda : None
222 path._writable_dir = lambda path: True
222 path._writable_dir = lambda path: True
223 os.name = "posix"
223 os.name = "posix"
224 env.pop('IPYTHON_DIR', None)
224 env.pop('IPYTHON_DIR', None)
225 env.pop('IPYTHONDIR', None)
225 env.pop('IPYTHONDIR', None)
226 env.pop('XDG_CONFIG_HOME', None)
226 env.pop('XDG_CONFIG_HOME', None)
227 ipdir = path.get_ipython_dir()
227 ipdir = path.get_ipython_dir()
228 nt.assert_equal(ipdir, os.path.join("someplace", ".ipython"))
228 nt.assert_equal(ipdir, os.path.join("someplace", ".ipython"))
229
229
230 @with_environment
230 @with_environment
231 def test_get_ipython_dir_3():
231 def test_get_ipython_dir_3():
232 """test_get_ipython_dir_3, use XDG if defined, and .ipython doesn't exist."""
232 """test_get_ipython_dir_3, use XDG if defined, and .ipython doesn't exist."""
233 path.get_home_dir = lambda : "someplace"
233 path.get_home_dir = lambda : "someplace"
234 path._writable_dir = lambda path: True
234 path._writable_dir = lambda path: True
235 os.name = "posix"
235 os.name = "posix"
236 env.pop('IPYTHON_DIR', None)
236 env.pop('IPYTHON_DIR', None)
237 env.pop('IPYTHONDIR', None)
237 env.pop('IPYTHONDIR', None)
238 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
238 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
239 ipdir = path.get_ipython_dir()
239 ipdir = path.get_ipython_dir()
240 if sys.platform == "darwin":
240 if sys.platform == "darwin":
241 expected = os.path.join("someplace", ".ipython")
241 expected = os.path.join("someplace", ".ipython")
242 else:
242 else:
243 expected = os.path.join(XDG_TEST_DIR, "ipython")
243 expected = os.path.join(XDG_TEST_DIR, "ipython")
244 nt.assert_equal(ipdir, expected)
244 nt.assert_equal(ipdir, expected)
245
245
246 @with_environment
246 @with_environment
247 def test_get_ipython_dir_4():
247 def test_get_ipython_dir_4():
248 """test_get_ipython_dir_4, use XDG if both exist."""
248 """test_get_ipython_dir_4, use XDG if both exist."""
249 path.get_home_dir = lambda : HOME_TEST_DIR
249 path.get_home_dir = lambda : HOME_TEST_DIR
250 os.name = "posix"
250 os.name = "posix"
251 env.pop('IPYTHON_DIR', None)
251 env.pop('IPYTHON_DIR', None)
252 env.pop('IPYTHONDIR', None)
252 env.pop('IPYTHONDIR', None)
253 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
253 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
254 ipdir = path.get_ipython_dir()
254 ipdir = path.get_ipython_dir()
255 if sys.platform == "darwin":
255 if sys.platform == "darwin":
256 expected = os.path.join(HOME_TEST_DIR, ".ipython")
256 expected = os.path.join(HOME_TEST_DIR, ".ipython")
257 else:
257 else:
258 expected = os.path.join(XDG_TEST_DIR, "ipython")
258 expected = os.path.join(XDG_TEST_DIR, "ipython")
259 nt.assert_equal(ipdir, expected)
259 nt.assert_equal(ipdir, expected)
260
260
261 @with_environment
261 @with_environment
262 def test_get_ipython_dir_5():
262 def test_get_ipython_dir_5():
263 """test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
263 """test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
264 path.get_home_dir = lambda : HOME_TEST_DIR
264 path.get_home_dir = lambda : HOME_TEST_DIR
265 os.name = "posix"
265 os.name = "posix"
266 env.pop('IPYTHON_DIR', None)
266 env.pop('IPYTHON_DIR', None)
267 env.pop('IPYTHONDIR', None)
267 env.pop('IPYTHONDIR', None)
268 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
268 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
269 os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
269 os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
270 ipdir = path.get_ipython_dir()
270 ipdir = path.get_ipython_dir()
271 nt.assert_equal(ipdir, IP_TEST_DIR)
271 nt.assert_equal(ipdir, IP_TEST_DIR)
272
272
273 @with_environment
273 @with_environment
274 def test_get_ipython_dir_6():
274 def test_get_ipython_dir_6():
275 """test_get_ipython_dir_6, use XDG if defined and neither exist."""
275 """test_get_ipython_dir_6, use XDG if defined and neither exist."""
276 xdg = os.path.join(HOME_TEST_DIR, 'somexdg')
276 xdg = os.path.join(HOME_TEST_DIR, 'somexdg')
277 os.mkdir(xdg)
277 os.mkdir(xdg)
278 shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython'))
278 shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython'))
279 path.get_home_dir = lambda : HOME_TEST_DIR
279 path.get_home_dir = lambda : HOME_TEST_DIR
280 path.get_xdg_dir = lambda : xdg
280 path.get_xdg_dir = lambda : xdg
281 os.name = "posix"
281 os.name = "posix"
282 env.pop('IPYTHON_DIR', None)
282 env.pop('IPYTHON_DIR', None)
283 env.pop('IPYTHONDIR', None)
283 env.pop('IPYTHONDIR', None)
284 env.pop('XDG_CONFIG_HOME', None)
284 env.pop('XDG_CONFIG_HOME', None)
285 xdg_ipdir = os.path.join(xdg, "ipython")
285 xdg_ipdir = os.path.join(xdg, "ipython")
286 ipdir = path.get_ipython_dir()
286 ipdir = path.get_ipython_dir()
287 nt.assert_equal(ipdir, xdg_ipdir)
287 nt.assert_equal(ipdir, xdg_ipdir)
288
288
289 @with_environment
289 @with_environment
290 def test_get_ipython_dir_7():
290 def test_get_ipython_dir_7():
291 """test_get_ipython_dir_7, test home directory expansion on IPYTHONDIR"""
291 """test_get_ipython_dir_7, test home directory expansion on IPYTHONDIR"""
292 path._writable_dir = lambda path: True
292 path._writable_dir = lambda path: True
293 home_dir = os.path.normpath(os.path.expanduser('~'))
293 home_dir = os.path.normpath(os.path.expanduser('~'))
294 env['IPYTHONDIR'] = os.path.join('~', 'somewhere')
294 env['IPYTHONDIR'] = os.path.join('~', 'somewhere')
295 ipdir = path.get_ipython_dir()
295 ipdir = path.get_ipython_dir()
296 nt.assert_equal(ipdir, os.path.join(home_dir, 'somewhere'))
296 nt.assert_equal(ipdir, os.path.join(home_dir, 'somewhere'))
297
297
298 @skip_win32
298 @skip_win32
299 @with_environment
299 @with_environment
300 def test_get_ipython_dir_8():
300 def test_get_ipython_dir_8():
301 """test_get_ipython_dir_8, test / home directory"""
301 """test_get_ipython_dir_8, test / home directory"""
302 old = path._writable_dir, path.get_xdg_dir
302 old = path._writable_dir, path.get_xdg_dir
303 try:
303 try:
304 path._writable_dir = lambda path: bool(path)
304 path._writable_dir = lambda path: bool(path)
305 path.get_xdg_dir = lambda: None
305 path.get_xdg_dir = lambda: None
306 env.pop('IPYTHON_DIR', None)
306 env.pop('IPYTHON_DIR', None)
307 env.pop('IPYTHONDIR', None)
307 env.pop('IPYTHONDIR', None)
308 env['HOME'] = '/'
308 env['HOME'] = '/'
309 nt.assert_equal(path.get_ipython_dir(), '/.ipython')
309 nt.assert_equal(path.get_ipython_dir(), '/.ipython')
310 finally:
310 finally:
311 path._writable_dir, path.get_xdg_dir = old
311 path._writable_dir, path.get_xdg_dir = old
312
312
313 @with_environment
313 @with_environment
314 def test_get_xdg_dir_0():
314 def test_get_xdg_dir_0():
315 """test_get_xdg_dir_0, check xdg_dir"""
315 """test_get_xdg_dir_0, check xdg_dir"""
316 reload(path)
316 reload(path)
317 path._writable_dir = lambda path: True
317 path._writable_dir = lambda path: True
318 path.get_home_dir = lambda : 'somewhere'
318 path.get_home_dir = lambda : 'somewhere'
319 os.name = "posix"
319 os.name = "posix"
320 sys.platform = "linux2"
320 sys.platform = "linux2"
321 env.pop('IPYTHON_DIR', None)
321 env.pop('IPYTHON_DIR', None)
322 env.pop('IPYTHONDIR', None)
322 env.pop('IPYTHONDIR', None)
323 env.pop('XDG_CONFIG_HOME', None)
323 env.pop('XDG_CONFIG_HOME', None)
324
324
325 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
325 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
326
326
327
327
328 @with_environment
328 @with_environment
329 def test_get_xdg_dir_1():
329 def test_get_xdg_dir_1():
330 """test_get_xdg_dir_1, check nonexistant xdg_dir"""
330 """test_get_xdg_dir_1, check nonexistant xdg_dir"""
331 reload(path)
331 reload(path)
332 path.get_home_dir = lambda : HOME_TEST_DIR
332 path.get_home_dir = lambda : HOME_TEST_DIR
333 os.name = "posix"
333 os.name = "posix"
334 sys.platform = "linux2"
334 sys.platform = "linux2"
335 env.pop('IPYTHON_DIR', None)
335 env.pop('IPYTHON_DIR', None)
336 env.pop('IPYTHONDIR', None)
336 env.pop('IPYTHONDIR', None)
337 env.pop('XDG_CONFIG_HOME', None)
337 env.pop('XDG_CONFIG_HOME', None)
338 nt.assert_equal(path.get_xdg_dir(), None)
338 nt.assert_equal(path.get_xdg_dir(), None)
339
339
340 @with_environment
340 @with_environment
341 def test_get_xdg_dir_2():
341 def test_get_xdg_dir_2():
342 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
342 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
343 reload(path)
343 reload(path)
344 path.get_home_dir = lambda : HOME_TEST_DIR
344 path.get_home_dir = lambda : HOME_TEST_DIR
345 os.name = "posix"
345 os.name = "posix"
346 sys.platform = "linux2"
346 sys.platform = "linux2"
347 env.pop('IPYTHON_DIR', None)
347 env.pop('IPYTHON_DIR', None)
348 env.pop('IPYTHONDIR', None)
348 env.pop('IPYTHONDIR', None)
349 env.pop('XDG_CONFIG_HOME', None)
349 env.pop('XDG_CONFIG_HOME', None)
350 cfgdir=os.path.join(path.get_home_dir(), '.config')
350 cfgdir=os.path.join(path.get_home_dir(), '.config')
351 if not os.path.exists(cfgdir):
351 if not os.path.exists(cfgdir):
352 os.makedirs(cfgdir)
352 os.makedirs(cfgdir)
353
353
354 nt.assert_equal(path.get_xdg_dir(), cfgdir)
354 nt.assert_equal(path.get_xdg_dir(), cfgdir)
355
355
356 @with_environment
356 @with_environment
357 def test_get_xdg_dir_3():
357 def test_get_xdg_dir_3():
358 """test_get_xdg_dir_3, check xdg_dir not used on OS X"""
358 """test_get_xdg_dir_3, check xdg_dir not used on OS X"""
359 reload(path)
359 reload(path)
360 path.get_home_dir = lambda : HOME_TEST_DIR
360 path.get_home_dir = lambda : HOME_TEST_DIR
361 os.name = "posix"
361 os.name = "posix"
362 sys.platform = "darwin"
362 sys.platform = "darwin"
363 env.pop('IPYTHON_DIR', None)
363 env.pop('IPYTHON_DIR', None)
364 env.pop('IPYTHONDIR', None)
364 env.pop('IPYTHONDIR', None)
365 env.pop('XDG_CONFIG_HOME', None)
365 env.pop('XDG_CONFIG_HOME', None)
366 cfgdir=os.path.join(path.get_home_dir(), '.config')
366 cfgdir=os.path.join(path.get_home_dir(), '.config')
367 if not os.path.exists(cfgdir):
367 if not os.path.exists(cfgdir):
368 os.makedirs(cfgdir)
368 os.makedirs(cfgdir)
369
369
370 nt.assert_equal(path.get_xdg_dir(), None)
370 nt.assert_equal(path.get_xdg_dir(), None)
371
371
372 def test_filefind():
372 def test_filefind():
373 """Various tests for filefind"""
373 """Various tests for filefind"""
374 f = tempfile.NamedTemporaryFile()
374 f = tempfile.NamedTemporaryFile()
375 # print 'fname:',f.name
375 # print 'fname:',f.name
376 alt_dirs = path.get_ipython_dir()
376 alt_dirs = path.get_ipython_dir()
377 t = path.filefind(f.name, alt_dirs)
377 t = path.filefind(f.name, alt_dirs)
378 # print 'found:',t
378 # print 'found:',t
379
379
380 @with_environment
380 @with_environment
381 def test_get_ipython_cache_dir():
381 def test_get_ipython_cache_dir():
382 os.environ["HOME"] = HOME_TEST_DIR
382 os.environ["HOME"] = HOME_TEST_DIR
383 if os.name == 'posix' and sys.platform != 'darwin':
383 if os.name == 'posix' and sys.platform != 'darwin':
384 # test default
384 # test default
385 os.makedirs(os.path.join(HOME_TEST_DIR, ".cache"))
385 os.makedirs(os.path.join(HOME_TEST_DIR, ".cache"))
386 os.environ.pop("XDG_CACHE_HOME", None)
386 os.environ.pop("XDG_CACHE_HOME", None)
387 ipdir = path.get_ipython_cache_dir()
387 ipdir = path.get_ipython_cache_dir()
388 nt.assert_equal(os.path.join(HOME_TEST_DIR, ".cache", "ipython"),
388 nt.assert_equal(os.path.join(HOME_TEST_DIR, ".cache", "ipython"),
389 ipdir)
389 ipdir)
390 nt.assert_true(os.path.isdir(ipdir))
390 nt.assert_true(os.path.isdir(ipdir))
391
391
392 # test env override
392 # test env override
393 os.environ["XDG_CACHE_HOME"] = XDG_CACHE_DIR
393 os.environ["XDG_CACHE_HOME"] = XDG_CACHE_DIR
394 ipdir = path.get_ipython_cache_dir()
394 ipdir = path.get_ipython_cache_dir()
395 nt.assert_true(os.path.isdir(ipdir))
395 nt.assert_true(os.path.isdir(ipdir))
396 nt.assert_equal(ipdir, os.path.join(XDG_CACHE_DIR, "ipython"))
396 nt.assert_equal(ipdir, os.path.join(XDG_CACHE_DIR, "ipython"))
397 else:
397 else:
398 nt.assert_equal(path.get_ipython_cache_dir(),
398 nt.assert_equal(path.get_ipython_cache_dir(),
399 path.get_ipython_dir())
399 path.get_ipython_dir())
400
400
401 def test_get_ipython_package_dir():
401 def test_get_ipython_package_dir():
402 ipdir = path.get_ipython_package_dir()
402 ipdir = path.get_ipython_package_dir()
403 nt.assert_true(os.path.isdir(ipdir))
403 nt.assert_true(os.path.isdir(ipdir))
404
404
405
405
406 def test_get_ipython_module_path():
406 def test_get_ipython_module_path():
407 ipapp_path = path.get_ipython_module_path('IPython.frontend.terminal.ipapp')
407 ipapp_path = path.get_ipython_module_path('IPython.terminal.ipapp')
408 nt.assert_true(os.path.isfile(ipapp_path))
408 nt.assert_true(os.path.isfile(ipapp_path))
409
409
410
410
411 @dec.skip_if_not_win32
411 @dec.skip_if_not_win32
412 def test_get_long_path_name_win32():
412 def test_get_long_path_name_win32():
413 p = path.get_long_path_name('c:\\docume~1')
413 p = path.get_long_path_name('c:\\docume~1')
414 nt.assert_equal(p,u'c:\\Documents and Settings')
414 nt.assert_equal(p,u'c:\\Documents and Settings')
415
415
416
416
417 @dec.skip_win32
417 @dec.skip_win32
418 def test_get_long_path_name():
418 def test_get_long_path_name():
419 p = path.get_long_path_name('/usr/local')
419 p = path.get_long_path_name('/usr/local')
420 nt.assert_equal(p,'/usr/local')
420 nt.assert_equal(p,'/usr/local')
421
421
422 @dec.skip_win32 # can't create not-user-writable dir on win
422 @dec.skip_win32 # can't create not-user-writable dir on win
423 @with_environment
423 @with_environment
424 def test_not_writable_ipdir():
424 def test_not_writable_ipdir():
425 tmpdir = tempfile.mkdtemp()
425 tmpdir = tempfile.mkdtemp()
426 os.name = "posix"
426 os.name = "posix"
427 env.pop('IPYTHON_DIR', None)
427 env.pop('IPYTHON_DIR', None)
428 env.pop('IPYTHONDIR', None)
428 env.pop('IPYTHONDIR', None)
429 env.pop('XDG_CONFIG_HOME', None)
429 env.pop('XDG_CONFIG_HOME', None)
430 env['HOME'] = tmpdir
430 env['HOME'] = tmpdir
431 ipdir = os.path.join(tmpdir, '.ipython')
431 ipdir = os.path.join(tmpdir, '.ipython')
432 os.mkdir(ipdir)
432 os.mkdir(ipdir)
433 os.chmod(ipdir, 600)
433 os.chmod(ipdir, 600)
434 with AssertPrints('is not a writable location', channel='stderr'):
434 with AssertPrints('is not a writable location', channel='stderr'):
435 ipdir = path.get_ipython_dir()
435 ipdir = path.get_ipython_dir()
436 env.pop('IPYTHON_DIR', None)
436 env.pop('IPYTHON_DIR', None)
437
437
438 def test_unquote_filename():
438 def test_unquote_filename():
439 for win32 in (True, False):
439 for win32 in (True, False):
440 nt.assert_equal(path.unquote_filename('foo.py', win32=win32), 'foo.py')
440 nt.assert_equal(path.unquote_filename('foo.py', win32=win32), 'foo.py')
441 nt.assert_equal(path.unquote_filename('foo bar.py', win32=win32), 'foo bar.py')
441 nt.assert_equal(path.unquote_filename('foo bar.py', win32=win32), 'foo bar.py')
442 nt.assert_equal(path.unquote_filename('"foo.py"', win32=True), 'foo.py')
442 nt.assert_equal(path.unquote_filename('"foo.py"', win32=True), 'foo.py')
443 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=True), 'foo bar.py')
443 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=True), 'foo bar.py')
444 nt.assert_equal(path.unquote_filename("'foo.py'", win32=True), 'foo.py')
444 nt.assert_equal(path.unquote_filename("'foo.py'", win32=True), 'foo.py')
445 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=True), 'foo bar.py')
445 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=True), 'foo bar.py')
446 nt.assert_equal(path.unquote_filename('"foo.py"', win32=False), '"foo.py"')
446 nt.assert_equal(path.unquote_filename('"foo.py"', win32=False), '"foo.py"')
447 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=False), '"foo bar.py"')
447 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=False), '"foo bar.py"')
448 nt.assert_equal(path.unquote_filename("'foo.py'", win32=False), "'foo.py'")
448 nt.assert_equal(path.unquote_filename("'foo.py'", win32=False), "'foo.py'")
449 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=False), "'foo bar.py'")
449 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=False), "'foo bar.py'")
450
450
451 @with_environment
451 @with_environment
452 def test_get_py_filename():
452 def test_get_py_filename():
453 os.chdir(TMP_TEST_DIR)
453 os.chdir(TMP_TEST_DIR)
454 for win32 in (True, False):
454 for win32 in (True, False):
455 with make_tempfile('foo.py'):
455 with make_tempfile('foo.py'):
456 nt.assert_equal(path.get_py_filename('foo.py', force_win32=win32), 'foo.py')
456 nt.assert_equal(path.get_py_filename('foo.py', force_win32=win32), 'foo.py')
457 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo.py')
457 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo.py')
458 with make_tempfile('foo'):
458 with make_tempfile('foo'):
459 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo')
459 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo')
460 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
460 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
461 nt.assert_raises(IOError, path.get_py_filename, 'foo', force_win32=win32)
461 nt.assert_raises(IOError, path.get_py_filename, 'foo', force_win32=win32)
462 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
462 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
463 true_fn = 'foo with spaces.py'
463 true_fn = 'foo with spaces.py'
464 with make_tempfile(true_fn):
464 with make_tempfile(true_fn):
465 nt.assert_equal(path.get_py_filename('foo with spaces', force_win32=win32), true_fn)
465 nt.assert_equal(path.get_py_filename('foo with spaces', force_win32=win32), true_fn)
466 nt.assert_equal(path.get_py_filename('foo with spaces.py', force_win32=win32), true_fn)
466 nt.assert_equal(path.get_py_filename('foo with spaces.py', force_win32=win32), true_fn)
467 if win32:
467 if win32:
468 nt.assert_equal(path.get_py_filename('"foo with spaces.py"', force_win32=True), true_fn)
468 nt.assert_equal(path.get_py_filename('"foo with spaces.py"', force_win32=True), true_fn)
469 nt.assert_equal(path.get_py_filename("'foo with spaces.py'", force_win32=True), true_fn)
469 nt.assert_equal(path.get_py_filename("'foo with spaces.py'", force_win32=True), true_fn)
470 else:
470 else:
471 nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"', force_win32=False)
471 nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"', force_win32=False)
472 nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'", force_win32=False)
472 nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'", force_win32=False)
473
473
474 def test_unicode_in_filename():
474 def test_unicode_in_filename():
475 """When a file doesn't exist, the exception raised should be safe to call
475 """When a file doesn't exist, the exception raised should be safe to call
476 str() on - i.e. in Python 2 it must only have ASCII characters.
476 str() on - i.e. in Python 2 it must only have ASCII characters.
477
477
478 https://github.com/ipython/ipython/issues/875
478 https://github.com/ipython/ipython/issues/875
479 """
479 """
480 try:
480 try:
481 # these calls should not throw unicode encode exceptions
481 # these calls should not throw unicode encode exceptions
482 path.get_py_filename(u'fooéè.py', force_win32=False)
482 path.get_py_filename(u'fooéè.py', force_win32=False)
483 except IOError as ex:
483 except IOError as ex:
484 str(ex)
484 str(ex)
485
485
486
486
487 class TestShellGlob(object):
487 class TestShellGlob(object):
488
488
489 @classmethod
489 @classmethod
490 def setUpClass(cls):
490 def setUpClass(cls):
491 cls.filenames_start_with_a = map('a{0}'.format, range(3))
491 cls.filenames_start_with_a = map('a{0}'.format, range(3))
492 cls.filenames_end_with_b = map('{0}b'.format, range(3))
492 cls.filenames_end_with_b = map('{0}b'.format, range(3))
493 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
493 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
494 cls.tempdir = TemporaryDirectory()
494 cls.tempdir = TemporaryDirectory()
495 td = cls.tempdir.name
495 td = cls.tempdir.name
496
496
497 with cls.in_tempdir():
497 with cls.in_tempdir():
498 # Create empty files
498 # Create empty files
499 for fname in cls.filenames:
499 for fname in cls.filenames:
500 open(os.path.join(td, fname), 'w').close()
500 open(os.path.join(td, fname), 'w').close()
501
501
502 @classmethod
502 @classmethod
503 def tearDownClass(cls):
503 def tearDownClass(cls):
504 cls.tempdir.cleanup()
504 cls.tempdir.cleanup()
505
505
506 @classmethod
506 @classmethod
507 @contextmanager
507 @contextmanager
508 def in_tempdir(cls):
508 def in_tempdir(cls):
509 save = os.getcwdu()
509 save = os.getcwdu()
510 try:
510 try:
511 os.chdir(cls.tempdir.name)
511 os.chdir(cls.tempdir.name)
512 yield
512 yield
513 finally:
513 finally:
514 os.chdir(save)
514 os.chdir(save)
515
515
516 def check_match(self, patterns, matches):
516 def check_match(self, patterns, matches):
517 with self.in_tempdir():
517 with self.in_tempdir():
518 # glob returns unordered list. that's why sorted is required.
518 # glob returns unordered list. that's why sorted is required.
519 nt.assert_equals(sorted(path.shellglob(patterns)),
519 nt.assert_equals(sorted(path.shellglob(patterns)),
520 sorted(matches))
520 sorted(matches))
521
521
522 def common_cases(self):
522 def common_cases(self):
523 return [
523 return [
524 (['*'], self.filenames),
524 (['*'], self.filenames),
525 (['a*'], self.filenames_start_with_a),
525 (['a*'], self.filenames_start_with_a),
526 (['*c'], ['*c']),
526 (['*c'], ['*c']),
527 (['*', 'a*', '*b', '*c'], self.filenames
527 (['*', 'a*', '*b', '*c'], self.filenames
528 + self.filenames_start_with_a
528 + self.filenames_start_with_a
529 + self.filenames_end_with_b
529 + self.filenames_end_with_b
530 + ['*c']),
530 + ['*c']),
531 (['a[012]'], self.filenames_start_with_a),
531 (['a[012]'], self.filenames_start_with_a),
532 ]
532 ]
533
533
534 @skip_win32
534 @skip_win32
535 def test_match_posix(self):
535 def test_match_posix(self):
536 for (patterns, matches) in self.common_cases() + [
536 for (patterns, matches) in self.common_cases() + [
537 ([r'\*'], ['*']),
537 ([r'\*'], ['*']),
538 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
538 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
539 ([r'a\[012]'], ['a[012]']),
539 ([r'a\[012]'], ['a[012]']),
540 ]:
540 ]:
541 yield (self.check_match, patterns, matches)
541 yield (self.check_match, patterns, matches)
542
542
543 @skip_if_not_win32
543 @skip_if_not_win32
544 def test_match_windows(self):
544 def test_match_windows(self):
545 for (patterns, matches) in self.common_cases() + [
545 for (patterns, matches) in self.common_cases() + [
546 # In windows, backslash is interpreted as path
546 # In windows, backslash is interpreted as path
547 # separator. Therefore, you can't escape glob
547 # separator. Therefore, you can't escape glob
548 # using it.
548 # using it.
549 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
549 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
550 ([r'a\[012]'], [r'a\[012]']),
550 ([r'a\[012]'], [r'a\[012]']),
551 ]:
551 ]:
552 yield (self.check_match, patterns, matches)
552 yield (self.check_match, patterns, matches)
553
553
554
554
555 def test_unescape_glob():
555 def test_unescape_glob():
556 nt.assert_equals(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
556 nt.assert_equals(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
557 nt.assert_equals(path.unescape_glob(r'\\*'), r'\*')
557 nt.assert_equals(path.unescape_glob(r'\\*'), r'\*')
558 nt.assert_equals(path.unescape_glob(r'\\\*'), r'\*')
558 nt.assert_equals(path.unescape_glob(r'\\\*'), r'\*')
559 nt.assert_equals(path.unescape_glob(r'\\a'), r'\a')
559 nt.assert_equals(path.unescape_glob(r'\\a'), r'\a')
560 nt.assert_equals(path.unescape_glob(r'\a'), r'\a')
560 nt.assert_equals(path.unescape_glob(r'\a'), r'\a')
General Comments 0
You need to be logged in to leave comments. Login now