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