##// END OF EJS Templates
Format code
gousaiyang -
Show More
@@ -1,490 +1,490 b''
1 1 # encoding: utf-8
2 2 """
3 3 An application for IPython.
4 4
5 5 All top-level applications should use the classes in this module for
6 6 handling configuration and creating configurables.
7 7
8 8 The job of an :class:`Application` is to create the master configuration
9 9 object and then create the configurable objects, passing the config to them.
10 10 """
11 11
12 12 # Copyright (c) IPython Development Team.
13 13 # Distributed under the terms of the Modified BSD License.
14 14
15 15 import atexit
16 16 from copy import deepcopy
17 17 import glob
18 18 import logging
19 19 import os
20 20 import shutil
21 21 import sys
22 22
23 23 from pathlib import Path
24 24
25 25 from traitlets.config.application import Application, catch_config_error
26 26 from traitlets.config.loader import ConfigFileNotFound, PyFileConfigLoader
27 27 from IPython.core import release, crashhandler
28 28 from IPython.core.profiledir import ProfileDir, ProfileDirError
29 29 from IPython.paths import get_ipython_dir, get_ipython_package_dir
30 30 from IPython.utils.path import ensure_dir_exists
31 31 from traitlets import (
32 32 List, Unicode, Type, Bool, Set, Instance, Undefined,
33 33 default, observe,
34 34 )
35 35
36 36 if os.name == "nt":
37 37 programdata = os.environ.get("PROGRAMDATA", None)
38 38 if programdata is not None:
39 39 SYSTEM_CONFIG_DIRS = [str(Path(programdata) / "ipython")]
40 40 else: # PROGRAMDATA is not defined by default on XP.
41 41 SYSTEM_CONFIG_DIRS = []
42 42 else:
43 43 SYSTEM_CONFIG_DIRS = [
44 44 "/usr/local/etc/ipython",
45 45 "/etc/ipython",
46 46 ]
47 47
48 48
49 49 ENV_CONFIG_DIRS = []
50 50 _env_config_dir = os.path.join(sys.prefix, 'etc', 'ipython')
51 51 if _env_config_dir not in SYSTEM_CONFIG_DIRS:
52 52 # only add ENV_CONFIG if sys.prefix is not already included
53 53 ENV_CONFIG_DIRS.append(_env_config_dir)
54 54
55 55
56 56 _envvar = os.environ.get('IPYTHON_SUPPRESS_CONFIG_ERRORS')
57 57 if _envvar in {None, ''}:
58 58 IPYTHON_SUPPRESS_CONFIG_ERRORS = None
59 59 else:
60 60 if _envvar.lower() in {'1','true'}:
61 61 IPYTHON_SUPPRESS_CONFIG_ERRORS = True
62 62 elif _envvar.lower() in {'0','false'} :
63 63 IPYTHON_SUPPRESS_CONFIG_ERRORS = False
64 64 else:
65 65 sys.exit("Unsupported value for environment variable: 'IPYTHON_SUPPRESS_CONFIG_ERRORS' is set to '%s' which is none of {'0', '1', 'false', 'true', ''}."% _envvar )
66 66
67 67 # aliases and flags
68 68
69 69 base_aliases = {}
70 70 if isinstance(Application.aliases, dict):
71 71 # traitlets 5
72 72 base_aliases.update(Application.aliases)
73 73 base_aliases.update(
74 74 {
75 75 "profile-dir": "ProfileDir.location",
76 76 "profile": "BaseIPythonApplication.profile",
77 77 "ipython-dir": "BaseIPythonApplication.ipython_dir",
78 78 "log-level": "Application.log_level",
79 79 "config": "BaseIPythonApplication.extra_config_file",
80 80 }
81 81 )
82 82
83 83 base_flags = dict()
84 84 if isinstance(Application.flags, dict):
85 85 # traitlets 5
86 86 base_flags.update(Application.flags)
87 87 base_flags.update(
88 88 dict(
89 89 debug=(
90 90 {"Application": {"log_level": logging.DEBUG}},
91 91 "set log level to logging.DEBUG (maximize logging output)",
92 92 ),
93 93 quiet=(
94 94 {"Application": {"log_level": logging.CRITICAL}},
95 95 "set log level to logging.CRITICAL (minimize logging output)",
96 96 ),
97 97 init=(
98 98 {
99 99 "BaseIPythonApplication": {
100 100 "copy_config_files": True,
101 101 "auto_create": True,
102 102 }
103 103 },
104 104 """Initialize profile with default config files. This is equivalent
105 105 to running `ipython profile create <profile>` prior to startup.
106 106 """,
107 107 ),
108 108 )
109 109 )
110 110
111 111
112 112 class ProfileAwareConfigLoader(PyFileConfigLoader):
113 113 """A Python file config loader that is aware of IPython profiles."""
114 114 def load_subconfig(self, fname, path=None, profile=None):
115 115 if profile is not None:
116 116 try:
117 117 profile_dir = ProfileDir.find_profile_dir_by_name(
118 118 get_ipython_dir(),
119 119 profile,
120 120 )
121 121 except ProfileDirError:
122 122 return
123 123 path = profile_dir.location
124 124 return super(ProfileAwareConfigLoader, self).load_subconfig(fname, path=path)
125 125
126 126 class BaseIPythonApplication(Application):
127 127
128 128 name = u'ipython'
129 129 description = Unicode(u'IPython: an enhanced interactive Python shell.')
130 130 version = Unicode(release.version)
131 131
132 132 aliases = base_aliases
133 133 flags = base_flags
134 134 classes = List([ProfileDir])
135 135
136 136 # enable `load_subconfig('cfg.py', profile='name')`
137 137 python_config_loader_class = ProfileAwareConfigLoader
138 138
139 139 # Track whether the config_file has changed,
140 140 # because some logic happens only if we aren't using the default.
141 141 config_file_specified = Set()
142 142
143 143 config_file_name = Unicode()
144 144 @default('config_file_name')
145 145 def _config_file_name_default(self):
146 146 return self.name.replace('-','_') + u'_config.py'
147 147 @observe('config_file_name')
148 148 def _config_file_name_changed(self, change):
149 149 if change['new'] != change['old']:
150 150 self.config_file_specified.add(change['new'])
151 151
152 152 # The directory that contains IPython's builtin profiles.
153 153 builtin_profile_dir = Unicode(
154 154 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
155 155 )
156 156
157 157 config_file_paths = List(Unicode())
158 158 @default('config_file_paths')
159 159 def _config_file_paths_default(self):
160 160 return []
161 161
162 162 extra_config_file = Unicode(
163 163 help="""Path to an extra config file to load.
164 164
165 165 If specified, load this config file in addition to any other IPython config.
166 166 """).tag(config=True)
167 167 @observe('extra_config_file')
168 168 def _extra_config_file_changed(self, change):
169 169 old = change['old']
170 170 new = change['new']
171 171 try:
172 172 self.config_files.remove(old)
173 173 except ValueError:
174 174 pass
175 175 self.config_file_specified.add(new)
176 176 self.config_files.append(new)
177 177
178 178 profile = Unicode(u'default',
179 179 help="""The IPython profile to use."""
180 180 ).tag(config=True)
181 181
182 182 @observe('profile')
183 183 def _profile_changed(self, change):
184 184 self.builtin_profile_dir = os.path.join(
185 185 get_ipython_package_dir(), u'config', u'profile', change['new']
186 186 )
187 187
188 188 add_ipython_dir_to_sys_path = Bool(
189 189 False,
190 190 """Should the IPython profile directory be added to sys path ?
191 191
192 192 This option was non-existing before IPython 8.0, and ipython_dir was added to
193 193 sys path to allow import of extensions present there. This was historical
194 194 baggage from when pip did not exist. This now default to false,
195 195 but can be set to true for legacy reasons.
196 196 """,
197 197 ).tag(config=True)
198 198
199 199 ipython_dir = Unicode(
200 200 help="""
201 201 The name of the IPython directory. This directory is used for logging
202 202 configuration (through profiles), history storage, etc. The default
203 203 is usually $HOME/.ipython. This option can also be specified through
204 204 the environment variable IPYTHONDIR.
205 205 """
206 206 ).tag(config=True)
207 207 @default('ipython_dir')
208 208 def _ipython_dir_default(self):
209 209 d = get_ipython_dir()
210 210 self._ipython_dir_changed({
211 211 'name': 'ipython_dir',
212 212 'old': d,
213 213 'new': d,
214 214 })
215 215 return d
216 216
217 217 _in_init_profile_dir = False
218 218 profile_dir = Instance(ProfileDir, allow_none=True)
219 219 @default('profile_dir')
220 220 def _profile_dir_default(self):
221 221 # avoid recursion
222 222 if self._in_init_profile_dir:
223 223 return
224 224 # profile_dir requested early, force initialization
225 225 self.init_profile_dir()
226 226 return self.profile_dir
227 227
228 228 overwrite = Bool(False,
229 229 help="""Whether to overwrite existing config files when copying"""
230 230 ).tag(config=True)
231 231 auto_create = Bool(False,
232 232 help="""Whether to create profile dir if it doesn't exist"""
233 233 ).tag(config=True)
234 234
235 235 config_files = List(Unicode())
236 236 @default('config_files')
237 237 def _config_files_default(self):
238 238 return [self.config_file_name]
239 239
240 240 copy_config_files = Bool(False,
241 241 help="""Whether to install the default config files into the profile dir.
242 242 If a new profile is being created, and IPython contains config files for that
243 243 profile, then they will be staged into the new directory. Otherwise,
244 244 default config files will be automatically generated.
245 245 """).tag(config=True)
246 246
247 247 verbose_crash = Bool(False,
248 248 help="""Create a massive crash report when IPython encounters what may be an
249 249 internal error. The default is to append a short message to the
250 250 usual traceback""").tag(config=True)
251 251
252 252 # The class to use as the crash handler.
253 253 crash_handler_class = Type(crashhandler.CrashHandler)
254 254
255 255 @catch_config_error
256 256 def __init__(self, **kwargs):
257 257 super(BaseIPythonApplication, self).__init__(**kwargs)
258 258 # ensure current working directory exists
259 259 try:
260 260 os.getcwd()
261 261 except:
262 262 # exit if cwd doesn't exist
263 263 self.log.error("Current working directory doesn't exist.")
264 264 self.exit(1)
265 265
266 266 #-------------------------------------------------------------------------
267 267 # Various stages of Application creation
268 268 #-------------------------------------------------------------------------
269 269
270 270 def init_crash_handler(self):
271 271 """Create a crash handler, typically setting sys.excepthook to it."""
272 272 self.crash_handler = self.crash_handler_class(self)
273 273 sys.excepthook = self.excepthook
274 274 def unset_crashhandler():
275 275 sys.excepthook = sys.__excepthook__
276 276 atexit.register(unset_crashhandler)
277 277
278 278 def excepthook(self, etype, evalue, tb):
279 279 """this is sys.excepthook after init_crashhandler
280 280
281 281 set self.verbose_crash=True to use our full crashhandler, instead of
282 282 a regular traceback with a short message (crash_handler_lite)
283 283 """
284 284
285 285 if self.verbose_crash:
286 286 return self.crash_handler(etype, evalue, tb)
287 287 else:
288 288 return crashhandler.crash_handler_lite(etype, evalue, tb)
289 289
290 290 @observe('ipython_dir')
291 291 def _ipython_dir_changed(self, change):
292 292 old = change['old']
293 293 new = change['new']
294 294 if old is not Undefined:
295 295 str_old = os.path.abspath(old)
296 296 if str_old in sys.path:
297 297 sys.path.remove(str_old)
298 298 if self.add_ipython_dir_to_sys_path:
299 299 str_path = os.path.abspath(new)
300 300 sys.path.append(str_path)
301 301 ensure_dir_exists(new)
302 302 readme = os.path.join(new, "README")
303 303 readme_src = os.path.join(
304 304 get_ipython_package_dir(), "config", "profile", "README"
305 305 )
306 306 if not os.path.exists(readme) and os.path.exists(readme_src):
307 307 shutil.copy(readme_src, readme)
308 308 for d in ("extensions", "nbextensions"):
309 309 path = os.path.join(new, d)
310 310 try:
311 311 ensure_dir_exists(path)
312 312 except OSError as e:
313 313 # this will not be EEXIST
314 314 self.log.error("couldn't create path %s: %s", path, e)
315 315 self.log.debug("IPYTHONDIR set to: %s" % new)
316 316
317 317 def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS):
318 318 """Load the config file.
319 319
320 320 By default, errors in loading config are handled, and a warning
321 321 printed on screen. For testing, the suppress_errors option is set
322 322 to False, so errors will make tests fail.
323 323
324 324 `suppress_errors` default value is to be `None` in which case the
325 325 behavior default to the one of `traitlets.Application`.
326 326
327 327 The default value can be set :
328 328 - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive).
329 329 - to `True` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '1' or 'true' (case insensitive).
330 330 - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset.
331 331
332 332 Any other value are invalid, and will make IPython exit with a non-zero return code.
333 333 """
334 334
335 335
336 336 self.log.debug("Searching path %s for config files", self.config_file_paths)
337 337 base_config = 'ipython_config.py'
338 338 self.log.debug("Attempting to load config file: %s" %
339 339 base_config)
340 340 try:
341 341 if suppress_errors is not None:
342 342 old_value = Application.raise_config_file_errors
343 343 Application.raise_config_file_errors = not suppress_errors;
344 344 Application.load_config_file(
345 345 self,
346 346 base_config,
347 347 path=self.config_file_paths
348 348 )
349 349 except ConfigFileNotFound:
350 350 # ignore errors loading parent
351 351 self.log.debug("Config file %s not found", base_config)
352 352 pass
353 353 if suppress_errors is not None:
354 354 Application.raise_config_file_errors = old_value
355 355
356 356 for config_file_name in self.config_files:
357 357 if not config_file_name or config_file_name == base_config:
358 358 continue
359 359 self.log.debug("Attempting to load config file: %s" %
360 360 self.config_file_name)
361 361 try:
362 362 Application.load_config_file(
363 363 self,
364 364 config_file_name,
365 365 path=self.config_file_paths
366 366 )
367 367 except ConfigFileNotFound:
368 368 # Only warn if the default config file was NOT being used.
369 369 if config_file_name in self.config_file_specified:
370 370 msg = self.log.warning
371 371 else:
372 372 msg = self.log.debug
373 373 msg("Config file not found, skipping: %s", config_file_name)
374 374 except Exception:
375 375 # For testing purposes.
376 376 if not suppress_errors:
377 377 raise
378 378 self.log.warning("Error loading config file: %s" %
379 379 self.config_file_name, exc_info=True)
380 380
381 381 def init_profile_dir(self):
382 382 """initialize the profile dir"""
383 383 self._in_init_profile_dir = True
384 384 if self.profile_dir is not None:
385 385 # already ran
386 386 return
387 387 if 'ProfileDir.location' not in self.config:
388 388 # location not specified, find by profile name
389 389 try:
390 390 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
391 391 except ProfileDirError:
392 392 # not found, maybe create it (always create default profile)
393 393 if self.auto_create or self.profile == 'default':
394 394 try:
395 395 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
396 396 except ProfileDirError:
397 397 self.log.fatal("Could not create profile: %r"%self.profile)
398 398 self.exit(1)
399 399 else:
400 400 self.log.info("Created profile dir: %r"%p.location)
401 401 else:
402 402 self.log.fatal("Profile %r not found."%self.profile)
403 403 self.exit(1)
404 404 else:
405 405 self.log.debug(f"Using existing profile dir: {p.location!r}")
406 406 else:
407 407 location = self.config.ProfileDir.location
408 408 # location is fully specified
409 409 try:
410 410 p = ProfileDir.find_profile_dir(location, self.config)
411 411 except ProfileDirError:
412 412 # not found, maybe create it
413 413 if self.auto_create:
414 414 try:
415 415 p = ProfileDir.create_profile_dir(location, self.config)
416 416 except ProfileDirError:
417 417 self.log.fatal("Could not create profile directory: %r"%location)
418 418 self.exit(1)
419 419 else:
420 420 self.log.debug("Creating new profile dir: %r"%location)
421 421 else:
422 422 self.log.fatal("Profile directory %r not found."%location)
423 423 self.exit(1)
424 424 else:
425 425 self.log.debug(f"Using existing profile dir: {p.location!r}")
426 426 # if profile_dir is specified explicitly, set profile name
427 427 dir_name = os.path.basename(p.location)
428 428 if dir_name.startswith('profile_'):
429 429 self.profile = dir_name[8:]
430 430
431 431 self.profile_dir = p
432 432 self.config_file_paths.append(p.location)
433 433 self._in_init_profile_dir = False
434 434
435 435 def init_config_files(self):
436 436 """[optionally] copy default config files into profile dir."""
437 437 self.config_file_paths.extend(ENV_CONFIG_DIRS)
438 438 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
439 439 # copy config files
440 440 path = Path(self.builtin_profile_dir)
441 441 if self.copy_config_files:
442 442 src = self.profile
443 443
444 444 cfg = self.config_file_name
445 445 if path and (path / cfg).exists():
446 446 self.log.warning(
447 447 "Staging %r from %s into %r [overwrite=%s]"
448 448 % (cfg, src, self.profile_dir.location, self.overwrite)
449 449 )
450 450 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
451 451 else:
452 452 self.stage_default_config_file()
453 453 else:
454 454 # Still stage *bundled* config files, but not generated ones
455 455 # This is necessary for `ipython profile=sympy` to load the profile
456 456 # on the first go
457 457 files = path.glob("*.py")
458 458 for fullpath in files:
459 459 cfg = fullpath.name
460 460 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
461 461 # file was copied
462 462 self.log.warning("Staging bundled %s from %s into %r"%(
463 463 cfg, self.profile, self.profile_dir.location)
464 464 )
465 465
466 466
467 467 def stage_default_config_file(self):
468 468 """auto generate default config file, and stage it into the profile."""
469 469 s = self.generate_config_file()
470 470 config_file = Path(self.profile_dir.location) / self.config_file_name
471 471 if self.overwrite or not config_file.exists():
472 472 self.log.warning("Generating default config file: %r" % (config_file))
473 config_file.write_text(s, encoding='utf-8')
473 config_file.write_text(s, encoding="utf-8")
474 474
475 475 @catch_config_error
476 476 def initialize(self, argv=None):
477 477 # don't hook up crash handler before parsing command-line
478 478 self.parse_command_line(argv)
479 479 self.init_crash_handler()
480 480 if self.subapp is not None:
481 481 # stop here if subapp is taking over
482 482 return
483 483 # save a copy of CLI config to re-load after config files
484 484 # so that it has highest priority
485 485 cl_config = deepcopy(self.config)
486 486 self.init_profile_dir()
487 487 self.init_config_files()
488 488 self.load_config_file()
489 489 # enforce cl-opts override configfile opts:
490 490 self.update_config(cl_config)
@@ -1,237 +1,237 b''
1 1 # encoding: utf-8
2 2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
3 3
4 4 Authors:
5 5
6 6 * Fernando Perez
7 7 * Brian E. Granger
8 8 """
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
12 12 # Copyright (C) 2008-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 import os
23 23 import sys
24 24 import traceback
25 25 from pprint import pformat
26 26 from pathlib import Path
27 27
28 28 from IPython.core import ultratb
29 29 from IPython.core.release import author_email
30 30 from IPython.utils.sysinfo import sys_info
31 31 from IPython.utils.py3compat import input
32 32
33 33 from IPython.core.release import __version__ as version
34 34
35 35 from typing import Optional
36 36
37 37 #-----------------------------------------------------------------------------
38 38 # Code
39 39 #-----------------------------------------------------------------------------
40 40
41 41 # Template for the user message.
42 42 _default_message_template = """\
43 43 Oops, {app_name} crashed. We do our best to make it stable, but...
44 44
45 45 A crash report was automatically generated with the following information:
46 46 - A verbatim copy of the crash traceback.
47 47 - A copy of your input history during this session.
48 48 - Data on your current {app_name} configuration.
49 49
50 50 It was left in the file named:
51 51 \t'{crash_report_fname}'
52 52 If you can email this file to the developers, the information in it will help
53 53 them in understanding and correcting the problem.
54 54
55 55 You can mail it to: {contact_name} at {contact_email}
56 56 with the subject '{app_name} Crash Report'.
57 57
58 58 If you want to do it now, the following command will work (under Unix):
59 59 mail -s '{app_name} Crash Report' {contact_email} < {crash_report_fname}
60 60
61 61 In your email, please also include information about:
62 62 - The operating system under which the crash happened: Linux, macOS, Windows,
63 63 other, and which exact version (for example: Ubuntu 16.04.3, macOS 10.13.2,
64 64 Windows 10 Pro), and whether it is 32-bit or 64-bit;
65 65 - How {app_name} was installed: using pip or conda, from GitHub, as part of
66 66 a Docker container, or other, providing more detail if possible;
67 67 - How to reproduce the crash: what exact sequence of instructions can one
68 68 input to get the same crash? Ideally, find a minimal yet complete sequence
69 69 of instructions that yields the crash.
70 70
71 71 To ensure accurate tracking of this issue, please file a report about it at:
72 72 {bug_tracker}
73 73 """
74 74
75 75 _lite_message_template = """
76 76 If you suspect this is an IPython {version} bug, please report it at:
77 77 https://github.com/ipython/ipython/issues
78 78 or send an email to the mailing list at {email}
79 79
80 80 You can print a more detailed traceback right now with "%tb", or use "%debug"
81 81 to interactively debug it.
82 82
83 83 Extra-detailed tracebacks for bug-reporting purposes can be enabled via:
84 84 {config}Application.verbose_crash=True
85 85 """
86 86
87 87
88 88 class CrashHandler(object):
89 89 """Customizable crash handlers for IPython applications.
90 90
91 91 Instances of this class provide a :meth:`__call__` method which can be
92 92 used as a ``sys.excepthook``. The :meth:`__call__` signature is::
93 93
94 94 def __call__(self, etype, evalue, etb)
95 95 """
96 96
97 97 message_template = _default_message_template
98 98 section_sep = '\n\n'+'*'*75+'\n\n'
99 99
100 100 def __init__(
101 101 self,
102 102 app,
103 103 contact_name: Optional[str] = None,
104 104 contact_email: Optional[str] = None,
105 105 bug_tracker: Optional[str] = None,
106 106 show_crash_traceback: bool = True,
107 107 call_pdb: bool = False,
108 108 ):
109 109 """Create a new crash handler
110 110
111 111 Parameters
112 112 ----------
113 113 app : Application
114 114 A running :class:`Application` instance, which will be queried at
115 115 crash time for internal information.
116 116 contact_name : str
117 117 A string with the name of the person to contact.
118 118 contact_email : str
119 119 A string with the email address of the contact.
120 120 bug_tracker : str
121 121 A string with the URL for your project's bug tracker.
122 122 show_crash_traceback : bool
123 123 If false, don't print the crash traceback on stderr, only generate
124 124 the on-disk report
125 125 call_pdb
126 126 Whether to call pdb on crash
127 127
128 128 Attributes
129 129 ----------
130 130 These instances contain some non-argument attributes which allow for
131 131 further customization of the crash handler's behavior. Please see the
132 132 source for further details.
133 133
134 134 """
135 135 self.crash_report_fname = "Crash_report_%s.txt" % app.name
136 136 self.app = app
137 137 self.call_pdb = call_pdb
138 138 #self.call_pdb = True # dbg
139 139 self.show_crash_traceback = show_crash_traceback
140 140 self.info = dict(app_name = app.name,
141 141 contact_name = contact_name,
142 142 contact_email = contact_email,
143 143 bug_tracker = bug_tracker,
144 144 crash_report_fname = self.crash_report_fname)
145 145
146 146
147 147 def __call__(self, etype, evalue, etb):
148 148 """Handle an exception, call for compatible with sys.excepthook"""
149 149
150 150 # do not allow the crash handler to be called twice without reinstalling it
151 151 # this prevents unlikely errors in the crash handling from entering an
152 152 # infinite loop.
153 153 sys.excepthook = sys.__excepthook__
154 154
155 155 # Report tracebacks shouldn't use color in general (safer for users)
156 156 color_scheme = 'NoColor'
157 157
158 158 # Use this ONLY for developer debugging (keep commented out for release)
159 159 #color_scheme = 'Linux' # dbg
160 160 try:
161 161 rptdir = self.app.ipython_dir
162 162 except:
163 163 rptdir = Path.cwd()
164 164 if rptdir is None or not Path.is_dir(rptdir):
165 165 rptdir = Path.cwd()
166 166 report_name = rptdir / self.crash_report_fname
167 167 # write the report filename into the instance dict so it can get
168 168 # properly expanded out in the user message template
169 169 self.crash_report_fname = report_name
170 170 self.info['crash_report_fname'] = report_name
171 171 TBhandler = ultratb.VerboseTB(
172 172 color_scheme=color_scheme,
173 173 long_header=1,
174 174 call_pdb=self.call_pdb,
175 175 )
176 176 if self.call_pdb:
177 177 TBhandler(etype,evalue,etb)
178 178 return
179 179 else:
180 180 traceback = TBhandler.text(etype,evalue,etb,context=31)
181 181
182 182 # print traceback to screen
183 183 if self.show_crash_traceback:
184 184 print(traceback, file=sys.stderr)
185 185
186 186 # and generate a complete report on disk
187 187 try:
188 report = open(report_name, 'w', encoding='utf-8')
188 report = open(report_name, "w", encoding="utf-8")
189 189 except:
190 190 print('Could not create crash report on disk.', file=sys.stderr)
191 191 return
192 192
193 193 with report:
194 194 # Inform user on stderr of what happened
195 195 print('\n'+'*'*70+'\n', file=sys.stderr)
196 196 print(self.message_template.format(**self.info), file=sys.stderr)
197 197
198 198 # Construct report on disk
199 199 report.write(self.make_report(traceback))
200 200
201 201 input("Hit <Enter> to quit (your terminal may close):")
202 202
203 203 def make_report(self,traceback):
204 204 """Return a string containing a crash report."""
205 205
206 206 sec_sep = self.section_sep
207 207
208 208 report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
209 209 rpt_add = report.append
210 210 rpt_add(sys_info())
211 211
212 212 try:
213 213 config = pformat(self.app.config)
214 214 rpt_add(sec_sep)
215 215 rpt_add('Application name: %s\n\n' % self.app_name)
216 216 rpt_add('Current user configuration structure:\n\n')
217 217 rpt_add(config)
218 218 except:
219 219 pass
220 220 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
221 221
222 222 return ''.join(report)
223 223
224 224
225 225 def crash_handler_lite(etype, evalue, tb):
226 226 """a light excepthook, adding a small message to the usual traceback"""
227 227 traceback.print_exception(etype, evalue, tb)
228 228
229 229 from IPython.core.interactiveshell import InteractiveShell
230 230 if InteractiveShell.initialized():
231 231 # we are in a Shell environment, give %magic example
232 232 config = "%config "
233 233 else:
234 234 # we are not in a shell, show generic config
235 235 config = "c."
236 236 print(_lite_message_template.format(email=author_email, config=config, version=version), file=sys.stderr)
237 237
@@ -1,1274 +1,1277 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Top-level display functions for displaying object in different formats."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7
8 8 from binascii import b2a_base64, hexlify
9 9 import html
10 10 import json
11 11 import mimetypes
12 12 import os
13 13 import struct
14 14 import warnings
15 15 from copy import deepcopy
16 16 from os.path import splitext
17 17 from pathlib import Path, PurePath
18 18
19 19 from IPython.utils.py3compat import cast_unicode
20 20 from IPython.testing.skipdoctest import skip_doctest
21 21 from . import display_functions
22 22
23 23
24 24 __all__ = ['display_pretty', 'display_html', 'display_markdown',
25 25 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
26 26 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
27 27 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON',
28 28 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats',
29 29 'set_matplotlib_close',
30 30 'Video']
31 31
32 32 _deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"]
33 33
34 34 __all__ = __all__ + _deprecated_names
35 35
36 36
37 37 # ----- warn to import from IPython.display -----
38 38
39 39 from warnings import warn
40 40
41 41
42 42 def __getattr__(name):
43 43 if name in _deprecated_names:
44 44 warn(f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython display", DeprecationWarning, stacklevel=2)
45 45 return getattr(display_functions, name)
46 46
47 47 if name in globals().keys():
48 48 return globals()[name]
49 49 else:
50 50 raise AttributeError(f"module {__name__} has no attribute {name}")
51 51
52 52
53 53 #-----------------------------------------------------------------------------
54 54 # utility functions
55 55 #-----------------------------------------------------------------------------
56 56
57 57 def _safe_exists(path):
58 58 """Check path, but don't let exceptions raise"""
59 59 try:
60 60 return os.path.exists(path)
61 61 except Exception:
62 62 return False
63 63
64 64
65 65 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
66 66 """internal implementation of all display_foo methods
67 67
68 68 Parameters
69 69 ----------
70 70 mimetype : str
71 71 The mimetype to be published (e.g. 'image/png')
72 72 *objs : object
73 73 The Python objects to display, or if raw=True raw text data to
74 74 display.
75 75 raw : bool
76 76 Are the data objects raw data or Python objects that need to be
77 77 formatted before display? [default: False]
78 78 metadata : dict (optional)
79 79 Metadata to be associated with the specific mimetype output.
80 80 """
81 81 if metadata:
82 82 metadata = {mimetype: metadata}
83 83 if raw:
84 84 # turn list of pngdata into list of { 'image/png': pngdata }
85 85 objs = [ {mimetype: obj} for obj in objs ]
86 86 display_functions.display(*objs, raw=raw, metadata=metadata, include=[mimetype])
87 87
88 88 #-----------------------------------------------------------------------------
89 89 # Main functions
90 90 #-----------------------------------------------------------------------------
91 91
92 92
93 93 def display_pretty(*objs, **kwargs):
94 94 """Display the pretty (default) representation of an object.
95 95
96 96 Parameters
97 97 ----------
98 98 *objs : object
99 99 The Python objects to display, or if raw=True raw text data to
100 100 display.
101 101 raw : bool
102 102 Are the data objects raw data or Python objects that need to be
103 103 formatted before display? [default: False]
104 104 metadata : dict (optional)
105 105 Metadata to be associated with the specific mimetype output.
106 106 """
107 107 _display_mimetype('text/plain', objs, **kwargs)
108 108
109 109
110 110 def display_html(*objs, **kwargs):
111 111 """Display the HTML representation of an object.
112 112
113 113 Note: If raw=False and the object does not have a HTML
114 114 representation, no HTML will be shown.
115 115
116 116 Parameters
117 117 ----------
118 118 *objs : object
119 119 The Python objects to display, or if raw=True raw HTML data to
120 120 display.
121 121 raw : bool
122 122 Are the data objects raw data or Python objects that need to be
123 123 formatted before display? [default: False]
124 124 metadata : dict (optional)
125 125 Metadata to be associated with the specific mimetype output.
126 126 """
127 127 _display_mimetype('text/html', objs, **kwargs)
128 128
129 129
130 130 def display_markdown(*objs, **kwargs):
131 131 """Displays the Markdown representation of an object.
132 132
133 133 Parameters
134 134 ----------
135 135 *objs : object
136 136 The Python objects to display, or if raw=True raw markdown data to
137 137 display.
138 138 raw : bool
139 139 Are the data objects raw data or Python objects that need to be
140 140 formatted before display? [default: False]
141 141 metadata : dict (optional)
142 142 Metadata to be associated with the specific mimetype output.
143 143 """
144 144
145 145 _display_mimetype('text/markdown', objs, **kwargs)
146 146
147 147
148 148 def display_svg(*objs, **kwargs):
149 149 """Display the SVG representation of an object.
150 150
151 151 Parameters
152 152 ----------
153 153 *objs : object
154 154 The Python objects to display, or if raw=True raw svg data to
155 155 display.
156 156 raw : bool
157 157 Are the data objects raw data or Python objects that need to be
158 158 formatted before display? [default: False]
159 159 metadata : dict (optional)
160 160 Metadata to be associated with the specific mimetype output.
161 161 """
162 162 _display_mimetype('image/svg+xml', objs, **kwargs)
163 163
164 164
165 165 def display_png(*objs, **kwargs):
166 166 """Display the PNG representation of an object.
167 167
168 168 Parameters
169 169 ----------
170 170 *objs : object
171 171 The Python objects to display, or if raw=True raw png data to
172 172 display.
173 173 raw : bool
174 174 Are the data objects raw data or Python objects that need to be
175 175 formatted before display? [default: False]
176 176 metadata : dict (optional)
177 177 Metadata to be associated with the specific mimetype output.
178 178 """
179 179 _display_mimetype('image/png', objs, **kwargs)
180 180
181 181
182 182 def display_jpeg(*objs, **kwargs):
183 183 """Display the JPEG representation of an object.
184 184
185 185 Parameters
186 186 ----------
187 187 *objs : object
188 188 The Python objects to display, or if raw=True raw JPEG data to
189 189 display.
190 190 raw : bool
191 191 Are the data objects raw data or Python objects that need to be
192 192 formatted before display? [default: False]
193 193 metadata : dict (optional)
194 194 Metadata to be associated with the specific mimetype output.
195 195 """
196 196 _display_mimetype('image/jpeg', objs, **kwargs)
197 197
198 198
199 199 def display_latex(*objs, **kwargs):
200 200 """Display the LaTeX representation of an object.
201 201
202 202 Parameters
203 203 ----------
204 204 *objs : object
205 205 The Python objects to display, or if raw=True raw latex data to
206 206 display.
207 207 raw : bool
208 208 Are the data objects raw data or Python objects that need to be
209 209 formatted before display? [default: False]
210 210 metadata : dict (optional)
211 211 Metadata to be associated with the specific mimetype output.
212 212 """
213 213 _display_mimetype('text/latex', objs, **kwargs)
214 214
215 215
216 216 def display_json(*objs, **kwargs):
217 217 """Display the JSON representation of an object.
218 218
219 219 Note that not many frontends support displaying JSON.
220 220
221 221 Parameters
222 222 ----------
223 223 *objs : object
224 224 The Python objects to display, or if raw=True raw json data to
225 225 display.
226 226 raw : bool
227 227 Are the data objects raw data or Python objects that need to be
228 228 formatted before display? [default: False]
229 229 metadata : dict (optional)
230 230 Metadata to be associated with the specific mimetype output.
231 231 """
232 232 _display_mimetype('application/json', objs, **kwargs)
233 233
234 234
235 235 def display_javascript(*objs, **kwargs):
236 236 """Display the Javascript representation of an object.
237 237
238 238 Parameters
239 239 ----------
240 240 *objs : object
241 241 The Python objects to display, or if raw=True raw javascript data to
242 242 display.
243 243 raw : bool
244 244 Are the data objects raw data or Python objects that need to be
245 245 formatted before display? [default: False]
246 246 metadata : dict (optional)
247 247 Metadata to be associated with the specific mimetype output.
248 248 """
249 249 _display_mimetype('application/javascript', objs, **kwargs)
250 250
251 251
252 252 def display_pdf(*objs, **kwargs):
253 253 """Display the PDF representation of an object.
254 254
255 255 Parameters
256 256 ----------
257 257 *objs : object
258 258 The Python objects to display, or if raw=True raw javascript data to
259 259 display.
260 260 raw : bool
261 261 Are the data objects raw data or Python objects that need to be
262 262 formatted before display? [default: False]
263 263 metadata : dict (optional)
264 264 Metadata to be associated with the specific mimetype output.
265 265 """
266 266 _display_mimetype('application/pdf', objs, **kwargs)
267 267
268 268
269 269 #-----------------------------------------------------------------------------
270 270 # Smart classes
271 271 #-----------------------------------------------------------------------------
272 272
273 273
274 274 class DisplayObject(object):
275 275 """An object that wraps data to be displayed."""
276 276
277 277 _read_flags = 'r'
278 278 _show_mem_addr = False
279 279 metadata = None
280 280
281 281 def __init__(self, data=None, url=None, filename=None, metadata=None):
282 282 """Create a display object given raw data.
283 283
284 284 When this object is returned by an expression or passed to the
285 285 display function, it will result in the data being displayed
286 286 in the frontend. The MIME type of the data should match the
287 287 subclasses used, so the Png subclass should be used for 'image/png'
288 288 data. If the data is a URL, the data will first be downloaded
289 289 and then displayed. If
290 290
291 291 Parameters
292 292 ----------
293 293 data : unicode, str or bytes
294 294 The raw data or a URL or file to load the data from
295 295 url : unicode
296 296 A URL to download the data from.
297 297 filename : unicode
298 298 Path to a local file to load the data from.
299 299 metadata : dict
300 300 Dict of metadata associated to be the object when displayed
301 301 """
302 302 if isinstance(data, (Path, PurePath)):
303 303 data = str(data)
304 304
305 305 if data is not None and isinstance(data, str):
306 306 if data.startswith('http') and url is None:
307 307 url = data
308 308 filename = None
309 309 data = None
310 310 elif _safe_exists(data) and filename is None:
311 311 url = None
312 312 filename = data
313 313 data = None
314 314
315 315 self.url = url
316 316 self.filename = filename
317 317 # because of @data.setter methods in
318 318 # subclasses ensure url and filename are set
319 319 # before assigning to self.data
320 320 self.data = data
321 321
322 322 if metadata is not None:
323 323 self.metadata = metadata
324 324 elif self.metadata is None:
325 325 self.metadata = {}
326 326
327 327 self.reload()
328 328 self._check_data()
329 329
330 330 def __repr__(self):
331 331 if not self._show_mem_addr:
332 332 cls = self.__class__
333 333 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
334 334 else:
335 335 r = super(DisplayObject, self).__repr__()
336 336 return r
337 337
338 338 def _check_data(self):
339 339 """Override in subclasses if there's something to check."""
340 340 pass
341 341
342 342 def _data_and_metadata(self):
343 343 """shortcut for returning metadata with shape information, if defined"""
344 344 if self.metadata:
345 345 return self.data, deepcopy(self.metadata)
346 346 else:
347 347 return self.data
348 348
349 349 def reload(self):
350 350 """Reload the raw data from file or URL."""
351 351 if self.filename is not None:
352 encoding = None if 'b' in self._read_flags else 'utf-8'
352 encoding = None if "b" in self._read_flags else "utf-8"
353 353 with open(self.filename, self._read_flags, encoding=encoding) as f:
354 354 self.data = f.read()
355 355 elif self.url is not None:
356 356 # Deferred import
357 357 from urllib.request import urlopen
358 358 response = urlopen(self.url)
359 359 data = response.read()
360 360 # extract encoding from header, if there is one:
361 361 encoding = None
362 362 if 'content-type' in response.headers:
363 363 for sub in response.headers['content-type'].split(';'):
364 364 sub = sub.strip()
365 365 if sub.startswith('charset'):
366 366 encoding = sub.split('=')[-1].strip()
367 367 break
368 368 if 'content-encoding' in response.headers:
369 369 # TODO: do deflate?
370 370 if 'gzip' in response.headers['content-encoding']:
371 371 import gzip
372 372 from io import BytesIO
373
373 374 # assume utf-8 if encoding is not specified
374 with gzip.open(BytesIO(data), 'rt', encoding=encoding or 'utf-8') as fp:
375 with gzip.open(
376 BytesIO(data), "rt", encoding=encoding or "utf-8"
377 ) as fp:
375 378 encoding = None
376 379 data = fp.read()
377 380
378 381 # decode data, if an encoding was specified
379 382 # We only touch self.data once since
380 383 # subclasses such as SVG have @data.setter methods
381 384 # that transform self.data into ... well svg.
382 385 if encoding:
383 386 self.data = data.decode(encoding, 'replace')
384 387 else:
385 388 self.data = data
386 389
387 390
388 391 class TextDisplayObject(DisplayObject):
389 392 """Validate that display data is text"""
390 393 def _check_data(self):
391 394 if self.data is not None and not isinstance(self.data, str):
392 395 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
393 396
394 397 class Pretty(TextDisplayObject):
395 398
396 399 def _repr_pretty_(self, pp, cycle):
397 400 return pp.text(self.data)
398 401
399 402
400 403 class HTML(TextDisplayObject):
401 404
402 405 def __init__(self, data=None, url=None, filename=None, metadata=None):
403 406 def warn():
404 407 if not data:
405 408 return False
406 409
407 410 #
408 411 # Avoid calling lower() on the entire data, because it could be a
409 412 # long string and we're only interested in its beginning and end.
410 413 #
411 414 prefix = data[:10].lower()
412 415 suffix = data[-10:].lower()
413 416 return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
414 417
415 418 if warn():
416 419 warnings.warn("Consider using IPython.display.IFrame instead")
417 420 super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
418 421
419 422 def _repr_html_(self):
420 423 return self._data_and_metadata()
421 424
422 425 def __html__(self):
423 426 """
424 427 This method exists to inform other HTML-using modules (e.g. Markupsafe,
425 428 htmltag, etc) that this object is HTML and does not need things like
426 429 special characters (<>&) escaped.
427 430 """
428 431 return self._repr_html_()
429 432
430 433
431 434 class Markdown(TextDisplayObject):
432 435
433 436 def _repr_markdown_(self):
434 437 return self._data_and_metadata()
435 438
436 439
437 440 class Math(TextDisplayObject):
438 441
439 442 def _repr_latex_(self):
440 443 s = r"$\displaystyle %s$" % self.data.strip('$')
441 444 if self.metadata:
442 445 return s, deepcopy(self.metadata)
443 446 else:
444 447 return s
445 448
446 449
447 450 class Latex(TextDisplayObject):
448 451
449 452 def _repr_latex_(self):
450 453 return self._data_and_metadata()
451 454
452 455
453 456 class SVG(DisplayObject):
454 457 """Embed an SVG into the display.
455 458
456 459 Note if you just want to view a svg image via a URL use `:class:Image` with
457 460 a url=URL keyword argument.
458 461 """
459 462
460 463 _read_flags = 'rb'
461 464 # wrap data in a property, which extracts the <svg> tag, discarding
462 465 # document headers
463 466 _data = None
464 467
465 468 @property
466 469 def data(self):
467 470 return self._data
468 471
469 472 @data.setter
470 473 def data(self, svg):
471 474 if svg is None:
472 475 self._data = None
473 476 return
474 477 # parse into dom object
475 478 from xml.dom import minidom
476 479 x = minidom.parseString(svg)
477 480 # get svg tag (should be 1)
478 481 found_svg = x.getElementsByTagName('svg')
479 482 if found_svg:
480 483 svg = found_svg[0].toxml()
481 484 else:
482 485 # fallback on the input, trust the user
483 486 # but this is probably an error.
484 487 pass
485 488 svg = cast_unicode(svg)
486 489 self._data = svg
487 490
488 491 def _repr_svg_(self):
489 492 return self._data_and_metadata()
490 493
491 494 class ProgressBar(DisplayObject):
492 495 """Progressbar supports displaying a progressbar like element
493 496 """
494 497 def __init__(self, total):
495 498 """Creates a new progressbar
496 499
497 500 Parameters
498 501 ----------
499 502 total : int
500 503 maximum size of the progressbar
501 504 """
502 505 self.total = total
503 506 self._progress = 0
504 507 self.html_width = '60ex'
505 508 self.text_width = 60
506 509 self._display_id = hexlify(os.urandom(8)).decode('ascii')
507 510
508 511 def __repr__(self):
509 512 fraction = self.progress / self.total
510 513 filled = '=' * int(fraction * self.text_width)
511 514 rest = ' ' * (self.text_width - len(filled))
512 515 return '[{}{}] {}/{}'.format(
513 516 filled, rest,
514 517 self.progress, self.total,
515 518 )
516 519
517 520 def _repr_html_(self):
518 521 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
519 522 self.html_width, self.total, self.progress)
520 523
521 524 def display(self):
522 525 display_functions.display(self, display_id=self._display_id)
523 526
524 527 def update(self):
525 528 display_functions.display(self, display_id=self._display_id, update=True)
526 529
527 530 @property
528 531 def progress(self):
529 532 return self._progress
530 533
531 534 @progress.setter
532 535 def progress(self, value):
533 536 self._progress = value
534 537 self.update()
535 538
536 539 def __iter__(self):
537 540 self.display()
538 541 self._progress = -1 # First iteration is 0
539 542 return self
540 543
541 544 def __next__(self):
542 545 """Returns current value and increments display by one."""
543 546 self.progress += 1
544 547 if self.progress < self.total:
545 548 return self.progress
546 549 else:
547 550 raise StopIteration()
548 551
549 552 class JSON(DisplayObject):
550 553 """JSON expects a JSON-able dict or list
551 554
552 555 not an already-serialized JSON string.
553 556
554 557 Scalar types (None, number, string) are not allowed, only dict or list containers.
555 558 """
556 559 # wrap data in a property, which warns about passing already-serialized JSON
557 560 _data = None
558 561 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs):
559 562 """Create a JSON display object given raw data.
560 563
561 564 Parameters
562 565 ----------
563 566 data : dict or list
564 567 JSON data to display. Not an already-serialized JSON string.
565 568 Scalar types (None, number, string) are not allowed, only dict
566 569 or list containers.
567 570 url : unicode
568 571 A URL to download the data from.
569 572 filename : unicode
570 573 Path to a local file to load the data from.
571 574 expanded : boolean
572 575 Metadata to control whether a JSON display component is expanded.
573 576 metadata : dict
574 577 Specify extra metadata to attach to the json display object.
575 578 root : str
576 579 The name of the root element of the JSON tree
577 580 """
578 581 self.metadata = {
579 582 'expanded': expanded,
580 583 'root': root,
581 584 }
582 585 if metadata:
583 586 self.metadata.update(metadata)
584 587 if kwargs:
585 588 self.metadata.update(kwargs)
586 589 super(JSON, self).__init__(data=data, url=url, filename=filename)
587 590
588 591 def _check_data(self):
589 592 if self.data is not None and not isinstance(self.data, (dict, list)):
590 593 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
591 594
592 595 @property
593 596 def data(self):
594 597 return self._data
595 598
596 599 @data.setter
597 600 def data(self, data):
598 601 if isinstance(data, (Path, PurePath)):
599 602 data = str(data)
600 603
601 604 if isinstance(data, str):
602 605 if self.filename is None and self.url is None:
603 606 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
604 607 data = json.loads(data)
605 608 self._data = data
606 609
607 610 def _data_and_metadata(self):
608 611 return self.data, self.metadata
609 612
610 613 def _repr_json_(self):
611 614 return self._data_and_metadata()
612 615
613 616 _css_t = """var link = document.createElement("link");
614 617 link.ref = "stylesheet";
615 618 link.type = "text/css";
616 619 link.href = "%s";
617 620 document.head.appendChild(link);
618 621 """
619 622
620 623 _lib_t1 = """new Promise(function(resolve, reject) {
621 624 var script = document.createElement("script");
622 625 script.onload = resolve;
623 626 script.onerror = reject;
624 627 script.src = "%s";
625 628 document.head.appendChild(script);
626 629 }).then(() => {
627 630 """
628 631
629 632 _lib_t2 = """
630 633 });"""
631 634
632 635 class GeoJSON(JSON):
633 636 """GeoJSON expects JSON-able dict
634 637
635 638 not an already-serialized JSON string.
636 639
637 640 Scalar types (None, number, string) are not allowed, only dict containers.
638 641 """
639 642
640 643 def __init__(self, *args, **kwargs):
641 644 """Create a GeoJSON display object given raw data.
642 645
643 646 Parameters
644 647 ----------
645 648 data : dict or list
646 649 VegaLite data. Not an already-serialized JSON string.
647 650 Scalar types (None, number, string) are not allowed, only dict
648 651 or list containers.
649 652 url_template : string
650 653 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
651 654 layer_options : dict
652 655 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
653 656 url : unicode
654 657 A URL to download the data from.
655 658 filename : unicode
656 659 Path to a local file to load the data from.
657 660 metadata : dict
658 661 Specify extra metadata to attach to the json display object.
659 662
660 663 Examples
661 664 --------
662 665 The following will display an interactive map of Mars with a point of
663 666 interest on frontend that do support GeoJSON display.
664 667
665 668 >>> from IPython.display import GeoJSON
666 669
667 670 >>> GeoJSON(data={
668 671 ... "type": "Feature",
669 672 ... "geometry": {
670 673 ... "type": "Point",
671 674 ... "coordinates": [-81.327, 296.038]
672 675 ... }
673 676 ... },
674 677 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
675 678 ... layer_options={
676 679 ... "basemap_id": "celestia_mars-shaded-16k_global",
677 680 ... "attribution" : "Celestia/praesepe",
678 681 ... "minZoom" : 0,
679 682 ... "maxZoom" : 18,
680 683 ... })
681 684 <IPython.core.display.GeoJSON object>
682 685
683 686 In the terminal IPython, you will only see the text representation of
684 687 the GeoJSON object.
685 688
686 689 """
687 690
688 691 super(GeoJSON, self).__init__(*args, **kwargs)
689 692
690 693
691 694 def _ipython_display_(self):
692 695 bundle = {
693 696 'application/geo+json': self.data,
694 697 'text/plain': '<IPython.display.GeoJSON object>'
695 698 }
696 699 metadata = {
697 700 'application/geo+json': self.metadata
698 701 }
699 702 display_functions.display(bundle, metadata=metadata, raw=True)
700 703
701 704 class Javascript(TextDisplayObject):
702 705
703 706 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
704 707 """Create a Javascript display object given raw data.
705 708
706 709 When this object is returned by an expression or passed to the
707 710 display function, it will result in the data being displayed
708 711 in the frontend. If the data is a URL, the data will first be
709 712 downloaded and then displayed.
710 713
711 714 In the Notebook, the containing element will be available as `element`,
712 715 and jQuery will be available. Content appended to `element` will be
713 716 visible in the output area.
714 717
715 718 Parameters
716 719 ----------
717 720 data : unicode, str or bytes
718 721 The Javascript source code or a URL to download it from.
719 722 url : unicode
720 723 A URL to download the data from.
721 724 filename : unicode
722 725 Path to a local file to load the data from.
723 726 lib : list or str
724 727 A sequence of Javascript library URLs to load asynchronously before
725 728 running the source code. The full URLs of the libraries should
726 729 be given. A single Javascript library URL can also be given as a
727 730 string.
728 731 css : list or str
729 732 A sequence of css files to load before running the source code.
730 733 The full URLs of the css files should be given. A single css URL
731 734 can also be given as a string.
732 735 """
733 736 if isinstance(lib, str):
734 737 lib = [lib]
735 738 elif lib is None:
736 739 lib = []
737 740 if isinstance(css, str):
738 741 css = [css]
739 742 elif css is None:
740 743 css = []
741 744 if not isinstance(lib, (list,tuple)):
742 745 raise TypeError('expected sequence, got: %r' % lib)
743 746 if not isinstance(css, (list,tuple)):
744 747 raise TypeError('expected sequence, got: %r' % css)
745 748 self.lib = lib
746 749 self.css = css
747 750 super(Javascript, self).__init__(data=data, url=url, filename=filename)
748 751
749 752 def _repr_javascript_(self):
750 753 r = ''
751 754 for c in self.css:
752 755 r += _css_t % c
753 756 for l in self.lib:
754 757 r += _lib_t1 % l
755 758 r += self.data
756 759 r += _lib_t2*len(self.lib)
757 760 return r
758 761
759 762 # constants for identifying png/jpeg data
760 763 _PNG = b'\x89PNG\r\n\x1a\n'
761 764 _JPEG = b'\xff\xd8'
762 765
763 766 def _pngxy(data):
764 767 """read the (width, height) from a PNG header"""
765 768 ihdr = data.index(b'IHDR')
766 769 # next 8 bytes are width/height
767 770 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
768 771
769 772 def _jpegxy(data):
770 773 """read the (width, height) from a JPEG header"""
771 774 # adapted from http://www.64lines.com/jpeg-width-height
772 775
773 776 idx = 4
774 777 while True:
775 778 block_size = struct.unpack('>H', data[idx:idx+2])[0]
776 779 idx = idx + block_size
777 780 if data[idx:idx+2] == b'\xFF\xC0':
778 781 # found Start of Frame
779 782 iSOF = idx
780 783 break
781 784 else:
782 785 # read another block
783 786 idx += 2
784 787
785 788 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
786 789 return w, h
787 790
788 791 def _gifxy(data):
789 792 """read the (width, height) from a GIF header"""
790 793 return struct.unpack('<HH', data[6:10])
791 794
792 795
793 796 class Image(DisplayObject):
794 797
795 798 _read_flags = 'rb'
796 799 _FMT_JPEG = u'jpeg'
797 800 _FMT_PNG = u'png'
798 801 _FMT_GIF = u'gif'
799 802 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
800 803 _MIMETYPES = {
801 804 _FMT_PNG: 'image/png',
802 805 _FMT_JPEG: 'image/jpeg',
803 806 _FMT_GIF: 'image/gif',
804 807 }
805 808
806 809 def __init__(
807 810 self,
808 811 data=None,
809 812 url=None,
810 813 filename=None,
811 814 format=None,
812 815 embed=None,
813 816 width=None,
814 817 height=None,
815 818 retina=False,
816 819 unconfined=False,
817 820 metadata=None,
818 821 alt=None,
819 822 ):
820 823 """Create a PNG/JPEG/GIF image object given raw data.
821 824
822 825 When this object is returned by an input cell or passed to the
823 826 display function, it will result in the image being displayed
824 827 in the frontend.
825 828
826 829 Parameters
827 830 ----------
828 831 data : unicode, str or bytes
829 832 The raw image data or a URL or filename to load the data from.
830 833 This always results in embedded image data.
831 834
832 835 url : unicode
833 836 A URL to download the data from. If you specify `url=`,
834 837 the image data will not be embedded unless you also specify `embed=True`.
835 838
836 839 filename : unicode
837 840 Path to a local file to load the data from.
838 841 Images from a file are always embedded.
839 842
840 843 format : unicode
841 844 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
842 845 for format will be inferred from the filename extension.
843 846
844 847 embed : bool
845 848 Should the image data be embedded using a data URI (True) or be
846 849 loaded using an <img> tag. Set this to True if you want the image
847 850 to be viewable later with no internet connection in the notebook.
848 851
849 852 Default is `True`, unless the keyword argument `url` is set, then
850 853 default value is `False`.
851 854
852 855 Note that QtConsole is not able to display images if `embed` is set to `False`
853 856
854 857 width : int
855 858 Width in pixels to which to constrain the image in html
856 859
857 860 height : int
858 861 Height in pixels to which to constrain the image in html
859 862
860 863 retina : bool
861 864 Automatically set the width and height to half of the measured
862 865 width and height.
863 866 This only works for embedded images because it reads the width/height
864 867 from image data.
865 868 For non-embedded images, you can just set the desired display width
866 869 and height directly.
867 870
868 871 unconfined : bool
869 872 Set unconfined=True to disable max-width confinement of the image.
870 873
871 874 metadata : dict
872 875 Specify extra metadata to attach to the image.
873 876
874 877 alt : unicode
875 878 Alternative text for the image, for use by screen readers.
876 879
877 880 Examples
878 881 --------
879 882 embedded image data, works in qtconsole and notebook
880 883 when passed positionally, the first arg can be any of raw image data,
881 884 a URL, or a filename from which to load image data.
882 885 The result is always embedding image data for inline images.
883 886
884 887 >>> Image('http://www.google.fr/images/srpr/logo3w.png')
885 888 <IPython.core.display.Image object>
886 889
887 890 >>> Image('/path/to/image.jpg')
888 891 <IPython.core.display.Image object>
889 892
890 893 >>> Image(b'RAW_PNG_DATA...')
891 894 <IPython.core.display.Image object>
892 895
893 896 Specifying Image(url=...) does not embed the image data,
894 897 it only generates ``<img>`` tag with a link to the source.
895 898 This will not work in the qtconsole or offline.
896 899
897 900 >>> Image(url='http://www.google.fr/images/srpr/logo3w.png')
898 901 <IPython.core.display.Image object>
899 902
900 903 """
901 904 if isinstance(data, (Path, PurePath)):
902 905 data = str(data)
903 906
904 907 if filename is not None:
905 908 ext = self._find_ext(filename)
906 909 elif url is not None:
907 910 ext = self._find_ext(url)
908 911 elif data is None:
909 912 raise ValueError("No image data found. Expecting filename, url, or data.")
910 913 elif isinstance(data, str) and (
911 914 data.startswith('http') or _safe_exists(data)
912 915 ):
913 916 ext = self._find_ext(data)
914 917 else:
915 918 ext = None
916 919
917 920 if format is None:
918 921 if ext is not None:
919 922 if ext == u'jpg' or ext == u'jpeg':
920 923 format = self._FMT_JPEG
921 924 elif ext == u'png':
922 925 format = self._FMT_PNG
923 926 elif ext == u'gif':
924 927 format = self._FMT_GIF
925 928 else:
926 929 format = ext.lower()
927 930 elif isinstance(data, bytes):
928 931 # infer image type from image data header,
929 932 # only if format has not been specified.
930 933 if data[:2] == _JPEG:
931 934 format = self._FMT_JPEG
932 935
933 936 # failed to detect format, default png
934 937 if format is None:
935 938 format = self._FMT_PNG
936 939
937 940 if format.lower() == 'jpg':
938 941 # jpg->jpeg
939 942 format = self._FMT_JPEG
940 943
941 944 self.format = format.lower()
942 945 self.embed = embed if embed is not None else (url is None)
943 946
944 947 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
945 948 raise ValueError("Cannot embed the '%s' image format" % (self.format))
946 949 if self.embed:
947 950 self._mimetype = self._MIMETYPES.get(self.format)
948 951
949 952 self.width = width
950 953 self.height = height
951 954 self.retina = retina
952 955 self.unconfined = unconfined
953 956 self.alt = alt
954 957 super(Image, self).__init__(data=data, url=url, filename=filename,
955 958 metadata=metadata)
956 959
957 960 if self.width is None and self.metadata.get('width', {}):
958 961 self.width = metadata['width']
959 962
960 963 if self.height is None and self.metadata.get('height', {}):
961 964 self.height = metadata['height']
962 965
963 966 if self.alt is None and self.metadata.get("alt", {}):
964 967 self.alt = metadata["alt"]
965 968
966 969 if retina:
967 970 self._retina_shape()
968 971
969 972
970 973 def _retina_shape(self):
971 974 """load pixel-doubled width and height from image data"""
972 975 if not self.embed:
973 976 return
974 977 if self.format == self._FMT_PNG:
975 978 w, h = _pngxy(self.data)
976 979 elif self.format == self._FMT_JPEG:
977 980 w, h = _jpegxy(self.data)
978 981 elif self.format == self._FMT_GIF:
979 982 w, h = _gifxy(self.data)
980 983 else:
981 984 # retina only supports png
982 985 return
983 986 self.width = w // 2
984 987 self.height = h // 2
985 988
986 989 def reload(self):
987 990 """Reload the raw data from file or URL."""
988 991 if self.embed:
989 992 super(Image,self).reload()
990 993 if self.retina:
991 994 self._retina_shape()
992 995
993 996 def _repr_html_(self):
994 997 if not self.embed:
995 998 width = height = klass = alt = ""
996 999 if self.width:
997 1000 width = ' width="%d"' % self.width
998 1001 if self.height:
999 1002 height = ' height="%d"' % self.height
1000 1003 if self.unconfined:
1001 1004 klass = ' class="unconfined"'
1002 1005 if self.alt:
1003 1006 alt = ' alt="%s"' % html.escape(self.alt)
1004 1007 return '<img src="{url}"{width}{height}{klass}{alt}/>'.format(
1005 1008 url=self.url,
1006 1009 width=width,
1007 1010 height=height,
1008 1011 klass=klass,
1009 1012 alt=alt,
1010 1013 )
1011 1014
1012 1015 def _repr_mimebundle_(self, include=None, exclude=None):
1013 1016 """Return the image as a mimebundle
1014 1017
1015 1018 Any new mimetype support should be implemented here.
1016 1019 """
1017 1020 if self.embed:
1018 1021 mimetype = self._mimetype
1019 1022 data, metadata = self._data_and_metadata(always_both=True)
1020 1023 if metadata:
1021 1024 metadata = {mimetype: metadata}
1022 1025 return {mimetype: data}, metadata
1023 1026 else:
1024 1027 return {'text/html': self._repr_html_()}
1025 1028
1026 1029 def _data_and_metadata(self, always_both=False):
1027 1030 """shortcut for returning metadata with shape information, if defined"""
1028 1031 try:
1029 1032 b64_data = b2a_base64(self.data).decode('ascii')
1030 1033 except TypeError as e:
1031 1034 raise FileNotFoundError(
1032 1035 "No such file or directory: '%s'" % (self.data)) from e
1033 1036 md = {}
1034 1037 if self.metadata:
1035 1038 md.update(self.metadata)
1036 1039 if self.width:
1037 1040 md['width'] = self.width
1038 1041 if self.height:
1039 1042 md['height'] = self.height
1040 1043 if self.unconfined:
1041 1044 md['unconfined'] = self.unconfined
1042 1045 if self.alt:
1043 1046 md["alt"] = self.alt
1044 1047 if md or always_both:
1045 1048 return b64_data, md
1046 1049 else:
1047 1050 return b64_data
1048 1051
1049 1052 def _repr_png_(self):
1050 1053 if self.embed and self.format == self._FMT_PNG:
1051 1054 return self._data_and_metadata()
1052 1055
1053 1056 def _repr_jpeg_(self):
1054 1057 if self.embed and self.format == self._FMT_JPEG:
1055 1058 return self._data_and_metadata()
1056 1059
1057 1060 def _find_ext(self, s):
1058 1061 base, ext = splitext(s)
1059 1062
1060 1063 if not ext:
1061 1064 return base
1062 1065
1063 1066 # `splitext` includes leading period, so we skip it
1064 1067 return ext[1:].lower()
1065 1068
1066 1069
1067 1070 class Video(DisplayObject):
1068 1071
1069 1072 def __init__(self, data=None, url=None, filename=None, embed=False,
1070 1073 mimetype=None, width=None, height=None, html_attributes="controls"):
1071 1074 """Create a video object given raw data or an URL.
1072 1075
1073 1076 When this object is returned by an input cell or passed to the
1074 1077 display function, it will result in the video being displayed
1075 1078 in the frontend.
1076 1079
1077 1080 Parameters
1078 1081 ----------
1079 1082 data : unicode, str or bytes
1080 1083 The raw video data or a URL or filename to load the data from.
1081 1084 Raw data will require passing ``embed=True``.
1082 1085
1083 1086 url : unicode
1084 1087 A URL for the video. If you specify ``url=``,
1085 1088 the image data will not be embedded.
1086 1089
1087 1090 filename : unicode
1088 1091 Path to a local file containing the video.
1089 1092 Will be interpreted as a local URL unless ``embed=True``.
1090 1093
1091 1094 embed : bool
1092 1095 Should the video be embedded using a data URI (True) or be
1093 1096 loaded using a <video> tag (False).
1094 1097
1095 1098 Since videos are large, embedding them should be avoided, if possible.
1096 1099 You must confirm embedding as your intention by passing ``embed=True``.
1097 1100
1098 1101 Local files can be displayed with URLs without embedding the content, via::
1099 1102
1100 1103 Video('./video.mp4')
1101 1104
1102 1105 mimetype : unicode
1103 1106 Specify the mimetype for embedded videos.
1104 1107 Default will be guessed from file extension, if available.
1105 1108
1106 1109 width : int
1107 1110 Width in pixels to which to constrain the video in HTML.
1108 1111 If not supplied, defaults to the width of the video.
1109 1112
1110 1113 height : int
1111 1114 Height in pixels to which to constrain the video in html.
1112 1115 If not supplied, defaults to the height of the video.
1113 1116
1114 1117 html_attributes : str
1115 1118 Attributes for the HTML ``<video>`` block.
1116 1119 Default: ``"controls"`` to get video controls.
1117 1120 Other examples: ``"controls muted"`` for muted video with controls,
1118 1121 ``"loop autoplay"`` for looping autoplaying video without controls.
1119 1122
1120 1123 Examples
1121 1124 --------
1122 1125 ::
1123 1126
1124 1127 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1125 1128 Video('path/to/video.mp4')
1126 1129 Video('path/to/video.mp4', embed=True)
1127 1130 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1128 1131 Video(b'raw-videodata', embed=True)
1129 1132 """
1130 1133 if isinstance(data, (Path, PurePath)):
1131 1134 data = str(data)
1132 1135
1133 1136 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1134 1137 url = data
1135 1138 data = None
1136 1139 elif data is not None and os.path.exists(data):
1137 1140 filename = data
1138 1141 data = None
1139 1142
1140 1143 if data and not embed:
1141 1144 msg = ''.join([
1142 1145 "To embed videos, you must pass embed=True ",
1143 1146 "(this may make your notebook files huge)\n",
1144 1147 "Consider passing Video(url='...')",
1145 1148 ])
1146 1149 raise ValueError(msg)
1147 1150
1148 1151 self.mimetype = mimetype
1149 1152 self.embed = embed
1150 1153 self.width = width
1151 1154 self.height = height
1152 1155 self.html_attributes = html_attributes
1153 1156 super(Video, self).__init__(data=data, url=url, filename=filename)
1154 1157
1155 1158 def _repr_html_(self):
1156 1159 width = height = ''
1157 1160 if self.width:
1158 1161 width = ' width="%d"' % self.width
1159 1162 if self.height:
1160 1163 height = ' height="%d"' % self.height
1161 1164
1162 1165 # External URLs and potentially local files are not embedded into the
1163 1166 # notebook output.
1164 1167 if not self.embed:
1165 1168 url = self.url if self.url is not None else self.filename
1166 1169 output = """<video src="{0}" {1} {2} {3}>
1167 1170 Your browser does not support the <code>video</code> element.
1168 1171 </video>""".format(url, self.html_attributes, width, height)
1169 1172 return output
1170 1173
1171 1174 # Embedded videos are base64-encoded.
1172 1175 mimetype = self.mimetype
1173 1176 if self.filename is not None:
1174 1177 if not mimetype:
1175 1178 mimetype, _ = mimetypes.guess_type(self.filename)
1176 1179
1177 1180 with open(self.filename, 'rb') as f:
1178 1181 video = f.read()
1179 1182 else:
1180 1183 video = self.data
1181 1184 if isinstance(video, str):
1182 1185 # unicode input is already b64-encoded
1183 1186 b64_video = video
1184 1187 else:
1185 1188 b64_video = b2a_base64(video).decode('ascii').rstrip()
1186 1189
1187 1190 output = """<video {0} {1} {2}>
1188 1191 <source src="data:{3};base64,{4}" type="{3}">
1189 1192 Your browser does not support the video tag.
1190 1193 </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1191 1194 return output
1192 1195
1193 1196 def reload(self):
1194 1197 # TODO
1195 1198 pass
1196 1199
1197 1200
1198 1201 @skip_doctest
1199 1202 def set_matplotlib_formats(*formats, **kwargs):
1200 1203 """
1201 1204 .. deprecated:: 7.23
1202 1205
1203 1206 use `matplotlib_inline.backend_inline.set_matplotlib_formats()`
1204 1207
1205 1208 Select figure formats for the inline backend. Optionally pass quality for JPEG.
1206 1209
1207 1210 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1208 1211
1209 1212 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1210 1213
1211 1214 To set this in your config files use the following::
1212 1215
1213 1216 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1214 1217 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1215 1218
1216 1219 Parameters
1217 1220 ----------
1218 1221 *formats : strs
1219 1222 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1220 1223 **kwargs
1221 1224 Keyword args will be relayed to ``figure.canvas.print_figure``.
1222 1225 """
1223 1226 warnings.warn(
1224 1227 "`set_matplotlib_formats` is deprecated since IPython 7.23, directly "
1225 1228 "use `matplotlib_inline.backend_inline.set_matplotlib_formats()`",
1226 1229 DeprecationWarning,
1227 1230 stacklevel=2,
1228 1231 )
1229 1232
1230 1233 from matplotlib_inline.backend_inline import (
1231 1234 set_matplotlib_formats as set_matplotlib_formats_orig,
1232 1235 )
1233 1236
1234 1237 set_matplotlib_formats_orig(*formats, **kwargs)
1235 1238
1236 1239 @skip_doctest
1237 1240 def set_matplotlib_close(close=True):
1238 1241 """
1239 1242 .. deprecated:: 7.23
1240 1243
1241 1244 use `matplotlib_inline.backend_inline.set_matplotlib_close()`
1242 1245
1243 1246 Set whether the inline backend closes all figures automatically or not.
1244 1247
1245 1248 By default, the inline backend used in the IPython Notebook will close all
1246 1249 matplotlib figures automatically after each cell is run. This means that
1247 1250 plots in different cells won't interfere. Sometimes, you may want to make
1248 1251 a plot in one cell and then refine it in later cells. This can be accomplished
1249 1252 by::
1250 1253
1251 1254 In [1]: set_matplotlib_close(False)
1252 1255
1253 1256 To set this in your config files use the following::
1254 1257
1255 1258 c.InlineBackend.close_figures = False
1256 1259
1257 1260 Parameters
1258 1261 ----------
1259 1262 close : bool
1260 1263 Should all matplotlib figures be automatically closed after each cell is
1261 1264 run?
1262 1265 """
1263 1266 warnings.warn(
1264 1267 "`set_matplotlib_close` is deprecated since IPython 7.23, directly "
1265 1268 "use `matplotlib_inline.backend_inline.set_matplotlib_close()`",
1266 1269 DeprecationWarning,
1267 1270 stacklevel=2,
1268 1271 )
1269 1272
1270 1273 from matplotlib_inline.backend_inline import (
1271 1274 set_matplotlib_close as set_matplotlib_close_orig,
1272 1275 )
1273 1276
1274 1277 set_matplotlib_close_orig(close)
@@ -1,3654 +1,3654 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Main IPython class."""
3 3
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de>
6 6 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
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 import abc
15 15 import ast
16 16 import atexit
17 17 import builtins as builtin_mod
18 18 import functools
19 19 import inspect
20 20 import os
21 21 import re
22 22 import runpy
23 23 import sys
24 24 import tempfile
25 25 import traceback
26 26 import types
27 27 import subprocess
28 28 import warnings
29 29 from io import open as io_open
30 30
31 31 from pathlib import Path
32 32 from pickleshare import PickleShareDB
33 33
34 34 from traitlets.config.configurable import SingletonConfigurable
35 35 from traitlets.utils.importstring import import_item
36 36 from IPython.core import oinspect
37 37 from IPython.core import magic
38 38 from IPython.core import page
39 39 from IPython.core import prefilter
40 40 from IPython.core import ultratb
41 41 from IPython.core.alias import Alias, AliasManager
42 42 from IPython.core.autocall import ExitAutocall
43 43 from IPython.core.builtin_trap import BuiltinTrap
44 44 from IPython.core.events import EventManager, available_events
45 45 from IPython.core.compilerop import CachingCompiler, check_linecache_ipython
46 46 from IPython.core.debugger import InterruptiblePdb
47 47 from IPython.core.display_trap import DisplayTrap
48 48 from IPython.core.displayhook import DisplayHook
49 49 from IPython.core.displaypub import DisplayPublisher
50 50 from IPython.core.error import InputRejected, UsageError
51 51 from IPython.core.extensions import ExtensionManager
52 52 from IPython.core.formatters import DisplayFormatter
53 53 from IPython.core.history import HistoryManager
54 54 from IPython.core.inputtransformer2 import ESC_MAGIC, ESC_MAGIC2
55 55 from IPython.core.logger import Logger
56 56 from IPython.core.macro import Macro
57 57 from IPython.core.payload import PayloadManager
58 58 from IPython.core.prefilter import PrefilterManager
59 59 from IPython.core.profiledir import ProfileDir
60 60 from IPython.core.usage import default_banner
61 61 from IPython.display import display
62 62 from IPython.testing.skipdoctest import skip_doctest
63 63 from IPython.utils import PyColorize
64 64 from IPython.utils import io
65 65 from IPython.utils import py3compat
66 66 from IPython.utils import openpy
67 67 from IPython.utils.decorators import undoc
68 68 from IPython.utils.io import ask_yes_no
69 69 from IPython.utils.ipstruct import Struct
70 70 from IPython.paths import get_ipython_dir
71 71 from IPython.utils.path import get_home_dir, get_py_filename, ensure_dir_exists
72 72 from IPython.utils.process import system, getoutput
73 73 from IPython.utils.strdispatch import StrDispatch
74 74 from IPython.utils.syspathcontext import prepended_to_syspath
75 75 from IPython.utils.text import format_screen, LSString, SList, DollarFormatter
76 76 from IPython.utils.tempdir import TemporaryDirectory
77 77 from traitlets import (
78 78 Integer, Bool, CaselessStrEnum, Enum, List, Dict, Unicode, Instance, Type,
79 79 observe, default, validate, Any
80 80 )
81 81 from warnings import warn
82 82 from logging import error
83 83 import IPython.core.hooks
84 84
85 85 from typing import List as ListType, Tuple, Optional, Callable
86 86 from ast import stmt
87 87
88 88 sphinxify: Optional[Callable]
89 89
90 90 try:
91 91 import docrepr.sphinxify as sphx
92 92
93 93 def sphinxify(oinfo):
94 94 wrapped_docstring = sphx.wrap_main_docstring(oinfo)
95 95
96 96 def sphinxify_docstring(docstring):
97 97 with TemporaryDirectory() as dirname:
98 98 return {
99 99 "text/html": sphx.sphinxify(wrapped_docstring, dirname),
100 100 "text/plain": docstring,
101 101 }
102 102
103 103 return sphinxify_docstring
104 104 except ImportError:
105 105 sphinxify = None
106 106
107 107
108 108 class ProvisionalWarning(DeprecationWarning):
109 109 """
110 110 Warning class for unstable features
111 111 """
112 112 pass
113 113
114 114 from ast import Module
115 115
116 116 _assign_nodes = (ast.AugAssign, ast.AnnAssign, ast.Assign)
117 117 _single_targets_nodes = (ast.AugAssign, ast.AnnAssign)
118 118
119 119 #-----------------------------------------------------------------------------
120 120 # Await Helpers
121 121 #-----------------------------------------------------------------------------
122 122
123 123 # we still need to run things using the asyncio eventloop, but there is no
124 124 # async integration
125 125 from .async_helpers import _asyncio_runner, _pseudo_sync_runner
126 126 from .async_helpers import _curio_runner, _trio_runner, _should_be_async
127 127
128 128 #-----------------------------------------------------------------------------
129 129 # Globals
130 130 #-----------------------------------------------------------------------------
131 131
132 132 # compiled regexps for autoindent management
133 133 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
134 134
135 135 #-----------------------------------------------------------------------------
136 136 # Utilities
137 137 #-----------------------------------------------------------------------------
138 138
139 139 @undoc
140 140 def softspace(file, newvalue):
141 141 """Copied from code.py, to remove the dependency"""
142 142
143 143 oldvalue = 0
144 144 try:
145 145 oldvalue = file.softspace
146 146 except AttributeError:
147 147 pass
148 148 try:
149 149 file.softspace = newvalue
150 150 except (AttributeError, TypeError):
151 151 # "attribute-less object" or "read-only attributes"
152 152 pass
153 153 return oldvalue
154 154
155 155 @undoc
156 156 def no_op(*a, **kw):
157 157 pass
158 158
159 159
160 160 class SpaceInInput(Exception): pass
161 161
162 162
163 163 class SeparateUnicode(Unicode):
164 164 r"""A Unicode subclass to validate separate_in, separate_out, etc.
165 165
166 166 This is a Unicode based trait that converts '0'->'' and ``'\\n'->'\n'``.
167 167 """
168 168
169 169 def validate(self, obj, value):
170 170 if value == '0': value = ''
171 171 value = value.replace('\\n','\n')
172 172 return super(SeparateUnicode, self).validate(obj, value)
173 173
174 174
175 175 @undoc
176 176 class DummyMod(object):
177 177 """A dummy module used for IPython's interactive module when
178 178 a namespace must be assigned to the module's __dict__."""
179 179 __spec__ = None
180 180
181 181
182 182 class ExecutionInfo(object):
183 183 """The arguments used for a call to :meth:`InteractiveShell.run_cell`
184 184
185 185 Stores information about what is going to happen.
186 186 """
187 187 raw_cell = None
188 188 store_history = False
189 189 silent = False
190 190 shell_futures = True
191 191
192 192 def __init__(self, raw_cell, store_history, silent, shell_futures):
193 193 self.raw_cell = raw_cell
194 194 self.store_history = store_history
195 195 self.silent = silent
196 196 self.shell_futures = shell_futures
197 197
198 198 def __repr__(self):
199 199 name = self.__class__.__qualname__
200 200 raw_cell = ((self.raw_cell[:50] + '..')
201 201 if len(self.raw_cell) > 50 else self.raw_cell)
202 202 return '<%s object at %x, raw_cell="%s" store_history=%s silent=%s shell_futures=%s>' %\
203 203 (name, id(self), raw_cell, self.store_history, self.silent, self.shell_futures)
204 204
205 205
206 206 class ExecutionResult(object):
207 207 """The result of a call to :meth:`InteractiveShell.run_cell`
208 208
209 209 Stores information about what took place.
210 210 """
211 211 execution_count = None
212 212 error_before_exec = None
213 213 error_in_exec: Optional[BaseException] = None
214 214 info = None
215 215 result = None
216 216
217 217 def __init__(self, info):
218 218 self.info = info
219 219
220 220 @property
221 221 def success(self):
222 222 return (self.error_before_exec is None) and (self.error_in_exec is None)
223 223
224 224 def raise_error(self):
225 225 """Reraises error if `success` is `False`, otherwise does nothing"""
226 226 if self.error_before_exec is not None:
227 227 raise self.error_before_exec
228 228 if self.error_in_exec is not None:
229 229 raise self.error_in_exec
230 230
231 231 def __repr__(self):
232 232 name = self.__class__.__qualname__
233 233 return '<%s object at %x, execution_count=%s error_before_exec=%s error_in_exec=%s info=%s result=%s>' %\
234 234 (name, id(self), self.execution_count, self.error_before_exec, self.error_in_exec, repr(self.info), repr(self.result))
235 235
236 236
237 237 class InteractiveShell(SingletonConfigurable):
238 238 """An enhanced, interactive shell for Python."""
239 239
240 240 _instance = None
241 241
242 242 ast_transformers = List([], help=
243 243 """
244 244 A list of ast.NodeTransformer subclass instances, which will be applied
245 245 to user input before code is run.
246 246 """
247 247 ).tag(config=True)
248 248
249 249 autocall = Enum((0,1,2), default_value=0, help=
250 250 """
251 251 Make IPython automatically call any callable object even if you didn't
252 252 type explicit parentheses. For example, 'str 43' becomes 'str(43)'
253 253 automatically. The value can be '0' to disable the feature, '1' for
254 254 'smart' autocall, where it is not applied if there are no more
255 255 arguments on the line, and '2' for 'full' autocall, where all callable
256 256 objects are automatically called (even if no arguments are present).
257 257 """
258 258 ).tag(config=True)
259 259
260 260 autoindent = Bool(True, help=
261 261 """
262 262 Autoindent IPython code entered interactively.
263 263 """
264 264 ).tag(config=True)
265 265
266 266 autoawait = Bool(True, help=
267 267 """
268 268 Automatically run await statement in the top level repl.
269 269 """
270 270 ).tag(config=True)
271 271
272 272 loop_runner_map ={
273 273 'asyncio':(_asyncio_runner, True),
274 274 'curio':(_curio_runner, True),
275 275 'trio':(_trio_runner, True),
276 276 'sync': (_pseudo_sync_runner, False)
277 277 }
278 278
279 279 loop_runner = Any(default_value="IPython.core.interactiveshell._asyncio_runner",
280 280 allow_none=True,
281 281 help="""Select the loop runner that will be used to execute top-level asynchronous code"""
282 282 ).tag(config=True)
283 283
284 284 @default('loop_runner')
285 285 def _default_loop_runner(self):
286 286 return import_item("IPython.core.interactiveshell._asyncio_runner")
287 287
288 288 @validate('loop_runner')
289 289 def _import_runner(self, proposal):
290 290 if isinstance(proposal.value, str):
291 291 if proposal.value in self.loop_runner_map:
292 292 runner, autoawait = self.loop_runner_map[proposal.value]
293 293 self.autoawait = autoawait
294 294 return runner
295 295 runner = import_item(proposal.value)
296 296 if not callable(runner):
297 297 raise ValueError('loop_runner must be callable')
298 298 return runner
299 299 if not callable(proposal.value):
300 300 raise ValueError('loop_runner must be callable')
301 301 return proposal.value
302 302
303 303 automagic = Bool(True, help=
304 304 """
305 305 Enable magic commands to be called without the leading %.
306 306 """
307 307 ).tag(config=True)
308 308
309 309 banner1 = Unicode(default_banner,
310 310 help="""The part of the banner to be printed before the profile"""
311 311 ).tag(config=True)
312 312 banner2 = Unicode('',
313 313 help="""The part of the banner to be printed after the profile"""
314 314 ).tag(config=True)
315 315
316 316 cache_size = Integer(1000, help=
317 317 """
318 318 Set the size of the output cache. The default is 1000, you can
319 319 change it permanently in your config file. Setting it to 0 completely
320 320 disables the caching system, and the minimum value accepted is 3 (if
321 321 you provide a value less than 3, it is reset to 0 and a warning is
322 322 issued). This limit is defined because otherwise you'll spend more
323 323 time re-flushing a too small cache than working
324 324 """
325 325 ).tag(config=True)
326 326 color_info = Bool(True, help=
327 327 """
328 328 Use colors for displaying information about objects. Because this
329 329 information is passed through a pager (like 'less'), and some pagers
330 330 get confused with color codes, this capability can be turned off.
331 331 """
332 332 ).tag(config=True)
333 333 colors = CaselessStrEnum(('Neutral', 'NoColor','LightBG','Linux'),
334 334 default_value='Neutral',
335 335 help="Set the color scheme (NoColor, Neutral, Linux, or LightBG)."
336 336 ).tag(config=True)
337 337 debug = Bool(False).tag(config=True)
338 338 disable_failing_post_execute = Bool(False,
339 339 help="Don't call post-execute functions that have failed in the past."
340 340 ).tag(config=True)
341 341 display_formatter = Instance(DisplayFormatter, allow_none=True)
342 342 displayhook_class = Type(DisplayHook)
343 343 display_pub_class = Type(DisplayPublisher)
344 344 compiler_class = Type(CachingCompiler)
345 345
346 346 sphinxify_docstring = Bool(False, help=
347 347 """
348 348 Enables rich html representation of docstrings. (This requires the
349 349 docrepr module).
350 350 """).tag(config=True)
351 351
352 352 @observe("sphinxify_docstring")
353 353 def _sphinxify_docstring_changed(self, change):
354 354 if change['new']:
355 355 warn("`sphinxify_docstring` is provisional since IPython 5.0 and might change in future versions." , ProvisionalWarning)
356 356
357 357 enable_html_pager = Bool(False, help=
358 358 """
359 359 (Provisional API) enables html representation in mime bundles sent
360 360 to pagers.
361 361 """).tag(config=True)
362 362
363 363 @observe("enable_html_pager")
364 364 def _enable_html_pager_changed(self, change):
365 365 if change['new']:
366 366 warn("`enable_html_pager` is provisional since IPython 5.0 and might change in future versions.", ProvisionalWarning)
367 367
368 368 data_pub_class = None
369 369
370 370 exit_now = Bool(False)
371 371 exiter = Instance(ExitAutocall)
372 372 @default('exiter')
373 373 def _exiter_default(self):
374 374 return ExitAutocall(self)
375 375 # Monotonically increasing execution counter
376 376 execution_count = Integer(1)
377 377 filename = Unicode("<ipython console>")
378 378 ipython_dir= Unicode('').tag(config=True) # Set to get_ipython_dir() in __init__
379 379
380 380 # Used to transform cells before running them, and check whether code is complete
381 381 input_transformer_manager = Instance('IPython.core.inputtransformer2.TransformerManager',
382 382 ())
383 383
384 384 @property
385 385 def input_transformers_cleanup(self):
386 386 return self.input_transformer_manager.cleanup_transforms
387 387
388 388 input_transformers_post = List([],
389 389 help="A list of string input transformers, to be applied after IPython's "
390 390 "own input transformations."
391 391 )
392 392
393 393 @property
394 394 def input_splitter(self):
395 395 """Make this available for backward compatibility (pre-7.0 release) with existing code.
396 396
397 397 For example, ipykernel ipykernel currently uses
398 398 `shell.input_splitter.check_complete`
399 399 """
400 400 from warnings import warn
401 401 warn("`input_splitter` is deprecated since IPython 7.0, prefer `input_transformer_manager`.",
402 402 DeprecationWarning, stacklevel=2
403 403 )
404 404 return self.input_transformer_manager
405 405
406 406 logstart = Bool(False, help=
407 407 """
408 408 Start logging to the default log file in overwrite mode.
409 409 Use `logappend` to specify a log file to **append** logs to.
410 410 """
411 411 ).tag(config=True)
412 412 logfile = Unicode('', help=
413 413 """
414 414 The name of the logfile to use.
415 415 """
416 416 ).tag(config=True)
417 417 logappend = Unicode('', help=
418 418 """
419 419 Start logging to the given file in append mode.
420 420 Use `logfile` to specify a log file to **overwrite** logs to.
421 421 """
422 422 ).tag(config=True)
423 423 object_info_string_level = Enum((0,1,2), default_value=0,
424 424 ).tag(config=True)
425 425 pdb = Bool(False, help=
426 426 """
427 427 Automatically call the pdb debugger after every exception.
428 428 """
429 429 ).tag(config=True)
430 430 display_page = Bool(False,
431 431 help="""If True, anything that would be passed to the pager
432 432 will be displayed as regular output instead."""
433 433 ).tag(config=True)
434 434
435 435
436 436 show_rewritten_input = Bool(True,
437 437 help="Show rewritten input, e.g. for autocall."
438 438 ).tag(config=True)
439 439
440 440 quiet = Bool(False).tag(config=True)
441 441
442 442 history_length = Integer(10000,
443 443 help='Total length of command history'
444 444 ).tag(config=True)
445 445
446 446 history_load_length = Integer(1000, help=
447 447 """
448 448 The number of saved history entries to be loaded
449 449 into the history buffer at startup.
450 450 """
451 451 ).tag(config=True)
452 452
453 453 ast_node_interactivity = Enum(['all', 'last', 'last_expr', 'none', 'last_expr_or_assign'],
454 454 default_value='last_expr',
455 455 help="""
456 456 'all', 'last', 'last_expr' or 'none', 'last_expr_or_assign' specifying
457 457 which nodes should be run interactively (displaying output from expressions).
458 458 """
459 459 ).tag(config=True)
460 460
461 461 # TODO: this part of prompt management should be moved to the frontends.
462 462 # Use custom TraitTypes that convert '0'->'' and '\\n'->'\n'
463 463 separate_in = SeparateUnicode('\n').tag(config=True)
464 464 separate_out = SeparateUnicode('').tag(config=True)
465 465 separate_out2 = SeparateUnicode('').tag(config=True)
466 466 wildcards_case_sensitive = Bool(True).tag(config=True)
467 467 xmode = CaselessStrEnum(('Context', 'Plain', 'Verbose', 'Minimal'),
468 468 default_value='Context',
469 469 help="Switch modes for the IPython exception handlers."
470 470 ).tag(config=True)
471 471
472 472 # Subcomponents of InteractiveShell
473 473 alias_manager = Instance('IPython.core.alias.AliasManager', allow_none=True)
474 474 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True)
475 475 builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap', allow_none=True)
476 476 display_trap = Instance('IPython.core.display_trap.DisplayTrap', allow_none=True)
477 477 extension_manager = Instance('IPython.core.extensions.ExtensionManager', allow_none=True)
478 478 payload_manager = Instance('IPython.core.payload.PayloadManager', allow_none=True)
479 479 history_manager = Instance('IPython.core.history.HistoryAccessorBase', allow_none=True)
480 480 magics_manager = Instance('IPython.core.magic.MagicsManager', allow_none=True)
481 481
482 482 profile_dir = Instance('IPython.core.application.ProfileDir', allow_none=True)
483 483 @property
484 484 def profile(self):
485 485 if self.profile_dir is not None:
486 486 name = os.path.basename(self.profile_dir.location)
487 487 return name.replace('profile_','')
488 488
489 489
490 490 # Private interface
491 491 _post_execute = Dict()
492 492
493 493 # Tracks any GUI loop loaded for pylab
494 494 pylab_gui_select = None
495 495
496 496 last_execution_succeeded = Bool(True, help='Did last executed command succeeded')
497 497
498 498 last_execution_result = Instance('IPython.core.interactiveshell.ExecutionResult', help='Result of executing the last command', allow_none=True)
499 499
500 500 def __init__(self, ipython_dir=None, profile_dir=None,
501 501 user_module=None, user_ns=None,
502 502 custom_exceptions=((), None), **kwargs):
503 503
504 504 # This is where traits with a config_key argument are updated
505 505 # from the values on config.
506 506 super(InteractiveShell, self).__init__(**kwargs)
507 507 if 'PromptManager' in self.config:
508 508 warn('As of IPython 5.0 `PromptManager` config will have no effect'
509 509 ' and has been replaced by TerminalInteractiveShell.prompts_class')
510 510 self.configurables = [self]
511 511
512 512 # These are relatively independent and stateless
513 513 self.init_ipython_dir(ipython_dir)
514 514 self.init_profile_dir(profile_dir)
515 515 self.init_instance_attrs()
516 516 self.init_environment()
517 517
518 518 # Check if we're in a virtualenv, and set up sys.path.
519 519 self.init_virtualenv()
520 520
521 521 # Create namespaces (user_ns, user_global_ns, etc.)
522 522 self.init_create_namespaces(user_module, user_ns)
523 523 # This has to be done after init_create_namespaces because it uses
524 524 # something in self.user_ns, but before init_sys_modules, which
525 525 # is the first thing to modify sys.
526 526 # TODO: When we override sys.stdout and sys.stderr before this class
527 527 # is created, we are saving the overridden ones here. Not sure if this
528 528 # is what we want to do.
529 529 self.save_sys_module_state()
530 530 self.init_sys_modules()
531 531
532 532 # While we're trying to have each part of the code directly access what
533 533 # it needs without keeping redundant references to objects, we have too
534 534 # much legacy code that expects ip.db to exist.
535 535 self.db = PickleShareDB(os.path.join(self.profile_dir.location, 'db'))
536 536
537 537 self.init_history()
538 538 self.init_encoding()
539 539 self.init_prefilter()
540 540
541 541 self.init_syntax_highlighting()
542 542 self.init_hooks()
543 543 self.init_events()
544 544 self.init_pushd_popd_magic()
545 545 self.init_user_ns()
546 546 self.init_logger()
547 547 self.init_builtins()
548 548
549 549 # The following was in post_config_initialization
550 550 self.init_inspector()
551 551 self.raw_input_original = input
552 552 self.init_completer()
553 553 # TODO: init_io() needs to happen before init_traceback handlers
554 554 # because the traceback handlers hardcode the stdout/stderr streams.
555 555 # This logic in in debugger.Pdb and should eventually be changed.
556 556 self.init_io()
557 557 self.init_traceback_handlers(custom_exceptions)
558 558 self.init_prompts()
559 559 self.init_display_formatter()
560 560 self.init_display_pub()
561 561 self.init_data_pub()
562 562 self.init_displayhook()
563 563 self.init_magics()
564 564 self.init_alias()
565 565 self.init_logstart()
566 566 self.init_pdb()
567 567 self.init_extension_manager()
568 568 self.init_payload()
569 569 self.events.trigger('shell_initialized', self)
570 570 atexit.register(self.atexit_operations)
571 571
572 572 # The trio runner is used for running Trio in the foreground thread. It
573 573 # is different from `_trio_runner(async_fn)` in `async_helpers.py`
574 574 # which calls `trio.run()` for every cell. This runner runs all cells
575 575 # inside a single Trio event loop. If used, it is set from
576 576 # `ipykernel.kernelapp`.
577 577 self.trio_runner = None
578 578
579 579 def get_ipython(self):
580 580 """Return the currently running IPython instance."""
581 581 return self
582 582
583 583 #-------------------------------------------------------------------------
584 584 # Trait changed handlers
585 585 #-------------------------------------------------------------------------
586 586 @observe('ipython_dir')
587 587 def _ipython_dir_changed(self, change):
588 588 ensure_dir_exists(change['new'])
589 589
590 590 def set_autoindent(self,value=None):
591 591 """Set the autoindent flag.
592 592
593 593 If called with no arguments, it acts as a toggle."""
594 594 if value is None:
595 595 self.autoindent = not self.autoindent
596 596 else:
597 597 self.autoindent = value
598 598
599 599 def set_trio_runner(self, tr):
600 600 self.trio_runner = tr
601 601
602 602 #-------------------------------------------------------------------------
603 603 # init_* methods called by __init__
604 604 #-------------------------------------------------------------------------
605 605
606 606 def init_ipython_dir(self, ipython_dir):
607 607 if ipython_dir is not None:
608 608 self.ipython_dir = ipython_dir
609 609 return
610 610
611 611 self.ipython_dir = get_ipython_dir()
612 612
613 613 def init_profile_dir(self, profile_dir):
614 614 if profile_dir is not None:
615 615 self.profile_dir = profile_dir
616 616 return
617 617 self.profile_dir = ProfileDir.create_profile_dir_by_name(
618 618 self.ipython_dir, "default"
619 619 )
620 620
621 621 def init_instance_attrs(self):
622 622 self.more = False
623 623
624 624 # command compiler
625 625 self.compile = self.compiler_class()
626 626
627 627 # Make an empty namespace, which extension writers can rely on both
628 628 # existing and NEVER being used by ipython itself. This gives them a
629 629 # convenient location for storing additional information and state
630 630 # their extensions may require, without fear of collisions with other
631 631 # ipython names that may develop later.
632 632 self.meta = Struct()
633 633
634 634 # Temporary files used for various purposes. Deleted at exit.
635 635 # The files here are stored with Path from Pathlib
636 636 self.tempfiles = []
637 637 self.tempdirs = []
638 638
639 639 # keep track of where we started running (mainly for crash post-mortem)
640 640 # This is not being used anywhere currently.
641 641 self.starting_dir = os.getcwd()
642 642
643 643 # Indentation management
644 644 self.indent_current_nsp = 0
645 645
646 646 # Dict to track post-execution functions that have been registered
647 647 self._post_execute = {}
648 648
649 649 def init_environment(self):
650 650 """Any changes we need to make to the user's environment."""
651 651 pass
652 652
653 653 def init_encoding(self):
654 654 # Get system encoding at startup time. Certain terminals (like Emacs
655 655 # under Win32 have it set to None, and we need to have a known valid
656 656 # encoding to use in the raw_input() method
657 657 try:
658 658 self.stdin_encoding = sys.stdin.encoding or 'ascii'
659 659 except AttributeError:
660 660 self.stdin_encoding = 'ascii'
661 661
662 662
663 663 @observe('colors')
664 664 def init_syntax_highlighting(self, changes=None):
665 665 # Python source parser/formatter for syntax highlighting
666 666 pyformat = PyColorize.Parser(style=self.colors, parent=self).format
667 667 self.pycolorize = lambda src: pyformat(src,'str')
668 668
669 669 def refresh_style(self):
670 670 # No-op here, used in subclass
671 671 pass
672 672
673 673 def init_pushd_popd_magic(self):
674 674 # for pushd/popd management
675 675 self.home_dir = get_home_dir()
676 676
677 677 self.dir_stack = []
678 678
679 679 def init_logger(self):
680 680 self.logger = Logger(self.home_dir, logfname='ipython_log.py',
681 681 logmode='rotate')
682 682
683 683 def init_logstart(self):
684 684 """Initialize logging in case it was requested at the command line.
685 685 """
686 686 if self.logappend:
687 687 self.magic('logstart %s append' % self.logappend)
688 688 elif self.logfile:
689 689 self.magic('logstart %s' % self.logfile)
690 690 elif self.logstart:
691 691 self.magic('logstart')
692 692
693 693
694 694 def init_builtins(self):
695 695 # A single, static flag that we set to True. Its presence indicates
696 696 # that an IPython shell has been created, and we make no attempts at
697 697 # removing on exit or representing the existence of more than one
698 698 # IPython at a time.
699 699 builtin_mod.__dict__['__IPYTHON__'] = True
700 700 builtin_mod.__dict__['display'] = display
701 701
702 702 self.builtin_trap = BuiltinTrap(shell=self)
703 703
704 704 @observe('colors')
705 705 def init_inspector(self, changes=None):
706 706 # Object inspector
707 707 self.inspector = oinspect.Inspector(oinspect.InspectColors,
708 708 PyColorize.ANSICodeColors,
709 709 self.colors,
710 710 self.object_info_string_level)
711 711
712 712 def init_io(self):
713 713 # implemented in subclasses, TerminalInteractiveShell does call
714 714 # colorama.init().
715 715 pass
716 716
717 717 def init_prompts(self):
718 718 # Set system prompts, so that scripts can decide if they are running
719 719 # interactively.
720 720 sys.ps1 = 'In : '
721 721 sys.ps2 = '...: '
722 722 sys.ps3 = 'Out: '
723 723
724 724 def init_display_formatter(self):
725 725 self.display_formatter = DisplayFormatter(parent=self)
726 726 self.configurables.append(self.display_formatter)
727 727
728 728 def init_display_pub(self):
729 729 self.display_pub = self.display_pub_class(parent=self, shell=self)
730 730 self.configurables.append(self.display_pub)
731 731
732 732 def init_data_pub(self):
733 733 if not self.data_pub_class:
734 734 self.data_pub = None
735 735 return
736 736 self.data_pub = self.data_pub_class(parent=self)
737 737 self.configurables.append(self.data_pub)
738 738
739 739 def init_displayhook(self):
740 740 # Initialize displayhook, set in/out prompts and printing system
741 741 self.displayhook = self.displayhook_class(
742 742 parent=self,
743 743 shell=self,
744 744 cache_size=self.cache_size,
745 745 )
746 746 self.configurables.append(self.displayhook)
747 747 # This is a context manager that installs/revmoes the displayhook at
748 748 # the appropriate time.
749 749 self.display_trap = DisplayTrap(hook=self.displayhook)
750 750
751 751 def init_virtualenv(self):
752 752 """Add the current virtualenv to sys.path so the user can import modules from it.
753 753 This isn't perfect: it doesn't use the Python interpreter with which the
754 754 virtualenv was built, and it ignores the --no-site-packages option. A
755 755 warning will appear suggesting the user installs IPython in the
756 756 virtualenv, but for many cases, it probably works well enough.
757 757
758 758 Adapted from code snippets online.
759 759
760 760 http://blog.ufsoft.org/2009/1/29/ipython-and-virtualenv
761 761 """
762 762 if 'VIRTUAL_ENV' not in os.environ:
763 763 # Not in a virtualenv
764 764 return
765 765 elif os.environ["VIRTUAL_ENV"] == "":
766 766 warn("Virtual env path set to '', please check if this is intended.")
767 767 return
768 768
769 769 p = Path(sys.executable)
770 770 p_venv = Path(os.environ["VIRTUAL_ENV"])
771 771
772 772 # fallback venv detection:
773 773 # stdlib venv may symlink sys.executable, so we can't use realpath.
774 774 # but others can symlink *to* the venv Python, so we can't just use sys.executable.
775 775 # So we just check every item in the symlink tree (generally <= 3)
776 776 paths = [p]
777 777 while p.is_symlink():
778 778 p = Path(os.readlink(p))
779 779 paths.append(p.resolve())
780 780
781 781 # In Cygwin paths like "c:\..." and '\cygdrive\c\...' are possible
782 782 if p_venv.parts[1] == "cygdrive":
783 783 drive_name = p_venv.parts[2]
784 784 p_venv = (drive_name + ":/") / Path(*p_venv.parts[3:])
785 785
786 786 if any(p_venv == p.parents[1] for p in paths):
787 787 # Our exe is inside or has access to the virtualenv, don't need to do anything.
788 788 return
789 789
790 790 if sys.platform == "win32":
791 791 virtual_env = str(Path(os.environ["VIRTUAL_ENV"], "Lib", "site-packages"))
792 792 else:
793 793 virtual_env_path = Path(
794 794 os.environ["VIRTUAL_ENV"], "lib", "python{}.{}", "site-packages"
795 795 )
796 796 p_ver = sys.version_info[:2]
797 797
798 798 # Predict version from py[thon]-x.x in the $VIRTUAL_ENV
799 799 re_m = re.search(r"\bpy(?:thon)?([23])\.(\d+)\b", os.environ["VIRTUAL_ENV"])
800 800 if re_m:
801 801 predicted_path = Path(str(virtual_env_path).format(*re_m.groups()))
802 802 if predicted_path.exists():
803 803 p_ver = re_m.groups()
804 804
805 805 virtual_env = str(virtual_env_path).format(*p_ver)
806 806
807 807 warn(
808 808 "Attempting to work in a virtualenv. If you encounter problems, "
809 809 "please install IPython inside the virtualenv."
810 810 )
811 811 import site
812 812 sys.path.insert(0, virtual_env)
813 813 site.addsitedir(virtual_env)
814 814
815 815 #-------------------------------------------------------------------------
816 816 # Things related to injections into the sys module
817 817 #-------------------------------------------------------------------------
818 818
819 819 def save_sys_module_state(self):
820 820 """Save the state of hooks in the sys module.
821 821
822 822 This has to be called after self.user_module is created.
823 823 """
824 824 self._orig_sys_module_state = {'stdin': sys.stdin,
825 825 'stdout': sys.stdout,
826 826 'stderr': sys.stderr,
827 827 'excepthook': sys.excepthook}
828 828 self._orig_sys_modules_main_name = self.user_module.__name__
829 829 self._orig_sys_modules_main_mod = sys.modules.get(self.user_module.__name__)
830 830
831 831 def restore_sys_module_state(self):
832 832 """Restore the state of the sys module."""
833 833 try:
834 834 for k, v in self._orig_sys_module_state.items():
835 835 setattr(sys, k, v)
836 836 except AttributeError:
837 837 pass
838 838 # Reset what what done in self.init_sys_modules
839 839 if self._orig_sys_modules_main_mod is not None:
840 840 sys.modules[self._orig_sys_modules_main_name] = self._orig_sys_modules_main_mod
841 841
842 842 #-------------------------------------------------------------------------
843 843 # Things related to the banner
844 844 #-------------------------------------------------------------------------
845 845
846 846 @property
847 847 def banner(self):
848 848 banner = self.banner1
849 849 if self.profile and self.profile != 'default':
850 850 banner += '\nIPython profile: %s\n' % self.profile
851 851 if self.banner2:
852 852 banner += '\n' + self.banner2
853 853 return banner
854 854
855 855 def show_banner(self, banner=None):
856 856 if banner is None:
857 857 banner = self.banner
858 858 sys.stdout.write(banner)
859 859
860 860 #-------------------------------------------------------------------------
861 861 # Things related to hooks
862 862 #-------------------------------------------------------------------------
863 863
864 864 def init_hooks(self):
865 865 # hooks holds pointers used for user-side customizations
866 866 self.hooks = Struct()
867 867
868 868 self.strdispatchers = {}
869 869
870 870 # Set all default hooks, defined in the IPython.hooks module.
871 871 hooks = IPython.core.hooks
872 872 for hook_name in hooks.__all__:
873 873 # default hooks have priority 100, i.e. low; user hooks should have
874 874 # 0-100 priority
875 875 self.set_hook(hook_name, getattr(hooks, hook_name), 100)
876 876
877 877 if self.display_page:
878 878 self.set_hook('show_in_pager', page.as_hook(page.display_page), 90)
879 879
880 880 def set_hook(self, name, hook, priority=50, str_key=None, re_key=None):
881 881 """set_hook(name,hook) -> sets an internal IPython hook.
882 882
883 883 IPython exposes some of its internal API as user-modifiable hooks. By
884 884 adding your function to one of these hooks, you can modify IPython's
885 885 behavior to call at runtime your own routines."""
886 886
887 887 # At some point in the future, this should validate the hook before it
888 888 # accepts it. Probably at least check that the hook takes the number
889 889 # of args it's supposed to.
890 890
891 891 f = types.MethodType(hook,self)
892 892
893 893 # check if the hook is for strdispatcher first
894 894 if str_key is not None:
895 895 sdp = self.strdispatchers.get(name, StrDispatch())
896 896 sdp.add_s(str_key, f, priority )
897 897 self.strdispatchers[name] = sdp
898 898 return
899 899 if re_key is not None:
900 900 sdp = self.strdispatchers.get(name, StrDispatch())
901 901 sdp.add_re(re.compile(re_key), f, priority )
902 902 self.strdispatchers[name] = sdp
903 903 return
904 904
905 905 dp = getattr(self.hooks, name, None)
906 906 if name not in IPython.core.hooks.__all__:
907 907 print("Warning! Hook '%s' is not one of %s" % \
908 908 (name, IPython.core.hooks.__all__ ))
909 909
910 910 if name in IPython.core.hooks.deprecated:
911 911 alternative = IPython.core.hooks.deprecated[name]
912 912 raise ValueError(
913 913 "Hook {} has been deprecated since IPython 5.0. Use {} instead.".format(
914 914 name, alternative
915 915 )
916 916 )
917 917
918 918 if not dp:
919 919 dp = IPython.core.hooks.CommandChainDispatcher()
920 920
921 921 try:
922 922 dp.add(f,priority)
923 923 except AttributeError:
924 924 # it was not commandchain, plain old func - replace
925 925 dp = f
926 926
927 927 setattr(self.hooks,name, dp)
928 928
929 929 #-------------------------------------------------------------------------
930 930 # Things related to events
931 931 #-------------------------------------------------------------------------
932 932
933 933 def init_events(self):
934 934 self.events = EventManager(self, available_events)
935 935
936 936 self.events.register("pre_execute", self._clear_warning_registry)
937 937
938 938 def register_post_execute(self, func):
939 939 """DEPRECATED: Use ip.events.register('post_run_cell', func)
940 940
941 941 Register a function for calling after code execution.
942 942 """
943 943 raise ValueError(
944 944 "ip.register_post_execute is deprecated since IPython 1.0, use "
945 945 "ip.events.register('post_run_cell', func) instead."
946 946 )
947 947
948 948 def _clear_warning_registry(self):
949 949 # clear the warning registry, so that different code blocks with
950 950 # overlapping line number ranges don't cause spurious suppression of
951 951 # warnings (see gh-6611 for details)
952 952 if "__warningregistry__" in self.user_global_ns:
953 953 del self.user_global_ns["__warningregistry__"]
954 954
955 955 #-------------------------------------------------------------------------
956 956 # Things related to the "main" module
957 957 #-------------------------------------------------------------------------
958 958
959 959 def new_main_mod(self, filename, modname):
960 960 """Return a new 'main' module object for user code execution.
961 961
962 962 ``filename`` should be the path of the script which will be run in the
963 963 module. Requests with the same filename will get the same module, with
964 964 its namespace cleared.
965 965
966 966 ``modname`` should be the module name - normally either '__main__' or
967 967 the basename of the file without the extension.
968 968
969 969 When scripts are executed via %run, we must keep a reference to their
970 970 __main__ module around so that Python doesn't
971 971 clear it, rendering references to module globals useless.
972 972
973 973 This method keeps said reference in a private dict, keyed by the
974 974 absolute path of the script. This way, for multiple executions of the
975 975 same script we only keep one copy of the namespace (the last one),
976 976 thus preventing memory leaks from old references while allowing the
977 977 objects from the last execution to be accessible.
978 978 """
979 979 filename = os.path.abspath(filename)
980 980 try:
981 981 main_mod = self._main_mod_cache[filename]
982 982 except KeyError:
983 983 main_mod = self._main_mod_cache[filename] = types.ModuleType(
984 984 modname,
985 985 doc="Module created for script run in IPython")
986 986 else:
987 987 main_mod.__dict__.clear()
988 988 main_mod.__name__ = modname
989 989
990 990 main_mod.__file__ = filename
991 991 # It seems pydoc (and perhaps others) needs any module instance to
992 992 # implement a __nonzero__ method
993 993 main_mod.__nonzero__ = lambda : True
994 994
995 995 return main_mod
996 996
997 997 def clear_main_mod_cache(self):
998 998 """Clear the cache of main modules.
999 999
1000 1000 Mainly for use by utilities like %reset.
1001 1001
1002 1002 Examples
1003 1003 --------
1004 1004 In [15]: import IPython
1005 1005
1006 1006 In [16]: m = _ip.new_main_mod(IPython.__file__, 'IPython')
1007 1007
1008 1008 In [17]: len(_ip._main_mod_cache) > 0
1009 1009 Out[17]: True
1010 1010
1011 1011 In [18]: _ip.clear_main_mod_cache()
1012 1012
1013 1013 In [19]: len(_ip._main_mod_cache) == 0
1014 1014 Out[19]: True
1015 1015 """
1016 1016 self._main_mod_cache.clear()
1017 1017
1018 1018 #-------------------------------------------------------------------------
1019 1019 # Things related to debugging
1020 1020 #-------------------------------------------------------------------------
1021 1021
1022 1022 def init_pdb(self):
1023 1023 # Set calling of pdb on exceptions
1024 1024 # self.call_pdb is a property
1025 1025 self.call_pdb = self.pdb
1026 1026
1027 1027 def _get_call_pdb(self):
1028 1028 return self._call_pdb
1029 1029
1030 1030 def _set_call_pdb(self,val):
1031 1031
1032 1032 if val not in (0,1,False,True):
1033 1033 raise ValueError('new call_pdb value must be boolean')
1034 1034
1035 1035 # store value in instance
1036 1036 self._call_pdb = val
1037 1037
1038 1038 # notify the actual exception handlers
1039 1039 self.InteractiveTB.call_pdb = val
1040 1040
1041 1041 call_pdb = property(_get_call_pdb,_set_call_pdb,None,
1042 1042 'Control auto-activation of pdb at exceptions')
1043 1043
1044 1044 def debugger(self,force=False):
1045 1045 """Call the pdb debugger.
1046 1046
1047 1047 Keywords:
1048 1048
1049 1049 - force(False): by default, this routine checks the instance call_pdb
1050 1050 flag and does not actually invoke the debugger if the flag is false.
1051 1051 The 'force' option forces the debugger to activate even if the flag
1052 1052 is false.
1053 1053 """
1054 1054
1055 1055 if not (force or self.call_pdb):
1056 1056 return
1057 1057
1058 1058 if not hasattr(sys,'last_traceback'):
1059 1059 error('No traceback has been produced, nothing to debug.')
1060 1060 return
1061 1061
1062 1062 self.InteractiveTB.debugger(force=True)
1063 1063
1064 1064 #-------------------------------------------------------------------------
1065 1065 # Things related to IPython's various namespaces
1066 1066 #-------------------------------------------------------------------------
1067 1067 default_user_namespaces = True
1068 1068
1069 1069 def init_create_namespaces(self, user_module=None, user_ns=None):
1070 1070 # Create the namespace where the user will operate. user_ns is
1071 1071 # normally the only one used, and it is passed to the exec calls as
1072 1072 # the locals argument. But we do carry a user_global_ns namespace
1073 1073 # given as the exec 'globals' argument, This is useful in embedding
1074 1074 # situations where the ipython shell opens in a context where the
1075 1075 # distinction between locals and globals is meaningful. For
1076 1076 # non-embedded contexts, it is just the same object as the user_ns dict.
1077 1077
1078 1078 # FIXME. For some strange reason, __builtins__ is showing up at user
1079 1079 # level as a dict instead of a module. This is a manual fix, but I
1080 1080 # should really track down where the problem is coming from. Alex
1081 1081 # Schmolck reported this problem first.
1082 1082
1083 1083 # A useful post by Alex Martelli on this topic:
1084 1084 # Re: inconsistent value from __builtins__
1085 1085 # Von: Alex Martelli <aleaxit@yahoo.com>
1086 1086 # Datum: Freitag 01 Oktober 2004 04:45:34 nachmittags/abends
1087 1087 # Gruppen: comp.lang.python
1088 1088
1089 1089 # Michael Hohn <hohn@hooknose.lbl.gov> wrote:
1090 1090 # > >>> print type(builtin_check.get_global_binding('__builtins__'))
1091 1091 # > <type 'dict'>
1092 1092 # > >>> print type(__builtins__)
1093 1093 # > <type 'module'>
1094 1094 # > Is this difference in return value intentional?
1095 1095
1096 1096 # Well, it's documented that '__builtins__' can be either a dictionary
1097 1097 # or a module, and it's been that way for a long time. Whether it's
1098 1098 # intentional (or sensible), I don't know. In any case, the idea is
1099 1099 # that if you need to access the built-in namespace directly, you
1100 1100 # should start with "import __builtin__" (note, no 's') which will
1101 1101 # definitely give you a module. Yeah, it's somewhat confusing:-(.
1102 1102
1103 1103 # These routines return a properly built module and dict as needed by
1104 1104 # the rest of the code, and can also be used by extension writers to
1105 1105 # generate properly initialized namespaces.
1106 1106 if (user_ns is not None) or (user_module is not None):
1107 1107 self.default_user_namespaces = False
1108 1108 self.user_module, self.user_ns = self.prepare_user_module(user_module, user_ns)
1109 1109
1110 1110 # A record of hidden variables we have added to the user namespace, so
1111 1111 # we can list later only variables defined in actual interactive use.
1112 1112 self.user_ns_hidden = {}
1113 1113
1114 1114 # Now that FakeModule produces a real module, we've run into a nasty
1115 1115 # problem: after script execution (via %run), the module where the user
1116 1116 # code ran is deleted. Now that this object is a true module (needed
1117 1117 # so doctest and other tools work correctly), the Python module
1118 1118 # teardown mechanism runs over it, and sets to None every variable
1119 1119 # present in that module. Top-level references to objects from the
1120 1120 # script survive, because the user_ns is updated with them. However,
1121 1121 # calling functions defined in the script that use other things from
1122 1122 # the script will fail, because the function's closure had references
1123 1123 # to the original objects, which are now all None. So we must protect
1124 1124 # these modules from deletion by keeping a cache.
1125 1125 #
1126 1126 # To avoid keeping stale modules around (we only need the one from the
1127 1127 # last run), we use a dict keyed with the full path to the script, so
1128 1128 # only the last version of the module is held in the cache. Note,
1129 1129 # however, that we must cache the module *namespace contents* (their
1130 1130 # __dict__). Because if we try to cache the actual modules, old ones
1131 1131 # (uncached) could be destroyed while still holding references (such as
1132 1132 # those held by GUI objects that tend to be long-lived)>
1133 1133 #
1134 1134 # The %reset command will flush this cache. See the cache_main_mod()
1135 1135 # and clear_main_mod_cache() methods for details on use.
1136 1136
1137 1137 # This is the cache used for 'main' namespaces
1138 1138 self._main_mod_cache = {}
1139 1139
1140 1140 # A table holding all the namespaces IPython deals with, so that
1141 1141 # introspection facilities can search easily.
1142 1142 self.ns_table = {'user_global':self.user_module.__dict__,
1143 1143 'user_local':self.user_ns,
1144 1144 'builtin':builtin_mod.__dict__
1145 1145 }
1146 1146
1147 1147 @property
1148 1148 def user_global_ns(self):
1149 1149 return self.user_module.__dict__
1150 1150
1151 1151 def prepare_user_module(self, user_module=None, user_ns=None):
1152 1152 """Prepare the module and namespace in which user code will be run.
1153 1153
1154 1154 When IPython is started normally, both parameters are None: a new module
1155 1155 is created automatically, and its __dict__ used as the namespace.
1156 1156
1157 1157 If only user_module is provided, its __dict__ is used as the namespace.
1158 1158 If only user_ns is provided, a dummy module is created, and user_ns
1159 1159 becomes the global namespace. If both are provided (as they may be
1160 1160 when embedding), user_ns is the local namespace, and user_module
1161 1161 provides the global namespace.
1162 1162
1163 1163 Parameters
1164 1164 ----------
1165 1165 user_module : module, optional
1166 1166 The current user module in which IPython is being run. If None,
1167 1167 a clean module will be created.
1168 1168 user_ns : dict, optional
1169 1169 A namespace in which to run interactive commands.
1170 1170
1171 1171 Returns
1172 1172 -------
1173 1173 A tuple of user_module and user_ns, each properly initialised.
1174 1174 """
1175 1175 if user_module is None and user_ns is not None:
1176 1176 user_ns.setdefault("__name__", "__main__")
1177 1177 user_module = DummyMod()
1178 1178 user_module.__dict__ = user_ns
1179 1179
1180 1180 if user_module is None:
1181 1181 user_module = types.ModuleType("__main__",
1182 1182 doc="Automatically created module for IPython interactive environment")
1183 1183
1184 1184 # We must ensure that __builtin__ (without the final 's') is always
1185 1185 # available and pointing to the __builtin__ *module*. For more details:
1186 1186 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
1187 1187 user_module.__dict__.setdefault('__builtin__', builtin_mod)
1188 1188 user_module.__dict__.setdefault('__builtins__', builtin_mod)
1189 1189
1190 1190 if user_ns is None:
1191 1191 user_ns = user_module.__dict__
1192 1192
1193 1193 return user_module, user_ns
1194 1194
1195 1195 def init_sys_modules(self):
1196 1196 # We need to insert into sys.modules something that looks like a
1197 1197 # module but which accesses the IPython namespace, for shelve and
1198 1198 # pickle to work interactively. Normally they rely on getting
1199 1199 # everything out of __main__, but for embedding purposes each IPython
1200 1200 # instance has its own private namespace, so we can't go shoving
1201 1201 # everything into __main__.
1202 1202
1203 1203 # note, however, that we should only do this for non-embedded
1204 1204 # ipythons, which really mimic the __main__.__dict__ with their own
1205 1205 # namespace. Embedded instances, on the other hand, should not do
1206 1206 # this because they need to manage the user local/global namespaces
1207 1207 # only, but they live within a 'normal' __main__ (meaning, they
1208 1208 # shouldn't overtake the execution environment of the script they're
1209 1209 # embedded in).
1210 1210
1211 1211 # This is overridden in the InteractiveShellEmbed subclass to a no-op.
1212 1212 main_name = self.user_module.__name__
1213 1213 sys.modules[main_name] = self.user_module
1214 1214
1215 1215 def init_user_ns(self):
1216 1216 """Initialize all user-visible namespaces to their minimum defaults.
1217 1217
1218 1218 Certain history lists are also initialized here, as they effectively
1219 1219 act as user namespaces.
1220 1220
1221 1221 Notes
1222 1222 -----
1223 1223 All data structures here are only filled in, they are NOT reset by this
1224 1224 method. If they were not empty before, data will simply be added to
1225 1225 them.
1226 1226 """
1227 1227 # This function works in two parts: first we put a few things in
1228 1228 # user_ns, and we sync that contents into user_ns_hidden so that these
1229 1229 # initial variables aren't shown by %who. After the sync, we add the
1230 1230 # rest of what we *do* want the user to see with %who even on a new
1231 1231 # session (probably nothing, so they really only see their own stuff)
1232 1232
1233 1233 # The user dict must *always* have a __builtin__ reference to the
1234 1234 # Python standard __builtin__ namespace, which must be imported.
1235 1235 # This is so that certain operations in prompt evaluation can be
1236 1236 # reliably executed with builtins. Note that we can NOT use
1237 1237 # __builtins__ (note the 's'), because that can either be a dict or a
1238 1238 # module, and can even mutate at runtime, depending on the context
1239 1239 # (Python makes no guarantees on it). In contrast, __builtin__ is
1240 1240 # always a module object, though it must be explicitly imported.
1241 1241
1242 1242 # For more details:
1243 1243 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
1244 1244 ns = {}
1245 1245
1246 1246 # make global variables for user access to the histories
1247 1247 ns['_ih'] = self.history_manager.input_hist_parsed
1248 1248 ns['_oh'] = self.history_manager.output_hist
1249 1249 ns['_dh'] = self.history_manager.dir_hist
1250 1250
1251 1251 # user aliases to input and output histories. These shouldn't show up
1252 1252 # in %who, as they can have very large reprs.
1253 1253 ns['In'] = self.history_manager.input_hist_parsed
1254 1254 ns['Out'] = self.history_manager.output_hist
1255 1255
1256 1256 # Store myself as the public api!!!
1257 1257 ns['get_ipython'] = self.get_ipython
1258 1258
1259 1259 ns['exit'] = self.exiter
1260 1260 ns['quit'] = self.exiter
1261 1261
1262 1262 # Sync what we've added so far to user_ns_hidden so these aren't seen
1263 1263 # by %who
1264 1264 self.user_ns_hidden.update(ns)
1265 1265
1266 1266 # Anything put into ns now would show up in %who. Think twice before
1267 1267 # putting anything here, as we really want %who to show the user their
1268 1268 # stuff, not our variables.
1269 1269
1270 1270 # Finally, update the real user's namespace
1271 1271 self.user_ns.update(ns)
1272 1272
1273 1273 @property
1274 1274 def all_ns_refs(self):
1275 1275 """Get a list of references to all the namespace dictionaries in which
1276 1276 IPython might store a user-created object.
1277 1277
1278 1278 Note that this does not include the displayhook, which also caches
1279 1279 objects from the output."""
1280 1280 return [self.user_ns, self.user_global_ns, self.user_ns_hidden] + \
1281 1281 [m.__dict__ for m in self._main_mod_cache.values()]
1282 1282
1283 1283 def reset(self, new_session=True, aggressive=False):
1284 1284 """Clear all internal namespaces, and attempt to release references to
1285 1285 user objects.
1286 1286
1287 1287 If new_session is True, a new history session will be opened.
1288 1288 """
1289 1289 # Clear histories
1290 1290 self.history_manager.reset(new_session)
1291 1291 # Reset counter used to index all histories
1292 1292 if new_session:
1293 1293 self.execution_count = 1
1294 1294
1295 1295 # Reset last execution result
1296 1296 self.last_execution_succeeded = True
1297 1297 self.last_execution_result = None
1298 1298
1299 1299 # Flush cached output items
1300 1300 if self.displayhook.do_full_cache:
1301 1301 self.displayhook.flush()
1302 1302
1303 1303 # The main execution namespaces must be cleared very carefully,
1304 1304 # skipping the deletion of the builtin-related keys, because doing so
1305 1305 # would cause errors in many object's __del__ methods.
1306 1306 if self.user_ns is not self.user_global_ns:
1307 1307 self.user_ns.clear()
1308 1308 ns = self.user_global_ns
1309 1309 drop_keys = set(ns.keys())
1310 1310 drop_keys.discard('__builtin__')
1311 1311 drop_keys.discard('__builtins__')
1312 1312 drop_keys.discard('__name__')
1313 1313 for k in drop_keys:
1314 1314 del ns[k]
1315 1315
1316 1316 self.user_ns_hidden.clear()
1317 1317
1318 1318 # Restore the user namespaces to minimal usability
1319 1319 self.init_user_ns()
1320 1320 if aggressive and not hasattr(self, "_sys_modules_keys"):
1321 1321 print("Cannot restore sys.module, no snapshot")
1322 1322 elif aggressive:
1323 1323 print("culling sys module...")
1324 1324 current_keys = set(sys.modules.keys())
1325 1325 for k in current_keys - self._sys_modules_keys:
1326 1326 if k.startswith("multiprocessing"):
1327 1327 continue
1328 1328 del sys.modules[k]
1329 1329
1330 1330 # Restore the default and user aliases
1331 1331 self.alias_manager.clear_aliases()
1332 1332 self.alias_manager.init_aliases()
1333 1333
1334 1334 # Now define aliases that only make sense on the terminal, because they
1335 1335 # need direct access to the console in a way that we can't emulate in
1336 1336 # GUI or web frontend
1337 1337 if os.name == 'posix':
1338 1338 for cmd in ('clear', 'more', 'less', 'man'):
1339 1339 if cmd not in self.magics_manager.magics['line']:
1340 1340 self.alias_manager.soft_define_alias(cmd, cmd)
1341 1341
1342 1342 # Flush the private list of module references kept for script
1343 1343 # execution protection
1344 1344 self.clear_main_mod_cache()
1345 1345
1346 1346 def del_var(self, varname, by_name=False):
1347 1347 """Delete a variable from the various namespaces, so that, as
1348 1348 far as possible, we're not keeping any hidden references to it.
1349 1349
1350 1350 Parameters
1351 1351 ----------
1352 1352 varname : str
1353 1353 The name of the variable to delete.
1354 1354 by_name : bool
1355 1355 If True, delete variables with the given name in each
1356 1356 namespace. If False (default), find the variable in the user
1357 1357 namespace, and delete references to it.
1358 1358 """
1359 1359 if varname in ('__builtin__', '__builtins__'):
1360 1360 raise ValueError("Refusing to delete %s" % varname)
1361 1361
1362 1362 ns_refs = self.all_ns_refs
1363 1363
1364 1364 if by_name: # Delete by name
1365 1365 for ns in ns_refs:
1366 1366 try:
1367 1367 del ns[varname]
1368 1368 except KeyError:
1369 1369 pass
1370 1370 else: # Delete by object
1371 1371 try:
1372 1372 obj = self.user_ns[varname]
1373 1373 except KeyError as e:
1374 1374 raise NameError("name '%s' is not defined" % varname) from e
1375 1375 # Also check in output history
1376 1376 ns_refs.append(self.history_manager.output_hist)
1377 1377 for ns in ns_refs:
1378 1378 to_delete = [n for n, o in ns.items() if o is obj]
1379 1379 for name in to_delete:
1380 1380 del ns[name]
1381 1381
1382 1382 # Ensure it is removed from the last execution result
1383 1383 if self.last_execution_result.result is obj:
1384 1384 self.last_execution_result = None
1385 1385
1386 1386 # displayhook keeps extra references, but not in a dictionary
1387 1387 for name in ('_', '__', '___'):
1388 1388 if getattr(self.displayhook, name) is obj:
1389 1389 setattr(self.displayhook, name, None)
1390 1390
1391 1391 def reset_selective(self, regex=None):
1392 1392 """Clear selective variables from internal namespaces based on a
1393 1393 specified regular expression.
1394 1394
1395 1395 Parameters
1396 1396 ----------
1397 1397 regex : string or compiled pattern, optional
1398 1398 A regular expression pattern that will be used in searching
1399 1399 variable names in the users namespaces.
1400 1400 """
1401 1401 if regex is not None:
1402 1402 try:
1403 1403 m = re.compile(regex)
1404 1404 except TypeError as e:
1405 1405 raise TypeError('regex must be a string or compiled pattern') from e
1406 1406 # Search for keys in each namespace that match the given regex
1407 1407 # If a match is found, delete the key/value pair.
1408 1408 for ns in self.all_ns_refs:
1409 1409 for var in ns:
1410 1410 if m.search(var):
1411 1411 del ns[var]
1412 1412
1413 1413 def push(self, variables, interactive=True):
1414 1414 """Inject a group of variables into the IPython user namespace.
1415 1415
1416 1416 Parameters
1417 1417 ----------
1418 1418 variables : dict, str or list/tuple of str
1419 1419 The variables to inject into the user's namespace. If a dict, a
1420 1420 simple update is done. If a str, the string is assumed to have
1421 1421 variable names separated by spaces. A list/tuple of str can also
1422 1422 be used to give the variable names. If just the variable names are
1423 1423 give (list/tuple/str) then the variable values looked up in the
1424 1424 callers frame.
1425 1425 interactive : bool
1426 1426 If True (default), the variables will be listed with the ``who``
1427 1427 magic.
1428 1428 """
1429 1429 vdict = None
1430 1430
1431 1431 # We need a dict of name/value pairs to do namespace updates.
1432 1432 if isinstance(variables, dict):
1433 1433 vdict = variables
1434 1434 elif isinstance(variables, (str, list, tuple)):
1435 1435 if isinstance(variables, str):
1436 1436 vlist = variables.split()
1437 1437 else:
1438 1438 vlist = variables
1439 1439 vdict = {}
1440 1440 cf = sys._getframe(1)
1441 1441 for name in vlist:
1442 1442 try:
1443 1443 vdict[name] = eval(name, cf.f_globals, cf.f_locals)
1444 1444 except:
1445 1445 print('Could not get variable %s from %s' %
1446 1446 (name,cf.f_code.co_name))
1447 1447 else:
1448 1448 raise ValueError('variables must be a dict/str/list/tuple')
1449 1449
1450 1450 # Propagate variables to user namespace
1451 1451 self.user_ns.update(vdict)
1452 1452
1453 1453 # And configure interactive visibility
1454 1454 user_ns_hidden = self.user_ns_hidden
1455 1455 if interactive:
1456 1456 for name in vdict:
1457 1457 user_ns_hidden.pop(name, None)
1458 1458 else:
1459 1459 user_ns_hidden.update(vdict)
1460 1460
1461 1461 def drop_by_id(self, variables):
1462 1462 """Remove a dict of variables from the user namespace, if they are the
1463 1463 same as the values in the dictionary.
1464 1464
1465 1465 This is intended for use by extensions: variables that they've added can
1466 1466 be taken back out if they are unloaded, without removing any that the
1467 1467 user has overwritten.
1468 1468
1469 1469 Parameters
1470 1470 ----------
1471 1471 variables : dict
1472 1472 A dictionary mapping object names (as strings) to the objects.
1473 1473 """
1474 1474 for name, obj in variables.items():
1475 1475 if name in self.user_ns and self.user_ns[name] is obj:
1476 1476 del self.user_ns[name]
1477 1477 self.user_ns_hidden.pop(name, None)
1478 1478
1479 1479 #-------------------------------------------------------------------------
1480 1480 # Things related to object introspection
1481 1481 #-------------------------------------------------------------------------
1482 1482
1483 1483 def _ofind(self, oname, namespaces=None):
1484 1484 """Find an object in the available namespaces.
1485 1485
1486 1486 self._ofind(oname) -> dict with keys: found,obj,ospace,ismagic
1487 1487
1488 1488 Has special code to detect magic functions.
1489 1489 """
1490 1490 oname = oname.strip()
1491 1491 if not oname.startswith(ESC_MAGIC) and \
1492 1492 not oname.startswith(ESC_MAGIC2) and \
1493 1493 not all(a.isidentifier() for a in oname.split(".")):
1494 1494 return {'found': False}
1495 1495
1496 1496 if namespaces is None:
1497 1497 # Namespaces to search in:
1498 1498 # Put them in a list. The order is important so that we
1499 1499 # find things in the same order that Python finds them.
1500 1500 namespaces = [ ('Interactive', self.user_ns),
1501 1501 ('Interactive (global)', self.user_global_ns),
1502 1502 ('Python builtin', builtin_mod.__dict__),
1503 1503 ]
1504 1504
1505 1505 ismagic = False
1506 1506 isalias = False
1507 1507 found = False
1508 1508 ospace = None
1509 1509 parent = None
1510 1510 obj = None
1511 1511
1512 1512
1513 1513 # Look for the given name by splitting it in parts. If the head is
1514 1514 # found, then we look for all the remaining parts as members, and only
1515 1515 # declare success if we can find them all.
1516 1516 oname_parts = oname.split('.')
1517 1517 oname_head, oname_rest = oname_parts[0],oname_parts[1:]
1518 1518 for nsname,ns in namespaces:
1519 1519 try:
1520 1520 obj = ns[oname_head]
1521 1521 except KeyError:
1522 1522 continue
1523 1523 else:
1524 1524 for idx, part in enumerate(oname_rest):
1525 1525 try:
1526 1526 parent = obj
1527 1527 # The last part is looked up in a special way to avoid
1528 1528 # descriptor invocation as it may raise or have side
1529 1529 # effects.
1530 1530 if idx == len(oname_rest) - 1:
1531 1531 obj = self._getattr_property(obj, part)
1532 1532 else:
1533 1533 obj = getattr(obj, part)
1534 1534 except:
1535 1535 # Blanket except b/c some badly implemented objects
1536 1536 # allow __getattr__ to raise exceptions other than
1537 1537 # AttributeError, which then crashes IPython.
1538 1538 break
1539 1539 else:
1540 1540 # If we finish the for loop (no break), we got all members
1541 1541 found = True
1542 1542 ospace = nsname
1543 1543 break # namespace loop
1544 1544
1545 1545 # Try to see if it's magic
1546 1546 if not found:
1547 1547 obj = None
1548 1548 if oname.startswith(ESC_MAGIC2):
1549 1549 oname = oname.lstrip(ESC_MAGIC2)
1550 1550 obj = self.find_cell_magic(oname)
1551 1551 elif oname.startswith(ESC_MAGIC):
1552 1552 oname = oname.lstrip(ESC_MAGIC)
1553 1553 obj = self.find_line_magic(oname)
1554 1554 else:
1555 1555 # search without prefix, so run? will find %run?
1556 1556 obj = self.find_line_magic(oname)
1557 1557 if obj is None:
1558 1558 obj = self.find_cell_magic(oname)
1559 1559 if obj is not None:
1560 1560 found = True
1561 1561 ospace = 'IPython internal'
1562 1562 ismagic = True
1563 1563 isalias = isinstance(obj, Alias)
1564 1564
1565 1565 # Last try: special-case some literals like '', [], {}, etc:
1566 1566 if not found and oname_head in ["''",'""','[]','{}','()']:
1567 1567 obj = eval(oname_head)
1568 1568 found = True
1569 1569 ospace = 'Interactive'
1570 1570
1571 1571 return {
1572 1572 'obj':obj,
1573 1573 'found':found,
1574 1574 'parent':parent,
1575 1575 'ismagic':ismagic,
1576 1576 'isalias':isalias,
1577 1577 'namespace':ospace
1578 1578 }
1579 1579
1580 1580 @staticmethod
1581 1581 def _getattr_property(obj, attrname):
1582 1582 """Property-aware getattr to use in object finding.
1583 1583
1584 1584 If attrname represents a property, return it unevaluated (in case it has
1585 1585 side effects or raises an error.
1586 1586
1587 1587 """
1588 1588 if not isinstance(obj, type):
1589 1589 try:
1590 1590 # `getattr(type(obj), attrname)` is not guaranteed to return
1591 1591 # `obj`, but does so for property:
1592 1592 #
1593 1593 # property.__get__(self, None, cls) -> self
1594 1594 #
1595 1595 # The universal alternative is to traverse the mro manually
1596 1596 # searching for attrname in class dicts.
1597 1597 attr = getattr(type(obj), attrname)
1598 1598 except AttributeError:
1599 1599 pass
1600 1600 else:
1601 1601 # This relies on the fact that data descriptors (with both
1602 1602 # __get__ & __set__ magic methods) take precedence over
1603 1603 # instance-level attributes:
1604 1604 #
1605 1605 # class A(object):
1606 1606 # @property
1607 1607 # def foobar(self): return 123
1608 1608 # a = A()
1609 1609 # a.__dict__['foobar'] = 345
1610 1610 # a.foobar # == 123
1611 1611 #
1612 1612 # So, a property may be returned right away.
1613 1613 if isinstance(attr, property):
1614 1614 return attr
1615 1615
1616 1616 # Nothing helped, fall back.
1617 1617 return getattr(obj, attrname)
1618 1618
1619 1619 def _object_find(self, oname, namespaces=None):
1620 1620 """Find an object and return a struct with info about it."""
1621 1621 return Struct(self._ofind(oname, namespaces))
1622 1622
1623 1623 def _inspect(self, meth, oname, namespaces=None, **kw):
1624 1624 """Generic interface to the inspector system.
1625 1625
1626 1626 This function is meant to be called by pdef, pdoc & friends.
1627 1627 """
1628 1628 info = self._object_find(oname, namespaces)
1629 1629 docformat = (
1630 1630 sphinxify(self.object_inspect(oname)) if self.sphinxify_docstring else None
1631 1631 )
1632 1632 if info.found:
1633 1633 pmethod = getattr(self.inspector, meth)
1634 1634 # TODO: only apply format_screen to the plain/text repr of the mime
1635 1635 # bundle.
1636 1636 formatter = format_screen if info.ismagic else docformat
1637 1637 if meth == 'pdoc':
1638 1638 pmethod(info.obj, oname, formatter)
1639 1639 elif meth == 'pinfo':
1640 1640 pmethod(
1641 1641 info.obj,
1642 1642 oname,
1643 1643 formatter,
1644 1644 info,
1645 1645 enable_html_pager=self.enable_html_pager,
1646 1646 **kw
1647 1647 )
1648 1648 else:
1649 1649 pmethod(info.obj, oname)
1650 1650 else:
1651 1651 print('Object `%s` not found.' % oname)
1652 1652 return 'not found' # so callers can take other action
1653 1653
1654 1654 def object_inspect(self, oname, detail_level=0):
1655 1655 """Get object info about oname"""
1656 1656 with self.builtin_trap:
1657 1657 info = self._object_find(oname)
1658 1658 if info.found:
1659 1659 return self.inspector.info(info.obj, oname, info=info,
1660 1660 detail_level=detail_level
1661 1661 )
1662 1662 else:
1663 1663 return oinspect.object_info(name=oname, found=False)
1664 1664
1665 1665 def object_inspect_text(self, oname, detail_level=0):
1666 1666 """Get object info as formatted text"""
1667 1667 return self.object_inspect_mime(oname, detail_level)['text/plain']
1668 1668
1669 1669 def object_inspect_mime(self, oname, detail_level=0, omit_sections=()):
1670 1670 """Get object info as a mimebundle of formatted representations.
1671 1671
1672 1672 A mimebundle is a dictionary, keyed by mime-type.
1673 1673 It must always have the key `'text/plain'`.
1674 1674 """
1675 1675 with self.builtin_trap:
1676 1676 info = self._object_find(oname)
1677 1677 if info.found:
1678 1678 docformat = (
1679 1679 sphinxify(self.object_inspect(oname))
1680 1680 if self.sphinxify_docstring
1681 1681 else None
1682 1682 )
1683 1683 return self.inspector._get_info(
1684 1684 info.obj,
1685 1685 oname,
1686 1686 info=info,
1687 1687 detail_level=detail_level,
1688 1688 formatter=docformat,
1689 1689 omit_sections=omit_sections,
1690 1690 )
1691 1691 else:
1692 1692 raise KeyError(oname)
1693 1693
1694 1694 #-------------------------------------------------------------------------
1695 1695 # Things related to history management
1696 1696 #-------------------------------------------------------------------------
1697 1697
1698 1698 def init_history(self):
1699 1699 """Sets up the command history, and starts regular autosaves."""
1700 1700 self.history_manager = HistoryManager(shell=self, parent=self)
1701 1701 self.configurables.append(self.history_manager)
1702 1702
1703 1703 #-------------------------------------------------------------------------
1704 1704 # Things related to exception handling and tracebacks (not debugging)
1705 1705 #-------------------------------------------------------------------------
1706 1706
1707 1707 debugger_cls = InterruptiblePdb
1708 1708
1709 1709 def init_traceback_handlers(self, custom_exceptions):
1710 1710 # Syntax error handler.
1711 1711 self.SyntaxTB = ultratb.SyntaxTB(color_scheme='NoColor', parent=self)
1712 1712
1713 1713 # The interactive one is initialized with an offset, meaning we always
1714 1714 # want to remove the topmost item in the traceback, which is our own
1715 1715 # internal code. Valid modes: ['Plain','Context','Verbose','Minimal']
1716 1716 self.InteractiveTB = ultratb.AutoFormattedTB(mode = 'Plain',
1717 1717 color_scheme='NoColor',
1718 1718 tb_offset = 1,
1719 1719 check_cache=check_linecache_ipython,
1720 1720 debugger_cls=self.debugger_cls, parent=self)
1721 1721
1722 1722 # The instance will store a pointer to the system-wide exception hook,
1723 1723 # so that runtime code (such as magics) can access it. This is because
1724 1724 # during the read-eval loop, it may get temporarily overwritten.
1725 1725 self.sys_excepthook = sys.excepthook
1726 1726
1727 1727 # and add any custom exception handlers the user may have specified
1728 1728 self.set_custom_exc(*custom_exceptions)
1729 1729
1730 1730 # Set the exception mode
1731 1731 self.InteractiveTB.set_mode(mode=self.xmode)
1732 1732
1733 1733 def set_custom_exc(self, exc_tuple, handler):
1734 1734 """set_custom_exc(exc_tuple, handler)
1735 1735
1736 1736 Set a custom exception handler, which will be called if any of the
1737 1737 exceptions in exc_tuple occur in the mainloop (specifically, in the
1738 1738 run_code() method).
1739 1739
1740 1740 Parameters
1741 1741 ----------
1742 1742 exc_tuple : tuple of exception classes
1743 1743 A *tuple* of exception classes, for which to call the defined
1744 1744 handler. It is very important that you use a tuple, and NOT A
1745 1745 LIST here, because of the way Python's except statement works. If
1746 1746 you only want to trap a single exception, use a singleton tuple::
1747 1747
1748 1748 exc_tuple == (MyCustomException,)
1749 1749
1750 1750 handler : callable
1751 1751 handler must have the following signature::
1752 1752
1753 1753 def my_handler(self, etype, value, tb, tb_offset=None):
1754 1754 ...
1755 1755 return structured_traceback
1756 1756
1757 1757 Your handler must return a structured traceback (a list of strings),
1758 1758 or None.
1759 1759
1760 1760 This will be made into an instance method (via types.MethodType)
1761 1761 of IPython itself, and it will be called if any of the exceptions
1762 1762 listed in the exc_tuple are caught. If the handler is None, an
1763 1763 internal basic one is used, which just prints basic info.
1764 1764
1765 1765 To protect IPython from crashes, if your handler ever raises an
1766 1766 exception or returns an invalid result, it will be immediately
1767 1767 disabled.
1768 1768
1769 1769 Notes
1770 1770 -----
1771 1771 WARNING: by putting in your own exception handler into IPython's main
1772 1772 execution loop, you run a very good chance of nasty crashes. This
1773 1773 facility should only be used if you really know what you are doing.
1774 1774 """
1775 1775
1776 1776 if not isinstance(exc_tuple, tuple):
1777 1777 raise TypeError("The custom exceptions must be given as a tuple.")
1778 1778
1779 1779 def dummy_handler(self, etype, value, tb, tb_offset=None):
1780 1780 print('*** Simple custom exception handler ***')
1781 1781 print('Exception type :', etype)
1782 1782 print('Exception value:', value)
1783 1783 print('Traceback :', tb)
1784 1784
1785 1785 def validate_stb(stb):
1786 1786 """validate structured traceback return type
1787 1787
1788 1788 return type of CustomTB *should* be a list of strings, but allow
1789 1789 single strings or None, which are harmless.
1790 1790
1791 1791 This function will *always* return a list of strings,
1792 1792 and will raise a TypeError if stb is inappropriate.
1793 1793 """
1794 1794 msg = "CustomTB must return list of strings, not %r" % stb
1795 1795 if stb is None:
1796 1796 return []
1797 1797 elif isinstance(stb, str):
1798 1798 return [stb]
1799 1799 elif not isinstance(stb, list):
1800 1800 raise TypeError(msg)
1801 1801 # it's a list
1802 1802 for line in stb:
1803 1803 # check every element
1804 1804 if not isinstance(line, str):
1805 1805 raise TypeError(msg)
1806 1806 return stb
1807 1807
1808 1808 if handler is None:
1809 1809 wrapped = dummy_handler
1810 1810 else:
1811 1811 def wrapped(self,etype,value,tb,tb_offset=None):
1812 1812 """wrap CustomTB handler, to protect IPython from user code
1813 1813
1814 1814 This makes it harder (but not impossible) for custom exception
1815 1815 handlers to crash IPython.
1816 1816 """
1817 1817 try:
1818 1818 stb = handler(self,etype,value,tb,tb_offset=tb_offset)
1819 1819 return validate_stb(stb)
1820 1820 except:
1821 1821 # clear custom handler immediately
1822 1822 self.set_custom_exc((), None)
1823 1823 print("Custom TB Handler failed, unregistering", file=sys.stderr)
1824 1824 # show the exception in handler first
1825 1825 stb = self.InteractiveTB.structured_traceback(*sys.exc_info())
1826 1826 print(self.InteractiveTB.stb2text(stb))
1827 1827 print("The original exception:")
1828 1828 stb = self.InteractiveTB.structured_traceback(
1829 1829 (etype,value,tb), tb_offset=tb_offset
1830 1830 )
1831 1831 return stb
1832 1832
1833 1833 self.CustomTB = types.MethodType(wrapped,self)
1834 1834 self.custom_exceptions = exc_tuple
1835 1835
1836 1836 def excepthook(self, etype, value, tb):
1837 1837 """One more defense for GUI apps that call sys.excepthook.
1838 1838
1839 1839 GUI frameworks like wxPython trap exceptions and call
1840 1840 sys.excepthook themselves. I guess this is a feature that
1841 1841 enables them to keep running after exceptions that would
1842 1842 otherwise kill their mainloop. This is a bother for IPython
1843 1843 which expects to catch all of the program exceptions with a try:
1844 1844 except: statement.
1845 1845
1846 1846 Normally, IPython sets sys.excepthook to a CrashHandler instance, so if
1847 1847 any app directly invokes sys.excepthook, it will look to the user like
1848 1848 IPython crashed. In order to work around this, we can disable the
1849 1849 CrashHandler and replace it with this excepthook instead, which prints a
1850 1850 regular traceback using our InteractiveTB. In this fashion, apps which
1851 1851 call sys.excepthook will generate a regular-looking exception from
1852 1852 IPython, and the CrashHandler will only be triggered by real IPython
1853 1853 crashes.
1854 1854
1855 1855 This hook should be used sparingly, only in places which are not likely
1856 1856 to be true IPython errors.
1857 1857 """
1858 1858 self.showtraceback((etype, value, tb), tb_offset=0)
1859 1859
1860 1860 def _get_exc_info(self, exc_tuple=None):
1861 1861 """get exc_info from a given tuple, sys.exc_info() or sys.last_type etc.
1862 1862
1863 1863 Ensures sys.last_type,value,traceback hold the exc_info we found,
1864 1864 from whichever source.
1865 1865
1866 1866 raises ValueError if none of these contain any information
1867 1867 """
1868 1868 if exc_tuple is None:
1869 1869 etype, value, tb = sys.exc_info()
1870 1870 else:
1871 1871 etype, value, tb = exc_tuple
1872 1872
1873 1873 if etype is None:
1874 1874 if hasattr(sys, 'last_type'):
1875 1875 etype, value, tb = sys.last_type, sys.last_value, \
1876 1876 sys.last_traceback
1877 1877
1878 1878 if etype is None:
1879 1879 raise ValueError("No exception to find")
1880 1880
1881 1881 # Now store the exception info in sys.last_type etc.
1882 1882 # WARNING: these variables are somewhat deprecated and not
1883 1883 # necessarily safe to use in a threaded environment, but tools
1884 1884 # like pdb depend on their existence, so let's set them. If we
1885 1885 # find problems in the field, we'll need to revisit their use.
1886 1886 sys.last_type = etype
1887 1887 sys.last_value = value
1888 1888 sys.last_traceback = tb
1889 1889
1890 1890 return etype, value, tb
1891 1891
1892 1892 def show_usage_error(self, exc):
1893 1893 """Show a short message for UsageErrors
1894 1894
1895 1895 These are special exceptions that shouldn't show a traceback.
1896 1896 """
1897 1897 print("UsageError: %s" % exc, file=sys.stderr)
1898 1898
1899 1899 def get_exception_only(self, exc_tuple=None):
1900 1900 """
1901 1901 Return as a string (ending with a newline) the exception that
1902 1902 just occurred, without any traceback.
1903 1903 """
1904 1904 etype, value, tb = self._get_exc_info(exc_tuple)
1905 1905 msg = traceback.format_exception_only(etype, value)
1906 1906 return ''.join(msg)
1907 1907
1908 1908 def showtraceback(self, exc_tuple=None, filename=None, tb_offset=None,
1909 1909 exception_only=False, running_compiled_code=False):
1910 1910 """Display the exception that just occurred.
1911 1911
1912 1912 If nothing is known about the exception, this is the method which
1913 1913 should be used throughout the code for presenting user tracebacks,
1914 1914 rather than directly invoking the InteractiveTB object.
1915 1915
1916 1916 A specific showsyntaxerror() also exists, but this method can take
1917 1917 care of calling it if needed, so unless you are explicitly catching a
1918 1918 SyntaxError exception, don't try to analyze the stack manually and
1919 1919 simply call this method."""
1920 1920
1921 1921 try:
1922 1922 try:
1923 1923 etype, value, tb = self._get_exc_info(exc_tuple)
1924 1924 except ValueError:
1925 1925 print('No traceback available to show.', file=sys.stderr)
1926 1926 return
1927 1927
1928 1928 if issubclass(etype, SyntaxError):
1929 1929 # Though this won't be called by syntax errors in the input
1930 1930 # line, there may be SyntaxError cases with imported code.
1931 1931 self.showsyntaxerror(filename, running_compiled_code)
1932 1932 elif etype is UsageError:
1933 1933 self.show_usage_error(value)
1934 1934 else:
1935 1935 if exception_only:
1936 1936 stb = ['An exception has occurred, use %tb to see '
1937 1937 'the full traceback.\n']
1938 1938 stb.extend(self.InteractiveTB.get_exception_only(etype,
1939 1939 value))
1940 1940 else:
1941 1941 try:
1942 1942 # Exception classes can customise their traceback - we
1943 1943 # use this in IPython.parallel for exceptions occurring
1944 1944 # in the engines. This should return a list of strings.
1945 1945 stb = value._render_traceback_()
1946 1946 except Exception:
1947 1947 stb = self.InteractiveTB.structured_traceback(etype,
1948 1948 value, tb, tb_offset=tb_offset)
1949 1949
1950 1950 self._showtraceback(etype, value, stb)
1951 1951 if self.call_pdb:
1952 1952 # drop into debugger
1953 1953 self.debugger(force=True)
1954 1954 return
1955 1955
1956 1956 # Actually show the traceback
1957 1957 self._showtraceback(etype, value, stb)
1958 1958
1959 1959 except KeyboardInterrupt:
1960 1960 print('\n' + self.get_exception_only(), file=sys.stderr)
1961 1961
1962 1962 def _showtraceback(self, etype, evalue, stb: str):
1963 1963 """Actually show a traceback.
1964 1964
1965 1965 Subclasses may override this method to put the traceback on a different
1966 1966 place, like a side channel.
1967 1967 """
1968 1968 val = self.InteractiveTB.stb2text(stb)
1969 1969 try:
1970 1970 print(val)
1971 1971 except UnicodeEncodeError:
1972 1972 print(val.encode("utf-8", "backslashreplace").decode())
1973 1973
1974 1974 def showsyntaxerror(self, filename=None, running_compiled_code=False):
1975 1975 """Display the syntax error that just occurred.
1976 1976
1977 1977 This doesn't display a stack trace because there isn't one.
1978 1978
1979 1979 If a filename is given, it is stuffed in the exception instead
1980 1980 of what was there before (because Python's parser always uses
1981 1981 "<string>" when reading from a string).
1982 1982
1983 1983 If the syntax error occurred when running a compiled code (i.e. running_compile_code=True),
1984 1984 longer stack trace will be displayed.
1985 1985 """
1986 1986 etype, value, last_traceback = self._get_exc_info()
1987 1987
1988 1988 if filename and issubclass(etype, SyntaxError):
1989 1989 try:
1990 1990 value.filename = filename
1991 1991 except:
1992 1992 # Not the format we expect; leave it alone
1993 1993 pass
1994 1994
1995 1995 # If the error occurred when executing compiled code, we should provide full stacktrace.
1996 1996 elist = traceback.extract_tb(last_traceback) if running_compiled_code else []
1997 1997 stb = self.SyntaxTB.structured_traceback(etype, value, elist)
1998 1998 self._showtraceback(etype, value, stb)
1999 1999
2000 2000 # This is overridden in TerminalInteractiveShell to show a message about
2001 2001 # the %paste magic.
2002 2002 def showindentationerror(self):
2003 2003 """Called by _run_cell when there's an IndentationError in code entered
2004 2004 at the prompt.
2005 2005
2006 2006 This is overridden in TerminalInteractiveShell to show a message about
2007 2007 the %paste magic."""
2008 2008 self.showsyntaxerror()
2009 2009
2010 2010 @skip_doctest
2011 2011 def set_next_input(self, s, replace=False):
2012 2012 """ Sets the 'default' input string for the next command line.
2013 2013
2014 2014 Example::
2015 2015
2016 2016 In [1]: _ip.set_next_input("Hello Word")
2017 2017 In [2]: Hello Word_ # cursor is here
2018 2018 """
2019 2019 self.rl_next_input = s
2020 2020
2021 2021 def _indent_current_str(self):
2022 2022 """return the current level of indentation as a string"""
2023 2023 return self.input_splitter.get_indent_spaces() * ' '
2024 2024
2025 2025 #-------------------------------------------------------------------------
2026 2026 # Things related to text completion
2027 2027 #-------------------------------------------------------------------------
2028 2028
2029 2029 def init_completer(self):
2030 2030 """Initialize the completion machinery.
2031 2031
2032 2032 This creates completion machinery that can be used by client code,
2033 2033 either interactively in-process (typically triggered by the readline
2034 2034 library), programmatically (such as in test suites) or out-of-process
2035 2035 (typically over the network by remote frontends).
2036 2036 """
2037 2037 from IPython.core.completer import IPCompleter
2038 2038 from IPython.core.completerlib import (module_completer,
2039 2039 magic_run_completer, cd_completer, reset_completer)
2040 2040
2041 2041 self.Completer = IPCompleter(shell=self,
2042 2042 namespace=self.user_ns,
2043 2043 global_namespace=self.user_global_ns,
2044 2044 parent=self,
2045 2045 )
2046 2046 self.configurables.append(self.Completer)
2047 2047
2048 2048 # Add custom completers to the basic ones built into IPCompleter
2049 2049 sdisp = self.strdispatchers.get('complete_command', StrDispatch())
2050 2050 self.strdispatchers['complete_command'] = sdisp
2051 2051 self.Completer.custom_completers = sdisp
2052 2052
2053 2053 self.set_hook('complete_command', module_completer, str_key = 'import')
2054 2054 self.set_hook('complete_command', module_completer, str_key = 'from')
2055 2055 self.set_hook('complete_command', module_completer, str_key = '%aimport')
2056 2056 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
2057 2057 self.set_hook('complete_command', cd_completer, str_key = '%cd')
2058 2058 self.set_hook('complete_command', reset_completer, str_key = '%reset')
2059 2059
2060 2060 @skip_doctest
2061 2061 def complete(self, text, line=None, cursor_pos=None):
2062 2062 """Return the completed text and a list of completions.
2063 2063
2064 2064 Parameters
2065 2065 ----------
2066 2066 text : string
2067 2067 A string of text to be completed on. It can be given as empty and
2068 2068 instead a line/position pair are given. In this case, the
2069 2069 completer itself will split the line like readline does.
2070 2070 line : string, optional
2071 2071 The complete line that text is part of.
2072 2072 cursor_pos : int, optional
2073 2073 The position of the cursor on the input line.
2074 2074
2075 2075 Returns
2076 2076 -------
2077 2077 text : string
2078 2078 The actual text that was completed.
2079 2079 matches : list
2080 2080 A sorted list with all possible completions.
2081 2081
2082 2082 Notes
2083 2083 -----
2084 2084 The optional arguments allow the completion to take more context into
2085 2085 account, and are part of the low-level completion API.
2086 2086
2087 2087 This is a wrapper around the completion mechanism, similar to what
2088 2088 readline does at the command line when the TAB key is hit. By
2089 2089 exposing it as a method, it can be used by other non-readline
2090 2090 environments (such as GUIs) for text completion.
2091 2091
2092 2092 Examples
2093 2093 --------
2094 2094 In [1]: x = 'hello'
2095 2095
2096 2096 In [2]: _ip.complete('x.l')
2097 2097 Out[2]: ('x.l', ['x.ljust', 'x.lower', 'x.lstrip'])
2098 2098 """
2099 2099
2100 2100 # Inject names into __builtin__ so we can complete on the added names.
2101 2101 with self.builtin_trap:
2102 2102 return self.Completer.complete(text, line, cursor_pos)
2103 2103
2104 2104 def set_custom_completer(self, completer, pos=0) -> None:
2105 2105 """Adds a new custom completer function.
2106 2106
2107 2107 The position argument (defaults to 0) is the index in the completers
2108 2108 list where you want the completer to be inserted.
2109 2109
2110 2110 `completer` should have the following signature::
2111 2111
2112 2112 def completion(self: Completer, text: string) -> List[str]:
2113 2113 raise NotImplementedError
2114 2114
2115 2115 It will be bound to the current Completer instance and pass some text
2116 2116 and return a list with current completions to suggest to the user.
2117 2117 """
2118 2118
2119 2119 newcomp = types.MethodType(completer, self.Completer)
2120 2120 self.Completer.custom_matchers.insert(pos,newcomp)
2121 2121
2122 2122 def set_completer_frame(self, frame=None):
2123 2123 """Set the frame of the completer."""
2124 2124 if frame:
2125 2125 self.Completer.namespace = frame.f_locals
2126 2126 self.Completer.global_namespace = frame.f_globals
2127 2127 else:
2128 2128 self.Completer.namespace = self.user_ns
2129 2129 self.Completer.global_namespace = self.user_global_ns
2130 2130
2131 2131 #-------------------------------------------------------------------------
2132 2132 # Things related to magics
2133 2133 #-------------------------------------------------------------------------
2134 2134
2135 2135 def init_magics(self):
2136 2136 from IPython.core import magics as m
2137 2137 self.magics_manager = magic.MagicsManager(shell=self,
2138 2138 parent=self,
2139 2139 user_magics=m.UserMagics(self))
2140 2140 self.configurables.append(self.magics_manager)
2141 2141
2142 2142 # Expose as public API from the magics manager
2143 2143 self.register_magics = self.magics_manager.register
2144 2144
2145 2145 self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics,
2146 2146 m.ConfigMagics, m.DisplayMagics, m.ExecutionMagics,
2147 2147 m.ExtensionMagics, m.HistoryMagics, m.LoggingMagics,
2148 2148 m.NamespaceMagics, m.OSMagics, m.PackagingMagics,
2149 2149 m.PylabMagics, m.ScriptMagics,
2150 2150 )
2151 2151 self.register_magics(m.AsyncMagics)
2152 2152
2153 2153 # Register Magic Aliases
2154 2154 mman = self.magics_manager
2155 2155 # FIXME: magic aliases should be defined by the Magics classes
2156 2156 # or in MagicsManager, not here
2157 2157 mman.register_alias('ed', 'edit')
2158 2158 mman.register_alias('hist', 'history')
2159 2159 mman.register_alias('rep', 'recall')
2160 2160 mman.register_alias('SVG', 'svg', 'cell')
2161 2161 mman.register_alias('HTML', 'html', 'cell')
2162 2162 mman.register_alias('file', 'writefile', 'cell')
2163 2163
2164 2164 # FIXME: Move the color initialization to the DisplayHook, which
2165 2165 # should be split into a prompt manager and displayhook. We probably
2166 2166 # even need a centralize colors management object.
2167 2167 self.run_line_magic('colors', self.colors)
2168 2168
2169 2169 # Defined here so that it's included in the documentation
2170 2170 @functools.wraps(magic.MagicsManager.register_function)
2171 2171 def register_magic_function(self, func, magic_kind='line', magic_name=None):
2172 2172 self.magics_manager.register_function(
2173 2173 func, magic_kind=magic_kind, magic_name=magic_name
2174 2174 )
2175 2175
2176 2176 def run_line_magic(self, magic_name, line, _stack_depth=1):
2177 2177 """Execute the given line magic.
2178 2178
2179 2179 Parameters
2180 2180 ----------
2181 2181 magic_name : str
2182 2182 Name of the desired magic function, without '%' prefix.
2183 2183 line : str
2184 2184 The rest of the input line as a single string.
2185 2185 _stack_depth : int
2186 2186 If run_line_magic() is called from magic() then _stack_depth=2.
2187 2187 This is added to ensure backward compatibility for use of 'get_ipython().magic()'
2188 2188 """
2189 2189 fn = self.find_line_magic(magic_name)
2190 2190 if fn is None:
2191 2191 cm = self.find_cell_magic(magic_name)
2192 2192 etpl = "Line magic function `%%%s` not found%s."
2193 2193 extra = '' if cm is None else (' (But cell magic `%%%%%s` exists, '
2194 2194 'did you mean that instead?)' % magic_name )
2195 2195 raise UsageError(etpl % (magic_name, extra))
2196 2196 else:
2197 2197 # Note: this is the distance in the stack to the user's frame.
2198 2198 # This will need to be updated if the internal calling logic gets
2199 2199 # refactored, or else we'll be expanding the wrong variables.
2200 2200
2201 2201 # Determine stack_depth depending on where run_line_magic() has been called
2202 2202 stack_depth = _stack_depth
2203 2203 if getattr(fn, magic.MAGIC_NO_VAR_EXPAND_ATTR, False):
2204 2204 # magic has opted out of var_expand
2205 2205 magic_arg_s = line
2206 2206 else:
2207 2207 magic_arg_s = self.var_expand(line, stack_depth)
2208 2208 # Put magic args in a list so we can call with f(*a) syntax
2209 2209 args = [magic_arg_s]
2210 2210 kwargs = {}
2211 2211 # Grab local namespace if we need it:
2212 2212 if getattr(fn, "needs_local_scope", False):
2213 2213 kwargs['local_ns'] = self.get_local_scope(stack_depth)
2214 2214 with self.builtin_trap:
2215 2215 result = fn(*args, **kwargs)
2216 2216 return result
2217 2217
2218 2218 def get_local_scope(self, stack_depth):
2219 2219 """Get local scope at given stack depth.
2220 2220
2221 2221 Parameters
2222 2222 ----------
2223 2223 stack_depth : int
2224 2224 Depth relative to calling frame
2225 2225 """
2226 2226 return sys._getframe(stack_depth + 1).f_locals
2227 2227
2228 2228 def run_cell_magic(self, magic_name, line, cell):
2229 2229 """Execute the given cell magic.
2230 2230
2231 2231 Parameters
2232 2232 ----------
2233 2233 magic_name : str
2234 2234 Name of the desired magic function, without '%' prefix.
2235 2235 line : str
2236 2236 The rest of the first input line as a single string.
2237 2237 cell : str
2238 2238 The body of the cell as a (possibly multiline) string.
2239 2239 """
2240 2240 fn = self.find_cell_magic(magic_name)
2241 2241 if fn is None:
2242 2242 lm = self.find_line_magic(magic_name)
2243 2243 etpl = "Cell magic `%%{0}` not found{1}."
2244 2244 extra = '' if lm is None else (' (But line magic `%{0}` exists, '
2245 2245 'did you mean that instead?)'.format(magic_name))
2246 2246 raise UsageError(etpl.format(magic_name, extra))
2247 2247 elif cell == '':
2248 2248 message = '%%{0} is a cell magic, but the cell body is empty.'.format(magic_name)
2249 2249 if self.find_line_magic(magic_name) is not None:
2250 2250 message += ' Did you mean the line magic %{0} (single %)?'.format(magic_name)
2251 2251 raise UsageError(message)
2252 2252 else:
2253 2253 # Note: this is the distance in the stack to the user's frame.
2254 2254 # This will need to be updated if the internal calling logic gets
2255 2255 # refactored, or else we'll be expanding the wrong variables.
2256 2256 stack_depth = 2
2257 2257 if getattr(fn, magic.MAGIC_NO_VAR_EXPAND_ATTR, False):
2258 2258 # magic has opted out of var_expand
2259 2259 magic_arg_s = line
2260 2260 else:
2261 2261 magic_arg_s = self.var_expand(line, stack_depth)
2262 2262 kwargs = {}
2263 2263 if getattr(fn, "needs_local_scope", False):
2264 2264 kwargs['local_ns'] = self.user_ns
2265 2265
2266 2266 with self.builtin_trap:
2267 2267 args = (magic_arg_s, cell)
2268 2268 result = fn(*args, **kwargs)
2269 2269 return result
2270 2270
2271 2271 def find_line_magic(self, magic_name):
2272 2272 """Find and return a line magic by name.
2273 2273
2274 2274 Returns None if the magic isn't found."""
2275 2275 return self.magics_manager.magics['line'].get(magic_name)
2276 2276
2277 2277 def find_cell_magic(self, magic_name):
2278 2278 """Find and return a cell magic by name.
2279 2279
2280 2280 Returns None if the magic isn't found."""
2281 2281 return self.magics_manager.magics['cell'].get(magic_name)
2282 2282
2283 2283 def find_magic(self, magic_name, magic_kind='line'):
2284 2284 """Find and return a magic of the given type by name.
2285 2285
2286 2286 Returns None if the magic isn't found."""
2287 2287 return self.magics_manager.magics[magic_kind].get(magic_name)
2288 2288
2289 2289 def magic(self, arg_s):
2290 2290 """DEPRECATED. Use run_line_magic() instead.
2291 2291
2292 2292 Call a magic function by name.
2293 2293
2294 2294 Input: a string containing the name of the magic function to call and
2295 2295 any additional arguments to be passed to the magic.
2296 2296
2297 2297 magic('name -opt foo bar') is equivalent to typing at the ipython
2298 2298 prompt:
2299 2299
2300 2300 In[1]: %name -opt foo bar
2301 2301
2302 2302 To call a magic without arguments, simply use magic('name').
2303 2303
2304 2304 This provides a proper Python function to call IPython's magics in any
2305 2305 valid Python code you can type at the interpreter, including loops and
2306 2306 compound statements.
2307 2307 """
2308 2308 # TODO: should we issue a loud deprecation warning here?
2309 2309 magic_name, _, magic_arg_s = arg_s.partition(' ')
2310 2310 magic_name = magic_name.lstrip(prefilter.ESC_MAGIC)
2311 2311 return self.run_line_magic(magic_name, magic_arg_s, _stack_depth=2)
2312 2312
2313 2313 #-------------------------------------------------------------------------
2314 2314 # Things related to macros
2315 2315 #-------------------------------------------------------------------------
2316 2316
2317 2317 def define_macro(self, name, themacro):
2318 2318 """Define a new macro
2319 2319
2320 2320 Parameters
2321 2321 ----------
2322 2322 name : str
2323 2323 The name of the macro.
2324 2324 themacro : str or Macro
2325 2325 The action to do upon invoking the macro. If a string, a new
2326 2326 Macro object is created by passing the string to it.
2327 2327 """
2328 2328
2329 2329 from IPython.core import macro
2330 2330
2331 2331 if isinstance(themacro, str):
2332 2332 themacro = macro.Macro(themacro)
2333 2333 if not isinstance(themacro, macro.Macro):
2334 2334 raise ValueError('A macro must be a string or a Macro instance.')
2335 2335 self.user_ns[name] = themacro
2336 2336
2337 2337 #-------------------------------------------------------------------------
2338 2338 # Things related to the running of system commands
2339 2339 #-------------------------------------------------------------------------
2340 2340
2341 2341 def system_piped(self, cmd):
2342 2342 """Call the given cmd in a subprocess, piping stdout/err
2343 2343
2344 2344 Parameters
2345 2345 ----------
2346 2346 cmd : str
2347 2347 Command to execute (can not end in '&', as background processes are
2348 2348 not supported. Should not be a command that expects input
2349 2349 other than simple text.
2350 2350 """
2351 2351 if cmd.rstrip().endswith('&'):
2352 2352 # this is *far* from a rigorous test
2353 2353 # We do not support backgrounding processes because we either use
2354 2354 # pexpect or pipes to read from. Users can always just call
2355 2355 # os.system() or use ip.system=ip.system_raw
2356 2356 # if they really want a background process.
2357 2357 raise OSError("Background processes not supported.")
2358 2358
2359 2359 # we explicitly do NOT return the subprocess status code, because
2360 2360 # a non-None value would trigger :func:`sys.displayhook` calls.
2361 2361 # Instead, we store the exit_code in user_ns.
2362 2362 self.user_ns['_exit_code'] = system(self.var_expand(cmd, depth=1))
2363 2363
2364 2364 def system_raw(self, cmd):
2365 2365 """Call the given cmd in a subprocess using os.system on Windows or
2366 2366 subprocess.call using the system shell on other platforms.
2367 2367
2368 2368 Parameters
2369 2369 ----------
2370 2370 cmd : str
2371 2371 Command to execute.
2372 2372 """
2373 2373 cmd = self.var_expand(cmd, depth=1)
2374 2374 # warn if there is an IPython magic alternative.
2375 2375 main_cmd = cmd.split()[0]
2376 2376 has_magic_alternatives = ("pip", "conda", "cd", "ls")
2377 2377
2378 2378 # had to check if the command was an alias expanded because of `ls`
2379 2379 is_alias_expanded = self.alias_manager.is_alias(main_cmd) and (
2380 2380 self.alias_manager.retrieve_alias(main_cmd).strip() == cmd.strip()
2381 2381 )
2382 2382
2383 2383 if main_cmd in has_magic_alternatives and not is_alias_expanded:
2384 2384 warnings.warn(
2385 2385 (
2386 2386 "You executed the system command !{0} which may not work "
2387 2387 "as expected. Try the IPython magic %{0} instead."
2388 2388 ).format(main_cmd)
2389 2389 )
2390 2390
2391 2391 # protect os.system from UNC paths on Windows, which it can't handle:
2392 2392 if sys.platform == 'win32':
2393 2393 from IPython.utils._process_win32 import AvoidUNCPath
2394 2394 with AvoidUNCPath() as path:
2395 2395 if path is not None:
2396 2396 cmd = '"pushd %s &&"%s' % (path, cmd)
2397 2397 try:
2398 2398 ec = os.system(cmd)
2399 2399 except KeyboardInterrupt:
2400 2400 print('\n' + self.get_exception_only(), file=sys.stderr)
2401 2401 ec = -2
2402 2402 else:
2403 2403 # For posix the result of the subprocess.call() below is an exit
2404 2404 # code, which by convention is zero for success, positive for
2405 2405 # program failure. Exit codes above 128 are reserved for signals,
2406 2406 # and the formula for converting a signal to an exit code is usually
2407 2407 # signal_number+128. To more easily differentiate between exit
2408 2408 # codes and signals, ipython uses negative numbers. For instance
2409 2409 # since control-c is signal 2 but exit code 130, ipython's
2410 2410 # _exit_code variable will read -2. Note that some shells like
2411 2411 # csh and fish don't follow sh/bash conventions for exit codes.
2412 2412 executable = os.environ.get('SHELL', None)
2413 2413 try:
2414 2414 # Use env shell instead of default /bin/sh
2415 2415 ec = subprocess.call(cmd, shell=True, executable=executable)
2416 2416 except KeyboardInterrupt:
2417 2417 # intercept control-C; a long traceback is not useful here
2418 2418 print('\n' + self.get_exception_only(), file=sys.stderr)
2419 2419 ec = 130
2420 2420 if ec > 128:
2421 2421 ec = -(ec - 128)
2422 2422
2423 2423 # We explicitly do NOT return the subprocess status code, because
2424 2424 # a non-None value would trigger :func:`sys.displayhook` calls.
2425 2425 # Instead, we store the exit_code in user_ns. Note the semantics
2426 2426 # of _exit_code: for control-c, _exit_code == -signal.SIGNIT,
2427 2427 # but raising SystemExit(_exit_code) will give status 254!
2428 2428 self.user_ns['_exit_code'] = ec
2429 2429
2430 2430 # use piped system by default, because it is better behaved
2431 2431 system = system_piped
2432 2432
2433 2433 def getoutput(self, cmd, split=True, depth=0):
2434 2434 """Get output (possibly including stderr) from a subprocess.
2435 2435
2436 2436 Parameters
2437 2437 ----------
2438 2438 cmd : str
2439 2439 Command to execute (can not end in '&', as background processes are
2440 2440 not supported.
2441 2441 split : bool, optional
2442 2442 If True, split the output into an IPython SList. Otherwise, an
2443 2443 IPython LSString is returned. These are objects similar to normal
2444 2444 lists and strings, with a few convenience attributes for easier
2445 2445 manipulation of line-based output. You can use '?' on them for
2446 2446 details.
2447 2447 depth : int, optional
2448 2448 How many frames above the caller are the local variables which should
2449 2449 be expanded in the command string? The default (0) assumes that the
2450 2450 expansion variables are in the stack frame calling this function.
2451 2451 """
2452 2452 if cmd.rstrip().endswith('&'):
2453 2453 # this is *far* from a rigorous test
2454 2454 raise OSError("Background processes not supported.")
2455 2455 out = getoutput(self.var_expand(cmd, depth=depth+1))
2456 2456 if split:
2457 2457 out = SList(out.splitlines())
2458 2458 else:
2459 2459 out = LSString(out)
2460 2460 return out
2461 2461
2462 2462 #-------------------------------------------------------------------------
2463 2463 # Things related to aliases
2464 2464 #-------------------------------------------------------------------------
2465 2465
2466 2466 def init_alias(self):
2467 2467 self.alias_manager = AliasManager(shell=self, parent=self)
2468 2468 self.configurables.append(self.alias_manager)
2469 2469
2470 2470 #-------------------------------------------------------------------------
2471 2471 # Things related to extensions
2472 2472 #-------------------------------------------------------------------------
2473 2473
2474 2474 def init_extension_manager(self):
2475 2475 self.extension_manager = ExtensionManager(shell=self, parent=self)
2476 2476 self.configurables.append(self.extension_manager)
2477 2477
2478 2478 #-------------------------------------------------------------------------
2479 2479 # Things related to payloads
2480 2480 #-------------------------------------------------------------------------
2481 2481
2482 2482 def init_payload(self):
2483 2483 self.payload_manager = PayloadManager(parent=self)
2484 2484 self.configurables.append(self.payload_manager)
2485 2485
2486 2486 #-------------------------------------------------------------------------
2487 2487 # Things related to the prefilter
2488 2488 #-------------------------------------------------------------------------
2489 2489
2490 2490 def init_prefilter(self):
2491 2491 self.prefilter_manager = PrefilterManager(shell=self, parent=self)
2492 2492 self.configurables.append(self.prefilter_manager)
2493 2493 # Ultimately this will be refactored in the new interpreter code, but
2494 2494 # for now, we should expose the main prefilter method (there's legacy
2495 2495 # code out there that may rely on this).
2496 2496 self.prefilter = self.prefilter_manager.prefilter_lines
2497 2497
2498 2498 def auto_rewrite_input(self, cmd):
2499 2499 """Print to the screen the rewritten form of the user's command.
2500 2500
2501 2501 This shows visual feedback by rewriting input lines that cause
2502 2502 automatic calling to kick in, like::
2503 2503
2504 2504 /f x
2505 2505
2506 2506 into::
2507 2507
2508 2508 ------> f(x)
2509 2509
2510 2510 after the user's input prompt. This helps the user understand that the
2511 2511 input line was transformed automatically by IPython.
2512 2512 """
2513 2513 if not self.show_rewritten_input:
2514 2514 return
2515 2515
2516 2516 # This is overridden in TerminalInteractiveShell to use fancy prompts
2517 2517 print("------> " + cmd)
2518 2518
2519 2519 #-------------------------------------------------------------------------
2520 2520 # Things related to extracting values/expressions from kernel and user_ns
2521 2521 #-------------------------------------------------------------------------
2522 2522
2523 2523 def _user_obj_error(self):
2524 2524 """return simple exception dict
2525 2525
2526 2526 for use in user_expressions
2527 2527 """
2528 2528
2529 2529 etype, evalue, tb = self._get_exc_info()
2530 2530 stb = self.InteractiveTB.get_exception_only(etype, evalue)
2531 2531
2532 2532 exc_info = {
2533 2533 "status": "error",
2534 2534 "traceback": stb,
2535 2535 "ename": etype.__name__,
2536 2536 "evalue": py3compat.safe_unicode(evalue),
2537 2537 }
2538 2538
2539 2539 return exc_info
2540 2540
2541 2541 def _format_user_obj(self, obj):
2542 2542 """format a user object to display dict
2543 2543
2544 2544 for use in user_expressions
2545 2545 """
2546 2546
2547 2547 data, md = self.display_formatter.format(obj)
2548 2548 value = {
2549 2549 'status' : 'ok',
2550 2550 'data' : data,
2551 2551 'metadata' : md,
2552 2552 }
2553 2553 return value
2554 2554
2555 2555 def user_expressions(self, expressions):
2556 2556 """Evaluate a dict of expressions in the user's namespace.
2557 2557
2558 2558 Parameters
2559 2559 ----------
2560 2560 expressions : dict
2561 2561 A dict with string keys and string values. The expression values
2562 2562 should be valid Python expressions, each of which will be evaluated
2563 2563 in the user namespace.
2564 2564
2565 2565 Returns
2566 2566 -------
2567 2567 A dict, keyed like the input expressions dict, with the rich mime-typed
2568 2568 display_data of each value.
2569 2569 """
2570 2570 out = {}
2571 2571 user_ns = self.user_ns
2572 2572 global_ns = self.user_global_ns
2573 2573
2574 2574 for key, expr in expressions.items():
2575 2575 try:
2576 2576 value = self._format_user_obj(eval(expr, global_ns, user_ns))
2577 2577 except:
2578 2578 value = self._user_obj_error()
2579 2579 out[key] = value
2580 2580 return out
2581 2581
2582 2582 #-------------------------------------------------------------------------
2583 2583 # Things related to the running of code
2584 2584 #-------------------------------------------------------------------------
2585 2585
2586 2586 def ex(self, cmd):
2587 2587 """Execute a normal python statement in user namespace."""
2588 2588 with self.builtin_trap:
2589 2589 exec(cmd, self.user_global_ns, self.user_ns)
2590 2590
2591 2591 def ev(self, expr):
2592 2592 """Evaluate python expression expr in user namespace.
2593 2593
2594 2594 Returns the result of evaluation
2595 2595 """
2596 2596 with self.builtin_trap:
2597 2597 return eval(expr, self.user_global_ns, self.user_ns)
2598 2598
2599 2599 def safe_execfile(self, fname, *where, exit_ignore=False, raise_exceptions=False, shell_futures=False):
2600 2600 """A safe version of the builtin execfile().
2601 2601
2602 2602 This version will never throw an exception, but instead print
2603 2603 helpful error messages to the screen. This only works on pure
2604 2604 Python files with the .py extension.
2605 2605
2606 2606 Parameters
2607 2607 ----------
2608 2608 fname : string
2609 2609 The name of the file to be executed.
2610 2610 *where : tuple
2611 2611 One or two namespaces, passed to execfile() as (globals,locals).
2612 2612 If only one is given, it is passed as both.
2613 2613 exit_ignore : bool (False)
2614 2614 If True, then silence SystemExit for non-zero status (it is always
2615 2615 silenced for zero status, as it is so common).
2616 2616 raise_exceptions : bool (False)
2617 2617 If True raise exceptions everywhere. Meant for testing.
2618 2618 shell_futures : bool (False)
2619 2619 If True, the code will share future statements with the interactive
2620 2620 shell. It will both be affected by previous __future__ imports, and
2621 2621 any __future__ imports in the code will affect the shell. If False,
2622 2622 __future__ imports are not shared in either direction.
2623 2623
2624 2624 """
2625 2625 fname = Path(fname).expanduser().resolve()
2626 2626
2627 2627 # Make sure we can open the file
2628 2628 try:
2629 with fname.open('rb'):
2629 with fname.open("rb"):
2630 2630 pass
2631 2631 except:
2632 2632 warn('Could not open file <%s> for safe execution.' % fname)
2633 2633 return
2634 2634
2635 2635 # Find things also in current directory. This is needed to mimic the
2636 2636 # behavior of running a script from the system command line, where
2637 2637 # Python inserts the script's directory into sys.path
2638 2638 dname = str(fname.parent)
2639 2639
2640 2640 with prepended_to_syspath(dname), self.builtin_trap:
2641 2641 try:
2642 2642 glob, loc = (where + (None, ))[:2]
2643 2643 py3compat.execfile(
2644 2644 fname, glob, loc,
2645 2645 self.compile if shell_futures else None)
2646 2646 except SystemExit as status:
2647 2647 # If the call was made with 0 or None exit status (sys.exit(0)
2648 2648 # or sys.exit() ), don't bother showing a traceback, as both of
2649 2649 # these are considered normal by the OS:
2650 2650 # > python -c'import sys;sys.exit(0)'; echo $?
2651 2651 # 0
2652 2652 # > python -c'import sys;sys.exit()'; echo $?
2653 2653 # 0
2654 2654 # For other exit status, we show the exception unless
2655 2655 # explicitly silenced, but only in short form.
2656 2656 if status.code:
2657 2657 if raise_exceptions:
2658 2658 raise
2659 2659 if not exit_ignore:
2660 2660 self.showtraceback(exception_only=True)
2661 2661 except:
2662 2662 if raise_exceptions:
2663 2663 raise
2664 2664 # tb offset is 2 because we wrap execfile
2665 2665 self.showtraceback(tb_offset=2)
2666 2666
2667 2667 def safe_execfile_ipy(self, fname, shell_futures=False, raise_exceptions=False):
2668 2668 """Like safe_execfile, but for .ipy or .ipynb files with IPython syntax.
2669 2669
2670 2670 Parameters
2671 2671 ----------
2672 2672 fname : str
2673 2673 The name of the file to execute. The filename must have a
2674 2674 .ipy or .ipynb extension.
2675 2675 shell_futures : bool (False)
2676 2676 If True, the code will share future statements with the interactive
2677 2677 shell. It will both be affected by previous __future__ imports, and
2678 2678 any __future__ imports in the code will affect the shell. If False,
2679 2679 __future__ imports are not shared in either direction.
2680 2680 raise_exceptions : bool (False)
2681 2681 If True raise exceptions everywhere. Meant for testing.
2682 2682 """
2683 2683 fname = Path(fname).expanduser().resolve()
2684 2684
2685 2685 # Make sure we can open the file
2686 2686 try:
2687 with fname.open('rb'):
2687 with fname.open("rb"):
2688 2688 pass
2689 2689 except:
2690 2690 warn('Could not open file <%s> for safe execution.' % fname)
2691 2691 return
2692 2692
2693 2693 # Find things also in current directory. This is needed to mimic the
2694 2694 # behavior of running a script from the system command line, where
2695 2695 # Python inserts the script's directory into sys.path
2696 2696 dname = str(fname.parent)
2697 2697
2698 2698 def get_cells():
2699 2699 """generator for sequence of code blocks to run"""
2700 2700 if fname.suffix == ".ipynb":
2701 2701 from nbformat import read
2702 2702 nb = read(fname, as_version=4)
2703 2703 if not nb.cells:
2704 2704 return
2705 2705 for cell in nb.cells:
2706 2706 if cell.cell_type == 'code':
2707 2707 yield cell.source
2708 2708 else:
2709 yield fname.read_text(encoding='utf-8')
2709 yield fname.read_text(encoding="utf-8")
2710 2710
2711 2711 with prepended_to_syspath(dname):
2712 2712 try:
2713 2713 for cell in get_cells():
2714 2714 result = self.run_cell(cell, silent=True, shell_futures=shell_futures)
2715 2715 if raise_exceptions:
2716 2716 result.raise_error()
2717 2717 elif not result.success:
2718 2718 break
2719 2719 except:
2720 2720 if raise_exceptions:
2721 2721 raise
2722 2722 self.showtraceback()
2723 2723 warn('Unknown failure executing file: <%s>' % fname)
2724 2724
2725 2725 def safe_run_module(self, mod_name, where):
2726 2726 """A safe version of runpy.run_module().
2727 2727
2728 2728 This version will never throw an exception, but instead print
2729 2729 helpful error messages to the screen.
2730 2730
2731 2731 `SystemExit` exceptions with status code 0 or None are ignored.
2732 2732
2733 2733 Parameters
2734 2734 ----------
2735 2735 mod_name : string
2736 2736 The name of the module to be executed.
2737 2737 where : dict
2738 2738 The globals namespace.
2739 2739 """
2740 2740 try:
2741 2741 try:
2742 2742 where.update(
2743 2743 runpy.run_module(str(mod_name), run_name="__main__",
2744 2744 alter_sys=True)
2745 2745 )
2746 2746 except SystemExit as status:
2747 2747 if status.code:
2748 2748 raise
2749 2749 except:
2750 2750 self.showtraceback()
2751 2751 warn('Unknown failure executing module: <%s>' % mod_name)
2752 2752
2753 2753 def run_cell(self, raw_cell, store_history=False, silent=False, shell_futures=True):
2754 2754 """Run a complete IPython cell.
2755 2755
2756 2756 Parameters
2757 2757 ----------
2758 2758 raw_cell : str
2759 2759 The code (including IPython code such as %magic functions) to run.
2760 2760 store_history : bool
2761 2761 If True, the raw and translated cell will be stored in IPython's
2762 2762 history. For user code calling back into IPython's machinery, this
2763 2763 should be set to False.
2764 2764 silent : bool
2765 2765 If True, avoid side-effects, such as implicit displayhooks and
2766 2766 and logging. silent=True forces store_history=False.
2767 2767 shell_futures : bool
2768 2768 If True, the code will share future statements with the interactive
2769 2769 shell. It will both be affected by previous __future__ imports, and
2770 2770 any __future__ imports in the code will affect the shell. If False,
2771 2771 __future__ imports are not shared in either direction.
2772 2772
2773 2773 Returns
2774 2774 -------
2775 2775 result : :class:`ExecutionResult`
2776 2776 """
2777 2777 result = None
2778 2778 try:
2779 2779 result = self._run_cell(
2780 2780 raw_cell, store_history, silent, shell_futures)
2781 2781 finally:
2782 2782 self.events.trigger('post_execute')
2783 2783 if not silent:
2784 2784 self.events.trigger('post_run_cell', result)
2785 2785 return result
2786 2786
2787 2787 def _run_cell(self, raw_cell:str, store_history:bool, silent:bool, shell_futures:bool) -> ExecutionResult:
2788 2788 """Internal method to run a complete IPython cell."""
2789 2789
2790 2790 # we need to avoid calling self.transform_cell multiple time on the same thing
2791 2791 # so we need to store some results:
2792 2792 preprocessing_exc_tuple = None
2793 2793 try:
2794 2794 transformed_cell = self.transform_cell(raw_cell)
2795 2795 except Exception:
2796 2796 transformed_cell = raw_cell
2797 2797 preprocessing_exc_tuple = sys.exc_info()
2798 2798
2799 2799 assert transformed_cell is not None
2800 2800 coro = self.run_cell_async(
2801 2801 raw_cell,
2802 2802 store_history=store_history,
2803 2803 silent=silent,
2804 2804 shell_futures=shell_futures,
2805 2805 transformed_cell=transformed_cell,
2806 2806 preprocessing_exc_tuple=preprocessing_exc_tuple,
2807 2807 )
2808 2808
2809 2809 # run_cell_async is async, but may not actually need an eventloop.
2810 2810 # when this is the case, we want to run it using the pseudo_sync_runner
2811 2811 # so that code can invoke eventloops (for example via the %run , and
2812 2812 # `%paste` magic.
2813 2813 if self.trio_runner:
2814 2814 runner = self.trio_runner
2815 2815 elif self.should_run_async(
2816 2816 raw_cell,
2817 2817 transformed_cell=transformed_cell,
2818 2818 preprocessing_exc_tuple=preprocessing_exc_tuple,
2819 2819 ):
2820 2820 runner = self.loop_runner
2821 2821 else:
2822 2822 runner = _pseudo_sync_runner
2823 2823
2824 2824 try:
2825 2825 return runner(coro)
2826 2826 except BaseException as e:
2827 2827 info = ExecutionInfo(raw_cell, store_history, silent, shell_futures)
2828 2828 result = ExecutionResult(info)
2829 2829 result.error_in_exec = e
2830 2830 self.showtraceback(running_compiled_code=True)
2831 2831 return result
2832 2832
2833 2833 def should_run_async(
2834 2834 self, raw_cell: str, *, transformed_cell=None, preprocessing_exc_tuple=None
2835 2835 ) -> bool:
2836 2836 """Return whether a cell should be run asynchronously via a coroutine runner
2837 2837
2838 2838 Parameters
2839 2839 ----------
2840 2840 raw_cell : str
2841 2841 The code to be executed
2842 2842
2843 2843 Returns
2844 2844 -------
2845 2845 result: bool
2846 2846 Whether the code needs to be run with a coroutine runner or not
2847 2847 .. versionadded:: 7.0
2848 2848 """
2849 2849 if not self.autoawait:
2850 2850 return False
2851 2851 if preprocessing_exc_tuple is not None:
2852 2852 return False
2853 2853 assert preprocessing_exc_tuple is None
2854 2854 if transformed_cell is None:
2855 2855 warnings.warn(
2856 2856 "`should_run_async` will not call `transform_cell`"
2857 2857 " automatically in the future. Please pass the result to"
2858 2858 " `transformed_cell` argument and any exception that happen"
2859 2859 " during the"
2860 2860 "transform in `preprocessing_exc_tuple` in"
2861 2861 " IPython 7.17 and above.",
2862 2862 DeprecationWarning,
2863 2863 stacklevel=2,
2864 2864 )
2865 2865 try:
2866 2866 cell = self.transform_cell(raw_cell)
2867 2867 except Exception:
2868 2868 # any exception during transform will be raised
2869 2869 # prior to execution
2870 2870 return False
2871 2871 else:
2872 2872 cell = transformed_cell
2873 2873 return _should_be_async(cell)
2874 2874
2875 2875 async def run_cell_async(
2876 2876 self,
2877 2877 raw_cell: str,
2878 2878 store_history=False,
2879 2879 silent=False,
2880 2880 shell_futures=True,
2881 2881 *,
2882 2882 transformed_cell: Optional[str] = None,
2883 2883 preprocessing_exc_tuple: Optional[Any] = None
2884 2884 ) -> ExecutionResult:
2885 2885 """Run a complete IPython cell asynchronously.
2886 2886
2887 2887 Parameters
2888 2888 ----------
2889 2889 raw_cell : str
2890 2890 The code (including IPython code such as %magic functions) to run.
2891 2891 store_history : bool
2892 2892 If True, the raw and translated cell will be stored in IPython's
2893 2893 history. For user code calling back into IPython's machinery, this
2894 2894 should be set to False.
2895 2895 silent : bool
2896 2896 If True, avoid side-effects, such as implicit displayhooks and
2897 2897 and logging. silent=True forces store_history=False.
2898 2898 shell_futures : bool
2899 2899 If True, the code will share future statements with the interactive
2900 2900 shell. It will both be affected by previous __future__ imports, and
2901 2901 any __future__ imports in the code will affect the shell. If False,
2902 2902 __future__ imports are not shared in either direction.
2903 2903 transformed_cell: str
2904 2904 cell that was passed through transformers
2905 2905 preprocessing_exc_tuple:
2906 2906 trace if the transformation failed.
2907 2907
2908 2908 Returns
2909 2909 -------
2910 2910 result : :class:`ExecutionResult`
2911 2911
2912 2912 .. versionadded:: 7.0
2913 2913 """
2914 2914 info = ExecutionInfo(
2915 2915 raw_cell, store_history, silent, shell_futures)
2916 2916 result = ExecutionResult(info)
2917 2917
2918 2918 if (not raw_cell) or raw_cell.isspace():
2919 2919 self.last_execution_succeeded = True
2920 2920 self.last_execution_result = result
2921 2921 return result
2922 2922
2923 2923 if silent:
2924 2924 store_history = False
2925 2925
2926 2926 if store_history:
2927 2927 result.execution_count = self.execution_count
2928 2928
2929 2929 def error_before_exec(value):
2930 2930 if store_history:
2931 2931 self.execution_count += 1
2932 2932 result.error_before_exec = value
2933 2933 self.last_execution_succeeded = False
2934 2934 self.last_execution_result = result
2935 2935 return result
2936 2936
2937 2937 self.events.trigger('pre_execute')
2938 2938 if not silent:
2939 2939 self.events.trigger('pre_run_cell', info)
2940 2940
2941 2941 if transformed_cell is None:
2942 2942 warnings.warn(
2943 2943 "`run_cell_async` will not call `transform_cell`"
2944 2944 " automatically in the future. Please pass the result to"
2945 2945 " `transformed_cell` argument and any exception that happen"
2946 2946 " during the"
2947 2947 "transform in `preprocessing_exc_tuple` in"
2948 2948 " IPython 7.17 and above.",
2949 2949 DeprecationWarning,
2950 2950 stacklevel=2,
2951 2951 )
2952 2952 # If any of our input transformation (input_transformer_manager or
2953 2953 # prefilter_manager) raises an exception, we store it in this variable
2954 2954 # so that we can display the error after logging the input and storing
2955 2955 # it in the history.
2956 2956 try:
2957 2957 cell = self.transform_cell(raw_cell)
2958 2958 except Exception:
2959 2959 preprocessing_exc_tuple = sys.exc_info()
2960 2960 cell = raw_cell # cell has to exist so it can be stored/logged
2961 2961 else:
2962 2962 preprocessing_exc_tuple = None
2963 2963 else:
2964 2964 if preprocessing_exc_tuple is None:
2965 2965 cell = transformed_cell
2966 2966 else:
2967 2967 cell = raw_cell
2968 2968
2969 2969 # Store raw and processed history
2970 2970 if store_history:
2971 2971 self.history_manager.store_inputs(self.execution_count,
2972 2972 cell, raw_cell)
2973 2973 if not silent:
2974 2974 self.logger.log(cell, raw_cell)
2975 2975
2976 2976 # Display the exception if input processing failed.
2977 2977 if preprocessing_exc_tuple is not None:
2978 2978 self.showtraceback(preprocessing_exc_tuple)
2979 2979 if store_history:
2980 2980 self.execution_count += 1
2981 2981 return error_before_exec(preprocessing_exc_tuple[1])
2982 2982
2983 2983 # Our own compiler remembers the __future__ environment. If we want to
2984 2984 # run code with a separate __future__ environment, use the default
2985 2985 # compiler
2986 2986 compiler = self.compile if shell_futures else self.compiler_class()
2987 2987
2988 2988 _run_async = False
2989 2989
2990 2990 with self.builtin_trap:
2991 2991 cell_name = compiler.cache(cell, self.execution_count, raw_code=raw_cell)
2992 2992
2993 2993 with self.display_trap:
2994 2994 # Compile to bytecode
2995 2995 try:
2996 2996 code_ast = compiler.ast_parse(cell, filename=cell_name)
2997 2997 except self.custom_exceptions as e:
2998 2998 etype, value, tb = sys.exc_info()
2999 2999 self.CustomTB(etype, value, tb)
3000 3000 return error_before_exec(e)
3001 3001 except IndentationError as e:
3002 3002 self.showindentationerror()
3003 3003 return error_before_exec(e)
3004 3004 except (OverflowError, SyntaxError, ValueError, TypeError,
3005 3005 MemoryError) as e:
3006 3006 self.showsyntaxerror()
3007 3007 return error_before_exec(e)
3008 3008
3009 3009 # Apply AST transformations
3010 3010 try:
3011 3011 code_ast = self.transform_ast(code_ast)
3012 3012 except InputRejected as e:
3013 3013 self.showtraceback()
3014 3014 return error_before_exec(e)
3015 3015
3016 3016 # Give the displayhook a reference to our ExecutionResult so it
3017 3017 # can fill in the output value.
3018 3018 self.displayhook.exec_result = result
3019 3019
3020 3020 # Execute the user code
3021 3021 interactivity = "none" if silent else self.ast_node_interactivity
3022 3022
3023 3023 has_raised = await self.run_ast_nodes(code_ast.body, cell_name,
3024 3024 interactivity=interactivity, compiler=compiler, result=result)
3025 3025
3026 3026 self.last_execution_succeeded = not has_raised
3027 3027 self.last_execution_result = result
3028 3028
3029 3029 # Reset this so later displayed values do not modify the
3030 3030 # ExecutionResult
3031 3031 self.displayhook.exec_result = None
3032 3032
3033 3033 if store_history:
3034 3034 # Write output to the database. Does nothing unless
3035 3035 # history output logging is enabled.
3036 3036 self.history_manager.store_output(self.execution_count)
3037 3037 # Each cell is a *single* input, regardless of how many lines it has
3038 3038 self.execution_count += 1
3039 3039
3040 3040 return result
3041 3041
3042 3042 def transform_cell(self, raw_cell):
3043 3043 """Transform an input cell before parsing it.
3044 3044
3045 3045 Static transformations, implemented in IPython.core.inputtransformer2,
3046 3046 deal with things like ``%magic`` and ``!system`` commands.
3047 3047 These run on all input.
3048 3048 Dynamic transformations, for things like unescaped magics and the exit
3049 3049 autocall, depend on the state of the interpreter.
3050 3050 These only apply to single line inputs.
3051 3051
3052 3052 These string-based transformations are followed by AST transformations;
3053 3053 see :meth:`transform_ast`.
3054 3054 """
3055 3055 # Static input transformations
3056 3056 cell = self.input_transformer_manager.transform_cell(raw_cell)
3057 3057
3058 3058 if len(cell.splitlines()) == 1:
3059 3059 # Dynamic transformations - only applied for single line commands
3060 3060 with self.builtin_trap:
3061 3061 # use prefilter_lines to handle trailing newlines
3062 3062 # restore trailing newline for ast.parse
3063 3063 cell = self.prefilter_manager.prefilter_lines(cell) + '\n'
3064 3064
3065 3065 lines = cell.splitlines(keepends=True)
3066 3066 for transform in self.input_transformers_post:
3067 3067 lines = transform(lines)
3068 3068 cell = ''.join(lines)
3069 3069
3070 3070 return cell
3071 3071
3072 3072 def transform_ast(self, node):
3073 3073 """Apply the AST transformations from self.ast_transformers
3074 3074
3075 3075 Parameters
3076 3076 ----------
3077 3077 node : ast.Node
3078 3078 The root node to be transformed. Typically called with the ast.Module
3079 3079 produced by parsing user input.
3080 3080
3081 3081 Returns
3082 3082 -------
3083 3083 An ast.Node corresponding to the node it was called with. Note that it
3084 3084 may also modify the passed object, so don't rely on references to the
3085 3085 original AST.
3086 3086 """
3087 3087 for transformer in self.ast_transformers:
3088 3088 try:
3089 3089 node = transformer.visit(node)
3090 3090 except InputRejected:
3091 3091 # User-supplied AST transformers can reject an input by raising
3092 3092 # an InputRejected. Short-circuit in this case so that we
3093 3093 # don't unregister the transform.
3094 3094 raise
3095 3095 except Exception:
3096 3096 warn("AST transformer %r threw an error. It will be unregistered." % transformer)
3097 3097 self.ast_transformers.remove(transformer)
3098 3098
3099 3099 if self.ast_transformers:
3100 3100 ast.fix_missing_locations(node)
3101 3101 return node
3102 3102
3103 3103 async def run_ast_nodes(
3104 3104 self,
3105 3105 nodelist: ListType[stmt],
3106 3106 cell_name: str,
3107 3107 interactivity="last_expr",
3108 3108 compiler=compile,
3109 3109 result=None,
3110 3110 ):
3111 3111 """Run a sequence of AST nodes. The execution mode depends on the
3112 3112 interactivity parameter.
3113 3113
3114 3114 Parameters
3115 3115 ----------
3116 3116 nodelist : list
3117 3117 A sequence of AST nodes to run.
3118 3118 cell_name : str
3119 3119 Will be passed to the compiler as the filename of the cell. Typically
3120 3120 the value returned by ip.compile.cache(cell).
3121 3121 interactivity : str
3122 3122 'all', 'last', 'last_expr' , 'last_expr_or_assign' or 'none',
3123 3123 specifying which nodes should be run interactively (displaying output
3124 3124 from expressions). 'last_expr' will run the last node interactively
3125 3125 only if it is an expression (i.e. expressions in loops or other blocks
3126 3126 are not displayed) 'last_expr_or_assign' will run the last expression
3127 3127 or the last assignment. Other values for this parameter will raise a
3128 3128 ValueError.
3129 3129
3130 3130 compiler : callable
3131 3131 A function with the same interface as the built-in compile(), to turn
3132 3132 the AST nodes into code objects. Default is the built-in compile().
3133 3133 result : ExecutionResult, optional
3134 3134 An object to store exceptions that occur during execution.
3135 3135
3136 3136 Returns
3137 3137 -------
3138 3138 True if an exception occurred while running code, False if it finished
3139 3139 running.
3140 3140 """
3141 3141 if not nodelist:
3142 3142 return
3143 3143
3144 3144
3145 3145 if interactivity == 'last_expr_or_assign':
3146 3146 if isinstance(nodelist[-1], _assign_nodes):
3147 3147 asg = nodelist[-1]
3148 3148 if isinstance(asg, ast.Assign) and len(asg.targets) == 1:
3149 3149 target = asg.targets[0]
3150 3150 elif isinstance(asg, _single_targets_nodes):
3151 3151 target = asg.target
3152 3152 else:
3153 3153 target = None
3154 3154 if isinstance(target, ast.Name):
3155 3155 nnode = ast.Expr(ast.Name(target.id, ast.Load()))
3156 3156 ast.fix_missing_locations(nnode)
3157 3157 nodelist.append(nnode)
3158 3158 interactivity = 'last_expr'
3159 3159
3160 3160 _async = False
3161 3161 if interactivity == 'last_expr':
3162 3162 if isinstance(nodelist[-1], ast.Expr):
3163 3163 interactivity = "last"
3164 3164 else:
3165 3165 interactivity = "none"
3166 3166
3167 3167 if interactivity == 'none':
3168 3168 to_run_exec, to_run_interactive = nodelist, []
3169 3169 elif interactivity == 'last':
3170 3170 to_run_exec, to_run_interactive = nodelist[:-1], nodelist[-1:]
3171 3171 elif interactivity == 'all':
3172 3172 to_run_exec, to_run_interactive = [], nodelist
3173 3173 else:
3174 3174 raise ValueError("Interactivity was %r" % interactivity)
3175 3175
3176 3176 try:
3177 3177
3178 3178 def compare(code):
3179 3179 is_async = inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE
3180 3180 return is_async
3181 3181
3182 3182 # refactor that to just change the mod constructor.
3183 3183 to_run = []
3184 3184 for node in to_run_exec:
3185 3185 to_run.append((node, "exec"))
3186 3186
3187 3187 for node in to_run_interactive:
3188 3188 to_run.append((node, "single"))
3189 3189
3190 3190 for node, mode in to_run:
3191 3191 if mode == "exec":
3192 3192 mod = Module([node], [])
3193 3193 elif mode == "single":
3194 3194 mod = ast.Interactive([node])
3195 3195 with compiler.extra_flags(
3196 3196 getattr(ast, "PyCF_ALLOW_TOP_LEVEL_AWAIT", 0x0)
3197 3197 if self.autoawait
3198 3198 else 0x0
3199 3199 ):
3200 3200 code = compiler(mod, cell_name, mode)
3201 3201 asy = compare(code)
3202 3202 if await self.run_code(code, result, async_=asy):
3203 3203 return True
3204 3204
3205 3205 # Flush softspace
3206 3206 if softspace(sys.stdout, 0):
3207 3207 print()
3208 3208
3209 3209 except:
3210 3210 # It's possible to have exceptions raised here, typically by
3211 3211 # compilation of odd code (such as a naked 'return' outside a
3212 3212 # function) that did parse but isn't valid. Typically the exception
3213 3213 # is a SyntaxError, but it's safest just to catch anything and show
3214 3214 # the user a traceback.
3215 3215
3216 3216 # We do only one try/except outside the loop to minimize the impact
3217 3217 # on runtime, and also because if any node in the node list is
3218 3218 # broken, we should stop execution completely.
3219 3219 if result:
3220 3220 result.error_before_exec = sys.exc_info()[1]
3221 3221 self.showtraceback()
3222 3222 return True
3223 3223
3224 3224 return False
3225 3225
3226 3226 async def run_code(self, code_obj, result=None, *, async_=False):
3227 3227 """Execute a code object.
3228 3228
3229 3229 When an exception occurs, self.showtraceback() is called to display a
3230 3230 traceback.
3231 3231
3232 3232 Parameters
3233 3233 ----------
3234 3234 code_obj : code object
3235 3235 A compiled code object, to be executed
3236 3236 result : ExecutionResult, optional
3237 3237 An object to store exceptions that occur during execution.
3238 3238 async_ : Bool (Experimental)
3239 3239 Attempt to run top-level asynchronous code in a default loop.
3240 3240
3241 3241 Returns
3242 3242 -------
3243 3243 False : successful execution.
3244 3244 True : an error occurred.
3245 3245 """
3246 3246 # special value to say that anything above is IPython and should be
3247 3247 # hidden.
3248 3248 __tracebackhide__ = "__ipython_bottom__"
3249 3249 # Set our own excepthook in case the user code tries to call it
3250 3250 # directly, so that the IPython crash handler doesn't get triggered
3251 3251 old_excepthook, sys.excepthook = sys.excepthook, self.excepthook
3252 3252
3253 3253 # we save the original sys.excepthook in the instance, in case config
3254 3254 # code (such as magics) needs access to it.
3255 3255 self.sys_excepthook = old_excepthook
3256 3256 outflag = True # happens in more places, so it's easier as default
3257 3257 try:
3258 3258 try:
3259 3259 if async_:
3260 3260 await eval(code_obj, self.user_global_ns, self.user_ns)
3261 3261 else:
3262 3262 exec(code_obj, self.user_global_ns, self.user_ns)
3263 3263 finally:
3264 3264 # Reset our crash handler in place
3265 3265 sys.excepthook = old_excepthook
3266 3266 except SystemExit as e:
3267 3267 if result is not None:
3268 3268 result.error_in_exec = e
3269 3269 self.showtraceback(exception_only=True)
3270 3270 warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)
3271 3271 except self.custom_exceptions:
3272 3272 etype, value, tb = sys.exc_info()
3273 3273 if result is not None:
3274 3274 result.error_in_exec = value
3275 3275 self.CustomTB(etype, value, tb)
3276 3276 except:
3277 3277 if result is not None:
3278 3278 result.error_in_exec = sys.exc_info()[1]
3279 3279 self.showtraceback(running_compiled_code=True)
3280 3280 else:
3281 3281 outflag = False
3282 3282 return outflag
3283 3283
3284 3284 # For backwards compatibility
3285 3285 runcode = run_code
3286 3286
3287 3287 def check_complete(self, code: str) -> Tuple[str, str]:
3288 3288 """Return whether a block of code is ready to execute, or should be continued
3289 3289
3290 3290 Parameters
3291 3291 ----------
3292 3292 code : string
3293 3293 Python input code, which can be multiline.
3294 3294
3295 3295 Returns
3296 3296 -------
3297 3297 status : str
3298 3298 One of 'complete', 'incomplete', or 'invalid' if source is not a
3299 3299 prefix of valid code.
3300 3300 indent : str
3301 3301 When status is 'incomplete', this is some whitespace to insert on
3302 3302 the next line of the prompt.
3303 3303 """
3304 3304 status, nspaces = self.input_transformer_manager.check_complete(code)
3305 3305 return status, ' ' * (nspaces or 0)
3306 3306
3307 3307 #-------------------------------------------------------------------------
3308 3308 # Things related to GUI support and pylab
3309 3309 #-------------------------------------------------------------------------
3310 3310
3311 3311 active_eventloop = None
3312 3312
3313 3313 def enable_gui(self, gui=None):
3314 3314 raise NotImplementedError('Implement enable_gui in a subclass')
3315 3315
3316 3316 def enable_matplotlib(self, gui=None):
3317 3317 """Enable interactive matplotlib and inline figure support.
3318 3318
3319 3319 This takes the following steps:
3320 3320
3321 3321 1. select the appropriate eventloop and matplotlib backend
3322 3322 2. set up matplotlib for interactive use with that backend
3323 3323 3. configure formatters for inline figure display
3324 3324 4. enable the selected gui eventloop
3325 3325
3326 3326 Parameters
3327 3327 ----------
3328 3328 gui : optional, string
3329 3329 If given, dictates the choice of matplotlib GUI backend to use
3330 3330 (should be one of IPython's supported backends, 'qt', 'osx', 'tk',
3331 3331 'gtk', 'wx' or 'inline'), otherwise we use the default chosen by
3332 3332 matplotlib (as dictated by the matplotlib build-time options plus the
3333 3333 user's matplotlibrc configuration file). Note that not all backends
3334 3334 make sense in all contexts, for example a terminal ipython can't
3335 3335 display figures inline.
3336 3336 """
3337 3337 from IPython.core import pylabtools as pt
3338 3338 from matplotlib_inline.backend_inline import configure_inline_support
3339 3339 gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select)
3340 3340
3341 3341 if gui != 'inline':
3342 3342 # If we have our first gui selection, store it
3343 3343 if self.pylab_gui_select is None:
3344 3344 self.pylab_gui_select = gui
3345 3345 # Otherwise if they are different
3346 3346 elif gui != self.pylab_gui_select:
3347 3347 print('Warning: Cannot change to a different GUI toolkit: %s.'
3348 3348 ' Using %s instead.' % (gui, self.pylab_gui_select))
3349 3349 gui, backend = pt.find_gui_and_backend(self.pylab_gui_select)
3350 3350
3351 3351 pt.activate_matplotlib(backend)
3352 3352 configure_inline_support(self, backend)
3353 3353
3354 3354 # Now we must activate the gui pylab wants to use, and fix %run to take
3355 3355 # plot updates into account
3356 3356 self.enable_gui(gui)
3357 3357 self.magics_manager.registry['ExecutionMagics'].default_runner = \
3358 3358 pt.mpl_runner(self.safe_execfile)
3359 3359
3360 3360 return gui, backend
3361 3361
3362 3362 def enable_pylab(self, gui=None, import_all=True, welcome_message=False):
3363 3363 """Activate pylab support at runtime.
3364 3364
3365 3365 This turns on support for matplotlib, preloads into the interactive
3366 3366 namespace all of numpy and pylab, and configures IPython to correctly
3367 3367 interact with the GUI event loop. The GUI backend to be used can be
3368 3368 optionally selected with the optional ``gui`` argument.
3369 3369
3370 3370 This method only adds preloading the namespace to InteractiveShell.enable_matplotlib.
3371 3371
3372 3372 Parameters
3373 3373 ----------
3374 3374 gui : optional, string
3375 3375 If given, dictates the choice of matplotlib GUI backend to use
3376 3376 (should be one of IPython's supported backends, 'qt', 'osx', 'tk',
3377 3377 'gtk', 'wx' or 'inline'), otherwise we use the default chosen by
3378 3378 matplotlib (as dictated by the matplotlib build-time options plus the
3379 3379 user's matplotlibrc configuration file). Note that not all backends
3380 3380 make sense in all contexts, for example a terminal ipython can't
3381 3381 display figures inline.
3382 3382 import_all : optional, bool, default: True
3383 3383 Whether to do `from numpy import *` and `from pylab import *`
3384 3384 in addition to module imports.
3385 3385 welcome_message : deprecated
3386 3386 This argument is ignored, no welcome message will be displayed.
3387 3387 """
3388 3388 from IPython.core.pylabtools import import_pylab
3389 3389
3390 3390 gui, backend = self.enable_matplotlib(gui)
3391 3391
3392 3392 # We want to prevent the loading of pylab to pollute the user's
3393 3393 # namespace as shown by the %who* magics, so we execute the activation
3394 3394 # code in an empty namespace, and we update *both* user_ns and
3395 3395 # user_ns_hidden with this information.
3396 3396 ns = {}
3397 3397 import_pylab(ns, import_all)
3398 3398 # warn about clobbered names
3399 3399 ignored = {"__builtins__"}
3400 3400 both = set(ns).intersection(self.user_ns).difference(ignored)
3401 3401 clobbered = [ name for name in both if self.user_ns[name] is not ns[name] ]
3402 3402 self.user_ns.update(ns)
3403 3403 self.user_ns_hidden.update(ns)
3404 3404 return gui, backend, clobbered
3405 3405
3406 3406 #-------------------------------------------------------------------------
3407 3407 # Utilities
3408 3408 #-------------------------------------------------------------------------
3409 3409
3410 3410 def var_expand(self, cmd, depth=0, formatter=DollarFormatter()):
3411 3411 """Expand python variables in a string.
3412 3412
3413 3413 The depth argument indicates how many frames above the caller should
3414 3414 be walked to look for the local namespace where to expand variables.
3415 3415
3416 3416 The global namespace for expansion is always the user's interactive
3417 3417 namespace.
3418 3418 """
3419 3419 ns = self.user_ns.copy()
3420 3420 try:
3421 3421 frame = sys._getframe(depth+1)
3422 3422 except ValueError:
3423 3423 # This is thrown if there aren't that many frames on the stack,
3424 3424 # e.g. if a script called run_line_magic() directly.
3425 3425 pass
3426 3426 else:
3427 3427 ns.update(frame.f_locals)
3428 3428
3429 3429 try:
3430 3430 # We have to use .vformat() here, because 'self' is a valid and common
3431 3431 # name, and expanding **ns for .format() would make it collide with
3432 3432 # the 'self' argument of the method.
3433 3433 cmd = formatter.vformat(cmd, args=[], kwargs=ns)
3434 3434 except Exception:
3435 3435 # if formatter couldn't format, just let it go untransformed
3436 3436 pass
3437 3437 return cmd
3438 3438
3439 3439 def mktempfile(self, data=None, prefix='ipython_edit_'):
3440 3440 """Make a new tempfile and return its filename.
3441 3441
3442 3442 This makes a call to tempfile.mkstemp (created in a tempfile.mkdtemp),
3443 3443 but it registers the created filename internally so ipython cleans it up
3444 3444 at exit time.
3445 3445
3446 3446 Optional inputs:
3447 3447
3448 3448 - data(None): if data is given, it gets written out to the temp file
3449 3449 immediately, and the file is closed again."""
3450 3450
3451 3451 dir_path = Path(tempfile.mkdtemp(prefix=prefix))
3452 3452 self.tempdirs.append(dir_path)
3453 3453
3454 3454 handle, filename = tempfile.mkstemp(".py", prefix, dir=str(dir_path))
3455 3455 os.close(handle) # On Windows, there can only be one open handle on a file
3456 3456
3457 3457 file_path = Path(filename)
3458 3458 self.tempfiles.append(file_path)
3459 3459
3460 3460 if data:
3461 file_path.write_text(data, encoding='utf-8')
3461 file_path.write_text(data, encoding="utf-8")
3462 3462 return filename
3463 3463
3464 3464 def ask_yes_no(self, prompt, default=None, interrupt=None):
3465 3465 if self.quiet:
3466 3466 return True
3467 3467 return ask_yes_no(prompt,default,interrupt)
3468 3468
3469 3469 def show_usage(self):
3470 3470 """Show a usage message"""
3471 3471 page.page(IPython.core.usage.interactive_usage)
3472 3472
3473 3473 def extract_input_lines(self, range_str, raw=False):
3474 3474 """Return as a string a set of input history slices.
3475 3475
3476 3476 Parameters
3477 3477 ----------
3478 3478 range_str : str
3479 3479 The set of slices is given as a string, like "~5/6-~4/2 4:8 9",
3480 3480 since this function is for use by magic functions which get their
3481 3481 arguments as strings. The number before the / is the session
3482 3482 number: ~n goes n back from the current session.
3483 3483
3484 3484 If empty string is given, returns history of current session
3485 3485 without the last input.
3486 3486
3487 3487 raw : bool, optional
3488 3488 By default, the processed input is used. If this is true, the raw
3489 3489 input history is used instead.
3490 3490
3491 3491 Notes
3492 3492 -----
3493 3493 Slices can be described with two notations:
3494 3494
3495 3495 * ``N:M`` -> standard python form, means including items N...(M-1).
3496 3496 * ``N-M`` -> include items N..M (closed endpoint).
3497 3497 """
3498 3498 lines = self.history_manager.get_range_by_str(range_str, raw=raw)
3499 3499 text = "\n".join(x for _, _, x in lines)
3500 3500
3501 3501 # Skip the last line, as it's probably the magic that called this
3502 3502 if not range_str:
3503 3503 if "\n" not in text:
3504 3504 text = ""
3505 3505 else:
3506 3506 text = text[: text.rfind("\n")]
3507 3507
3508 3508 return text
3509 3509
3510 3510 def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True, search_ns=False):
3511 3511 """Get a code string from history, file, url, or a string or macro.
3512 3512
3513 3513 This is mainly used by magic functions.
3514 3514
3515 3515 Parameters
3516 3516 ----------
3517 3517 target : str
3518 3518 A string specifying code to retrieve. This will be tried respectively
3519 3519 as: ranges of input history (see %history for syntax), url,
3520 3520 corresponding .py file, filename, or an expression evaluating to a
3521 3521 string or Macro in the user namespace.
3522 3522
3523 3523 If empty string is given, returns complete history of current
3524 3524 session, without the last line.
3525 3525
3526 3526 raw : bool
3527 3527 If true (default), retrieve raw history. Has no effect on the other
3528 3528 retrieval mechanisms.
3529 3529
3530 3530 py_only : bool (default False)
3531 3531 Only try to fetch python code, do not try alternative methods to decode file
3532 3532 if unicode fails.
3533 3533
3534 3534 Returns
3535 3535 -------
3536 3536 A string of code.
3537 3537 ValueError is raised if nothing is found, and TypeError if it evaluates
3538 3538 to an object of another type. In each case, .args[0] is a printable
3539 3539 message.
3540 3540 """
3541 3541 code = self.extract_input_lines(target, raw=raw) # Grab history
3542 3542 if code:
3543 3543 return code
3544 3544 try:
3545 3545 if target.startswith(('http://', 'https://')):
3546 3546 return openpy.read_py_url(target, skip_encoding_cookie=skip_encoding_cookie)
3547 3547 except UnicodeDecodeError as e:
3548 3548 if not py_only :
3549 3549 # Deferred import
3550 3550 from urllib.request import urlopen
3551 3551 response = urlopen(target)
3552 3552 return response.read().decode('latin1')
3553 3553 raise ValueError(("'%s' seem to be unreadable.") % target) from e
3554 3554
3555 3555 potential_target = [target]
3556 3556 try :
3557 3557 potential_target.insert(0,get_py_filename(target))
3558 3558 except IOError:
3559 3559 pass
3560 3560
3561 3561 for tgt in potential_target :
3562 3562 if os.path.isfile(tgt): # Read file
3563 3563 try :
3564 3564 return openpy.read_py_file(tgt, skip_encoding_cookie=skip_encoding_cookie)
3565 3565 except UnicodeDecodeError as e:
3566 3566 if not py_only :
3567 3567 with io_open(tgt,'r', encoding='latin1') as f :
3568 3568 return f.read()
3569 3569 raise ValueError(("'%s' seem to be unreadable.") % target) from e
3570 3570 elif os.path.isdir(os.path.expanduser(tgt)):
3571 3571 raise ValueError("'%s' is a directory, not a regular file." % target)
3572 3572
3573 3573 if search_ns:
3574 3574 # Inspect namespace to load object source
3575 3575 object_info = self.object_inspect(target, detail_level=1)
3576 3576 if object_info['found'] and object_info['source']:
3577 3577 return object_info['source']
3578 3578
3579 3579 try: # User namespace
3580 3580 codeobj = eval(target, self.user_ns)
3581 3581 except Exception as e:
3582 3582 raise ValueError(("'%s' was not found in history, as a file, url, "
3583 3583 "nor in the user namespace.") % target) from e
3584 3584
3585 3585 if isinstance(codeobj, str):
3586 3586 return codeobj
3587 3587 elif isinstance(codeobj, Macro):
3588 3588 return codeobj.value
3589 3589
3590 3590 raise TypeError("%s is neither a string nor a macro." % target,
3591 3591 codeobj)
3592 3592
3593 3593 def _atexit_once(self):
3594 3594 """
3595 3595 At exist operation that need to be called at most once.
3596 3596 Second call to this function per instance will do nothing.
3597 3597 """
3598 3598
3599 3599 if not getattr(self, "_atexit_once_called", False):
3600 3600 self._atexit_once_called = True
3601 3601 # Clear all user namespaces to release all references cleanly.
3602 3602 self.reset(new_session=False)
3603 3603 # Close the history session (this stores the end time and line count)
3604 3604 # this must be *before* the tempfile cleanup, in case of temporary
3605 3605 # history db
3606 3606 self.history_manager.end_session()
3607 3607 self.history_manager = None
3608 3608
3609 3609 #-------------------------------------------------------------------------
3610 3610 # Things related to IPython exiting
3611 3611 #-------------------------------------------------------------------------
3612 3612 def atexit_operations(self):
3613 3613 """This will be executed at the time of exit.
3614 3614
3615 3615 Cleanup operations and saving of persistent data that is done
3616 3616 unconditionally by IPython should be performed here.
3617 3617
3618 3618 For things that may depend on startup flags or platform specifics (such
3619 3619 as having readline or not), register a separate atexit function in the
3620 3620 code that has the appropriate information, rather than trying to
3621 3621 clutter
3622 3622 """
3623 3623 self._atexit_once()
3624 3624
3625 3625 # Cleanup all tempfiles and folders left around
3626 3626 for tfile in self.tempfiles:
3627 3627 try:
3628 3628 tfile.unlink()
3629 3629 self.tempfiles.remove(tfile)
3630 3630 except FileNotFoundError:
3631 3631 pass
3632 3632 del self.tempfiles
3633 3633 for tdir in self.tempdirs:
3634 3634 try:
3635 3635 tdir.rmdir()
3636 3636 self.tempdirs.remove(tdir)
3637 3637 except FileNotFoundError:
3638 3638 pass
3639 3639 del self.tempdirs
3640 3640
3641 3641
3642 3642 def cleanup(self):
3643 3643 self.restore_sys_module_state()
3644 3644
3645 3645
3646 3646 # Overridden in terminal subclass to change prompts
3647 3647 def switch_doctest_mode(self, mode):
3648 3648 pass
3649 3649
3650 3650
3651 3651 class InteractiveShellABC(metaclass=abc.ABCMeta):
3652 3652 """An abstract base class for InteractiveShell."""
3653 3653
3654 3654 InteractiveShellABC.register(InteractiveShell)
@@ -1,755 +1,755 b''
1 1 """Implementation of code management magic functions.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012 The IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 # Stdlib
16 16 import inspect
17 17 import io
18 18 import os
19 19 import re
20 20 import sys
21 21 import ast
22 22 from itertools import chain
23 23 from urllib.request import Request, urlopen
24 24 from urllib.parse import urlencode
25 25 from pathlib import Path
26 26
27 27 # Our own packages
28 28 from IPython.core.error import TryNext, StdinNotImplementedError, UsageError
29 29 from IPython.core.macro import Macro
30 30 from IPython.core.magic import Magics, magics_class, line_magic
31 31 from IPython.core.oinspect import find_file, find_source_lines
32 32 from IPython.core.release import version
33 33 from IPython.testing.skipdoctest import skip_doctest
34 34 from IPython.utils.contexts import preserve_keys
35 35 from IPython.utils.path import get_py_filename
36 36 from warnings import warn
37 37 from logging import error
38 38 from IPython.utils.text import get_text_list
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Magic implementation classes
42 42 #-----------------------------------------------------------------------------
43 43
44 44 # Used for exception handling in magic_edit
45 45 class MacroToEdit(ValueError): pass
46 46
47 47 ipython_input_pat = re.compile(r"<ipython\-input\-(\d+)-[a-z\d]+>$")
48 48
49 49 # To match, e.g. 8-10 1:5 :10 3-
50 50 range_re = re.compile(r"""
51 51 (?P<start>\d+)?
52 52 ((?P<sep>[\-:])
53 53 (?P<end>\d+)?)?
54 54 $""", re.VERBOSE)
55 55
56 56
57 57 def extract_code_ranges(ranges_str):
58 58 """Turn a string of range for %%load into 2-tuples of (start, stop)
59 59 ready to use as a slice of the content split by lines.
60 60
61 61 Examples
62 62 --------
63 63 list(extract_input_ranges("5-10 2"))
64 64 [(4, 10), (1, 2)]
65 65 """
66 66 for range_str in ranges_str.split():
67 67 rmatch = range_re.match(range_str)
68 68 if not rmatch:
69 69 continue
70 70 sep = rmatch.group("sep")
71 71 start = rmatch.group("start")
72 72 end = rmatch.group("end")
73 73
74 74 if sep == '-':
75 75 start = int(start) - 1 if start else None
76 76 end = int(end) if end else None
77 77 elif sep == ':':
78 78 start = int(start) - 1 if start else None
79 79 end = int(end) - 1 if end else None
80 80 else:
81 81 end = int(start)
82 82 start = int(start) - 1
83 83 yield (start, end)
84 84
85 85
86 86 def extract_symbols(code, symbols):
87 87 """
88 88 Return a tuple (blocks, not_found)
89 89 where ``blocks`` is a list of code fragments
90 90 for each symbol parsed from code, and ``not_found`` are
91 91 symbols not found in the code.
92 92
93 93 For example::
94 94
95 95 In [1]: code = '''a = 10
96 96 ...: def b(): return 42
97 97 ...: class A: pass'''
98 98
99 99 In [2]: extract_symbols(code, 'A,b,z')
100 100 Out[2]: (['class A: pass\\n', 'def b(): return 42\\n'], ['z'])
101 101 """
102 102 symbols = symbols.split(',')
103 103
104 104 # this will raise SyntaxError if code isn't valid Python
105 105 py_code = ast.parse(code)
106 106
107 107 marks = [(getattr(s, 'name', None), s.lineno) for s in py_code.body]
108 108 code = code.split('\n')
109 109
110 110 symbols_lines = {}
111 111
112 112 # we already know the start_lineno of each symbol (marks).
113 113 # To find each end_lineno, we traverse in reverse order until each
114 114 # non-blank line
115 115 end = len(code)
116 116 for name, start in reversed(marks):
117 117 while not code[end - 1].strip():
118 118 end -= 1
119 119 if name:
120 120 symbols_lines[name] = (start - 1, end)
121 121 end = start - 1
122 122
123 123 # Now symbols_lines is a map
124 124 # {'symbol_name': (start_lineno, end_lineno), ...}
125 125
126 126 # fill a list with chunks of codes for each requested symbol
127 127 blocks = []
128 128 not_found = []
129 129 for symbol in symbols:
130 130 if symbol in symbols_lines:
131 131 start, end = symbols_lines[symbol]
132 132 blocks.append('\n'.join(code[start:end]) + '\n')
133 133 else:
134 134 not_found.append(symbol)
135 135
136 136 return blocks, not_found
137 137
138 138 def strip_initial_indent(lines):
139 139 """For %load, strip indent from lines until finding an unindented line.
140 140
141 141 https://github.com/ipython/ipython/issues/9775
142 142 """
143 143 indent_re = re.compile(r'\s+')
144 144
145 145 it = iter(lines)
146 146 first_line = next(it)
147 147 indent_match = indent_re.match(first_line)
148 148
149 149 if indent_match:
150 150 # First line was indented
151 151 indent = indent_match.group()
152 152 yield first_line[len(indent):]
153 153
154 154 for line in it:
155 155 if line.startswith(indent):
156 156 yield line[len(indent):]
157 157 else:
158 158 # Less indented than the first line - stop dedenting
159 159 yield line
160 160 break
161 161 else:
162 162 yield first_line
163 163
164 164 # Pass the remaining lines through without dedenting
165 165 for line in it:
166 166 yield line
167 167
168 168
169 169 class InteractivelyDefined(Exception):
170 170 """Exception for interactively defined variable in magic_edit"""
171 171 def __init__(self, index):
172 172 self.index = index
173 173
174 174
175 175 @magics_class
176 176 class CodeMagics(Magics):
177 177 """Magics related to code management (loading, saving, editing, ...)."""
178 178
179 179 def __init__(self, *args, **kwargs):
180 180 self._knowntemps = set()
181 181 super(CodeMagics, self).__init__(*args, **kwargs)
182 182
183 183 @line_magic
184 184 def save(self, parameter_s=''):
185 185 """Save a set of lines or a macro to a given filename.
186 186
187 187 Usage:\\
188 188 %save [options] filename [history]
189 189
190 190 Options:
191 191
192 192 -r: use 'raw' input. By default, the 'processed' history is used,
193 193 so that magics are loaded in their transformed version to valid
194 194 Python. If this option is given, the raw input as typed as the
195 195 command line is used instead.
196 196
197 197 -f: force overwrite. If file exists, %save will prompt for overwrite
198 198 unless -f is given.
199 199
200 200 -a: append to the file instead of overwriting it.
201 201
202 202 The history argument uses the same syntax as %history for input ranges,
203 203 then saves the lines to the filename you specify.
204 204
205 205 If no ranges are specified, saves history of the current session up to
206 206 this point.
207 207
208 208 It adds a '.py' extension to the file if you don't do so yourself, and
209 209 it asks for confirmation before overwriting existing files.
210 210
211 211 If `-r` option is used, the default extension is `.ipy`.
212 212 """
213 213
214 214 opts,args = self.parse_options(parameter_s,'fra',mode='list')
215 215 if not args:
216 216 raise UsageError('Missing filename.')
217 217 raw = 'r' in opts
218 218 force = 'f' in opts
219 219 append = 'a' in opts
220 220 mode = 'a' if append else 'w'
221 221 ext = '.ipy' if raw else '.py'
222 222 fname, codefrom = args[0], " ".join(args[1:])
223 223 if not fname.endswith(('.py','.ipy')):
224 224 fname += ext
225 225 fname = os.path.expanduser(fname)
226 226 file_exists = os.path.isfile(fname)
227 227 if file_exists and not force and not append:
228 228 try:
229 229 overwrite = self.shell.ask_yes_no('File `%s` exists. Overwrite (y/[N])? ' % fname, default='n')
230 230 except StdinNotImplementedError:
231 231 print("File `%s` exists. Use `%%save -f %s` to force overwrite" % (fname, parameter_s))
232 232 return
233 233 if not overwrite :
234 234 print('Operation cancelled.')
235 235 return
236 236 try:
237 237 cmds = self.shell.find_user_code(codefrom,raw)
238 238 except (TypeError, ValueError) as e:
239 239 print(e.args[0])
240 240 return
241 241 with io.open(fname, mode, encoding="utf-8") as f:
242 242 if not file_exists or not append:
243 243 f.write("# coding: utf-8\n")
244 244 f.write(cmds)
245 245 # make sure we end on a newline
246 246 if not cmds.endswith('\n'):
247 247 f.write('\n')
248 248 print('The following commands were written to file `%s`:' % fname)
249 249 print(cmds)
250 250
251 251 @line_magic
252 252 def pastebin(self, parameter_s=''):
253 253 """Upload code to dpaste.com, returning the URL.
254 254
255 255 Usage:\\
256 256 %pastebin [-d "Custom description"][-e 24] 1-7
257 257
258 258 The argument can be an input history range, a filename, or the name of a
259 259 string or macro.
260 260
261 261 If no arguments are given, uploads the history of this session up to
262 262 this point.
263 263
264 264 Options:
265 265
266 266 -d: Pass a custom description. The default will say
267 267 "Pasted from IPython".
268 268 -e: Pass number of days for the link to be expired.
269 269 The default will be 7 days.
270 270 """
271 271 opts, args = self.parse_options(parameter_s, "d:e:")
272 272
273 273 try:
274 274 code = self.shell.find_user_code(args)
275 275 except (ValueError, TypeError) as e:
276 276 print(e.args[0])
277 277 return
278 278
279 279 expiry_days = 7
280 280 try:
281 281 expiry_days = int(opts.get("e", 7))
282 282 except ValueError as e:
283 283 print(e.args[0].capitalize())
284 284 return
285 285 if expiry_days < 1 or expiry_days > 365:
286 286 print("Expiry days should be in range of 1 to 365")
287 287 return
288 288
289 289 post_data = urlencode(
290 290 {
291 291 "title": opts.get("d", "Pasted from IPython"),
292 292 "syntax": "python",
293 293 "content": code,
294 294 "expiry_days": expiry_days,
295 295 }
296 296 ).encode("utf-8")
297 297
298 298 request = Request(
299 299 "https://dpaste.com/api/v2/",
300 300 headers={"User-Agent": "IPython v{}".format(version)},
301 301 )
302 302 response = urlopen(request, post_data)
303 303 return response.headers.get('Location')
304 304
305 305 @line_magic
306 306 def loadpy(self, arg_s):
307 307 """Alias of `%load`
308 308
309 309 `%loadpy` has gained some flexibility and dropped the requirement of a `.py`
310 310 extension. So it has been renamed simply into %load. You can look at
311 311 `%load`'s docstring for more info.
312 312 """
313 313 self.load(arg_s)
314 314
315 315 @line_magic
316 316 def load(self, arg_s):
317 317 """Load code into the current frontend.
318 318
319 319 Usage:\\
320 320 %load [options] source
321 321
322 322 where source can be a filename, URL, input history range, macro, or
323 323 element in the user namespace
324 324
325 325 If no arguments are given, loads the history of this session up to this
326 326 point.
327 327
328 328 Options:
329 329
330 330 -r <lines>: Specify lines or ranges of lines to load from the source.
331 331 Ranges could be specified as x-y (x..y) or in python-style x:y
332 332 (x..(y-1)). Both limits x and y can be left blank (meaning the
333 333 beginning and end of the file, respectively).
334 334
335 335 -s <symbols>: Specify function or classes to load from python source.
336 336
337 337 -y : Don't ask confirmation for loading source above 200 000 characters.
338 338
339 339 -n : Include the user's namespace when searching for source code.
340 340
341 341 This magic command can either take a local filename, a URL, an history
342 342 range (see %history) or a macro as argument, it will prompt for
343 343 confirmation before loading source with more than 200 000 characters, unless
344 344 -y flag is passed or if the frontend does not support raw_input::
345 345
346 346 %load
347 347 %load myscript.py
348 348 %load 7-27
349 349 %load myMacro
350 350 %load http://www.example.com/myscript.py
351 351 %load -r 5-10 myscript.py
352 352 %load -r 10-20,30,40: foo.py
353 353 %load -s MyClass,wonder_function myscript.py
354 354 %load -n MyClass
355 355 %load -n my_module.wonder_function
356 356 """
357 357 opts,args = self.parse_options(arg_s,'yns:r:')
358 358 search_ns = 'n' in opts
359 359 contents = self.shell.find_user_code(args, search_ns=search_ns)
360 360
361 361 if 's' in opts:
362 362 try:
363 363 blocks, not_found = extract_symbols(contents, opts['s'])
364 364 except SyntaxError:
365 365 # non python code
366 366 error("Unable to parse the input as valid Python code")
367 367 return
368 368
369 369 if len(not_found) == 1:
370 370 warn('The symbol `%s` was not found' % not_found[0])
371 371 elif len(not_found) > 1:
372 372 warn('The symbols %s were not found' % get_text_list(not_found,
373 373 wrap_item_with='`')
374 374 )
375 375
376 376 contents = '\n'.join(blocks)
377 377
378 378 if 'r' in opts:
379 379 ranges = opts['r'].replace(',', ' ')
380 380 lines = contents.split('\n')
381 381 slices = extract_code_ranges(ranges)
382 382 contents = [lines[slice(*slc)] for slc in slices]
383 383 contents = '\n'.join(strip_initial_indent(chain.from_iterable(contents)))
384 384
385 385 l = len(contents)
386 386
387 387 # 200 000 is ~ 2500 full 80 character lines
388 388 # so in average, more than 5000 lines
389 389 if l > 200000 and 'y' not in opts:
390 390 try:
391 391 ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\
392 392 " (%d characters). Continue (y/[N]) ?" % l), default='n' )
393 393 except StdinNotImplementedError:
394 394 #assume yes if raw input not implemented
395 395 ans = True
396 396
397 397 if ans is False :
398 398 print('Operation cancelled.')
399 399 return
400 400
401 401 contents = "# %load {}\n".format(arg_s) + contents
402 402
403 403 self.shell.set_next_input(contents, replace=True)
404 404
405 405 @staticmethod
406 406 def _find_edit_target(shell, args, opts, last_call):
407 407 """Utility method used by magic_edit to find what to edit."""
408 408
409 409 def make_filename(arg):
410 410 "Make a filename from the given args"
411 411 try:
412 412 filename = get_py_filename(arg)
413 413 except IOError:
414 414 # If it ends with .py but doesn't already exist, assume we want
415 415 # a new file.
416 416 if arg.endswith('.py'):
417 417 filename = arg
418 418 else:
419 419 filename = None
420 420 return filename
421 421
422 422 # Set a few locals from the options for convenience:
423 423 opts_prev = 'p' in opts
424 424 opts_raw = 'r' in opts
425 425
426 426 # custom exceptions
427 427 class DataIsObject(Exception): pass
428 428
429 429 # Default line number value
430 430 lineno = opts.get('n',None)
431 431
432 432 if opts_prev:
433 433 args = '_%s' % last_call[0]
434 434 if args not in shell.user_ns:
435 435 args = last_call[1]
436 436
437 437 # by default this is done with temp files, except when the given
438 438 # arg is a filename
439 439 use_temp = True
440 440
441 441 data = ''
442 442
443 443 # First, see if the arguments should be a filename.
444 444 filename = make_filename(args)
445 445 if filename:
446 446 use_temp = False
447 447 elif args:
448 448 # Mode where user specifies ranges of lines, like in %macro.
449 449 data = shell.extract_input_lines(args, opts_raw)
450 450 if not data:
451 451 try:
452 452 # Load the parameter given as a variable. If not a string,
453 453 # process it as an object instead (below)
454 454
455 455 #print '*** args',args,'type',type(args) # dbg
456 456 data = eval(args, shell.user_ns)
457 457 if not isinstance(data, str):
458 458 raise DataIsObject
459 459
460 460 except (NameError,SyntaxError):
461 461 # given argument is not a variable, try as a filename
462 462 filename = make_filename(args)
463 463 if filename is None:
464 464 warn("Argument given (%s) can't be found as a variable "
465 465 "or as a filename." % args)
466 466 return (None, None, None)
467 467 use_temp = False
468 468
469 469 except DataIsObject as e:
470 470 # macros have a special edit function
471 471 if isinstance(data, Macro):
472 472 raise MacroToEdit(data) from e
473 473
474 474 # For objects, try to edit the file where they are defined
475 475 filename = find_file(data)
476 476 if filename:
477 477 if 'fakemodule' in filename.lower() and \
478 478 inspect.isclass(data):
479 479 # class created by %edit? Try to find source
480 480 # by looking for method definitions instead, the
481 481 # __module__ in those classes is FakeModule.
482 482 attrs = [getattr(data, aname) for aname in dir(data)]
483 483 for attr in attrs:
484 484 if not inspect.ismethod(attr):
485 485 continue
486 486 filename = find_file(attr)
487 487 if filename and \
488 488 'fakemodule' not in filename.lower():
489 489 # change the attribute to be the edit
490 490 # target instead
491 491 data = attr
492 492 break
493 493
494 494 m = ipython_input_pat.match(os.path.basename(filename))
495 495 if m:
496 496 raise InteractivelyDefined(int(m.groups()[0])) from e
497 497
498 498 datafile = 1
499 499 if filename is None:
500 500 filename = make_filename(args)
501 501 datafile = 1
502 502 if filename is not None:
503 503 # only warn about this if we get a real name
504 504 warn('Could not find file where `%s` is defined.\n'
505 505 'Opening a file named `%s`' % (args, filename))
506 506 # Now, make sure we can actually read the source (if it was
507 507 # in a temp file it's gone by now).
508 508 if datafile:
509 509 if lineno is None:
510 510 lineno = find_source_lines(data)
511 511 if lineno is None:
512 512 filename = make_filename(args)
513 513 if filename is None:
514 514 warn('The file where `%s` was defined '
515 515 'cannot be read or found.' % data)
516 516 return (None, None, None)
517 517 use_temp = False
518 518
519 519 if use_temp:
520 520 filename = shell.mktempfile(data)
521 521 print('IPython will make a temporary file named:',filename)
522 522
523 523 # use last_call to remember the state of the previous call, but don't
524 524 # let it be clobbered by successive '-p' calls.
525 525 try:
526 526 last_call[0] = shell.displayhook.prompt_count
527 527 if not opts_prev:
528 528 last_call[1] = args
529 529 except:
530 530 pass
531 531
532 532
533 533 return filename, lineno, use_temp
534 534
535 535 def _edit_macro(self,mname,macro):
536 536 """open an editor with the macro data in a file"""
537 537 filename = self.shell.mktempfile(macro.value)
538 538 self.shell.hooks.editor(filename)
539 539
540 540 # and make a new macro object, to replace the old one
541 mvalue = Path(filename).read_text(encoding='utf-8')
541 mvalue = Path(filename).read_text(encoding="utf-8")
542 542 self.shell.user_ns[mname] = Macro(mvalue)
543 543
544 544 @skip_doctest
545 545 @line_magic
546 546 def edit(self, parameter_s='',last_call=['','']):
547 547 """Bring up an editor and execute the resulting code.
548 548
549 549 Usage:
550 550 %edit [options] [args]
551 551
552 552 %edit runs IPython's editor hook. The default version of this hook is
553 553 set to call the editor specified by your $EDITOR environment variable.
554 554 If this isn't found, it will default to vi under Linux/Unix and to
555 555 notepad under Windows. See the end of this docstring for how to change
556 556 the editor hook.
557 557
558 558 You can also set the value of this editor via the
559 559 ``TerminalInteractiveShell.editor`` option in your configuration file.
560 560 This is useful if you wish to use a different editor from your typical
561 561 default with IPython (and for Windows users who typically don't set
562 562 environment variables).
563 563
564 564 This command allows you to conveniently edit multi-line code right in
565 565 your IPython session.
566 566
567 567 If called without arguments, %edit opens up an empty editor with a
568 568 temporary file and will execute the contents of this file when you
569 569 close it (don't forget to save it!).
570 570
571 571
572 572 Options:
573 573
574 574 -n <number>: open the editor at a specified line number. By default,
575 575 the IPython editor hook uses the unix syntax 'editor +N filename', but
576 576 you can configure this by providing your own modified hook if your
577 577 favorite editor supports line-number specifications with a different
578 578 syntax.
579 579
580 580 -p: this will call the editor with the same data as the previous time
581 581 it was used, regardless of how long ago (in your current session) it
582 582 was.
583 583
584 584 -r: use 'raw' input. This option only applies to input taken from the
585 585 user's history. By default, the 'processed' history is used, so that
586 586 magics are loaded in their transformed version to valid Python. If
587 587 this option is given, the raw input as typed as the command line is
588 588 used instead. When you exit the editor, it will be executed by
589 589 IPython's own processor.
590 590
591 591 -x: do not execute the edited code immediately upon exit. This is
592 592 mainly useful if you are editing programs which need to be called with
593 593 command line arguments, which you can then do using %run.
594 594
595 595
596 596 Arguments:
597 597
598 598 If arguments are given, the following possibilities exist:
599 599
600 600 - If the argument is a filename, IPython will load that into the
601 601 editor. It will execute its contents with execfile() when you exit,
602 602 loading any code in the file into your interactive namespace.
603 603
604 604 - The arguments are ranges of input history, e.g. "7 ~1/4-6".
605 605 The syntax is the same as in the %history magic.
606 606
607 607 - If the argument is a string variable, its contents are loaded
608 608 into the editor. You can thus edit any string which contains
609 609 python code (including the result of previous edits).
610 610
611 611 - If the argument is the name of an object (other than a string),
612 612 IPython will try to locate the file where it was defined and open the
613 613 editor at the point where it is defined. You can use `%edit function`
614 614 to load an editor exactly at the point where 'function' is defined,
615 615 edit it and have the file be executed automatically.
616 616
617 617 - If the object is a macro (see %macro for details), this opens up your
618 618 specified editor with a temporary file containing the macro's data.
619 619 Upon exit, the macro is reloaded with the contents of the file.
620 620
621 621 Note: opening at an exact line is only supported under Unix, and some
622 622 editors (like kedit and gedit up to Gnome 2.8) do not understand the
623 623 '+NUMBER' parameter necessary for this feature. Good editors like
624 624 (X)Emacs, vi, jed, pico and joe all do.
625 625
626 626 After executing your code, %edit will return as output the code you
627 627 typed in the editor (except when it was an existing file). This way
628 628 you can reload the code in further invocations of %edit as a variable,
629 629 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
630 630 the output.
631 631
632 632 Note that %edit is also available through the alias %ed.
633 633
634 634 This is an example of creating a simple function inside the editor and
635 635 then modifying it. First, start up the editor::
636 636
637 637 In [1]: edit
638 638 Editing... done. Executing edited code...
639 639 Out[1]: 'def foo():\\n print "foo() was defined in an editing
640 640 session"\\n'
641 641
642 642 We can then call the function foo()::
643 643
644 644 In [2]: foo()
645 645 foo() was defined in an editing session
646 646
647 647 Now we edit foo. IPython automatically loads the editor with the
648 648 (temporary) file where foo() was previously defined::
649 649
650 650 In [3]: edit foo
651 651 Editing... done. Executing edited code...
652 652
653 653 And if we call foo() again we get the modified version::
654 654
655 655 In [4]: foo()
656 656 foo() has now been changed!
657 657
658 658 Here is an example of how to edit a code snippet successive
659 659 times. First we call the editor::
660 660
661 661 In [5]: edit
662 662 Editing... done. Executing edited code...
663 663 hello
664 664 Out[5]: "print 'hello'\\n"
665 665
666 666 Now we call it again with the previous output (stored in _)::
667 667
668 668 In [6]: edit _
669 669 Editing... done. Executing edited code...
670 670 hello world
671 671 Out[6]: "print 'hello world'\\n"
672 672
673 673 Now we call it with the output #8 (stored in _8, also as Out[8])::
674 674
675 675 In [7]: edit _8
676 676 Editing... done. Executing edited code...
677 677 hello again
678 678 Out[7]: "print 'hello again'\\n"
679 679
680 680
681 681 Changing the default editor hook:
682 682
683 683 If you wish to write your own editor hook, you can put it in a
684 684 configuration file which you load at startup time. The default hook
685 685 is defined in the IPython.core.hooks module, and you can use that as a
686 686 starting example for further modifications. That file also has
687 687 general instructions on how to set a new hook for use once you've
688 688 defined it."""
689 689 opts,args = self.parse_options(parameter_s,'prxn:')
690 690
691 691 try:
692 692 filename, lineno, is_temp = self._find_edit_target(self.shell,
693 693 args, opts, last_call)
694 694 except MacroToEdit as e:
695 695 self._edit_macro(args, e.args[0])
696 696 return
697 697 except InteractivelyDefined as e:
698 698 print("Editing In[%i]" % e.index)
699 699 args = str(e.index)
700 700 filename, lineno, is_temp = self._find_edit_target(self.shell,
701 701 args, opts, last_call)
702 702 if filename is None:
703 703 # nothing was found, warnings have already been issued,
704 704 # just give up.
705 705 return
706 706
707 707 if is_temp:
708 708 self._knowntemps.add(filename)
709 709 elif (filename in self._knowntemps):
710 710 is_temp = True
711 711
712 712
713 713 # do actual editing here
714 714 print('Editing...', end=' ')
715 715 sys.stdout.flush()
716 716 filepath = Path(filename)
717 717 try:
718 718 # Quote filenames that may have spaces in them when opening
719 719 # the editor
720 720 quoted = filename = str(filepath.absolute())
721 721 if " " in quoted:
722 722 quoted = "'%s'" % quoted
723 723 self.shell.hooks.editor(quoted, lineno)
724 724 except TryNext:
725 725 warn('Could not open editor')
726 726 return
727 727
728 728 # XXX TODO: should this be generalized for all string vars?
729 729 # For now, this is special-cased to blocks created by cpaste
730 730 if args.strip() == "pasted_block":
731 self.shell.user_ns["pasted_block"] = filepath.read_text(encoding='utf-8')
731 self.shell.user_ns["pasted_block"] = filepath.read_text(encoding="utf-8")
732 732
733 733 if 'x' in opts: # -x prevents actual execution
734 734 print()
735 735 else:
736 736 print('done. Executing edited code...')
737 737 with preserve_keys(self.shell.user_ns, '__file__'):
738 738 if not is_temp:
739 self.shell.user_ns['__file__'] = filename
740 if 'r' in opts: # Untranslated IPython code
741 source = filepath.read_text(encoding='utf-8')
739 self.shell.user_ns["__file__"] = filename
740 if "r" in opts: # Untranslated IPython code
741 source = filepath.read_text(encoding="utf-8")
742 742 self.shell.run_cell(source, store_history=False)
743 743 else:
744 744 self.shell.safe_execfile(filename, self.shell.user_ns,
745 745 self.shell.user_ns)
746 746
747 747 if is_temp:
748 748 try:
749 return filepath.read_text(encoding='utf-8')
749 return filepath.read_text(encoding="utf-8")
750 750 except IOError as msg:
751 751 if Path(msg.filename) == filepath:
752 752 warn('File not found. Did you forget to save?')
753 753 return
754 754 else:
755 755 self.shell.showtraceback()
@@ -1,1510 +1,1510 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Implementation of execution-related magic functions."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7
8 8 import ast
9 9 import bdb
10 10 import builtins as builtin_mod
11 11 import cProfile as profile
12 12 import gc
13 13 import itertools
14 14 import math
15 15 import os
16 16 import pstats
17 17 import re
18 18 import shlex
19 19 import sys
20 20 import time
21 21 import timeit
22 22 from ast import Module
23 23 from io import StringIO
24 24 from logging import error
25 25 from pathlib import Path
26 26 from pdb import Restart
27 27 from warnings import warn
28 28
29 29 from IPython.core import magic_arguments, oinspect, page
30 30 from IPython.core.error import UsageError
31 31 from IPython.core.macro import Macro
32 32 from IPython.core.magic import (
33 33 Magics,
34 34 cell_magic,
35 35 line_cell_magic,
36 36 line_magic,
37 37 magics_class,
38 38 needs_local_scope,
39 39 no_var_expand,
40 40 on_off,
41 41 )
42 42 from IPython.testing.skipdoctest import skip_doctest
43 43 from IPython.utils.capture import capture_output
44 44 from IPython.utils.contexts import preserve_keys
45 45 from IPython.utils.ipstruct import Struct
46 46 from IPython.utils.module_paths import find_mod
47 47 from IPython.utils.path import get_py_filename, shellglob
48 48 from IPython.utils.timing import clock, clock2
49 49
50 50 #-----------------------------------------------------------------------------
51 51 # Magic implementation classes
52 52 #-----------------------------------------------------------------------------
53 53
54 54
55 55 class TimeitResult(object):
56 56 """
57 57 Object returned by the timeit magic with info about the run.
58 58
59 59 Contains the following attributes :
60 60
61 61 loops: (int) number of loops done per measurement
62 62 repeat: (int) number of times the measurement has been repeated
63 63 best: (float) best execution time / number
64 64 all_runs: (list of float) execution time of each run (in s)
65 65 compile_time: (float) time of statement compilation (s)
66 66
67 67 """
68 68 def __init__(self, loops, repeat, best, worst, all_runs, compile_time, precision):
69 69 self.loops = loops
70 70 self.repeat = repeat
71 71 self.best = best
72 72 self.worst = worst
73 73 self.all_runs = all_runs
74 74 self.compile_time = compile_time
75 75 self._precision = precision
76 76 self.timings = [ dt / self.loops for dt in all_runs]
77 77
78 78 @property
79 79 def average(self):
80 80 return math.fsum(self.timings) / len(self.timings)
81 81
82 82 @property
83 83 def stdev(self):
84 84 mean = self.average
85 85 return (math.fsum([(x - mean) ** 2 for x in self.timings]) / len(self.timings)) ** 0.5
86 86
87 87 def __str__(self):
88 88 pm = '+-'
89 89 if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding:
90 90 try:
91 91 u'\xb1'.encode(sys.stdout.encoding)
92 92 pm = u'\xb1'
93 93 except:
94 94 pass
95 95 return "{mean} {pm} {std} per loop (mean {pm} std. dev. of {runs} run{run_plural}, {loops:,} loop{loop_plural} each)".format(
96 96 pm=pm,
97 97 runs=self.repeat,
98 98 loops=self.loops,
99 99 loop_plural="" if self.loops == 1 else "s",
100 100 run_plural="" if self.repeat == 1 else "s",
101 101 mean=_format_time(self.average, self._precision),
102 102 std=_format_time(self.stdev, self._precision),
103 103 )
104 104
105 105 def _repr_pretty_(self, p , cycle):
106 106 unic = self.__str__()
107 107 p.text(u'<TimeitResult : '+unic+u'>')
108 108
109 109
110 110 class TimeitTemplateFiller(ast.NodeTransformer):
111 111 """Fill in the AST template for timing execution.
112 112
113 113 This is quite closely tied to the template definition, which is in
114 114 :meth:`ExecutionMagics.timeit`.
115 115 """
116 116 def __init__(self, ast_setup, ast_stmt):
117 117 self.ast_setup = ast_setup
118 118 self.ast_stmt = ast_stmt
119 119
120 120 def visit_FunctionDef(self, node):
121 121 "Fill in the setup statement"
122 122 self.generic_visit(node)
123 123 if node.name == "inner":
124 124 node.body[:1] = self.ast_setup.body
125 125
126 126 return node
127 127
128 128 def visit_For(self, node):
129 129 "Fill in the statement to be timed"
130 130 if getattr(getattr(node.body[0], 'value', None), 'id', None) == 'stmt':
131 131 node.body = self.ast_stmt.body
132 132 return node
133 133
134 134
135 135 class Timer(timeit.Timer):
136 136 """Timer class that explicitly uses self.inner
137 137
138 138 which is an undocumented implementation detail of CPython,
139 139 not shared by PyPy.
140 140 """
141 141 # Timer.timeit copied from CPython 3.4.2
142 142 def timeit(self, number=timeit.default_number):
143 143 """Time 'number' executions of the main statement.
144 144
145 145 To be precise, this executes the setup statement once, and
146 146 then returns the time it takes to execute the main statement
147 147 a number of times, as a float measured in seconds. The
148 148 argument is the number of times through the loop, defaulting
149 149 to one million. The main statement, the setup statement and
150 150 the timer function to be used are passed to the constructor.
151 151 """
152 152 it = itertools.repeat(None, number)
153 153 gcold = gc.isenabled()
154 154 gc.disable()
155 155 try:
156 156 timing = self.inner(it, self.timer)
157 157 finally:
158 158 if gcold:
159 159 gc.enable()
160 160 return timing
161 161
162 162
163 163 @magics_class
164 164 class ExecutionMagics(Magics):
165 165 """Magics related to code execution, debugging, profiling, etc.
166 166
167 167 """
168 168
169 169 def __init__(self, shell):
170 170 super(ExecutionMagics, self).__init__(shell)
171 171 # Default execution function used to actually run user code.
172 172 self.default_runner = None
173 173
174 174 @skip_doctest
175 175 @no_var_expand
176 176 @line_cell_magic
177 177 def prun(self, parameter_s='', cell=None):
178 178
179 179 """Run a statement through the python code profiler.
180 180
181 181 Usage, in line mode:
182 182 %prun [options] statement
183 183
184 184 Usage, in cell mode:
185 185 %%prun [options] [statement]
186 186 code...
187 187 code...
188 188
189 189 In cell mode, the additional code lines are appended to the (possibly
190 190 empty) statement in the first line. Cell mode allows you to easily
191 191 profile multiline blocks without having to put them in a separate
192 192 function.
193 193
194 194 The given statement (which doesn't require quote marks) is run via the
195 195 python profiler in a manner similar to the profile.run() function.
196 196 Namespaces are internally managed to work correctly; profile.run
197 197 cannot be used in IPython because it makes certain assumptions about
198 198 namespaces which do not hold under IPython.
199 199
200 200 Options:
201 201
202 202 -l <limit>
203 203 you can place restrictions on what or how much of the
204 204 profile gets printed. The limit value can be:
205 205
206 206 * A string: only information for function names containing this string
207 207 is printed.
208 208
209 209 * An integer: only these many lines are printed.
210 210
211 211 * A float (between 0 and 1): this fraction of the report is printed
212 212 (for example, use a limit of 0.4 to see the topmost 40% only).
213 213
214 214 You can combine several limits with repeated use of the option. For
215 215 example, ``-l __init__ -l 5`` will print only the topmost 5 lines of
216 216 information about class constructors.
217 217
218 218 -r
219 219 return the pstats.Stats object generated by the profiling. This
220 220 object has all the information about the profile in it, and you can
221 221 later use it for further analysis or in other functions.
222 222
223 223 -s <key>
224 224 sort profile by given key. You can provide more than one key
225 225 by using the option several times: '-s key1 -s key2 -s key3...'. The
226 226 default sorting key is 'time'.
227 227
228 228 The following is copied verbatim from the profile documentation
229 229 referenced below:
230 230
231 231 When more than one key is provided, additional keys are used as
232 232 secondary criteria when the there is equality in all keys selected
233 233 before them.
234 234
235 235 Abbreviations can be used for any key names, as long as the
236 236 abbreviation is unambiguous. The following are the keys currently
237 237 defined:
238 238
239 239 ============ =====================
240 240 Valid Arg Meaning
241 241 ============ =====================
242 242 "calls" call count
243 243 "cumulative" cumulative time
244 244 "file" file name
245 245 "module" file name
246 246 "pcalls" primitive call count
247 247 "line" line number
248 248 "name" function name
249 249 "nfl" name/file/line
250 250 "stdname" standard name
251 251 "time" internal time
252 252 ============ =====================
253 253
254 254 Note that all sorts on statistics are in descending order (placing
255 255 most time consuming items first), where as name, file, and line number
256 256 searches are in ascending order (i.e., alphabetical). The subtle
257 257 distinction between "nfl" and "stdname" is that the standard name is a
258 258 sort of the name as printed, which means that the embedded line
259 259 numbers get compared in an odd way. For example, lines 3, 20, and 40
260 260 would (if the file names were the same) appear in the string order
261 261 "20" "3" and "40". In contrast, "nfl" does a numeric compare of the
262 262 line numbers. In fact, sort_stats("nfl") is the same as
263 263 sort_stats("name", "file", "line").
264 264
265 265 -T <filename>
266 266 save profile results as shown on screen to a text
267 267 file. The profile is still shown on screen.
268 268
269 269 -D <filename>
270 270 save (via dump_stats) profile statistics to given
271 271 filename. This data is in a format understood by the pstats module, and
272 272 is generated by a call to the dump_stats() method of profile
273 273 objects. The profile is still shown on screen.
274 274
275 275 -q
276 276 suppress output to the pager. Best used with -T and/or -D above.
277 277
278 278 If you want to run complete programs under the profiler's control, use
279 279 ``%run -p [prof_opts] filename.py [args to program]`` where prof_opts
280 280 contains profiler specific options as described here.
281 281
282 282 You can read the complete documentation for the profile module with::
283 283
284 284 In [1]: import profile; profile.help()
285 285
286 286 .. versionchanged:: 7.3
287 287 User variables are no longer expanded,
288 288 the magic line is always left unmodified.
289 289
290 290 """
291 291 opts, arg_str = self.parse_options(parameter_s, 'D:l:rs:T:q',
292 292 list_all=True, posix=False)
293 293 if cell is not None:
294 294 arg_str += '\n' + cell
295 295 arg_str = self.shell.transform_cell(arg_str)
296 296 return self._run_with_profiler(arg_str, opts, self.shell.user_ns)
297 297
298 298 def _run_with_profiler(self, code, opts, namespace):
299 299 """
300 300 Run `code` with profiler. Used by ``%prun`` and ``%run -p``.
301 301
302 302 Parameters
303 303 ----------
304 304 code : str
305 305 Code to be executed.
306 306 opts : Struct
307 307 Options parsed by `self.parse_options`.
308 308 namespace : dict
309 309 A dictionary for Python namespace (e.g., `self.shell.user_ns`).
310 310
311 311 """
312 312
313 313 # Fill default values for unspecified options:
314 314 opts.merge(Struct(D=[''], l=[], s=['time'], T=['']))
315 315
316 316 prof = profile.Profile()
317 317 try:
318 318 prof = prof.runctx(code, namespace, namespace)
319 319 sys_exit = ''
320 320 except SystemExit:
321 321 sys_exit = """*** SystemExit exception caught in code being profiled."""
322 322
323 323 stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s)
324 324
325 325 lims = opts.l
326 326 if lims:
327 327 lims = [] # rebuild lims with ints/floats/strings
328 328 for lim in opts.l:
329 329 try:
330 330 lims.append(int(lim))
331 331 except ValueError:
332 332 try:
333 333 lims.append(float(lim))
334 334 except ValueError:
335 335 lims.append(lim)
336 336
337 337 # Trap output.
338 338 stdout_trap = StringIO()
339 339 stats_stream = stats.stream
340 340 try:
341 341 stats.stream = stdout_trap
342 342 stats.print_stats(*lims)
343 343 finally:
344 344 stats.stream = stats_stream
345 345
346 346 output = stdout_trap.getvalue()
347 347 output = output.rstrip()
348 348
349 349 if 'q' not in opts:
350 350 page.page(output)
351 351 print(sys_exit, end=' ')
352 352
353 353 dump_file = opts.D[0]
354 354 text_file = opts.T[0]
355 355 if dump_file:
356 356 prof.dump_stats(dump_file)
357 357 print(
358 358 f"\n*** Profile stats marshalled to file {repr(dump_file)}.{sys_exit}"
359 359 )
360 360 if text_file:
361 361 pfile = Path(text_file)
362 362 pfile.touch(exist_ok=True)
363 pfile.write_text(output, encoding='utf-8')
363 pfile.write_text(output, encoding="utf-8")
364 364
365 365 print(
366 366 f"\n*** Profile printout saved to text file {repr(text_file)}.{sys_exit}"
367 367 )
368 368
369 369 if 'r' in opts:
370 370 return stats
371 371
372 372 return None
373 373
374 374 @line_magic
375 375 def pdb(self, parameter_s=''):
376 376 """Control the automatic calling of the pdb interactive debugger.
377 377
378 378 Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without
379 379 argument it works as a toggle.
380 380
381 381 When an exception is triggered, IPython can optionally call the
382 382 interactive pdb debugger after the traceback printout. %pdb toggles
383 383 this feature on and off.
384 384
385 385 The initial state of this feature is set in your configuration
386 386 file (the option is ``InteractiveShell.pdb``).
387 387
388 388 If you want to just activate the debugger AFTER an exception has fired,
389 389 without having to type '%pdb on' and rerunning your code, you can use
390 390 the %debug magic."""
391 391
392 392 par = parameter_s.strip().lower()
393 393
394 394 if par:
395 395 try:
396 396 new_pdb = {'off':0,'0':0,'on':1,'1':1}[par]
397 397 except KeyError:
398 398 print ('Incorrect argument. Use on/1, off/0, '
399 399 'or nothing for a toggle.')
400 400 return
401 401 else:
402 402 # toggle
403 403 new_pdb = not self.shell.call_pdb
404 404
405 405 # set on the shell
406 406 self.shell.call_pdb = new_pdb
407 407 print('Automatic pdb calling has been turned',on_off(new_pdb))
408 408
409 409 @magic_arguments.magic_arguments()
410 410 @magic_arguments.argument('--breakpoint', '-b', metavar='FILE:LINE',
411 411 help="""
412 412 Set break point at LINE in FILE.
413 413 """
414 414 )
415 415 @magic_arguments.argument('statement', nargs='*',
416 416 help="""
417 417 Code to run in debugger.
418 418 You can omit this in cell magic mode.
419 419 """
420 420 )
421 421 @no_var_expand
422 422 @line_cell_magic
423 423 def debug(self, line='', cell=None):
424 424 """Activate the interactive debugger.
425 425
426 426 This magic command support two ways of activating debugger.
427 427 One is to activate debugger before executing code. This way, you
428 428 can set a break point, to step through the code from the point.
429 429 You can use this mode by giving statements to execute and optionally
430 430 a breakpoint.
431 431
432 432 The other one is to activate debugger in post-mortem mode. You can
433 433 activate this mode simply running %debug without any argument.
434 434 If an exception has just occurred, this lets you inspect its stack
435 435 frames interactively. Note that this will always work only on the last
436 436 traceback that occurred, so you must call this quickly after an
437 437 exception that you wish to inspect has fired, because if another one
438 438 occurs, it clobbers the previous one.
439 439
440 440 If you want IPython to automatically do this on every exception, see
441 441 the %pdb magic for more details.
442 442
443 443 .. versionchanged:: 7.3
444 444 When running code, user variables are no longer expanded,
445 445 the magic line is always left unmodified.
446 446
447 447 """
448 448 args = magic_arguments.parse_argstring(self.debug, line)
449 449
450 450 if not (args.breakpoint or args.statement or cell):
451 451 self._debug_post_mortem()
452 452 elif not (args.breakpoint or cell):
453 453 # If there is no breakpoints, the line is just code to execute
454 454 self._debug_exec(line, None)
455 455 else:
456 456 # Here we try to reconstruct the code from the output of
457 457 # parse_argstring. This might not work if the code has spaces
458 458 # For example this fails for `print("a b")`
459 459 code = "\n".join(args.statement)
460 460 if cell:
461 461 code += "\n" + cell
462 462 self._debug_exec(code, args.breakpoint)
463 463
464 464 def _debug_post_mortem(self):
465 465 self.shell.debugger(force=True)
466 466
467 467 def _debug_exec(self, code, breakpoint):
468 468 if breakpoint:
469 469 (filename, bp_line) = breakpoint.rsplit(':', 1)
470 470 bp_line = int(bp_line)
471 471 else:
472 472 (filename, bp_line) = (None, None)
473 473 self._run_with_debugger(code, self.shell.user_ns, filename, bp_line)
474 474
475 475 @line_magic
476 476 def tb(self, s):
477 477 """Print the last traceback.
478 478
479 479 Optionally, specify an exception reporting mode, tuning the
480 480 verbosity of the traceback. By default the currently-active exception
481 481 mode is used. See %xmode for changing exception reporting modes.
482 482
483 483 Valid modes: Plain, Context, Verbose, and Minimal.
484 484 """
485 485 interactive_tb = self.shell.InteractiveTB
486 486 if s:
487 487 # Switch exception reporting mode for this one call.
488 488 # Ensure it is switched back.
489 489 def xmode_switch_err(name):
490 490 warn('Error changing %s exception modes.\n%s' %
491 491 (name,sys.exc_info()[1]))
492 492
493 493 new_mode = s.strip().capitalize()
494 494 original_mode = interactive_tb.mode
495 495 try:
496 496 try:
497 497 interactive_tb.set_mode(mode=new_mode)
498 498 except Exception:
499 499 xmode_switch_err('user')
500 500 else:
501 501 self.shell.showtraceback()
502 502 finally:
503 503 interactive_tb.set_mode(mode=original_mode)
504 504 else:
505 505 self.shell.showtraceback()
506 506
507 507 @skip_doctest
508 508 @line_magic
509 509 def run(self, parameter_s='', runner=None,
510 510 file_finder=get_py_filename):
511 511 """Run the named file inside IPython as a program.
512 512
513 513 Usage::
514 514
515 515 %run [-n -i -e -G]
516 516 [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )]
517 517 ( -m mod | filename ) [args]
518 518
519 519 The filename argument should be either a pure Python script (with
520 520 extension ``.py``), or a file with custom IPython syntax (such as
521 521 magics). If the latter, the file can be either a script with ``.ipy``
522 522 extension, or a Jupyter notebook with ``.ipynb`` extension. When running
523 523 a Jupyter notebook, the output from print statements and other
524 524 displayed objects will appear in the terminal (even matplotlib figures
525 525 will open, if a terminal-compliant backend is being used). Note that,
526 526 at the system command line, the ``jupyter run`` command offers similar
527 527 functionality for executing notebooks (albeit currently with some
528 528 differences in supported options).
529 529
530 530 Parameters after the filename are passed as command-line arguments to
531 531 the program (put in sys.argv). Then, control returns to IPython's
532 532 prompt.
533 533
534 534 This is similar to running at a system prompt ``python file args``,
535 535 but with the advantage of giving you IPython's tracebacks, and of
536 536 loading all variables into your interactive namespace for further use
537 537 (unless -p is used, see below).
538 538
539 539 The file is executed in a namespace initially consisting only of
540 540 ``__name__=='__main__'`` and sys.argv constructed as indicated. It thus
541 541 sees its environment as if it were being run as a stand-alone program
542 542 (except for sharing global objects such as previously imported
543 543 modules). But after execution, the IPython interactive namespace gets
544 544 updated with all variables defined in the program (except for __name__
545 545 and sys.argv). This allows for very convenient loading of code for
546 546 interactive work, while giving each program a 'clean sheet' to run in.
547 547
548 548 Arguments are expanded using shell-like glob match. Patterns
549 549 '*', '?', '[seq]' and '[!seq]' can be used. Additionally,
550 550 tilde '~' will be expanded into user's home directory. Unlike
551 551 real shells, quotation does not suppress expansions. Use
552 552 *two* back slashes (e.g. ``\\\\*``) to suppress expansions.
553 553 To completely disable these expansions, you can use -G flag.
554 554
555 555 On Windows systems, the use of single quotes `'` when specifying
556 556 a file is not supported. Use double quotes `"`.
557 557
558 558 Options:
559 559
560 560 -n
561 561 __name__ is NOT set to '__main__', but to the running file's name
562 562 without extension (as python does under import). This allows running
563 563 scripts and reloading the definitions in them without calling code
564 564 protected by an ``if __name__ == "__main__"`` clause.
565 565
566 566 -i
567 567 run the file in IPython's namespace instead of an empty one. This
568 568 is useful if you are experimenting with code written in a text editor
569 569 which depends on variables defined interactively.
570 570
571 571 -e
572 572 ignore sys.exit() calls or SystemExit exceptions in the script
573 573 being run. This is particularly useful if IPython is being used to
574 574 run unittests, which always exit with a sys.exit() call. In such
575 575 cases you are interested in the output of the test results, not in
576 576 seeing a traceback of the unittest module.
577 577
578 578 -t
579 579 print timing information at the end of the run. IPython will give
580 580 you an estimated CPU time consumption for your script, which under
581 581 Unix uses the resource module to avoid the wraparound problems of
582 582 time.clock(). Under Unix, an estimate of time spent on system tasks
583 583 is also given (for Windows platforms this is reported as 0.0).
584 584
585 585 If -t is given, an additional ``-N<N>`` option can be given, where <N>
586 586 must be an integer indicating how many times you want the script to
587 587 run. The final timing report will include total and per run results.
588 588
589 589 For example (testing the script uniq_stable.py)::
590 590
591 591 In [1]: run -t uniq_stable
592 592
593 593 IPython CPU timings (estimated):
594 594 User : 0.19597 s.
595 595 System: 0.0 s.
596 596
597 597 In [2]: run -t -N5 uniq_stable
598 598
599 599 IPython CPU timings (estimated):
600 600 Total runs performed: 5
601 601 Times : Total Per run
602 602 User : 0.910862 s, 0.1821724 s.
603 603 System: 0.0 s, 0.0 s.
604 604
605 605 -d
606 606 run your program under the control of pdb, the Python debugger.
607 607 This allows you to execute your program step by step, watch variables,
608 608 etc. Internally, what IPython does is similar to calling::
609 609
610 610 pdb.run('execfile("YOURFILENAME")')
611 611
612 612 with a breakpoint set on line 1 of your file. You can change the line
613 613 number for this automatic breakpoint to be <N> by using the -bN option
614 614 (where N must be an integer). For example::
615 615
616 616 %run -d -b40 myscript
617 617
618 618 will set the first breakpoint at line 40 in myscript.py. Note that
619 619 the first breakpoint must be set on a line which actually does
620 620 something (not a comment or docstring) for it to stop execution.
621 621
622 622 Or you can specify a breakpoint in a different file::
623 623
624 624 %run -d -b myotherfile.py:20 myscript
625 625
626 626 When the pdb debugger starts, you will see a (Pdb) prompt. You must
627 627 first enter 'c' (without quotes) to start execution up to the first
628 628 breakpoint.
629 629
630 630 Entering 'help' gives information about the use of the debugger. You
631 631 can easily see pdb's full documentation with "import pdb;pdb.help()"
632 632 at a prompt.
633 633
634 634 -p
635 635 run program under the control of the Python profiler module (which
636 636 prints a detailed report of execution times, function calls, etc).
637 637
638 638 You can pass other options after -p which affect the behavior of the
639 639 profiler itself. See the docs for %prun for details.
640 640
641 641 In this mode, the program's variables do NOT propagate back to the
642 642 IPython interactive namespace (because they remain in the namespace
643 643 where the profiler executes them).
644 644
645 645 Internally this triggers a call to %prun, see its documentation for
646 646 details on the options available specifically for profiling.
647 647
648 648 There is one special usage for which the text above doesn't apply:
649 649 if the filename ends with .ipy[nb], the file is run as ipython script,
650 650 just as if the commands were written on IPython prompt.
651 651
652 652 -m
653 653 specify module name to load instead of script path. Similar to
654 654 the -m option for the python interpreter. Use this option last if you
655 655 want to combine with other %run options. Unlike the python interpreter
656 656 only source modules are allowed no .pyc or .pyo files.
657 657 For example::
658 658
659 659 %run -m example
660 660
661 661 will run the example module.
662 662
663 663 -G
664 664 disable shell-like glob expansion of arguments.
665 665
666 666 """
667 667
668 668 # Logic to handle issue #3664
669 669 # Add '--' after '-m <module_name>' to ignore additional args passed to a module.
670 670 if '-m' in parameter_s and '--' not in parameter_s:
671 671 argv = shlex.split(parameter_s, posix=(os.name == 'posix'))
672 672 for idx, arg in enumerate(argv):
673 673 if arg and arg.startswith('-') and arg != '-':
674 674 if arg == '-m':
675 675 argv.insert(idx + 2, '--')
676 676 break
677 677 else:
678 678 # Positional arg, break
679 679 break
680 680 parameter_s = ' '.join(shlex.quote(arg) for arg in argv)
681 681
682 682 # get arguments and set sys.argv for program to be run.
683 683 opts, arg_lst = self.parse_options(parameter_s,
684 684 'nidtN:b:pD:l:rs:T:em:G',
685 685 mode='list', list_all=1)
686 686 if "m" in opts:
687 687 modulename = opts["m"][0]
688 688 modpath = find_mod(modulename)
689 689 if modpath is None:
690 690 msg = '%r is not a valid modulename on sys.path'%modulename
691 691 raise Exception(msg)
692 692 arg_lst = [modpath] + arg_lst
693 693 try:
694 694 fpath = None # initialize to make sure fpath is in scope later
695 695 fpath = arg_lst[0]
696 696 filename = file_finder(fpath)
697 697 except IndexError as e:
698 698 msg = 'you must provide at least a filename.'
699 699 raise Exception(msg) from e
700 700 except IOError as e:
701 701 try:
702 702 msg = str(e)
703 703 except UnicodeError:
704 704 msg = e.message
705 705 if os.name == 'nt' and re.match(r"^'.*'$",fpath):
706 706 warn('For Windows, use double quotes to wrap a filename: %run "mypath\\myfile.py"')
707 707 raise Exception(msg) from e
708 708 except TypeError:
709 709 if fpath in sys.meta_path:
710 710 filename = ""
711 711 else:
712 712 raise
713 713
714 714 if filename.lower().endswith(('.ipy', '.ipynb')):
715 715 with preserve_keys(self.shell.user_ns, '__file__'):
716 716 self.shell.user_ns['__file__'] = filename
717 717 self.shell.safe_execfile_ipy(filename, raise_exceptions=True)
718 718 return
719 719
720 720 # Control the response to exit() calls made by the script being run
721 721 exit_ignore = 'e' in opts
722 722
723 723 # Make sure that the running script gets a proper sys.argv as if it
724 724 # were run from a system shell.
725 725 save_argv = sys.argv # save it for later restoring
726 726
727 727 if 'G' in opts:
728 728 args = arg_lst[1:]
729 729 else:
730 730 # tilde and glob expansion
731 731 args = shellglob(map(os.path.expanduser, arg_lst[1:]))
732 732
733 733 sys.argv = [filename] + args # put in the proper filename
734 734
735 735 if 'n' in opts:
736 736 name = Path(filename).stem
737 737 else:
738 738 name = '__main__'
739 739
740 740 if 'i' in opts:
741 741 # Run in user's interactive namespace
742 742 prog_ns = self.shell.user_ns
743 743 __name__save = self.shell.user_ns['__name__']
744 744 prog_ns['__name__'] = name
745 745 main_mod = self.shell.user_module
746 746
747 747 # Since '%run foo' emulates 'python foo.py' at the cmd line, we must
748 748 # set the __file__ global in the script's namespace
749 749 # TK: Is this necessary in interactive mode?
750 750 prog_ns['__file__'] = filename
751 751 else:
752 752 # Run in a fresh, empty namespace
753 753
754 754 # The shell MUST hold a reference to prog_ns so after %run
755 755 # exits, the python deletion mechanism doesn't zero it out
756 756 # (leaving dangling references). See interactiveshell for details
757 757 main_mod = self.shell.new_main_mod(filename, name)
758 758 prog_ns = main_mod.__dict__
759 759
760 760 # pickle fix. See interactiveshell for an explanation. But we need to
761 761 # make sure that, if we overwrite __main__, we replace it at the end
762 762 main_mod_name = prog_ns['__name__']
763 763
764 764 if main_mod_name == '__main__':
765 765 restore_main = sys.modules['__main__']
766 766 else:
767 767 restore_main = False
768 768
769 769 # This needs to be undone at the end to prevent holding references to
770 770 # every single object ever created.
771 771 sys.modules[main_mod_name] = main_mod
772 772
773 773 if 'p' in opts or 'd' in opts:
774 774 if 'm' in opts:
775 775 code = 'run_module(modulename, prog_ns)'
776 776 code_ns = {
777 777 'run_module': self.shell.safe_run_module,
778 778 'prog_ns': prog_ns,
779 779 'modulename': modulename,
780 780 }
781 781 else:
782 782 if 'd' in opts:
783 783 # allow exceptions to raise in debug mode
784 784 code = 'execfile(filename, prog_ns, raise_exceptions=True)'
785 785 else:
786 786 code = 'execfile(filename, prog_ns)'
787 787 code_ns = {
788 788 'execfile': self.shell.safe_execfile,
789 789 'prog_ns': prog_ns,
790 790 'filename': get_py_filename(filename),
791 791 }
792 792
793 793 try:
794 794 stats = None
795 795 if 'p' in opts:
796 796 stats = self._run_with_profiler(code, opts, code_ns)
797 797 else:
798 798 if 'd' in opts:
799 799 bp_file, bp_line = parse_breakpoint(
800 800 opts.get('b', ['1'])[0], filename)
801 801 self._run_with_debugger(
802 802 code, code_ns, filename, bp_line, bp_file)
803 803 else:
804 804 if 'm' in opts:
805 805 def run():
806 806 self.shell.safe_run_module(modulename, prog_ns)
807 807 else:
808 808 if runner is None:
809 809 runner = self.default_runner
810 810 if runner is None:
811 811 runner = self.shell.safe_execfile
812 812
813 813 def run():
814 814 runner(filename, prog_ns, prog_ns,
815 815 exit_ignore=exit_ignore)
816 816
817 817 if 't' in opts:
818 818 # timed execution
819 819 try:
820 820 nruns = int(opts['N'][0])
821 821 if nruns < 1:
822 822 error('Number of runs must be >=1')
823 823 return
824 824 except (KeyError):
825 825 nruns = 1
826 826 self._run_with_timing(run, nruns)
827 827 else:
828 828 # regular execution
829 829 run()
830 830
831 831 if 'i' in opts:
832 832 self.shell.user_ns['__name__'] = __name__save
833 833 else:
834 834 # update IPython interactive namespace
835 835
836 836 # Some forms of read errors on the file may mean the
837 837 # __name__ key was never set; using pop we don't have to
838 838 # worry about a possible KeyError.
839 839 prog_ns.pop('__name__', None)
840 840
841 841 with preserve_keys(self.shell.user_ns, '__file__'):
842 842 self.shell.user_ns.update(prog_ns)
843 843 finally:
844 844 # It's a bit of a mystery why, but __builtins__ can change from
845 845 # being a module to becoming a dict missing some key data after
846 846 # %run. As best I can see, this is NOT something IPython is doing
847 847 # at all, and similar problems have been reported before:
848 848 # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html
849 849 # Since this seems to be done by the interpreter itself, the best
850 850 # we can do is to at least restore __builtins__ for the user on
851 851 # exit.
852 852 self.shell.user_ns['__builtins__'] = builtin_mod
853 853
854 854 # Ensure key global structures are restored
855 855 sys.argv = save_argv
856 856 if restore_main:
857 857 sys.modules['__main__'] = restore_main
858 858 if '__mp_main__' in sys.modules:
859 859 sys.modules['__mp_main__'] = restore_main
860 860 else:
861 861 # Remove from sys.modules the reference to main_mod we'd
862 862 # added. Otherwise it will trap references to objects
863 863 # contained therein.
864 864 del sys.modules[main_mod_name]
865 865
866 866 return stats
867 867
868 868 def _run_with_debugger(self, code, code_ns, filename=None,
869 869 bp_line=None, bp_file=None):
870 870 """
871 871 Run `code` in debugger with a break point.
872 872
873 873 Parameters
874 874 ----------
875 875 code : str
876 876 Code to execute.
877 877 code_ns : dict
878 878 A namespace in which `code` is executed.
879 879 filename : str
880 880 `code` is ran as if it is in `filename`.
881 881 bp_line : int, optional
882 882 Line number of the break point.
883 883 bp_file : str, optional
884 884 Path to the file in which break point is specified.
885 885 `filename` is used if not given.
886 886
887 887 Raises
888 888 ------
889 889 UsageError
890 890 If the break point given by `bp_line` is not valid.
891 891
892 892 """
893 893 deb = self.shell.InteractiveTB.pdb
894 894 if not deb:
895 895 self.shell.InteractiveTB.pdb = self.shell.InteractiveTB.debugger_cls()
896 896 deb = self.shell.InteractiveTB.pdb
897 897
898 898 # deb.checkline() fails if deb.curframe exists but is None; it can
899 899 # handle it not existing. https://github.com/ipython/ipython/issues/10028
900 900 if hasattr(deb, 'curframe'):
901 901 del deb.curframe
902 902
903 903 # reset Breakpoint state, which is moronically kept
904 904 # in a class
905 905 bdb.Breakpoint.next = 1
906 906 bdb.Breakpoint.bplist = {}
907 907 bdb.Breakpoint.bpbynumber = [None]
908 908 deb.clear_all_breaks()
909 909 if bp_line is not None:
910 910 # Set an initial breakpoint to stop execution
911 911 maxtries = 10
912 912 bp_file = bp_file or filename
913 913 checkline = deb.checkline(bp_file, bp_line)
914 914 if not checkline:
915 915 for bp in range(bp_line + 1, bp_line + maxtries + 1):
916 916 if deb.checkline(bp_file, bp):
917 917 break
918 918 else:
919 919 msg = ("\nI failed to find a valid line to set "
920 920 "a breakpoint\n"
921 921 "after trying up to line: %s.\n"
922 922 "Please set a valid breakpoint manually "
923 923 "with the -b option." % bp)
924 924 raise UsageError(msg)
925 925 # if we find a good linenumber, set the breakpoint
926 926 deb.do_break('%s:%s' % (bp_file, bp_line))
927 927
928 928 if filename:
929 929 # Mimic Pdb._runscript(...)
930 930 deb._wait_for_mainpyfile = True
931 931 deb.mainpyfile = deb.canonic(filename)
932 932
933 933 # Start file run
934 934 print("NOTE: Enter 'c' at the %s prompt to continue execution." % deb.prompt)
935 935 try:
936 936 if filename:
937 937 # save filename so it can be used by methods on the deb object
938 938 deb._exec_filename = filename
939 939 while True:
940 940 try:
941 941 trace = sys.gettrace()
942 942 deb.run(code, code_ns)
943 943 except Restart:
944 944 print("Restarting")
945 945 if filename:
946 946 deb._wait_for_mainpyfile = True
947 947 deb.mainpyfile = deb.canonic(filename)
948 948 continue
949 949 else:
950 950 break
951 951 finally:
952 952 sys.settrace(trace)
953 953
954 954
955 955 except:
956 956 etype, value, tb = sys.exc_info()
957 957 # Skip three frames in the traceback: the %run one,
958 958 # one inside bdb.py, and the command-line typed by the
959 959 # user (run by exec in pdb itself).
960 960 self.shell.InteractiveTB(etype, value, tb, tb_offset=3)
961 961
962 962 @staticmethod
963 963 def _run_with_timing(run, nruns):
964 964 """
965 965 Run function `run` and print timing information.
966 966
967 967 Parameters
968 968 ----------
969 969 run : callable
970 970 Any callable object which takes no argument.
971 971 nruns : int
972 972 Number of times to execute `run`.
973 973
974 974 """
975 975 twall0 = time.perf_counter()
976 976 if nruns == 1:
977 977 t0 = clock2()
978 978 run()
979 979 t1 = clock2()
980 980 t_usr = t1[0] - t0[0]
981 981 t_sys = t1[1] - t0[1]
982 982 print("\nIPython CPU timings (estimated):")
983 983 print(" User : %10.2f s." % t_usr)
984 984 print(" System : %10.2f s." % t_sys)
985 985 else:
986 986 runs = range(nruns)
987 987 t0 = clock2()
988 988 for nr in runs:
989 989 run()
990 990 t1 = clock2()
991 991 t_usr = t1[0] - t0[0]
992 992 t_sys = t1[1] - t0[1]
993 993 print("\nIPython CPU timings (estimated):")
994 994 print("Total runs performed:", nruns)
995 995 print(" Times : %10s %10s" % ('Total', 'Per run'))
996 996 print(" User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns))
997 997 print(" System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns))
998 998 twall1 = time.perf_counter()
999 999 print("Wall time: %10.2f s." % (twall1 - twall0))
1000 1000
1001 1001 @skip_doctest
1002 1002 @no_var_expand
1003 1003 @line_cell_magic
1004 1004 @needs_local_scope
1005 1005 def timeit(self, line='', cell=None, local_ns=None):
1006 1006 """Time execution of a Python statement or expression
1007 1007
1008 1008 Usage, in line mode:
1009 1009 %timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] statement
1010 1010 or in cell mode:
1011 1011 %%timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] setup_code
1012 1012 code
1013 1013 code...
1014 1014
1015 1015 Time execution of a Python statement or expression using the timeit
1016 1016 module. This function can be used both as a line and cell magic:
1017 1017
1018 1018 - In line mode you can time a single-line statement (though multiple
1019 1019 ones can be chained with using semicolons).
1020 1020
1021 1021 - In cell mode, the statement in the first line is used as setup code
1022 1022 (executed but not timed) and the body of the cell is timed. The cell
1023 1023 body has access to any variables created in the setup code.
1024 1024
1025 1025 Options:
1026 1026 -n<N>: execute the given statement <N> times in a loop. If <N> is not
1027 1027 provided, <N> is determined so as to get sufficient accuracy.
1028 1028
1029 1029 -r<R>: number of repeats <R>, each consisting of <N> loops, and take the
1030 1030 best result.
1031 1031 Default: 7
1032 1032
1033 1033 -t: use time.time to measure the time, which is the default on Unix.
1034 1034 This function measures wall time.
1035 1035
1036 1036 -c: use time.clock to measure the time, which is the default on
1037 1037 Windows and measures wall time. On Unix, resource.getrusage is used
1038 1038 instead and returns the CPU user time.
1039 1039
1040 1040 -p<P>: use a precision of <P> digits to display the timing result.
1041 1041 Default: 3
1042 1042
1043 1043 -q: Quiet, do not print result.
1044 1044
1045 1045 -o: return a TimeitResult that can be stored in a variable to inspect
1046 1046 the result in more details.
1047 1047
1048 1048 .. versionchanged:: 7.3
1049 1049 User variables are no longer expanded,
1050 1050 the magic line is always left unmodified.
1051 1051
1052 1052 Examples
1053 1053 --------
1054 1054 ::
1055 1055
1056 1056 In [1]: %timeit pass
1057 1057 8.26 ns ± 0.12 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
1058 1058
1059 1059 In [2]: u = None
1060 1060
1061 1061 In [3]: %timeit u is None
1062 1062 29.9 ns ± 0.643 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
1063 1063
1064 1064 In [4]: %timeit -r 4 u == None
1065 1065
1066 1066 In [5]: import time
1067 1067
1068 1068 In [6]: %timeit -n1 time.sleep(2)
1069 1069
1070 1070 The times reported by %timeit will be slightly higher than those
1071 1071 reported by the timeit.py script when variables are accessed. This is
1072 1072 due to the fact that %timeit executes the statement in the namespace
1073 1073 of the shell, compared with timeit.py, which uses a single setup
1074 1074 statement to import function or create variables. Generally, the bias
1075 1075 does not matter as long as results from timeit.py are not mixed with
1076 1076 those from %timeit."""
1077 1077
1078 1078 opts, stmt = self.parse_options(
1079 1079 line, "n:r:tcp:qo", posix=False, strict=False, preserve_non_opts=True
1080 1080 )
1081 1081 if stmt == "" and cell is None:
1082 1082 return
1083 1083
1084 1084 timefunc = timeit.default_timer
1085 1085 number = int(getattr(opts, "n", 0))
1086 1086 default_repeat = 7 if timeit.default_repeat < 7 else timeit.default_repeat
1087 1087 repeat = int(getattr(opts, "r", default_repeat))
1088 1088 precision = int(getattr(opts, "p", 3))
1089 1089 quiet = 'q' in opts
1090 1090 return_result = 'o' in opts
1091 1091 if hasattr(opts, "t"):
1092 1092 timefunc = time.time
1093 1093 if hasattr(opts, "c"):
1094 1094 timefunc = clock
1095 1095
1096 1096 timer = Timer(timer=timefunc)
1097 1097 # this code has tight coupling to the inner workings of timeit.Timer,
1098 1098 # but is there a better way to achieve that the code stmt has access
1099 1099 # to the shell namespace?
1100 1100 transform = self.shell.transform_cell
1101 1101
1102 1102 if cell is None:
1103 1103 # called as line magic
1104 1104 ast_setup = self.shell.compile.ast_parse("pass")
1105 1105 ast_stmt = self.shell.compile.ast_parse(transform(stmt))
1106 1106 else:
1107 1107 ast_setup = self.shell.compile.ast_parse(transform(stmt))
1108 1108 ast_stmt = self.shell.compile.ast_parse(transform(cell))
1109 1109
1110 1110 ast_setup = self.shell.transform_ast(ast_setup)
1111 1111 ast_stmt = self.shell.transform_ast(ast_stmt)
1112 1112
1113 1113 # Check that these compile to valid Python code *outside* the timer func
1114 1114 # Invalid code may become valid when put inside the function & loop,
1115 1115 # which messes up error messages.
1116 1116 # https://github.com/ipython/ipython/issues/10636
1117 1117 self.shell.compile(ast_setup, "<magic-timeit-setup>", "exec")
1118 1118 self.shell.compile(ast_stmt, "<magic-timeit-stmt>", "exec")
1119 1119
1120 1120 # This codestring is taken from timeit.template - we fill it in as an
1121 1121 # AST, so that we can apply our AST transformations to the user code
1122 1122 # without affecting the timing code.
1123 1123 timeit_ast_template = ast.parse('def inner(_it, _timer):\n'
1124 1124 ' setup\n'
1125 1125 ' _t0 = _timer()\n'
1126 1126 ' for _i in _it:\n'
1127 1127 ' stmt\n'
1128 1128 ' _t1 = _timer()\n'
1129 1129 ' return _t1 - _t0\n')
1130 1130
1131 1131 timeit_ast = TimeitTemplateFiller(ast_setup, ast_stmt).visit(timeit_ast_template)
1132 1132 timeit_ast = ast.fix_missing_locations(timeit_ast)
1133 1133
1134 1134 # Track compilation time so it can be reported if too long
1135 1135 # Minimum time above which compilation time will be reported
1136 1136 tc_min = 0.1
1137 1137
1138 1138 t0 = clock()
1139 1139 code = self.shell.compile(timeit_ast, "<magic-timeit>", "exec")
1140 1140 tc = clock()-t0
1141 1141
1142 1142 ns = {}
1143 1143 glob = self.shell.user_ns
1144 1144 # handles global vars with same name as local vars. We store them in conflict_globs.
1145 1145 conflict_globs = {}
1146 1146 if local_ns and cell is None:
1147 1147 for var_name, var_val in glob.items():
1148 1148 if var_name in local_ns:
1149 1149 conflict_globs[var_name] = var_val
1150 1150 glob.update(local_ns)
1151 1151
1152 1152 exec(code, glob, ns)
1153 1153 timer.inner = ns["inner"]
1154 1154
1155 1155 # This is used to check if there is a huge difference between the
1156 1156 # best and worst timings.
1157 1157 # Issue: https://github.com/ipython/ipython/issues/6471
1158 1158 if number == 0:
1159 1159 # determine number so that 0.2 <= total time < 2.0
1160 1160 for index in range(0, 10):
1161 1161 number = 10 ** index
1162 1162 time_number = timer.timeit(number)
1163 1163 if time_number >= 0.2:
1164 1164 break
1165 1165
1166 1166 all_runs = timer.repeat(repeat, number)
1167 1167 best = min(all_runs) / number
1168 1168 worst = max(all_runs) / number
1169 1169 timeit_result = TimeitResult(number, repeat, best, worst, all_runs, tc, precision)
1170 1170
1171 1171 # Restore global vars from conflict_globs
1172 1172 if conflict_globs:
1173 1173 glob.update(conflict_globs)
1174 1174
1175 1175 if not quiet :
1176 1176 # Check best timing is greater than zero to avoid a
1177 1177 # ZeroDivisionError.
1178 1178 # In cases where the slowest timing is lesser than a microsecond
1179 1179 # we assume that it does not really matter if the fastest
1180 1180 # timing is 4 times faster than the slowest timing or not.
1181 1181 if worst > 4 * best and best > 0 and worst > 1e-6:
1182 1182 print("The slowest run took %0.2f times longer than the "
1183 1183 "fastest. This could mean that an intermediate result "
1184 1184 "is being cached." % (worst / best))
1185 1185
1186 1186 print( timeit_result )
1187 1187
1188 1188 if tc > tc_min:
1189 1189 print("Compiler time: %.2f s" % tc)
1190 1190 if return_result:
1191 1191 return timeit_result
1192 1192
1193 1193 @skip_doctest
1194 1194 @no_var_expand
1195 1195 @needs_local_scope
1196 1196 @line_cell_magic
1197 1197 def time(self,line='', cell=None, local_ns=None):
1198 1198 """Time execution of a Python statement or expression.
1199 1199
1200 1200 The CPU and wall clock times are printed, and the value of the
1201 1201 expression (if any) is returned. Note that under Win32, system time
1202 1202 is always reported as 0, since it can not be measured.
1203 1203
1204 1204 This function can be used both as a line and cell magic:
1205 1205
1206 1206 - In line mode you can time a single-line statement (though multiple
1207 1207 ones can be chained with using semicolons).
1208 1208
1209 1209 - In cell mode, you can time the cell body (a directly
1210 1210 following statement raises an error).
1211 1211
1212 1212 This function provides very basic timing functionality. Use the timeit
1213 1213 magic for more control over the measurement.
1214 1214
1215 1215 .. versionchanged:: 7.3
1216 1216 User variables are no longer expanded,
1217 1217 the magic line is always left unmodified.
1218 1218
1219 1219 Examples
1220 1220 --------
1221 1221 ::
1222 1222
1223 1223 In [1]: %time 2**128
1224 1224 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1225 1225 Wall time: 0.00
1226 1226 Out[1]: 340282366920938463463374607431768211456L
1227 1227
1228 1228 In [2]: n = 1000000
1229 1229
1230 1230 In [3]: %time sum(range(n))
1231 1231 CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s
1232 1232 Wall time: 1.37
1233 1233 Out[3]: 499999500000L
1234 1234
1235 1235 In [4]: %time print 'hello world'
1236 1236 hello world
1237 1237 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1238 1238 Wall time: 0.00
1239 1239
1240 1240 .. note::
1241 1241 The time needed by Python to compile the given expression will be
1242 1242 reported if it is more than 0.1s.
1243 1243
1244 1244 In the example below, the actual exponentiation is done by Python
1245 1245 at compilation time, so while the expression can take a noticeable
1246 1246 amount of time to compute, that time is purely due to the
1247 1247 compilation::
1248 1248
1249 1249 In [5]: %time 3**9999;
1250 1250 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1251 1251 Wall time: 0.00 s
1252 1252
1253 1253 In [6]: %time 3**999999;
1254 1254 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1255 1255 Wall time: 0.00 s
1256 1256 Compiler : 0.78 s
1257 1257 """
1258 1258 # fail immediately if the given expression can't be compiled
1259 1259
1260 1260 if line and cell:
1261 1261 raise UsageError("Can't use statement directly after '%%time'!")
1262 1262
1263 1263 if cell:
1264 1264 expr = self.shell.transform_cell(cell)
1265 1265 else:
1266 1266 expr = self.shell.transform_cell(line)
1267 1267
1268 1268 # Minimum time above which parse time will be reported
1269 1269 tp_min = 0.1
1270 1270
1271 1271 t0 = clock()
1272 1272 expr_ast = self.shell.compile.ast_parse(expr)
1273 1273 tp = clock()-t0
1274 1274
1275 1275 # Apply AST transformations
1276 1276 expr_ast = self.shell.transform_ast(expr_ast)
1277 1277
1278 1278 # Minimum time above which compilation time will be reported
1279 1279 tc_min = 0.1
1280 1280
1281 1281 expr_val=None
1282 1282 if len(expr_ast.body)==1 and isinstance(expr_ast.body[0], ast.Expr):
1283 1283 mode = 'eval'
1284 1284 source = '<timed eval>'
1285 1285 expr_ast = ast.Expression(expr_ast.body[0].value)
1286 1286 else:
1287 1287 mode = 'exec'
1288 1288 source = '<timed exec>'
1289 1289 # multi-line %%time case
1290 1290 if len(expr_ast.body) > 1 and isinstance(expr_ast.body[-1], ast.Expr):
1291 1291 expr_val= expr_ast.body[-1]
1292 1292 expr_ast = expr_ast.body[:-1]
1293 1293 expr_ast = Module(expr_ast, [])
1294 1294 expr_val = ast.Expression(expr_val.value)
1295 1295
1296 1296 t0 = clock()
1297 1297 code = self.shell.compile(expr_ast, source, mode)
1298 1298 tc = clock()-t0
1299 1299
1300 1300 # skew measurement as little as possible
1301 1301 glob = self.shell.user_ns
1302 1302 wtime = time.time
1303 1303 # time execution
1304 1304 wall_st = wtime()
1305 1305 if mode=='eval':
1306 1306 st = clock2()
1307 1307 try:
1308 1308 out = eval(code, glob, local_ns)
1309 1309 except:
1310 1310 self.shell.showtraceback()
1311 1311 return
1312 1312 end = clock2()
1313 1313 else:
1314 1314 st = clock2()
1315 1315 try:
1316 1316 exec(code, glob, local_ns)
1317 1317 out=None
1318 1318 # multi-line %%time case
1319 1319 if expr_val is not None:
1320 1320 code_2 = self.shell.compile(expr_val, source, 'eval')
1321 1321 out = eval(code_2, glob, local_ns)
1322 1322 except:
1323 1323 self.shell.showtraceback()
1324 1324 return
1325 1325 end = clock2()
1326 1326
1327 1327 wall_end = wtime()
1328 1328 # Compute actual times and report
1329 1329 wall_time = wall_end - wall_st
1330 1330 cpu_user = end[0] - st[0]
1331 1331 cpu_sys = end[1] - st[1]
1332 1332 cpu_tot = cpu_user + cpu_sys
1333 1333 # On windows cpu_sys is always zero, so only total is displayed
1334 1334 if sys.platform != "win32":
1335 1335 print(
1336 1336 f"CPU times: user {_format_time(cpu_user)}, sys: {_format_time(cpu_sys)}, total: {_format_time(cpu_tot)}"
1337 1337 )
1338 1338 else:
1339 1339 print(f"CPU times: total: {_format_time(cpu_tot)}")
1340 1340 print(f"Wall time: {_format_time(wall_time)}")
1341 1341 if tc > tc_min:
1342 1342 print(f"Compiler : {_format_time(tc)}")
1343 1343 if tp > tp_min:
1344 1344 print(f"Parser : {_format_time(tp)}")
1345 1345 return out
1346 1346
1347 1347 @skip_doctest
1348 1348 @line_magic
1349 1349 def macro(self, parameter_s=''):
1350 1350 """Define a macro for future re-execution. It accepts ranges of history,
1351 1351 filenames or string objects.
1352 1352
1353 1353 Usage:\\
1354 1354 %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ...
1355 1355
1356 1356 Options:
1357 1357
1358 1358 -r: use 'raw' input. By default, the 'processed' history is used,
1359 1359 so that magics are loaded in their transformed version to valid
1360 1360 Python. If this option is given, the raw input as typed at the
1361 1361 command line is used instead.
1362 1362
1363 1363 -q: quiet macro definition. By default, a tag line is printed
1364 1364 to indicate the macro has been created, and then the contents of
1365 1365 the macro are printed. If this option is given, then no printout
1366 1366 is produced once the macro is created.
1367 1367
1368 1368 This will define a global variable called `name` which is a string
1369 1369 made of joining the slices and lines you specify (n1,n2,... numbers
1370 1370 above) from your input history into a single string. This variable
1371 1371 acts like an automatic function which re-executes those lines as if
1372 1372 you had typed them. You just type 'name' at the prompt and the code
1373 1373 executes.
1374 1374
1375 1375 The syntax for indicating input ranges is described in %history.
1376 1376
1377 1377 Note: as a 'hidden' feature, you can also use traditional python slice
1378 1378 notation, where N:M means numbers N through M-1.
1379 1379
1380 1380 For example, if your history contains (print using %hist -n )::
1381 1381
1382 1382 44: x=1
1383 1383 45: y=3
1384 1384 46: z=x+y
1385 1385 47: print x
1386 1386 48: a=5
1387 1387 49: print 'x',x,'y',y
1388 1388
1389 1389 you can create a macro with lines 44 through 47 (included) and line 49
1390 1390 called my_macro with::
1391 1391
1392 1392 In [55]: %macro my_macro 44-47 49
1393 1393
1394 1394 Now, typing `my_macro` (without quotes) will re-execute all this code
1395 1395 in one pass.
1396 1396
1397 1397 You don't need to give the line-numbers in order, and any given line
1398 1398 number can appear multiple times. You can assemble macros with any
1399 1399 lines from your input history in any order.
1400 1400
1401 1401 The macro is a simple object which holds its value in an attribute,
1402 1402 but IPython's display system checks for macros and executes them as
1403 1403 code instead of printing them when you type their name.
1404 1404
1405 1405 You can view a macro's contents by explicitly printing it with::
1406 1406
1407 1407 print macro_name
1408 1408
1409 1409 """
1410 1410 opts,args = self.parse_options(parameter_s,'rq',mode='list')
1411 1411 if not args: # List existing macros
1412 1412 return sorted(k for k,v in self.shell.user_ns.items() if isinstance(v, Macro))
1413 1413 if len(args) == 1:
1414 1414 raise UsageError(
1415 1415 "%macro insufficient args; usage '%macro name n1-n2 n3-4...")
1416 1416 name, codefrom = args[0], " ".join(args[1:])
1417 1417
1418 1418 #print 'rng',ranges # dbg
1419 1419 try:
1420 1420 lines = self.shell.find_user_code(codefrom, 'r' in opts)
1421 1421 except (ValueError, TypeError) as e:
1422 1422 print(e.args[0])
1423 1423 return
1424 1424 macro = Macro(lines)
1425 1425 self.shell.define_macro(name, macro)
1426 1426 if not ( 'q' in opts) :
1427 1427 print('Macro `%s` created. To execute, type its name (without quotes).' % name)
1428 1428 print('=== Macro contents: ===')
1429 1429 print(macro, end=' ')
1430 1430
1431 1431 @magic_arguments.magic_arguments()
1432 1432 @magic_arguments.argument('output', type=str, default='', nargs='?',
1433 1433 help="""The name of the variable in which to store output.
1434 1434 This is a utils.io.CapturedIO object with stdout/err attributes
1435 1435 for the text of the captured output.
1436 1436
1437 1437 CapturedOutput also has a show() method for displaying the output,
1438 1438 and __call__ as well, so you can use that to quickly display the
1439 1439 output.
1440 1440
1441 1441 If unspecified, captured output is discarded.
1442 1442 """
1443 1443 )
1444 1444 @magic_arguments.argument('--no-stderr', action="store_true",
1445 1445 help="""Don't capture stderr."""
1446 1446 )
1447 1447 @magic_arguments.argument('--no-stdout', action="store_true",
1448 1448 help="""Don't capture stdout."""
1449 1449 )
1450 1450 @magic_arguments.argument('--no-display', action="store_true",
1451 1451 help="""Don't capture IPython's rich display."""
1452 1452 )
1453 1453 @cell_magic
1454 1454 def capture(self, line, cell):
1455 1455 """run the cell, capturing stdout, stderr, and IPython's rich display() calls."""
1456 1456 args = magic_arguments.parse_argstring(self.capture, line)
1457 1457 out = not args.no_stdout
1458 1458 err = not args.no_stderr
1459 1459 disp = not args.no_display
1460 1460 with capture_output(out, err, disp) as io:
1461 1461 self.shell.run_cell(cell)
1462 1462 if args.output:
1463 1463 self.shell.user_ns[args.output] = io
1464 1464
1465 1465 def parse_breakpoint(text, current_file):
1466 1466 '''Returns (file, line) for file:line and (current_file, line) for line'''
1467 1467 colon = text.find(':')
1468 1468 if colon == -1:
1469 1469 return current_file, int(text)
1470 1470 else:
1471 1471 return text[:colon], int(text[colon+1:])
1472 1472
1473 1473 def _format_time(timespan, precision=3):
1474 1474 """Formats the timespan in a human readable form"""
1475 1475
1476 1476 if timespan >= 60.0:
1477 1477 # we have more than a minute, format that in a human readable form
1478 1478 # Idea from http://snipplr.com/view/5713/
1479 1479 parts = [("d", 60*60*24),("h", 60*60),("min", 60), ("s", 1)]
1480 1480 time = []
1481 1481 leftover = timespan
1482 1482 for suffix, length in parts:
1483 1483 value = int(leftover / length)
1484 1484 if value > 0:
1485 1485 leftover = leftover % length
1486 1486 time.append(u'%s%s' % (str(value), suffix))
1487 1487 if leftover < 1:
1488 1488 break
1489 1489 return " ".join(time)
1490 1490
1491 1491
1492 1492 # Unfortunately the unicode 'micro' symbol can cause problems in
1493 1493 # certain terminals.
1494 1494 # See bug: https://bugs.launchpad.net/ipython/+bug/348466
1495 1495 # Try to prevent crashes by being more secure than it needs to
1496 1496 # E.g. eclipse is able to print a µ, but has no sys.stdout.encoding set.
1497 1497 units = [u"s", u"ms",u'us',"ns"] # the save value
1498 1498 if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding:
1499 1499 try:
1500 1500 u'\xb5'.encode(sys.stdout.encoding)
1501 1501 units = [u"s", u"ms",u'\xb5s',"ns"]
1502 1502 except:
1503 1503 pass
1504 1504 scaling = [1, 1e3, 1e6, 1e9]
1505 1505
1506 1506 if timespan > 0.0:
1507 1507 order = min(-int(math.floor(math.log10(timespan)) // 3), 3)
1508 1508 else:
1509 1509 order = 3
1510 1510 return u"%.*g %s" % (precision, timespan * scaling[order], units[order])
@@ -1,112 +1,112 b''
1 1 """Implementation of packaging-related magic functions.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2018 The IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 import re
12 12 import shlex
13 13 import sys
14 14 from pathlib import Path
15 15
16 16 from IPython.core.magic import Magics, magics_class, line_magic
17 17
18 18
19 19 def _is_conda_environment():
20 20 """Return True if the current Python executable is in a conda env"""
21 21 # TODO: does this need to change on windows?
22 22 return Path(sys.prefix, "conda-meta", "history").exists()
23 23
24 24
25 25 def _get_conda_executable():
26 26 """Find the path to the conda executable"""
27 27 # Check if there is a conda executable in the same directory as the Python executable.
28 28 # This is the case within conda's root environment.
29 29 conda = Path(sys.executable).parent / "conda"
30 30 if conda.is_file():
31 31 return str(conda)
32 32
33 33 # Otherwise, attempt to extract the executable from conda history.
34 34 # This applies in any conda environment.
35 history = Path(sys.prefix, "conda-meta", "history").read_text(encoding='utf-8')
35 history = Path(sys.prefix, "conda-meta", "history").read_text(encoding="utf-8")
36 36 match = re.search(
37 37 r"^#\s*cmd:\s*(?P<command>.*conda)\s[create|install]",
38 38 history,
39 39 flags=re.MULTILINE,
40 40 )
41 41 if match:
42 42 return match.groupdict()["command"]
43 43
44 44 # Fallback: assume conda is available on the system path.
45 45 return "conda"
46 46
47 47
48 48 CONDA_COMMANDS_REQUIRING_PREFIX = {
49 49 'install', 'list', 'remove', 'uninstall', 'update', 'upgrade',
50 50 }
51 51 CONDA_COMMANDS_REQUIRING_YES = {
52 52 'install', 'remove', 'uninstall', 'update', 'upgrade',
53 53 }
54 54 CONDA_ENV_FLAGS = {'-p', '--prefix', '-n', '--name'}
55 55 CONDA_YES_FLAGS = {'-y', '--y'}
56 56
57 57
58 58 @magics_class
59 59 class PackagingMagics(Magics):
60 60 """Magics related to packaging & installation"""
61 61
62 62 @line_magic
63 63 def pip(self, line):
64 64 """Run the pip package manager within the current kernel.
65 65
66 66 Usage:
67 67 %pip install [pkgs]
68 68 """
69 69 python = sys.executable
70 70 if sys.platform == "win32":
71 71 python = '"' + python + '"'
72 72 else:
73 73 python = shlex.quote(python)
74 74
75 75 self.shell.system(" ".join([python, "-m", "pip", line]))
76 76
77 77 print("Note: you may need to restart the kernel to use updated packages.")
78 78
79 79 @line_magic
80 80 def conda(self, line):
81 81 """Run the conda package manager within the current kernel.
82 82
83 83 Usage:
84 84 %conda install [pkgs]
85 85 """
86 86 if not _is_conda_environment():
87 87 raise ValueError("The python kernel does not appear to be a conda environment. "
88 88 "Please use ``%pip install`` instead.")
89 89
90 90 conda = _get_conda_executable()
91 91 args = shlex.split(line)
92 92 command = args[0] if len(args) > 0 else ""
93 93 args = args[1:] if len(args) > 1 else [""]
94 94
95 95 extra_args = []
96 96
97 97 # When the subprocess does not allow us to respond "yes" during the installation,
98 98 # we need to insert --yes in the argument list for some commands
99 99 stdin_disabled = getattr(self.shell, 'kernel', None) is not None
100 100 needs_yes = command in CONDA_COMMANDS_REQUIRING_YES
101 101 has_yes = set(args).intersection(CONDA_YES_FLAGS)
102 102 if stdin_disabled and needs_yes and not has_yes:
103 103 extra_args.append("--yes")
104 104
105 105 # Add --prefix to point conda installation to the current environment
106 106 needs_prefix = command in CONDA_COMMANDS_REQUIRING_PREFIX
107 107 has_prefix = set(args).intersection(CONDA_ENV_FLAGS)
108 108 if needs_prefix and not has_prefix:
109 109 extra_args.extend(["--prefix", sys.prefix])
110 110
111 111 self.shell.system(' '.join([conda, command] + extra_args + args))
112 112 print("\nNote: you may need to restart the kernel to use updated packages.")
@@ -1,345 +1,348 b''
1 1 # encoding: utf-8
2 2 """
3 3 Paging capabilities for IPython.core
4 4
5 5 Notes
6 6 -----
7 7
8 8 For now this uses IPython hooks, so it can't be in IPython.utils. If we can get
9 9 rid of that dependency, we could move it there.
10 10 -----
11 11 """
12 12
13 13 # Copyright (c) IPython Development Team.
14 14 # Distributed under the terms of the Modified BSD License.
15 15
16 16
17 17 import os
18 18 import io
19 19 import re
20 20 import sys
21 21 import tempfile
22 22 import subprocess
23 23
24 24 from io import UnsupportedOperation
25 25 from pathlib import Path
26 26
27 27 from IPython import get_ipython
28 28 from IPython.display import display
29 29 from IPython.core.error import TryNext
30 30 from IPython.utils.data import chop
31 31 from IPython.utils.process import system
32 32 from IPython.utils.terminal import get_terminal_size
33 33 from IPython.utils import py3compat
34 34
35 35
36 36 def display_page(strng, start=0, screen_lines=25):
37 37 """Just display, no paging. screen_lines is ignored."""
38 38 if isinstance(strng, dict):
39 39 data = strng
40 40 else:
41 41 if start:
42 42 strng = u'\n'.join(strng.splitlines()[start:])
43 43 data = { 'text/plain': strng }
44 44 display(data, raw=True)
45 45
46 46
47 47 def as_hook(page_func):
48 48 """Wrap a pager func to strip the `self` arg
49 49
50 50 so it can be called as a hook.
51 51 """
52 52 return lambda self, *args, **kwargs: page_func(*args, **kwargs)
53 53
54 54
55 55 esc_re = re.compile(r"(\x1b[^m]+m)")
56 56
57 57 def page_dumb(strng, start=0, screen_lines=25):
58 58 """Very dumb 'pager' in Python, for when nothing else works.
59 59
60 60 Only moves forward, same interface as page(), except for pager_cmd and
61 61 mode.
62 62 """
63 63 if isinstance(strng, dict):
64 64 strng = strng.get('text/plain', '')
65 65 out_ln = strng.splitlines()[start:]
66 66 screens = chop(out_ln,screen_lines-1)
67 67 if len(screens) == 1:
68 68 print(os.linesep.join(screens[0]))
69 69 else:
70 70 last_escape = ""
71 71 for scr in screens[0:-1]:
72 72 hunk = os.linesep.join(scr)
73 73 print(last_escape + hunk)
74 74 if not page_more():
75 75 return
76 76 esc_list = esc_re.findall(hunk)
77 77 if len(esc_list) > 0:
78 78 last_escape = esc_list[-1]
79 79 print(last_escape + os.linesep.join(screens[-1]))
80 80
81 81 def _detect_screen_size(screen_lines_def):
82 82 """Attempt to work out the number of lines on the screen.
83 83
84 84 This is called by page(). It can raise an error (e.g. when run in the
85 85 test suite), so it's separated out so it can easily be called in a try block.
86 86 """
87 87 TERM = os.environ.get('TERM',None)
88 88 if not((TERM=='xterm' or TERM=='xterm-color') and sys.platform != 'sunos5'):
89 89 # curses causes problems on many terminals other than xterm, and
90 90 # some termios calls lock up on Sun OS5.
91 91 return screen_lines_def
92 92
93 93 try:
94 94 import termios
95 95 import curses
96 96 except ImportError:
97 97 return screen_lines_def
98 98
99 99 # There is a bug in curses, where *sometimes* it fails to properly
100 100 # initialize, and then after the endwin() call is made, the
101 101 # terminal is left in an unusable state. Rather than trying to
102 102 # check every time for this (by requesting and comparing termios
103 103 # flags each time), we just save the initial terminal state and
104 104 # unconditionally reset it every time. It's cheaper than making
105 105 # the checks.
106 106 try:
107 107 term_flags = termios.tcgetattr(sys.stdout)
108 108 except termios.error as err:
109 109 # can fail on Linux 2.6, pager_page will catch the TypeError
110 110 raise TypeError('termios error: {0}'.format(err)) from err
111 111
112 112 try:
113 113 scr = curses.initscr()
114 114 except AttributeError:
115 115 # Curses on Solaris may not be complete, so we can't use it there
116 116 return screen_lines_def
117 117
118 118 screen_lines_real,screen_cols = scr.getmaxyx()
119 119 curses.endwin()
120 120
121 121 # Restore terminal state in case endwin() didn't.
122 122 termios.tcsetattr(sys.stdout,termios.TCSANOW,term_flags)
123 123 # Now we have what we needed: the screen size in rows/columns
124 124 return screen_lines_real
125 125 #print '***Screen size:',screen_lines_real,'lines x',\
126 126 #screen_cols,'columns.' # dbg
127 127
128 128 def pager_page(strng, start=0, screen_lines=0, pager_cmd=None):
129 129 """Display a string, piping through a pager after a certain length.
130 130
131 131 strng can be a mime-bundle dict, supplying multiple representations,
132 132 keyed by mime-type.
133 133
134 134 The screen_lines parameter specifies the number of *usable* lines of your
135 135 terminal screen (total lines minus lines you need to reserve to show other
136 136 information).
137 137
138 138 If you set screen_lines to a number <=0, page() will try to auto-determine
139 139 your screen size and will only use up to (screen_size+screen_lines) for
140 140 printing, paging after that. That is, if you want auto-detection but need
141 141 to reserve the bottom 3 lines of the screen, use screen_lines = -3, and for
142 142 auto-detection without any lines reserved simply use screen_lines = 0.
143 143
144 144 If a string won't fit in the allowed lines, it is sent through the
145 145 specified pager command. If none given, look for PAGER in the environment,
146 146 and ultimately default to less.
147 147
148 148 If no system pager works, the string is sent through a 'dumb pager'
149 149 written in python, very simplistic.
150 150 """
151 151
152 152 # for compatibility with mime-bundle form:
153 153 if isinstance(strng, dict):
154 154 strng = strng['text/plain']
155 155
156 156 # Ugly kludge, but calling curses.initscr() flat out crashes in emacs
157 157 TERM = os.environ.get('TERM','dumb')
158 158 if TERM in ['dumb','emacs'] and os.name != 'nt':
159 159 print(strng)
160 160 return
161 161 # chop off the topmost part of the string we don't want to see
162 162 str_lines = strng.splitlines()[start:]
163 163 str_toprint = os.linesep.join(str_lines)
164 164 num_newlines = len(str_lines)
165 165 len_str = len(str_toprint)
166 166
167 167 # Dumb heuristics to guesstimate number of on-screen lines the string
168 168 # takes. Very basic, but good enough for docstrings in reasonable
169 169 # terminals. If someone later feels like refining it, it's not hard.
170 170 numlines = max(num_newlines,int(len_str/80)+1)
171 171
172 172 screen_lines_def = get_terminal_size()[1]
173 173
174 174 # auto-determine screen size
175 175 if screen_lines <= 0:
176 176 try:
177 177 screen_lines += _detect_screen_size(screen_lines_def)
178 178 except (TypeError, UnsupportedOperation):
179 179 print(str_toprint)
180 180 return
181 181
182 182 #print 'numlines',numlines,'screenlines',screen_lines # dbg
183 183 if numlines <= screen_lines :
184 184 #print '*** normal print' # dbg
185 185 print(str_toprint)
186 186 else:
187 187 # Try to open pager and default to internal one if that fails.
188 188 # All failure modes are tagged as 'retval=1', to match the return
189 189 # value of a failed system command. If any intermediate attempt
190 190 # sets retval to 1, at the end we resort to our own page_dumb() pager.
191 191 pager_cmd = get_pager_cmd(pager_cmd)
192 192 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
193 193 if os.name == 'nt':
194 194 if pager_cmd.startswith('type'):
195 195 # The default WinXP 'type' command is failing on complex strings.
196 196 retval = 1
197 197 else:
198 198 fd, tmpname = tempfile.mkstemp('.txt')
199 199 tmppath = Path(tmpname)
200 200 try:
201 201 os.close(fd)
202 with tmppath.open("wt", encoding='utf-8') as tmpfile:
202 with tmppath.open("wt", encoding="utf-8") as tmpfile:
203 203 tmpfile.write(strng)
204 204 cmd = "%s < %s" % (pager_cmd, tmppath)
205 205 # tmpfile needs to be closed for windows
206 206 if os.system(cmd):
207 207 retval = 1
208 208 else:
209 209 retval = None
210 210 finally:
211 211 Path.unlink(tmppath)
212 212 else:
213 213 try:
214 214 retval = None
215 215 # Emulate os.popen, but redirect stderr
216 proc = subprocess.Popen(pager_cmd,
217 shell=True,
218 stdin=subprocess.PIPE,
219 stderr=subprocess.DEVNULL
220 )
221 pager = os._wrap_close(io.TextIOWrapper(proc.stdin, encoding='utf-8'), proc)
216 proc = subprocess.Popen(
217 pager_cmd,
218 shell=True,
219 stdin=subprocess.PIPE,
220 stderr=subprocess.DEVNULL,
221 )
222 pager = os._wrap_close(
223 io.TextIOWrapper(proc.stdin, encoding="utf-8"), proc
224 )
222 225 try:
223 226 pager_encoding = pager.encoding or sys.stdout.encoding
224 227 pager.write(strng)
225 228 finally:
226 229 retval = pager.close()
227 230 except IOError as msg: # broken pipe when user quits
228 231 if msg.args == (32, 'Broken pipe'):
229 232 retval = None
230 233 else:
231 234 retval = 1
232 235 except OSError:
233 236 # Other strange problems, sometimes seen in Win2k/cygwin
234 237 retval = 1
235 238 if retval is not None:
236 239 page_dumb(strng,screen_lines=screen_lines)
237 240
238 241
239 242 def page(data, start=0, screen_lines=0, pager_cmd=None):
240 243 """Display content in a pager, piping through a pager after a certain length.
241 244
242 245 data can be a mime-bundle dict, supplying multiple representations,
243 246 keyed by mime-type, or text.
244 247
245 248 Pager is dispatched via the `show_in_pager` IPython hook.
246 249 If no hook is registered, `pager_page` will be used.
247 250 """
248 251 # Some routines may auto-compute start offsets incorrectly and pass a
249 252 # negative value. Offset to 0 for robustness.
250 253 start = max(0, start)
251 254
252 255 # first, try the hook
253 256 ip = get_ipython()
254 257 if ip:
255 258 try:
256 259 ip.hooks.show_in_pager(data, start=start, screen_lines=screen_lines)
257 260 return
258 261 except TryNext:
259 262 pass
260 263
261 264 # fallback on default pager
262 265 return pager_page(data, start, screen_lines, pager_cmd)
263 266
264 267
265 268 def page_file(fname, start=0, pager_cmd=None):
266 269 """Page a file, using an optional pager command and starting line.
267 270 """
268 271
269 272 pager_cmd = get_pager_cmd(pager_cmd)
270 273 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
271 274
272 275 try:
273 276 if os.environ['TERM'] in ['emacs','dumb']:
274 277 raise EnvironmentError
275 278 system(pager_cmd + ' ' + fname)
276 279 except:
277 280 try:
278 281 if start > 0:
279 282 start -= 1
280 page(open(fname, encoding='utf-8').read(),start)
283 page(open(fname, encoding="utf-8").read(), start)
281 284 except:
282 285 print('Unable to show file',repr(fname))
283 286
284 287
285 288 def get_pager_cmd(pager_cmd=None):
286 289 """Return a pager command.
287 290
288 291 Makes some attempts at finding an OS-correct one.
289 292 """
290 293 if os.name == 'posix':
291 294 default_pager_cmd = 'less -R' # -R for color control sequences
292 295 elif os.name in ['nt','dos']:
293 296 default_pager_cmd = 'type'
294 297
295 298 if pager_cmd is None:
296 299 try:
297 300 pager_cmd = os.environ['PAGER']
298 301 except:
299 302 pager_cmd = default_pager_cmd
300 303
301 304 if pager_cmd == 'less' and '-r' not in os.environ.get('LESS', '').lower():
302 305 pager_cmd += ' -R'
303 306
304 307 return pager_cmd
305 308
306 309
307 310 def get_pager_start(pager, start):
308 311 """Return the string for paging files with an offset.
309 312
310 313 This is the '+N' argument which less and more (under Unix) accept.
311 314 """
312 315
313 316 if pager in ['less','more']:
314 317 if start:
315 318 start_string = '+' + str(start)
316 319 else:
317 320 start_string = ''
318 321 else:
319 322 start_string = ''
320 323 return start_string
321 324
322 325
323 326 # (X)emacs on win32 doesn't like to be bypassed with msvcrt.getch()
324 327 if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
325 328 import msvcrt
326 329 def page_more():
327 330 """ Smart pausing between pages
328 331
329 332 @return: True if need print more lines, False if quit
330 333 """
331 334 sys.stdout.write('---Return to continue, q to quit--- ')
332 335 ans = msvcrt.getwch()
333 336 if ans in ("q", "Q"):
334 337 result = False
335 338 else:
336 339 result = True
337 340 sys.stdout.write("\b"*37 + " "*37 + "\b"*37)
338 341 return result
339 342 else:
340 343 def page_more():
341 344 ans = py3compat.input('---Return to continue, q to quit--- ')
342 345 if ans.lower().startswith('q'):
343 346 return False
344 347 else:
345 348 return True
@@ -1,70 +1,70 b''
1 1 # coding: utf-8
2 2 """Tests for IPython.core.application"""
3 3
4 4 import os
5 5 import tempfile
6 6
7 7 from traitlets import Unicode
8 8
9 9 from IPython.core.application import BaseIPythonApplication
10 10 from IPython.testing import decorators as dec
11 11 from IPython.utils.tempdir import TemporaryDirectory
12 12
13 13
14 14 @dec.onlyif_unicode_paths
15 15 def test_unicode_cwd():
16 16 """Check that IPython starts with non-ascii characters in the path."""
17 17 wd = tempfile.mkdtemp(suffix=u"€")
18 18
19 19 old_wd = os.getcwd()
20 20 os.chdir(wd)
21 21 #raise Exception(repr(os.getcwd()))
22 22 try:
23 23 app = BaseIPythonApplication()
24 24 # The lines below are copied from Application.initialize()
25 25 app.init_profile_dir()
26 26 app.init_config_files()
27 27 app.load_config_file(suppress_errors=False)
28 28 finally:
29 29 os.chdir(old_wd)
30 30
31 31 @dec.onlyif_unicode_paths
32 32 def test_unicode_ipdir():
33 33 """Check that IPython starts with non-ascii characters in the IP dir."""
34 34 ipdir = tempfile.mkdtemp(suffix=u"€")
35 35
36 36 # Create the config file, so it tries to load it.
37 with open(os.path.join(ipdir, 'ipython_config.py'), "w", encoding='utf-8') as f:
37 with open(os.path.join(ipdir, "ipython_config.py"), "w", encoding="utf-8") as f:
38 38 pass
39 39
40 40 old_ipdir1 = os.environ.pop("IPYTHONDIR", None)
41 41 old_ipdir2 = os.environ.pop("IPYTHON_DIR", None)
42 42 os.environ["IPYTHONDIR"] = ipdir
43 43 try:
44 44 app = BaseIPythonApplication()
45 45 # The lines below are copied from Application.initialize()
46 46 app.init_profile_dir()
47 47 app.init_config_files()
48 48 app.load_config_file(suppress_errors=False)
49 49 finally:
50 50 if old_ipdir1:
51 51 os.environ["IPYTHONDIR"] = old_ipdir1
52 52 if old_ipdir2:
53 53 os.environ["IPYTHONDIR"] = old_ipdir2
54 54
55 55 def test_cli_priority():
56 56 with TemporaryDirectory() as td:
57 57
58 58 class TestApp(BaseIPythonApplication):
59 59 test = Unicode().tag(config=True)
60 60
61 61 # Create the config file, so it tries to load it.
62 with open(os.path.join(td, 'ipython_config.py'), "w", encoding='utf-8') as f:
62 with open(os.path.join(td, "ipython_config.py"), "w", encoding="utf-8") as f:
63 63 f.write("c.TestApp.test = 'config file'")
64 64
65 65 app = TestApp()
66 66 app.initialize(["--profile-dir", td])
67 67 assert app.test == "config file"
68 68 app = TestApp()
69 69 app.initialize(["--profile-dir", td, "--TestApp.test=cli"])
70 70 assert app.test == "cli"
@@ -1,1264 +1,1264 b''
1 1 # encoding: utf-8
2 2 """Tests for the IPython tab-completion machinery."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 import os
8 8 import pytest
9 9 import sys
10 10 import textwrap
11 11 import unittest
12 12
13 13 from contextlib import contextmanager
14 14
15 15 from traitlets.config.loader import Config
16 16 from IPython import get_ipython
17 17 from IPython.core import completer
18 18 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
19 19 from IPython.utils.generics import complete_object
20 20 from IPython.testing import decorators as dec
21 21
22 22 from IPython.core.completer import (
23 23 Completion,
24 24 provisionalcompleter,
25 25 match_dict_keys,
26 26 _deduplicate_completions,
27 27 )
28 28
29 29 # -----------------------------------------------------------------------------
30 30 # Test functions
31 31 # -----------------------------------------------------------------------------
32 32
33 33 def recompute_unicode_ranges():
34 34 """
35 35 utility to recompute the largest unicode range without any characters
36 36
37 37 use to recompute the gap in the global _UNICODE_RANGES of completer.py
38 38 """
39 39 import itertools
40 40 import unicodedata
41 41 valid = []
42 42 for c in range(0,0x10FFFF + 1):
43 43 try:
44 44 unicodedata.name(chr(c))
45 45 except ValueError:
46 46 continue
47 47 valid.append(c)
48 48
49 49 def ranges(i):
50 50 for a, b in itertools.groupby(enumerate(i), lambda pair: pair[1] - pair[0]):
51 51 b = list(b)
52 52 yield b[0][1], b[-1][1]
53 53
54 54 rg = list(ranges(valid))
55 55 lens = []
56 56 gap_lens = []
57 57 pstart, pstop = 0,0
58 58 for start, stop in rg:
59 59 lens.append(stop-start)
60 60 gap_lens.append((start - pstop, hex(pstop), hex(start), f'{round((start - pstop)/0xe01f0*100)}%'))
61 61 pstart, pstop = start, stop
62 62
63 63 return sorted(gap_lens)[-1]
64 64
65 65
66 66
67 67 def test_unicode_range():
68 68 """
69 69 Test that the ranges we test for unicode names give the same number of
70 70 results than testing the full length.
71 71 """
72 72 from IPython.core.completer import _unicode_name_compute, _UNICODE_RANGES
73 73
74 74 expected_list = _unicode_name_compute([(0, 0x110000)])
75 75 test = _unicode_name_compute(_UNICODE_RANGES)
76 76 len_exp = len(expected_list)
77 77 len_test = len(test)
78 78
79 79 # do not inline the len() or on error pytest will try to print the 130 000 +
80 80 # elements.
81 81 message = None
82 82 if len_exp != len_test or len_exp > 131808:
83 83 size, start, stop, prct = recompute_unicode_ranges()
84 84 message = f"""_UNICODE_RANGES likely wrong and need updating. This is
85 85 likely due to a new release of Python. We've find that the biggest gap
86 86 in unicode characters has reduces in size to be {size} characters
87 87 ({prct}), from {start}, to {stop}. In completer.py likely update to
88 88
89 89 _UNICODE_RANGES = [(32, {start}), ({stop}, 0xe01f0)]
90 90
91 91 And update the assertion below to use
92 92
93 93 len_exp <= {len_exp}
94 94 """
95 95 assert len_exp == len_test, message
96 96
97 97 # fail if new unicode symbols have been added.
98 98 assert len_exp <= 138552, message
99 99
100 100
101 101 @contextmanager
102 102 def greedy_completion():
103 103 ip = get_ipython()
104 104 greedy_original = ip.Completer.greedy
105 105 try:
106 106 ip.Completer.greedy = True
107 107 yield
108 108 finally:
109 109 ip.Completer.greedy = greedy_original
110 110
111 111
112 112 def test_protect_filename():
113 113 if sys.platform == "win32":
114 114 pairs = [
115 115 ("abc", "abc"),
116 116 (" abc", '" abc"'),
117 117 ("a bc", '"a bc"'),
118 118 ("a bc", '"a bc"'),
119 119 (" bc", '" bc"'),
120 120 ]
121 121 else:
122 122 pairs = [
123 123 ("abc", "abc"),
124 124 (" abc", r"\ abc"),
125 125 ("a bc", r"a\ bc"),
126 126 ("a bc", r"a\ \ bc"),
127 127 (" bc", r"\ \ bc"),
128 128 # On posix, we also protect parens and other special characters.
129 129 ("a(bc", r"a\(bc"),
130 130 ("a)bc", r"a\)bc"),
131 131 ("a( )bc", r"a\(\ \)bc"),
132 132 ("a[1]bc", r"a\[1\]bc"),
133 133 ("a{1}bc", r"a\{1\}bc"),
134 134 ("a#bc", r"a\#bc"),
135 135 ("a?bc", r"a\?bc"),
136 136 ("a=bc", r"a\=bc"),
137 137 ("a\\bc", r"a\\bc"),
138 138 ("a|bc", r"a\|bc"),
139 139 ("a;bc", r"a\;bc"),
140 140 ("a:bc", r"a\:bc"),
141 141 ("a'bc", r"a\'bc"),
142 142 ("a*bc", r"a\*bc"),
143 143 ('a"bc', r"a\"bc"),
144 144 ("a^bc", r"a\^bc"),
145 145 ("a&bc", r"a\&bc"),
146 146 ]
147 147 # run the actual tests
148 148 for s1, s2 in pairs:
149 149 s1p = completer.protect_filename(s1)
150 150 assert s1p == s2
151 151
152 152
153 153 def check_line_split(splitter, test_specs):
154 154 for part1, part2, split in test_specs:
155 155 cursor_pos = len(part1)
156 156 line = part1 + part2
157 157 out = splitter.split_line(line, cursor_pos)
158 158 assert out == split
159 159
160 160
161 161 def test_line_split():
162 162 """Basic line splitter test with default specs."""
163 163 sp = completer.CompletionSplitter()
164 164 # The format of the test specs is: part1, part2, expected answer. Parts 1
165 165 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
166 166 # was at the end of part1. So an empty part2 represents someone hitting
167 167 # tab at the end of the line, the most common case.
168 168 t = [
169 169 ("run some/scrip", "", "some/scrip"),
170 170 ("run scripts/er", "ror.py foo", "scripts/er"),
171 171 ("echo $HOM", "", "HOM"),
172 172 ("print sys.pa", "", "sys.pa"),
173 173 ("print(sys.pa", "", "sys.pa"),
174 174 ("execfile('scripts/er", "", "scripts/er"),
175 175 ("a[x.", "", "x."),
176 176 ("a[x.", "y", "x."),
177 177 ('cd "some_file/', "", "some_file/"),
178 178 ]
179 179 check_line_split(sp, t)
180 180 # Ensure splitting works OK with unicode by re-running the tests with
181 181 # all inputs turned into unicode
182 182 check_line_split(sp, [map(str, p) for p in t])
183 183
184 184
185 185 class NamedInstanceClass:
186 186 instances = {}
187 187
188 188 def __init__(self, name):
189 189 self.instances[name] = self
190 190
191 191 @classmethod
192 192 def _ipython_key_completions_(cls):
193 193 return cls.instances.keys()
194 194
195 195
196 196 class KeyCompletable:
197 197 def __init__(self, things=()):
198 198 self.things = things
199 199
200 200 def _ipython_key_completions_(self):
201 201 return list(self.things)
202 202
203 203
204 204 class TestCompleter(unittest.TestCase):
205 205 def setUp(self):
206 206 """
207 207 We want to silence all PendingDeprecationWarning when testing the completer
208 208 """
209 209 self._assertwarns = self.assertWarns(PendingDeprecationWarning)
210 210 self._assertwarns.__enter__()
211 211
212 212 def tearDown(self):
213 213 try:
214 214 self._assertwarns.__exit__(None, None, None)
215 215 except AssertionError:
216 216 pass
217 217
218 218 def test_custom_completion_error(self):
219 219 """Test that errors from custom attribute completers are silenced."""
220 220 ip = get_ipython()
221 221
222 222 class A:
223 223 pass
224 224
225 225 ip.user_ns["x"] = A()
226 226
227 227 @complete_object.register(A)
228 228 def complete_A(a, existing_completions):
229 229 raise TypeError("this should be silenced")
230 230
231 231 ip.complete("x.")
232 232
233 233 def test_custom_completion_ordering(self):
234 234 """Test that errors from custom attribute completers are silenced."""
235 235 ip = get_ipython()
236 236
237 237 _, matches = ip.complete('in')
238 238 assert matches.index('input') < matches.index('int')
239 239
240 240 def complete_example(a):
241 241 return ['example2', 'example1']
242 242
243 243 ip.Completer.custom_completers.add_re('ex*', complete_example)
244 244 _, matches = ip.complete('ex')
245 245 assert matches.index('example2') < matches.index('example1')
246 246
247 247 def test_unicode_completions(self):
248 248 ip = get_ipython()
249 249 # Some strings that trigger different types of completion. Check them both
250 250 # in str and unicode forms
251 251 s = ["ru", "%ru", "cd /", "floa", "float(x)/"]
252 252 for t in s + list(map(str, s)):
253 253 # We don't need to check exact completion values (they may change
254 254 # depending on the state of the namespace, but at least no exceptions
255 255 # should be thrown and the return value should be a pair of text, list
256 256 # values.
257 257 text, matches = ip.complete(t)
258 258 self.assertIsInstance(text, str)
259 259 self.assertIsInstance(matches, list)
260 260
261 261 def test_latex_completions(self):
262 262 from IPython.core.latex_symbols import latex_symbols
263 263 import random
264 264
265 265 ip = get_ipython()
266 266 # Test some random unicode symbols
267 267 keys = random.sample(sorted(latex_symbols), 10)
268 268 for k in keys:
269 269 text, matches = ip.complete(k)
270 270 self.assertEqual(text, k)
271 271 self.assertEqual(matches, [latex_symbols[k]])
272 272 # Test a more complex line
273 273 text, matches = ip.complete("print(\\alpha")
274 274 self.assertEqual(text, "\\alpha")
275 275 self.assertEqual(matches[0], latex_symbols["\\alpha"])
276 276 # Test multiple matching latex symbols
277 277 text, matches = ip.complete("\\al")
278 278 self.assertIn("\\alpha", matches)
279 279 self.assertIn("\\aleph", matches)
280 280
281 281 def test_latex_no_results(self):
282 282 """
283 283 forward latex should really return nothing in either field if nothing is found.
284 284 """
285 285 ip = get_ipython()
286 286 text, matches = ip.Completer.latex_matches("\\really_i_should_match_nothing")
287 287 self.assertEqual(text, "")
288 288 self.assertEqual(matches, ())
289 289
290 290 def test_back_latex_completion(self):
291 291 ip = get_ipython()
292 292
293 293 # do not return more than 1 matches for \beta, only the latex one.
294 294 name, matches = ip.complete("\\β")
295 295 self.assertEqual(matches, ["\\beta"])
296 296
297 297 def test_back_unicode_completion(self):
298 298 ip = get_ipython()
299 299
300 300 name, matches = ip.complete("\\â…¤")
301 301 self.assertEqual(matches, ("\\ROMAN NUMERAL FIVE",))
302 302
303 303 def test_forward_unicode_completion(self):
304 304 ip = get_ipython()
305 305
306 306 name, matches = ip.complete("\\ROMAN NUMERAL FIVE")
307 307 self.assertEqual(matches, ["â…¤"]) # This is not a V
308 308 self.assertEqual(matches, ["\u2164"]) # same as above but explicit.
309 309
310 310 def test_delim_setting(self):
311 311 sp = completer.CompletionSplitter()
312 312 sp.delims = " "
313 313 self.assertEqual(sp.delims, " ")
314 314 self.assertEqual(sp._delim_expr, r"[\ ]")
315 315
316 316 def test_spaces(self):
317 317 """Test with only spaces as split chars."""
318 318 sp = completer.CompletionSplitter()
319 319 sp.delims = " "
320 320 t = [("foo", "", "foo"), ("run foo", "", "foo"), ("run foo", "bar", "foo")]
321 321 check_line_split(sp, t)
322 322
323 323 def test_has_open_quotes1(self):
324 324 for s in ["'", "'''", "'hi' '"]:
325 325 self.assertEqual(completer.has_open_quotes(s), "'")
326 326
327 327 def test_has_open_quotes2(self):
328 328 for s in ['"', '"""', '"hi" "']:
329 329 self.assertEqual(completer.has_open_quotes(s), '"')
330 330
331 331 def test_has_open_quotes3(self):
332 332 for s in ["''", "''' '''", "'hi' 'ipython'"]:
333 333 self.assertFalse(completer.has_open_quotes(s))
334 334
335 335 def test_has_open_quotes4(self):
336 336 for s in ['""', '""" """', '"hi" "ipython"']:
337 337 self.assertFalse(completer.has_open_quotes(s))
338 338
339 339 @pytest.mark.xfail(
340 340 sys.platform == "win32", reason="abspath completions fail on Windows"
341 341 )
342 342 def test_abspath_file_completions(self):
343 343 ip = get_ipython()
344 344 with TemporaryDirectory() as tmpdir:
345 345 prefix = os.path.join(tmpdir, "foo")
346 346 suffixes = ["1", "2"]
347 347 names = [prefix + s for s in suffixes]
348 348 for n in names:
349 open(n, "w", encoding='utf-8').close()
349 open(n, "w", encoding="utf-8").close()
350 350
351 351 # Check simple completion
352 352 c = ip.complete(prefix)[1]
353 353 self.assertEqual(c, names)
354 354
355 355 # Now check with a function call
356 356 cmd = 'a = f("%s' % prefix
357 357 c = ip.complete(prefix, cmd)[1]
358 358 comp = [prefix + s for s in suffixes]
359 359 self.assertEqual(c, comp)
360 360
361 361 def test_local_file_completions(self):
362 362 ip = get_ipython()
363 363 with TemporaryWorkingDirectory():
364 364 prefix = "./foo"
365 365 suffixes = ["1", "2"]
366 366 names = [prefix + s for s in suffixes]
367 367 for n in names:
368 open(n, "w", encoding='utf-8').close()
368 open(n, "w", encoding="utf-8").close()
369 369
370 370 # Check simple completion
371 371 c = ip.complete(prefix)[1]
372 372 self.assertEqual(c, names)
373 373
374 374 # Now check with a function call
375 375 cmd = 'a = f("%s' % prefix
376 376 c = ip.complete(prefix, cmd)[1]
377 377 comp = {prefix + s for s in suffixes}
378 378 self.assertTrue(comp.issubset(set(c)))
379 379
380 380 def test_quoted_file_completions(self):
381 381 ip = get_ipython()
382 382 with TemporaryWorkingDirectory():
383 383 name = "foo'bar"
384 open(name, "w", encoding='utf-8').close()
384 open(name, "w", encoding="utf-8").close()
385 385
386 386 # Don't escape Windows
387 387 escaped = name if sys.platform == "win32" else "foo\\'bar"
388 388
389 389 # Single quote matches embedded single quote
390 390 text = "open('foo"
391 391 c = ip.Completer._complete(
392 392 cursor_line=0, cursor_pos=len(text), full_text=text
393 393 )[1]
394 394 self.assertEqual(c, [escaped])
395 395
396 396 # Double quote requires no escape
397 397 text = 'open("foo'
398 398 c = ip.Completer._complete(
399 399 cursor_line=0, cursor_pos=len(text), full_text=text
400 400 )[1]
401 401 self.assertEqual(c, [name])
402 402
403 403 # No quote requires an escape
404 404 text = "%ls foo"
405 405 c = ip.Completer._complete(
406 406 cursor_line=0, cursor_pos=len(text), full_text=text
407 407 )[1]
408 408 self.assertEqual(c, [escaped])
409 409
410 410 def test_all_completions_dups(self):
411 411 """
412 412 Make sure the output of `IPCompleter.all_completions` does not have
413 413 duplicated prefixes.
414 414 """
415 415 ip = get_ipython()
416 416 c = ip.Completer
417 417 ip.ex("class TestClass():\n\ta=1\n\ta1=2")
418 418 for jedi_status in [True, False]:
419 419 with provisionalcompleter():
420 420 ip.Completer.use_jedi = jedi_status
421 421 matches = c.all_completions("TestCl")
422 422 assert matches == ["TestClass"], (jedi_status, matches)
423 423 matches = c.all_completions("TestClass.")
424 424 assert len(matches) > 2, (jedi_status, matches)
425 425 matches = c.all_completions("TestClass.a")
426 426 assert matches == ['TestClass.a', 'TestClass.a1'], jedi_status
427 427
428 428 def test_jedi(self):
429 429 """
430 430 A couple of issue we had with Jedi
431 431 """
432 432 ip = get_ipython()
433 433
434 434 def _test_complete(reason, s, comp, start=None, end=None):
435 435 l = len(s)
436 436 start = start if start is not None else l
437 437 end = end if end is not None else l
438 438 with provisionalcompleter():
439 439 ip.Completer.use_jedi = True
440 440 completions = set(ip.Completer.completions(s, l))
441 441 ip.Completer.use_jedi = False
442 442 assert Completion(start, end, comp) in completions, reason
443 443
444 444 def _test_not_complete(reason, s, comp):
445 445 l = len(s)
446 446 with provisionalcompleter():
447 447 ip.Completer.use_jedi = True
448 448 completions = set(ip.Completer.completions(s, l))
449 449 ip.Completer.use_jedi = False
450 450 assert Completion(l, l, comp) not in completions, reason
451 451
452 452 import jedi
453 453
454 454 jedi_version = tuple(int(i) for i in jedi.__version__.split(".")[:3])
455 455 if jedi_version > (0, 10):
456 456 _test_complete("jedi >0.9 should complete and not crash", "a=1;a.", "real")
457 457 _test_complete("can infer first argument", 'a=(1,"foo");a[0].', "real")
458 458 _test_complete("can infer second argument", 'a=(1,"foo");a[1].', "capitalize")
459 459 _test_complete("cover duplicate completions", "im", "import", 0, 2)
460 460
461 461 _test_not_complete("does not mix types", 'a=(1,"foo");a[0].', "capitalize")
462 462
463 463 def test_completion_have_signature(self):
464 464 """
465 465 Lets make sure jedi is capable of pulling out the signature of the function we are completing.
466 466 """
467 467 ip = get_ipython()
468 468 with provisionalcompleter():
469 469 ip.Completer.use_jedi = True
470 470 completions = ip.Completer.completions("ope", 3)
471 471 c = next(completions) # should be `open`
472 472 ip.Completer.use_jedi = False
473 473 assert "file" in c.signature, "Signature of function was not found by completer"
474 474 assert (
475 475 "encoding" in c.signature
476 476 ), "Signature of function was not found by completer"
477 477
478 478 @pytest.mark.xfail(reason="Known failure on jedi<=0.18.0")
479 479 def test_deduplicate_completions(self):
480 480 """
481 481 Test that completions are correctly deduplicated (even if ranges are not the same)
482 482 """
483 483 ip = get_ipython()
484 484 ip.ex(
485 485 textwrap.dedent(
486 486 """
487 487 class Z:
488 488 zoo = 1
489 489 """
490 490 )
491 491 )
492 492 with provisionalcompleter():
493 493 ip.Completer.use_jedi = True
494 494 l = list(
495 495 _deduplicate_completions("Z.z", ip.Completer.completions("Z.z", 3))
496 496 )
497 497 ip.Completer.use_jedi = False
498 498
499 499 assert len(l) == 1, "Completions (Z.z<tab>) correctly deduplicate: %s " % l
500 500 assert l[0].text == "zoo" # and not `it.accumulate`
501 501
502 502 def test_greedy_completions(self):
503 503 """
504 504 Test the capability of the Greedy completer.
505 505
506 506 Most of the test here does not really show off the greedy completer, for proof
507 507 each of the text below now pass with Jedi. The greedy completer is capable of more.
508 508
509 509 See the :any:`test_dict_key_completion_contexts`
510 510
511 511 """
512 512 ip = get_ipython()
513 513 ip.ex("a=list(range(5))")
514 514 _, c = ip.complete(".", line="a[0].")
515 515 self.assertFalse(".real" in c, "Shouldn't have completed on a[0]: %s" % c)
516 516
517 517 def _(line, cursor_pos, expect, message, completion):
518 518 with greedy_completion(), provisionalcompleter():
519 519 ip.Completer.use_jedi = False
520 520 _, c = ip.complete(".", line=line, cursor_pos=cursor_pos)
521 521 self.assertIn(expect, c, message % c)
522 522
523 523 ip.Completer.use_jedi = True
524 524 with provisionalcompleter():
525 525 completions = ip.Completer.completions(line, cursor_pos)
526 526 self.assertIn(completion, completions)
527 527
528 528 with provisionalcompleter():
529 529 _(
530 530 "a[0].",
531 531 5,
532 532 "a[0].real",
533 533 "Should have completed on a[0].: %s",
534 534 Completion(5, 5, "real"),
535 535 )
536 536 _(
537 537 "a[0].r",
538 538 6,
539 539 "a[0].real",
540 540 "Should have completed on a[0].r: %s",
541 541 Completion(5, 6, "real"),
542 542 )
543 543
544 544 _(
545 545 "a[0].from_",
546 546 10,
547 547 "a[0].from_bytes",
548 548 "Should have completed on a[0].from_: %s",
549 549 Completion(5, 10, "from_bytes"),
550 550 )
551 551
552 552 def test_omit__names(self):
553 553 # also happens to test IPCompleter as a configurable
554 554 ip = get_ipython()
555 555 ip._hidden_attr = 1
556 556 ip._x = {}
557 557 c = ip.Completer
558 558 ip.ex("ip=get_ipython()")
559 559 cfg = Config()
560 560 cfg.IPCompleter.omit__names = 0
561 561 c.update_config(cfg)
562 562 with provisionalcompleter():
563 563 c.use_jedi = False
564 564 s, matches = c.complete("ip.")
565 565 self.assertIn("ip.__str__", matches)
566 566 self.assertIn("ip._hidden_attr", matches)
567 567
568 568 # c.use_jedi = True
569 569 # completions = set(c.completions('ip.', 3))
570 570 # self.assertIn(Completion(3, 3, '__str__'), completions)
571 571 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
572 572
573 573 cfg = Config()
574 574 cfg.IPCompleter.omit__names = 1
575 575 c.update_config(cfg)
576 576 with provisionalcompleter():
577 577 c.use_jedi = False
578 578 s, matches = c.complete("ip.")
579 579 self.assertNotIn("ip.__str__", matches)
580 580 # self.assertIn('ip._hidden_attr', matches)
581 581
582 582 # c.use_jedi = True
583 583 # completions = set(c.completions('ip.', 3))
584 584 # self.assertNotIn(Completion(3,3,'__str__'), completions)
585 585 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
586 586
587 587 cfg = Config()
588 588 cfg.IPCompleter.omit__names = 2
589 589 c.update_config(cfg)
590 590 with provisionalcompleter():
591 591 c.use_jedi = False
592 592 s, matches = c.complete("ip.")
593 593 self.assertNotIn("ip.__str__", matches)
594 594 self.assertNotIn("ip._hidden_attr", matches)
595 595
596 596 # c.use_jedi = True
597 597 # completions = set(c.completions('ip.', 3))
598 598 # self.assertNotIn(Completion(3,3,'__str__'), completions)
599 599 # self.assertNotIn(Completion(3,3, "_hidden_attr"), completions)
600 600
601 601 with provisionalcompleter():
602 602 c.use_jedi = False
603 603 s, matches = c.complete("ip._x.")
604 604 self.assertIn("ip._x.keys", matches)
605 605
606 606 # c.use_jedi = True
607 607 # completions = set(c.completions('ip._x.', 6))
608 608 # self.assertIn(Completion(6,6, "keys"), completions)
609 609
610 610 del ip._hidden_attr
611 611 del ip._x
612 612
613 613 def test_limit_to__all__False_ok(self):
614 614 """
615 615 Limit to all is deprecated, once we remove it this test can go away.
616 616 """
617 617 ip = get_ipython()
618 618 c = ip.Completer
619 619 c.use_jedi = False
620 620 ip.ex("class D: x=24")
621 621 ip.ex("d=D()")
622 622 cfg = Config()
623 623 cfg.IPCompleter.limit_to__all__ = False
624 624 c.update_config(cfg)
625 625 s, matches = c.complete("d.")
626 626 self.assertIn("d.x", matches)
627 627
628 628 def test_get__all__entries_ok(self):
629 629 class A:
630 630 __all__ = ["x", 1]
631 631
632 632 words = completer.get__all__entries(A())
633 633 self.assertEqual(words, ["x"])
634 634
635 635 def test_get__all__entries_no__all__ok(self):
636 636 class A:
637 637 pass
638 638
639 639 words = completer.get__all__entries(A())
640 640 self.assertEqual(words, [])
641 641
642 642 def test_func_kw_completions(self):
643 643 ip = get_ipython()
644 644 c = ip.Completer
645 645 c.use_jedi = False
646 646 ip.ex("def myfunc(a=1,b=2): return a+b")
647 647 s, matches = c.complete(None, "myfunc(1,b")
648 648 self.assertIn("b=", matches)
649 649 # Simulate completing with cursor right after b (pos==10):
650 650 s, matches = c.complete(None, "myfunc(1,b)", 10)
651 651 self.assertIn("b=", matches)
652 652 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
653 653 self.assertIn("b=", matches)
654 654 # builtin function
655 655 s, matches = c.complete(None, "min(k, k")
656 656 self.assertIn("key=", matches)
657 657
658 658 def test_default_arguments_from_docstring(self):
659 659 ip = get_ipython()
660 660 c = ip.Completer
661 661 kwd = c._default_arguments_from_docstring("min(iterable[, key=func]) -> value")
662 662 self.assertEqual(kwd, ["key"])
663 663 # with cython type etc
664 664 kwd = c._default_arguments_from_docstring(
665 665 "Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
666 666 )
667 667 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
668 668 # white spaces
669 669 kwd = c._default_arguments_from_docstring(
670 670 "\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
671 671 )
672 672 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
673 673
674 674 def test_line_magics(self):
675 675 ip = get_ipython()
676 676 c = ip.Completer
677 677 s, matches = c.complete(None, "lsmag")
678 678 self.assertIn("%lsmagic", matches)
679 679 s, matches = c.complete(None, "%lsmag")
680 680 self.assertIn("%lsmagic", matches)
681 681
682 682 def test_cell_magics(self):
683 683 from IPython.core.magic import register_cell_magic
684 684
685 685 @register_cell_magic
686 686 def _foo_cellm(line, cell):
687 687 pass
688 688
689 689 ip = get_ipython()
690 690 c = ip.Completer
691 691
692 692 s, matches = c.complete(None, "_foo_ce")
693 693 self.assertIn("%%_foo_cellm", matches)
694 694 s, matches = c.complete(None, "%%_foo_ce")
695 695 self.assertIn("%%_foo_cellm", matches)
696 696
697 697 def test_line_cell_magics(self):
698 698 from IPython.core.magic import register_line_cell_magic
699 699
700 700 @register_line_cell_magic
701 701 def _bar_cellm(line, cell):
702 702 pass
703 703
704 704 ip = get_ipython()
705 705 c = ip.Completer
706 706
707 707 # The policy here is trickier, see comments in completion code. The
708 708 # returned values depend on whether the user passes %% or not explicitly,
709 709 # and this will show a difference if the same name is both a line and cell
710 710 # magic.
711 711 s, matches = c.complete(None, "_bar_ce")
712 712 self.assertIn("%_bar_cellm", matches)
713 713 self.assertIn("%%_bar_cellm", matches)
714 714 s, matches = c.complete(None, "%_bar_ce")
715 715 self.assertIn("%_bar_cellm", matches)
716 716 self.assertIn("%%_bar_cellm", matches)
717 717 s, matches = c.complete(None, "%%_bar_ce")
718 718 self.assertNotIn("%_bar_cellm", matches)
719 719 self.assertIn("%%_bar_cellm", matches)
720 720
721 721 def test_magic_completion_order(self):
722 722 ip = get_ipython()
723 723 c = ip.Completer
724 724
725 725 # Test ordering of line and cell magics.
726 726 text, matches = c.complete("timeit")
727 727 self.assertEqual(matches, ["%timeit", "%%timeit"])
728 728
729 729 def test_magic_completion_shadowing(self):
730 730 ip = get_ipython()
731 731 c = ip.Completer
732 732 c.use_jedi = False
733 733
734 734 # Before importing matplotlib, %matplotlib magic should be the only option.
735 735 text, matches = c.complete("mat")
736 736 self.assertEqual(matches, ["%matplotlib"])
737 737
738 738 # The newly introduced name should shadow the magic.
739 739 ip.run_cell("matplotlib = 1")
740 740 text, matches = c.complete("mat")
741 741 self.assertEqual(matches, ["matplotlib"])
742 742
743 743 # After removing matplotlib from namespace, the magic should again be
744 744 # the only option.
745 745 del ip.user_ns["matplotlib"]
746 746 text, matches = c.complete("mat")
747 747 self.assertEqual(matches, ["%matplotlib"])
748 748
749 749 def test_magic_completion_shadowing_explicit(self):
750 750 """
751 751 If the user try to complete a shadowed magic, and explicit % start should
752 752 still return the completions.
753 753 """
754 754 ip = get_ipython()
755 755 c = ip.Completer
756 756
757 757 # Before importing matplotlib, %matplotlib magic should be the only option.
758 758 text, matches = c.complete("%mat")
759 759 self.assertEqual(matches, ["%matplotlib"])
760 760
761 761 ip.run_cell("matplotlib = 1")
762 762
763 763 # After removing matplotlib from namespace, the magic should still be
764 764 # the only option.
765 765 text, matches = c.complete("%mat")
766 766 self.assertEqual(matches, ["%matplotlib"])
767 767
768 768 def test_magic_config(self):
769 769 ip = get_ipython()
770 770 c = ip.Completer
771 771
772 772 s, matches = c.complete(None, "conf")
773 773 self.assertIn("%config", matches)
774 774 s, matches = c.complete(None, "conf")
775 775 self.assertNotIn("AliasManager", matches)
776 776 s, matches = c.complete(None, "config ")
777 777 self.assertIn("AliasManager", matches)
778 778 s, matches = c.complete(None, "%config ")
779 779 self.assertIn("AliasManager", matches)
780 780 s, matches = c.complete(None, "config Ali")
781 781 self.assertListEqual(["AliasManager"], matches)
782 782 s, matches = c.complete(None, "%config Ali")
783 783 self.assertListEqual(["AliasManager"], matches)
784 784 s, matches = c.complete(None, "config AliasManager")
785 785 self.assertListEqual(["AliasManager"], matches)
786 786 s, matches = c.complete(None, "%config AliasManager")
787 787 self.assertListEqual(["AliasManager"], matches)
788 788 s, matches = c.complete(None, "config AliasManager.")
789 789 self.assertIn("AliasManager.default_aliases", matches)
790 790 s, matches = c.complete(None, "%config AliasManager.")
791 791 self.assertIn("AliasManager.default_aliases", matches)
792 792 s, matches = c.complete(None, "config AliasManager.de")
793 793 self.assertListEqual(["AliasManager.default_aliases"], matches)
794 794 s, matches = c.complete(None, "config AliasManager.de")
795 795 self.assertListEqual(["AliasManager.default_aliases"], matches)
796 796
797 797 def test_magic_color(self):
798 798 ip = get_ipython()
799 799 c = ip.Completer
800 800
801 801 s, matches = c.complete(None, "colo")
802 802 self.assertIn("%colors", matches)
803 803 s, matches = c.complete(None, "colo")
804 804 self.assertNotIn("NoColor", matches)
805 805 s, matches = c.complete(None, "%colors") # No trailing space
806 806 self.assertNotIn("NoColor", matches)
807 807 s, matches = c.complete(None, "colors ")
808 808 self.assertIn("NoColor", matches)
809 809 s, matches = c.complete(None, "%colors ")
810 810 self.assertIn("NoColor", matches)
811 811 s, matches = c.complete(None, "colors NoCo")
812 812 self.assertListEqual(["NoColor"], matches)
813 813 s, matches = c.complete(None, "%colors NoCo")
814 814 self.assertListEqual(["NoColor"], matches)
815 815
816 816 def test_match_dict_keys(self):
817 817 """
818 818 Test that match_dict_keys works on a couple of use case does return what
819 819 expected, and does not crash
820 820 """
821 821 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
822 822
823 823 keys = ["foo", b"far"]
824 824 assert match_dict_keys(keys, "b'", delims=delims) == ("'", 2, ["far"])
825 825 assert match_dict_keys(keys, "b'f", delims=delims) == ("'", 2, ["far"])
826 826 assert match_dict_keys(keys, 'b"', delims=delims) == ('"', 2, ["far"])
827 827 assert match_dict_keys(keys, 'b"f', delims=delims) == ('"', 2, ["far"])
828 828
829 829 assert match_dict_keys(keys, "'", delims=delims) == ("'", 1, ["foo"])
830 830 assert match_dict_keys(keys, "'f", delims=delims) == ("'", 1, ["foo"])
831 831 assert match_dict_keys(keys, '"', delims=delims) == ('"', 1, ["foo"])
832 832 assert match_dict_keys(keys, '"f', delims=delims) == ('"', 1, ["foo"])
833 833
834 834 match_dict_keys
835 835
836 836 def test_match_dict_keys_tuple(self):
837 837 """
838 838 Test that match_dict_keys called with extra prefix works on a couple of use case,
839 839 does return what expected, and does not crash.
840 840 """
841 841 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
842 842
843 843 keys = [("foo", "bar"), ("foo", "oof"), ("foo", b"bar"), ('other', 'test')]
844 844
845 845 # Completion on first key == "foo"
846 846 assert match_dict_keys(keys, "'", delims=delims, extra_prefix=("foo",)) == ("'", 1, ["bar", "oof"])
847 847 assert match_dict_keys(keys, "\"", delims=delims, extra_prefix=("foo",)) == ("\"", 1, ["bar", "oof"])
848 848 assert match_dict_keys(keys, "'o", delims=delims, extra_prefix=("foo",)) == ("'", 1, ["oof"])
849 849 assert match_dict_keys(keys, "\"o", delims=delims, extra_prefix=("foo",)) == ("\"", 1, ["oof"])
850 850 assert match_dict_keys(keys, "b'", delims=delims, extra_prefix=("foo",)) == ("'", 2, ["bar"])
851 851 assert match_dict_keys(keys, "b\"", delims=delims, extra_prefix=("foo",)) == ("\"", 2, ["bar"])
852 852 assert match_dict_keys(keys, "b'b", delims=delims, extra_prefix=("foo",)) == ("'", 2, ["bar"])
853 853 assert match_dict_keys(keys, "b\"b", delims=delims, extra_prefix=("foo",)) == ("\"", 2, ["bar"])
854 854
855 855 # No Completion
856 856 assert match_dict_keys(keys, "'", delims=delims, extra_prefix=("no_foo",)) == ("'", 1, [])
857 857 assert match_dict_keys(keys, "'", delims=delims, extra_prefix=("fo",)) == ("'", 1, [])
858 858
859 859 keys = [('foo1', 'foo2', 'foo3', 'foo4'), ('foo1', 'foo2', 'bar', 'foo4')]
860 860 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1',)) == ("'", 1, ["foo2", "foo2"])
861 861 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1', 'foo2')) == ("'", 1, ["foo3"])
862 862 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1', 'foo2', 'foo3')) == ("'", 1, ["foo4"])
863 863 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1', 'foo2', 'foo3', 'foo4')) == ("'", 1, [])
864 864
865 865 def test_dict_key_completion_string(self):
866 866 """Test dictionary key completion for string keys"""
867 867 ip = get_ipython()
868 868 complete = ip.Completer.complete
869 869
870 870 ip.user_ns["d"] = {"abc": None}
871 871
872 872 # check completion at different stages
873 873 _, matches = complete(line_buffer="d[")
874 874 self.assertIn("'abc'", matches)
875 875 self.assertNotIn("'abc']", matches)
876 876
877 877 _, matches = complete(line_buffer="d['")
878 878 self.assertIn("abc", matches)
879 879 self.assertNotIn("abc']", matches)
880 880
881 881 _, matches = complete(line_buffer="d['a")
882 882 self.assertIn("abc", matches)
883 883 self.assertNotIn("abc']", matches)
884 884
885 885 # check use of different quoting
886 886 _, matches = complete(line_buffer='d["')
887 887 self.assertIn("abc", matches)
888 888 self.assertNotIn('abc"]', matches)
889 889
890 890 _, matches = complete(line_buffer='d["a')
891 891 self.assertIn("abc", matches)
892 892 self.assertNotIn('abc"]', matches)
893 893
894 894 # check sensitivity to following context
895 895 _, matches = complete(line_buffer="d[]", cursor_pos=2)
896 896 self.assertIn("'abc'", matches)
897 897
898 898 _, matches = complete(line_buffer="d['']", cursor_pos=3)
899 899 self.assertIn("abc", matches)
900 900 self.assertNotIn("abc'", matches)
901 901 self.assertNotIn("abc']", matches)
902 902
903 903 # check multiple solutions are correctly returned and that noise is not
904 904 ip.user_ns["d"] = {
905 905 "abc": None,
906 906 "abd": None,
907 907 "bad": None,
908 908 object(): None,
909 909 5: None,
910 910 ("abe", None): None,
911 911 (None, "abf"): None
912 912 }
913 913
914 914 _, matches = complete(line_buffer="d['a")
915 915 self.assertIn("abc", matches)
916 916 self.assertIn("abd", matches)
917 917 self.assertNotIn("bad", matches)
918 918 self.assertNotIn("abe", matches)
919 919 self.assertNotIn("abf", matches)
920 920 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
921 921
922 922 # check escaping and whitespace
923 923 ip.user_ns["d"] = {"a\nb": None, "a'b": None, 'a"b': None, "a word": None}
924 924 _, matches = complete(line_buffer="d['a")
925 925 self.assertIn("a\\nb", matches)
926 926 self.assertIn("a\\'b", matches)
927 927 self.assertIn('a"b', matches)
928 928 self.assertIn("a word", matches)
929 929 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
930 930
931 931 # - can complete on non-initial word of the string
932 932 _, matches = complete(line_buffer="d['a w")
933 933 self.assertIn("word", matches)
934 934
935 935 # - understands quote escaping
936 936 _, matches = complete(line_buffer="d['a\\'")
937 937 self.assertIn("b", matches)
938 938
939 939 # - default quoting should work like repr
940 940 _, matches = complete(line_buffer="d[")
941 941 self.assertIn('"a\'b"', matches)
942 942
943 943 # - when opening quote with ", possible to match with unescaped apostrophe
944 944 _, matches = complete(line_buffer="d[\"a'")
945 945 self.assertIn("b", matches)
946 946
947 947 # need to not split at delims that readline won't split at
948 948 if "-" not in ip.Completer.splitter.delims:
949 949 ip.user_ns["d"] = {"before-after": None}
950 950 _, matches = complete(line_buffer="d['before-af")
951 951 self.assertIn("before-after", matches)
952 952
953 953 # check completion on tuple-of-string keys at different stage - on first key
954 954 ip.user_ns["d"] = {('foo', 'bar'): None}
955 955 _, matches = complete(line_buffer="d[")
956 956 self.assertIn("'foo'", matches)
957 957 self.assertNotIn("'foo']", matches)
958 958 self.assertNotIn("'bar'", matches)
959 959 self.assertNotIn("foo", matches)
960 960 self.assertNotIn("bar", matches)
961 961
962 962 # - match the prefix
963 963 _, matches = complete(line_buffer="d['f")
964 964 self.assertIn("foo", matches)
965 965 self.assertNotIn("foo']", matches)
966 966 self.assertNotIn('foo"]', matches)
967 967 _, matches = complete(line_buffer="d['foo")
968 968 self.assertIn("foo", matches)
969 969
970 970 # - can complete on second key
971 971 _, matches = complete(line_buffer="d['foo', ")
972 972 self.assertIn("'bar'", matches)
973 973 _, matches = complete(line_buffer="d['foo', 'b")
974 974 self.assertIn("bar", matches)
975 975 self.assertNotIn("foo", matches)
976 976
977 977 # - does not propose missing keys
978 978 _, matches = complete(line_buffer="d['foo', 'f")
979 979 self.assertNotIn("bar", matches)
980 980 self.assertNotIn("foo", matches)
981 981
982 982 # check sensitivity to following context
983 983 _, matches = complete(line_buffer="d['foo',]", cursor_pos=8)
984 984 self.assertIn("'bar'", matches)
985 985 self.assertNotIn("bar", matches)
986 986 self.assertNotIn("'foo'", matches)
987 987 self.assertNotIn("foo", matches)
988 988
989 989 _, matches = complete(line_buffer="d['']", cursor_pos=3)
990 990 self.assertIn("foo", matches)
991 991 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
992 992
993 993 _, matches = complete(line_buffer='d[""]', cursor_pos=3)
994 994 self.assertIn("foo", matches)
995 995 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
996 996
997 997 _, matches = complete(line_buffer='d["foo","]', cursor_pos=9)
998 998 self.assertIn("bar", matches)
999 999 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1000 1000
1001 1001 _, matches = complete(line_buffer='d["foo",]', cursor_pos=8)
1002 1002 self.assertIn("'bar'", matches)
1003 1003 self.assertNotIn("bar", matches)
1004 1004
1005 1005 # Can complete with longer tuple keys
1006 1006 ip.user_ns["d"] = {('foo', 'bar', 'foobar'): None}
1007 1007
1008 1008 # - can complete second key
1009 1009 _, matches = complete(line_buffer="d['foo', 'b")
1010 1010 self.assertIn("bar", matches)
1011 1011 self.assertNotIn("foo", matches)
1012 1012 self.assertNotIn("foobar", matches)
1013 1013
1014 1014 # - can complete third key
1015 1015 _, matches = complete(line_buffer="d['foo', 'bar', 'fo")
1016 1016 self.assertIn("foobar", matches)
1017 1017 self.assertNotIn("foo", matches)
1018 1018 self.assertNotIn("bar", matches)
1019 1019
1020 1020 def test_dict_key_completion_contexts(self):
1021 1021 """Test expression contexts in which dict key completion occurs"""
1022 1022 ip = get_ipython()
1023 1023 complete = ip.Completer.complete
1024 1024 d = {"abc": None}
1025 1025 ip.user_ns["d"] = d
1026 1026
1027 1027 class C:
1028 1028 data = d
1029 1029
1030 1030 ip.user_ns["C"] = C
1031 1031 ip.user_ns["get"] = lambda: d
1032 1032
1033 1033 def assert_no_completion(**kwargs):
1034 1034 _, matches = complete(**kwargs)
1035 1035 self.assertNotIn("abc", matches)
1036 1036 self.assertNotIn("abc'", matches)
1037 1037 self.assertNotIn("abc']", matches)
1038 1038 self.assertNotIn("'abc'", matches)
1039 1039 self.assertNotIn("'abc']", matches)
1040 1040
1041 1041 def assert_completion(**kwargs):
1042 1042 _, matches = complete(**kwargs)
1043 1043 self.assertIn("'abc'", matches)
1044 1044 self.assertNotIn("'abc']", matches)
1045 1045
1046 1046 # no completion after string closed, even if reopened
1047 1047 assert_no_completion(line_buffer="d['a'")
1048 1048 assert_no_completion(line_buffer='d["a"')
1049 1049 assert_no_completion(line_buffer="d['a' + ")
1050 1050 assert_no_completion(line_buffer="d['a' + '")
1051 1051
1052 1052 # completion in non-trivial expressions
1053 1053 assert_completion(line_buffer="+ d[")
1054 1054 assert_completion(line_buffer="(d[")
1055 1055 assert_completion(line_buffer="C.data[")
1056 1056
1057 1057 # greedy flag
1058 1058 def assert_completion(**kwargs):
1059 1059 _, matches = complete(**kwargs)
1060 1060 self.assertIn("get()['abc']", matches)
1061 1061
1062 1062 assert_no_completion(line_buffer="get()[")
1063 1063 with greedy_completion():
1064 1064 assert_completion(line_buffer="get()[")
1065 1065 assert_completion(line_buffer="get()['")
1066 1066 assert_completion(line_buffer="get()['a")
1067 1067 assert_completion(line_buffer="get()['ab")
1068 1068 assert_completion(line_buffer="get()['abc")
1069 1069
1070 1070 def test_dict_key_completion_bytes(self):
1071 1071 """Test handling of bytes in dict key completion"""
1072 1072 ip = get_ipython()
1073 1073 complete = ip.Completer.complete
1074 1074
1075 1075 ip.user_ns["d"] = {"abc": None, b"abd": None}
1076 1076
1077 1077 _, matches = complete(line_buffer="d[")
1078 1078 self.assertIn("'abc'", matches)
1079 1079 self.assertIn("b'abd'", matches)
1080 1080
1081 1081 if False: # not currently implemented
1082 1082 _, matches = complete(line_buffer="d[b")
1083 1083 self.assertIn("b'abd'", matches)
1084 1084 self.assertNotIn("b'abc'", matches)
1085 1085
1086 1086 _, matches = complete(line_buffer="d[b'")
1087 1087 self.assertIn("abd", matches)
1088 1088 self.assertNotIn("abc", matches)
1089 1089
1090 1090 _, matches = complete(line_buffer="d[B'")
1091 1091 self.assertIn("abd", matches)
1092 1092 self.assertNotIn("abc", matches)
1093 1093
1094 1094 _, matches = complete(line_buffer="d['")
1095 1095 self.assertIn("abc", matches)
1096 1096 self.assertNotIn("abd", matches)
1097 1097
1098 1098 def test_dict_key_completion_unicode_py3(self):
1099 1099 """Test handling of unicode in dict key completion"""
1100 1100 ip = get_ipython()
1101 1101 complete = ip.Completer.complete
1102 1102
1103 1103 ip.user_ns["d"] = {"a\u05d0": None}
1104 1104
1105 1105 # query using escape
1106 1106 if sys.platform != "win32":
1107 1107 # Known failure on Windows
1108 1108 _, matches = complete(line_buffer="d['a\\u05d0")
1109 1109 self.assertIn("u05d0", matches) # tokenized after \\
1110 1110
1111 1111 # query using character
1112 1112 _, matches = complete(line_buffer="d['a\u05d0")
1113 1113 self.assertIn("a\u05d0", matches)
1114 1114
1115 1115 with greedy_completion():
1116 1116 # query using escape
1117 1117 _, matches = complete(line_buffer="d['a\\u05d0")
1118 1118 self.assertIn("d['a\\u05d0']", matches) # tokenized after \\
1119 1119
1120 1120 # query using character
1121 1121 _, matches = complete(line_buffer="d['a\u05d0")
1122 1122 self.assertIn("d['a\u05d0']", matches)
1123 1123
1124 1124 @dec.skip_without("numpy")
1125 1125 def test_struct_array_key_completion(self):
1126 1126 """Test dict key completion applies to numpy struct arrays"""
1127 1127 import numpy
1128 1128
1129 1129 ip = get_ipython()
1130 1130 complete = ip.Completer.complete
1131 1131 ip.user_ns["d"] = numpy.array([], dtype=[("hello", "f"), ("world", "f")])
1132 1132 _, matches = complete(line_buffer="d['")
1133 1133 self.assertIn("hello", matches)
1134 1134 self.assertIn("world", matches)
1135 1135 # complete on the numpy struct itself
1136 1136 dt = numpy.dtype(
1137 1137 [("my_head", [("my_dt", ">u4"), ("my_df", ">u4")]), ("my_data", ">f4", 5)]
1138 1138 )
1139 1139 x = numpy.zeros(2, dtype=dt)
1140 1140 ip.user_ns["d"] = x[1]
1141 1141 _, matches = complete(line_buffer="d['")
1142 1142 self.assertIn("my_head", matches)
1143 1143 self.assertIn("my_data", matches)
1144 1144 # complete on a nested level
1145 1145 with greedy_completion():
1146 1146 ip.user_ns["d"] = numpy.zeros(2, dtype=dt)
1147 1147 _, matches = complete(line_buffer="d[1]['my_head']['")
1148 1148 self.assertTrue(any(["my_dt" in m for m in matches]))
1149 1149 self.assertTrue(any(["my_df" in m for m in matches]))
1150 1150
1151 1151 @dec.skip_without("pandas")
1152 1152 def test_dataframe_key_completion(self):
1153 1153 """Test dict key completion applies to pandas DataFrames"""
1154 1154 import pandas
1155 1155
1156 1156 ip = get_ipython()
1157 1157 complete = ip.Completer.complete
1158 1158 ip.user_ns["d"] = pandas.DataFrame({"hello": [1], "world": [2]})
1159 1159 _, matches = complete(line_buffer="d['")
1160 1160 self.assertIn("hello", matches)
1161 1161 self.assertIn("world", matches)
1162 1162
1163 1163 def test_dict_key_completion_invalids(self):
1164 1164 """Smoke test cases dict key completion can't handle"""
1165 1165 ip = get_ipython()
1166 1166 complete = ip.Completer.complete
1167 1167
1168 1168 ip.user_ns["no_getitem"] = None
1169 1169 ip.user_ns["no_keys"] = []
1170 1170 ip.user_ns["cant_call_keys"] = dict
1171 1171 ip.user_ns["empty"] = {}
1172 1172 ip.user_ns["d"] = {"abc": 5}
1173 1173
1174 1174 _, matches = complete(line_buffer="no_getitem['")
1175 1175 _, matches = complete(line_buffer="no_keys['")
1176 1176 _, matches = complete(line_buffer="cant_call_keys['")
1177 1177 _, matches = complete(line_buffer="empty['")
1178 1178 _, matches = complete(line_buffer="name_error['")
1179 1179 _, matches = complete(line_buffer="d['\\") # incomplete escape
1180 1180
1181 1181 def test_object_key_completion(self):
1182 1182 ip = get_ipython()
1183 1183 ip.user_ns["key_completable"] = KeyCompletable(["qwerty", "qwick"])
1184 1184
1185 1185 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
1186 1186 self.assertIn("qwerty", matches)
1187 1187 self.assertIn("qwick", matches)
1188 1188
1189 1189 def test_class_key_completion(self):
1190 1190 ip = get_ipython()
1191 1191 NamedInstanceClass("qwerty")
1192 1192 NamedInstanceClass("qwick")
1193 1193 ip.user_ns["named_instance_class"] = NamedInstanceClass
1194 1194
1195 1195 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw")
1196 1196 self.assertIn("qwerty", matches)
1197 1197 self.assertIn("qwick", matches)
1198 1198
1199 1199 def test_tryimport(self):
1200 1200 """
1201 1201 Test that try-import don't crash on trailing dot, and import modules before
1202 1202 """
1203 1203 from IPython.core.completerlib import try_import
1204 1204
1205 1205 assert try_import("IPython.")
1206 1206
1207 1207 def test_aimport_module_completer(self):
1208 1208 ip = get_ipython()
1209 1209 _, matches = ip.complete("i", "%aimport i")
1210 1210 self.assertIn("io", matches)
1211 1211 self.assertNotIn("int", matches)
1212 1212
1213 1213 def test_nested_import_module_completer(self):
1214 1214 ip = get_ipython()
1215 1215 _, matches = ip.complete(None, "import IPython.co", 17)
1216 1216 self.assertIn("IPython.core", matches)
1217 1217 self.assertNotIn("import IPython.core", matches)
1218 1218 self.assertNotIn("IPython.display", matches)
1219 1219
1220 1220 def test_import_module_completer(self):
1221 1221 ip = get_ipython()
1222 1222 _, matches = ip.complete("i", "import i")
1223 1223 self.assertIn("io", matches)
1224 1224 self.assertNotIn("int", matches)
1225 1225
1226 1226 def test_from_module_completer(self):
1227 1227 ip = get_ipython()
1228 1228 _, matches = ip.complete("B", "from io import B", 16)
1229 1229 self.assertIn("BytesIO", matches)
1230 1230 self.assertNotIn("BaseException", matches)
1231 1231
1232 1232 def test_snake_case_completion(self):
1233 1233 ip = get_ipython()
1234 1234 ip.Completer.use_jedi = False
1235 1235 ip.user_ns["some_three"] = 3
1236 1236 ip.user_ns["some_four"] = 4
1237 1237 _, matches = ip.complete("s_", "print(s_f")
1238 1238 self.assertIn("some_three", matches)
1239 1239 self.assertIn("some_four", matches)
1240 1240
1241 1241 def test_mix_terms(self):
1242 1242 ip = get_ipython()
1243 1243 from textwrap import dedent
1244 1244
1245 1245 ip.Completer.use_jedi = False
1246 1246 ip.ex(
1247 1247 dedent(
1248 1248 """
1249 1249 class Test:
1250 1250 def meth(self, meth_arg1):
1251 1251 print("meth")
1252 1252
1253 1253 def meth_1(self, meth1_arg1, meth1_arg2):
1254 1254 print("meth1")
1255 1255
1256 1256 def meth_2(self, meth2_arg1, meth2_arg2):
1257 1257 print("meth2")
1258 1258 test = Test()
1259 1259 """
1260 1260 )
1261 1261 )
1262 1262 _, matches = ip.complete(None, "test.meth(")
1263 1263 self.assertIn("meth_arg1=", matches)
1264 1264 self.assertNotIn("meth2_arg1=", matches)
@@ -1,192 +1,192 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for completerlib.
3 3
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Imports
8 8 #-----------------------------------------------------------------------------
9 9
10 10 import os
11 11 import shutil
12 12 import sys
13 13 import tempfile
14 14 import unittest
15 15 from os.path import join
16 16
17 17 from IPython.core.completerlib import magic_run_completer, module_completion, try_import
18 18 from IPython.utils.tempdir import TemporaryDirectory
19 19 from IPython.testing.decorators import onlyif_unicode_paths
20 20
21 21
22 22 class MockEvent(object):
23 23 def __init__(self, line):
24 24 self.line = line
25 25
26 26 #-----------------------------------------------------------------------------
27 27 # Test functions begin
28 28 #-----------------------------------------------------------------------------
29 29 class Test_magic_run_completer(unittest.TestCase):
30 30 files = [u"aao.py", u"a.py", u"b.py", u"aao.txt"]
31 31 dirs = [u"adir/", "bdir/"]
32 32
33 33 def setUp(self):
34 34 self.BASETESTDIR = tempfile.mkdtemp()
35 35 for fil in self.files:
36 with open(join(self.BASETESTDIR, fil), "w", encoding='utf-8') as sfile:
36 with open(join(self.BASETESTDIR, fil), "w", encoding="utf-8") as sfile:
37 37 sfile.write("pass\n")
38 38 for d in self.dirs:
39 39 os.mkdir(join(self.BASETESTDIR, d))
40 40
41 41 self.oldpath = os.getcwd()
42 42 os.chdir(self.BASETESTDIR)
43 43
44 44 def tearDown(self):
45 45 os.chdir(self.oldpath)
46 46 shutil.rmtree(self.BASETESTDIR)
47 47
48 48 def test_1(self):
49 49 """Test magic_run_completer, should match two alternatives
50 50 """
51 51 event = MockEvent(u"%run a")
52 52 mockself = None
53 53 match = set(magic_run_completer(mockself, event))
54 54 self.assertEqual(match, {u"a.py", u"aao.py", u"adir/"})
55 55
56 56 def test_2(self):
57 57 """Test magic_run_completer, should match one alternative
58 58 """
59 59 event = MockEvent(u"%run aa")
60 60 mockself = None
61 61 match = set(magic_run_completer(mockself, event))
62 62 self.assertEqual(match, {u"aao.py"})
63 63
64 64 def test_3(self):
65 65 """Test magic_run_completer with unterminated " """
66 66 event = MockEvent(u'%run "a')
67 67 mockself = None
68 68 match = set(magic_run_completer(mockself, event))
69 69 self.assertEqual(match, {u"a.py", u"aao.py", u"adir/"})
70 70
71 71 def test_completion_more_args(self):
72 72 event = MockEvent(u'%run a.py ')
73 73 match = set(magic_run_completer(None, event))
74 74 self.assertEqual(match, set(self.files + self.dirs))
75 75
76 76 def test_completion_in_dir(self):
77 77 # Github issue #3459
78 78 event = MockEvent(u'%run a.py {}'.format(join(self.BASETESTDIR, 'a')))
79 79 print(repr(event.line))
80 80 match = set(magic_run_completer(None, event))
81 81 # We specifically use replace here rather than normpath, because
82 82 # at one point there were duplicates 'adir' and 'adir/', and normpath
83 83 # would hide the failure for that.
84 84 self.assertEqual(match, {join(self.BASETESTDIR, f).replace('\\','/')
85 85 for f in (u'a.py', u'aao.py', u'aao.txt', u'adir/')})
86 86
87 87 class Test_magic_run_completer_nonascii(unittest.TestCase):
88 88 @onlyif_unicode_paths
89 89 def setUp(self):
90 90 self.BASETESTDIR = tempfile.mkdtemp()
91 91 for fil in [u"aaø.py", u"a.py", u"b.py"]:
92 with open(join(self.BASETESTDIR, fil), "w", encoding='utf-8') as sfile:
92 with open(join(self.BASETESTDIR, fil), "w", encoding="utf-8") as sfile:
93 93 sfile.write("pass\n")
94 94 self.oldpath = os.getcwd()
95 95 os.chdir(self.BASETESTDIR)
96 96
97 97 def tearDown(self):
98 98 os.chdir(self.oldpath)
99 99 shutil.rmtree(self.BASETESTDIR)
100 100
101 101 @onlyif_unicode_paths
102 102 def test_1(self):
103 103 """Test magic_run_completer, should match two alternatives
104 104 """
105 105 event = MockEvent(u"%run a")
106 106 mockself = None
107 107 match = set(magic_run_completer(mockself, event))
108 108 self.assertEqual(match, {u"a.py", u"aaø.py"})
109 109
110 110 @onlyif_unicode_paths
111 111 def test_2(self):
112 112 """Test magic_run_completer, should match one alternative
113 113 """
114 114 event = MockEvent(u"%run aa")
115 115 mockself = None
116 116 match = set(magic_run_completer(mockself, event))
117 117 self.assertEqual(match, {u"aaø.py"})
118 118
119 119 @onlyif_unicode_paths
120 120 def test_3(self):
121 121 """Test magic_run_completer with unterminated " """
122 122 event = MockEvent(u'%run "a')
123 123 mockself = None
124 124 match = set(magic_run_completer(mockself, event))
125 125 self.assertEqual(match, {u"a.py", u"aaø.py"})
126 126
127 127 # module_completer:
128 128
129 129 def test_import_invalid_module():
130 130 """Testing of issue https://github.com/ipython/ipython/issues/1107"""
131 131 invalid_module_names = {'foo-bar', 'foo:bar', '10foo'}
132 132 valid_module_names = {'foobar'}
133 133 with TemporaryDirectory() as tmpdir:
134 134 sys.path.insert( 0, tmpdir )
135 135 for name in invalid_module_names | valid_module_names:
136 filename = os.path.join(tmpdir, name + '.py')
137 open(filename, 'w', encoding='utf-8').close()
136 filename = os.path.join(tmpdir, name + ".py")
137 open(filename, "w", encoding="utf-8").close()
138 138
139 139 s = set( module_completion('import foo') )
140 140 intersection = s.intersection(invalid_module_names)
141 141 assert intersection == set()
142 142
143 143 assert valid_module_names.issubset(s), valid_module_names.intersection(s)
144 144
145 145
146 146 def test_bad_module_all():
147 147 """Test module with invalid __all__
148 148
149 149 https://github.com/ipython/ipython/issues/9678
150 150 """
151 151 testsdir = os.path.dirname(__file__)
152 152 sys.path.insert(0, testsdir)
153 153 try:
154 154 results = module_completion("from bad_all import ")
155 155 assert "puppies" in results
156 156 for r in results:
157 157 assert isinstance(r, str)
158 158
159 159 # bad_all doesn't contain submodules, but this completion
160 160 # should finish without raising an exception:
161 161 results = module_completion("import bad_all.")
162 162 assert results == []
163 163 finally:
164 164 sys.path.remove(testsdir)
165 165
166 166
167 167 def test_module_without_init():
168 168 """
169 169 Test module without __init__.py.
170 170
171 171 https://github.com/ipython/ipython/issues/11226
172 172 """
173 173 fake_module_name = "foo"
174 174 with TemporaryDirectory() as tmpdir:
175 175 sys.path.insert(0, tmpdir)
176 176 try:
177 177 os.makedirs(os.path.join(tmpdir, fake_module_name))
178 178 s = try_import(mod=fake_module_name)
179 179 assert s == []
180 180 finally:
181 181 sys.path.remove(tmpdir)
182 182
183 183
184 184 def test_valid_exported_submodules():
185 185 """
186 186 Test checking exported (__all__) objects are submodules
187 187 """
188 188 results = module_completion("import os.pa")
189 189 # ensure we get a valid submodule:
190 190 assert "os.path" in results
191 191 # ensure we don't get objects that aren't submodules:
192 192 assert "os.pathconf" not in results
@@ -1,94 +1,94 b''
1 1 import os.path
2 2
3 3 import IPython.testing.tools as tt
4 4 from IPython.utils.syspathcontext import prepended_to_syspath
5 5 from IPython.utils.tempdir import TemporaryDirectory
6 6
7 7 ext1_content = """
8 8 def load_ipython_extension(ip):
9 9 print("Running ext1 load")
10 10
11 11 def unload_ipython_extension(ip):
12 12 print("Running ext1 unload")
13 13 """
14 14
15 15 ext2_content = """
16 16 def load_ipython_extension(ip):
17 17 print("Running ext2 load")
18 18 """
19 19
20 20 ext3_content = """
21 21 def load_ipython_extension(ip):
22 22 ip2 = get_ipython()
23 23 print(ip is ip2)
24 24 """
25 25
26 26 def test_extension_loading():
27 27 em = get_ipython().extension_manager
28 28 with TemporaryDirectory() as td:
29 ext1 = os.path.join(td, 'ext1.py')
30 with open(ext1, 'w', encoding='utf-8') as f:
29 ext1 = os.path.join(td, "ext1.py")
30 with open(ext1, "w", encoding="utf-8") as f:
31 31 f.write(ext1_content)
32
33 ext2 = os.path.join(td, 'ext2.py')
34 with open(ext2, 'w', encoding='utf-8') as f:
32
33 ext2 = os.path.join(td, "ext2.py")
34 with open(ext2, "w", encoding="utf-8") as f:
35 35 f.write(ext2_content)
36 36
37 37 with prepended_to_syspath(td):
38 38 assert 'ext1' not in em.loaded
39 39 assert 'ext2' not in em.loaded
40 40
41 41 # Load extension
42 42 with tt.AssertPrints("Running ext1 load"):
43 43 assert em.load_extension('ext1') is None
44 44 assert 'ext1' in em.loaded
45 45
46 46 # Should refuse to load it again
47 47 with tt.AssertNotPrints("Running ext1 load"):
48 48 assert em.load_extension('ext1') == 'already loaded'
49 49
50 50 # Reload
51 51 with tt.AssertPrints("Running ext1 unload"):
52 52 with tt.AssertPrints("Running ext1 load", suppress=False):
53 53 em.reload_extension('ext1')
54 54
55 55 # Unload
56 56 with tt.AssertPrints("Running ext1 unload"):
57 57 assert em.unload_extension('ext1') is None
58 58
59 59 # Can't unload again
60 60 with tt.AssertNotPrints("Running ext1 unload"):
61 61 assert em.unload_extension('ext1') == 'not loaded'
62 62 assert em.unload_extension('ext2') == 'not loaded'
63 63
64 64 # Load extension 2
65 65 with tt.AssertPrints("Running ext2 load"):
66 66 assert em.load_extension('ext2') is None
67 67
68 68 # Can't unload this
69 69 assert em.unload_extension('ext2') == 'no unload function'
70 70
71 71 # But can reload it
72 72 with tt.AssertPrints("Running ext2 load"):
73 73 em.reload_extension('ext2')
74 74
75 75
76 76 def test_extension_builtins():
77 77 em = get_ipython().extension_manager
78 78 with TemporaryDirectory() as td:
79 ext3 = os.path.join(td, 'ext3.py')
80 with open(ext3, 'w', encoding='utf-8') as f:
79 ext3 = os.path.join(td, "ext3.py")
80 with open(ext3, "w", encoding="utf-8") as f:
81 81 f.write(ext3_content)
82 82
83 83 assert 'ext3' not in em.loaded
84 84
85 85 with prepended_to_syspath(td):
86 86 # Load extension
87 87 with tt.AssertPrints("True"):
88 88 assert em.load_extension('ext3') is None
89 89 assert 'ext3' in em.loaded
90 90
91 91
92 92 def test_non_extension():
93 93 em = get_ipython().extension_manager
94 94 assert em.load_extension("sys") == "no load function"
@@ -1,1098 +1,1100 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the key interactiveshell module.
3 3
4 4 Historically the main classes in interactiveshell have been under-tested. This
5 5 module should grow as many single-method tests as possible to trap many of the
6 6 recurring bugs we seem to encounter with high-level interaction.
7 7 """
8 8
9 9 # Copyright (c) IPython Development Team.
10 10 # Distributed under the terms of the Modified BSD License.
11 11
12 12 import asyncio
13 13 import ast
14 14 import os
15 15 import signal
16 16 import shutil
17 17 import sys
18 18 import tempfile
19 19 import unittest
20 20 from unittest import mock
21 21
22 22 from os.path import join
23 23
24 24 from IPython.core.error import InputRejected
25 25 from IPython.core.inputtransformer import InputTransformer
26 26 from IPython.core import interactiveshell
27 27 from IPython.testing.decorators import (
28 28 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
29 29 )
30 30 from IPython.testing import tools as tt
31 31 from IPython.utils.process import find_cmd
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Globals
35 35 #-----------------------------------------------------------------------------
36 36 # This is used by every single test, no point repeating it ad nauseam
37 37
38 38 #-----------------------------------------------------------------------------
39 39 # Tests
40 40 #-----------------------------------------------------------------------------
41 41
42 42 class DerivedInterrupt(KeyboardInterrupt):
43 43 pass
44 44
45 45 class InteractiveShellTestCase(unittest.TestCase):
46 46 def test_naked_string_cells(self):
47 47 """Test that cells with only naked strings are fully executed"""
48 48 # First, single-line inputs
49 49 ip.run_cell('"a"\n')
50 50 self.assertEqual(ip.user_ns['_'], 'a')
51 51 # And also multi-line cells
52 52 ip.run_cell('"""a\nb"""\n')
53 53 self.assertEqual(ip.user_ns['_'], 'a\nb')
54 54
55 55 def test_run_empty_cell(self):
56 56 """Just make sure we don't get a horrible error with a blank
57 57 cell of input. Yes, I did overlook that."""
58 58 old_xc = ip.execution_count
59 59 res = ip.run_cell('')
60 60 self.assertEqual(ip.execution_count, old_xc)
61 61 self.assertEqual(res.execution_count, None)
62 62
63 63 def test_run_cell_multiline(self):
64 64 """Multi-block, multi-line cells must execute correctly.
65 65 """
66 66 src = '\n'.join(["x=1",
67 67 "y=2",
68 68 "if 1:",
69 69 " x += 1",
70 70 " y += 1",])
71 71 res = ip.run_cell(src)
72 72 self.assertEqual(ip.user_ns['x'], 2)
73 73 self.assertEqual(ip.user_ns['y'], 3)
74 74 self.assertEqual(res.success, True)
75 75 self.assertEqual(res.result, None)
76 76
77 77 def test_multiline_string_cells(self):
78 78 "Code sprinkled with multiline strings should execute (GH-306)"
79 79 ip.run_cell('tmp=0')
80 80 self.assertEqual(ip.user_ns['tmp'], 0)
81 81 res = ip.run_cell('tmp=1;"""a\nb"""\n')
82 82 self.assertEqual(ip.user_ns['tmp'], 1)
83 83 self.assertEqual(res.success, True)
84 84 self.assertEqual(res.result, "a\nb")
85 85
86 86 def test_dont_cache_with_semicolon(self):
87 87 "Ending a line with semicolon should not cache the returned object (GH-307)"
88 88 oldlen = len(ip.user_ns['Out'])
89 89 for cell in ['1;', '1;1;']:
90 90 res = ip.run_cell(cell, store_history=True)
91 91 newlen = len(ip.user_ns['Out'])
92 92 self.assertEqual(oldlen, newlen)
93 93 self.assertIsNone(res.result)
94 94 i = 0
95 95 #also test the default caching behavior
96 96 for cell in ['1', '1;1']:
97 97 ip.run_cell(cell, store_history=True)
98 98 newlen = len(ip.user_ns['Out'])
99 99 i += 1
100 100 self.assertEqual(oldlen+i, newlen)
101 101
102 102 def test_syntax_error(self):
103 103 res = ip.run_cell("raise = 3")
104 104 self.assertIsInstance(res.error_before_exec, SyntaxError)
105 105
106 106 def test_In_variable(self):
107 107 "Verify that In variable grows with user input (GH-284)"
108 108 oldlen = len(ip.user_ns['In'])
109 109 ip.run_cell('1;', store_history=True)
110 110 newlen = len(ip.user_ns['In'])
111 111 self.assertEqual(oldlen+1, newlen)
112 112 self.assertEqual(ip.user_ns['In'][-1],'1;')
113 113
114 114 def test_magic_names_in_string(self):
115 115 ip.run_cell('a = """\n%exit\n"""')
116 116 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
117 117
118 118 def test_trailing_newline(self):
119 119 """test that running !(command) does not raise a SyntaxError"""
120 120 ip.run_cell('!(true)\n', False)
121 121 ip.run_cell('!(true)\n\n\n', False)
122 122
123 123 def test_gh_597(self):
124 124 """Pretty-printing lists of objects with non-ascii reprs may cause
125 125 problems."""
126 126 class Spam(object):
127 127 def __repr__(self):
128 128 return "\xe9"*50
129 129 import IPython.core.formatters
130 130 f = IPython.core.formatters.PlainTextFormatter()
131 131 f([Spam(),Spam()])
132 132
133 133
134 134 def test_future_flags(self):
135 135 """Check that future flags are used for parsing code (gh-777)"""
136 136 ip.run_cell('from __future__ import barry_as_FLUFL')
137 137 try:
138 138 ip.run_cell('prfunc_return_val = 1 <> 2')
139 139 assert 'prfunc_return_val' in ip.user_ns
140 140 finally:
141 141 # Reset compiler flags so we don't mess up other tests.
142 142 ip.compile.reset_compiler_flags()
143 143
144 144 def test_can_pickle(self):
145 145 "Can we pickle objects defined interactively (GH-29)"
146 146 ip = get_ipython()
147 147 ip.reset()
148 148 ip.run_cell(("class Mylist(list):\n"
149 149 " def __init__(self,x=[]):\n"
150 150 " list.__init__(self,x)"))
151 151 ip.run_cell("w=Mylist([1,2,3])")
152 152
153 153 from pickle import dumps
154 154
155 155 # We need to swap in our main module - this is only necessary
156 156 # inside the test framework, because IPython puts the interactive module
157 157 # in place (but the test framework undoes this).
158 158 _main = sys.modules['__main__']
159 159 sys.modules['__main__'] = ip.user_module
160 160 try:
161 161 res = dumps(ip.user_ns["w"])
162 162 finally:
163 163 sys.modules['__main__'] = _main
164 164 self.assertTrue(isinstance(res, bytes))
165 165
166 166 def test_global_ns(self):
167 167 "Code in functions must be able to access variables outside them."
168 168 ip = get_ipython()
169 169 ip.run_cell("a = 10")
170 170 ip.run_cell(("def f(x):\n"
171 171 " return x + a"))
172 172 ip.run_cell("b = f(12)")
173 173 self.assertEqual(ip.user_ns["b"], 22)
174 174
175 175 def test_bad_custom_tb(self):
176 176 """Check that InteractiveShell is protected from bad custom exception handlers"""
177 177 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
178 178 self.assertEqual(ip.custom_exceptions, (IOError,))
179 179 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
180 180 ip.run_cell(u'raise IOError("foo")')
181 181 self.assertEqual(ip.custom_exceptions, ())
182 182
183 183 def test_bad_custom_tb_return(self):
184 184 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
185 185 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
186 186 self.assertEqual(ip.custom_exceptions, (NameError,))
187 187 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
188 188 ip.run_cell(u'a=abracadabra')
189 189 self.assertEqual(ip.custom_exceptions, ())
190 190
191 191 def test_drop_by_id(self):
192 192 myvars = {"a":object(), "b":object(), "c": object()}
193 193 ip.push(myvars, interactive=False)
194 194 for name in myvars:
195 195 assert name in ip.user_ns, name
196 196 assert name in ip.user_ns_hidden, name
197 197 ip.user_ns['b'] = 12
198 198 ip.drop_by_id(myvars)
199 199 for name in ["a", "c"]:
200 200 assert name not in ip.user_ns, name
201 201 assert name not in ip.user_ns_hidden, name
202 202 assert ip.user_ns['b'] == 12
203 203 ip.reset()
204 204
205 205 def test_var_expand(self):
206 206 ip.user_ns['f'] = u'Ca\xf1o'
207 207 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
208 208 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
209 209 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
210 210 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
211 211
212 212 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
213 213
214 214 ip.user_ns['f'] = b'Ca\xc3\xb1o'
215 215 # This should not raise any exception:
216 216 ip.var_expand(u'echo $f')
217 217
218 218 def test_var_expand_local(self):
219 219 """Test local variable expansion in !system and %magic calls"""
220 220 # !system
221 221 ip.run_cell(
222 222 "def test():\n"
223 223 ' lvar = "ttt"\n'
224 224 " ret = !echo {lvar}\n"
225 225 " return ret[0]\n"
226 226 )
227 227 res = ip.user_ns["test"]()
228 228 self.assertIn("ttt", res)
229 229
230 230 # %magic
231 231 ip.run_cell(
232 232 "def makemacro():\n"
233 233 ' macroname = "macro_var_expand_locals"\n'
234 234 " %macro {macroname} codestr\n"
235 235 )
236 236 ip.user_ns["codestr"] = "str(12)"
237 237 ip.run_cell("makemacro()")
238 238 self.assertIn("macro_var_expand_locals", ip.user_ns)
239 239
240 240 def test_var_expand_self(self):
241 241 """Test variable expansion with the name 'self', which was failing.
242 242
243 243 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
244 244 """
245 245 ip.run_cell(
246 246 "class cTest:\n"
247 247 ' classvar="see me"\n'
248 248 " def test(self):\n"
249 249 " res = !echo Variable: {self.classvar}\n"
250 250 " return res[0]\n"
251 251 )
252 252 self.assertIn("see me", ip.user_ns["cTest"]().test())
253 253
254 254 def test_bad_var_expand(self):
255 255 """var_expand on invalid formats shouldn't raise"""
256 256 # SyntaxError
257 257 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
258 258 # NameError
259 259 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
260 260 # ZeroDivisionError
261 261 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
262 262
263 263 def test_silent_postexec(self):
264 264 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
265 265 pre_explicit = mock.Mock()
266 266 pre_always = mock.Mock()
267 267 post_explicit = mock.Mock()
268 268 post_always = mock.Mock()
269 269 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
270 270
271 271 ip.events.register('pre_run_cell', pre_explicit)
272 272 ip.events.register('pre_execute', pre_always)
273 273 ip.events.register('post_run_cell', post_explicit)
274 274 ip.events.register('post_execute', post_always)
275 275
276 276 try:
277 277 ip.run_cell("1", silent=True)
278 278 assert pre_always.called
279 279 assert not pre_explicit.called
280 280 assert post_always.called
281 281 assert not post_explicit.called
282 282 # double-check that non-silent exec did what we expected
283 283 # silent to avoid
284 284 ip.run_cell("1")
285 285 assert pre_explicit.called
286 286 assert post_explicit.called
287 287 info, = pre_explicit.call_args[0]
288 288 result, = post_explicit.call_args[0]
289 289 self.assertEqual(info, result.info)
290 290 # check that post hooks are always called
291 291 [m.reset_mock() for m in all_mocks]
292 292 ip.run_cell("syntax error")
293 293 assert pre_always.called
294 294 assert pre_explicit.called
295 295 assert post_always.called
296 296 assert post_explicit.called
297 297 info, = pre_explicit.call_args[0]
298 298 result, = post_explicit.call_args[0]
299 299 self.assertEqual(info, result.info)
300 300 finally:
301 301 # remove post-exec
302 302 ip.events.unregister('pre_run_cell', pre_explicit)
303 303 ip.events.unregister('pre_execute', pre_always)
304 304 ip.events.unregister('post_run_cell', post_explicit)
305 305 ip.events.unregister('post_execute', post_always)
306 306
307 307 def test_silent_noadvance(self):
308 308 """run_cell(silent=True) doesn't advance execution_count"""
309 309 ec = ip.execution_count
310 310 # silent should force store_history=False
311 311 ip.run_cell("1", store_history=True, silent=True)
312 312
313 313 self.assertEqual(ec, ip.execution_count)
314 314 # double-check that non-silent exec did what we expected
315 315 # silent to avoid
316 316 ip.run_cell("1", store_history=True)
317 317 self.assertEqual(ec+1, ip.execution_count)
318 318
319 319 def test_silent_nodisplayhook(self):
320 320 """run_cell(silent=True) doesn't trigger displayhook"""
321 321 d = dict(called=False)
322 322
323 323 trap = ip.display_trap
324 324 save_hook = trap.hook
325 325
326 326 def failing_hook(*args, **kwargs):
327 327 d['called'] = True
328 328
329 329 try:
330 330 trap.hook = failing_hook
331 331 res = ip.run_cell("1", silent=True)
332 332 self.assertFalse(d['called'])
333 333 self.assertIsNone(res.result)
334 334 # double-check that non-silent exec did what we expected
335 335 # silent to avoid
336 336 ip.run_cell("1")
337 337 self.assertTrue(d['called'])
338 338 finally:
339 339 trap.hook = save_hook
340 340
341 341 def test_ofind_line_magic(self):
342 342 from IPython.core.magic import register_line_magic
343 343
344 344 @register_line_magic
345 345 def lmagic(line):
346 346 "A line magic"
347 347
348 348 # Get info on line magic
349 349 lfind = ip._ofind("lmagic")
350 350 info = dict(
351 351 found=True,
352 352 isalias=False,
353 353 ismagic=True,
354 354 namespace="IPython internal",
355 355 obj=lmagic,
356 356 parent=None,
357 357 )
358 358 self.assertEqual(lfind, info)
359 359
360 360 def test_ofind_cell_magic(self):
361 361 from IPython.core.magic import register_cell_magic
362 362
363 363 @register_cell_magic
364 364 def cmagic(line, cell):
365 365 "A cell magic"
366 366
367 367 # Get info on cell magic
368 368 find = ip._ofind("cmagic")
369 369 info = dict(
370 370 found=True,
371 371 isalias=False,
372 372 ismagic=True,
373 373 namespace="IPython internal",
374 374 obj=cmagic,
375 375 parent=None,
376 376 )
377 377 self.assertEqual(find, info)
378 378
379 379 def test_ofind_property_with_error(self):
380 380 class A(object):
381 381 @property
382 382 def foo(self):
383 383 raise NotImplementedError()
384 384 a = A()
385 385
386 386 found = ip._ofind('a.foo', [('locals', locals())])
387 387 info = dict(found=True, isalias=False, ismagic=False,
388 388 namespace='locals', obj=A.foo, parent=a)
389 389 self.assertEqual(found, info)
390 390
391 391 def test_ofind_multiple_attribute_lookups(self):
392 392 class A(object):
393 393 @property
394 394 def foo(self):
395 395 raise NotImplementedError()
396 396
397 397 a = A()
398 398 a.a = A()
399 399 a.a.a = A()
400 400
401 401 found = ip._ofind('a.a.a.foo', [('locals', locals())])
402 402 info = dict(found=True, isalias=False, ismagic=False,
403 403 namespace='locals', obj=A.foo, parent=a.a.a)
404 404 self.assertEqual(found, info)
405 405
406 406 def test_ofind_slotted_attributes(self):
407 407 class A(object):
408 408 __slots__ = ['foo']
409 409 def __init__(self):
410 410 self.foo = 'bar'
411 411
412 412 a = A()
413 413 found = ip._ofind('a.foo', [('locals', locals())])
414 414 info = dict(found=True, isalias=False, ismagic=False,
415 415 namespace='locals', obj=a.foo, parent=a)
416 416 self.assertEqual(found, info)
417 417
418 418 found = ip._ofind('a.bar', [('locals', locals())])
419 419 info = dict(found=False, isalias=False, ismagic=False,
420 420 namespace=None, obj=None, parent=a)
421 421 self.assertEqual(found, info)
422 422
423 423 def test_ofind_prefers_property_to_instance_level_attribute(self):
424 424 class A(object):
425 425 @property
426 426 def foo(self):
427 427 return 'bar'
428 428 a = A()
429 429 a.__dict__["foo"] = "baz"
430 430 self.assertEqual(a.foo, "bar")
431 431 found = ip._ofind("a.foo", [("locals", locals())])
432 432 self.assertIs(found["obj"], A.foo)
433 433
434 434 def test_custom_syntaxerror_exception(self):
435 435 called = []
436 436 def my_handler(shell, etype, value, tb, tb_offset=None):
437 437 called.append(etype)
438 438 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
439 439
440 440 ip.set_custom_exc((SyntaxError,), my_handler)
441 441 try:
442 442 ip.run_cell("1f")
443 443 # Check that this was called, and only once.
444 444 self.assertEqual(called, [SyntaxError])
445 445 finally:
446 446 # Reset the custom exception hook
447 447 ip.set_custom_exc((), None)
448 448
449 449 def test_custom_exception(self):
450 450 called = []
451 451 def my_handler(shell, etype, value, tb, tb_offset=None):
452 452 called.append(etype)
453 453 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
454 454
455 455 ip.set_custom_exc((ValueError,), my_handler)
456 456 try:
457 457 res = ip.run_cell("raise ValueError('test')")
458 458 # Check that this was called, and only once.
459 459 self.assertEqual(called, [ValueError])
460 460 # Check that the error is on the result object
461 461 self.assertIsInstance(res.error_in_exec, ValueError)
462 462 finally:
463 463 # Reset the custom exception hook
464 464 ip.set_custom_exc((), None)
465 465
466 466 @mock.patch("builtins.print")
467 467 def test_showtraceback_with_surrogates(self, mocked_print):
468 468 values = []
469 469
470 470 def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False):
471 471 values.append(value)
472 472 if value == chr(0xD8FF):
473 473 raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "")
474 474
475 475 # mock builtins.print
476 476 mocked_print.side_effect = mock_print_func
477 477
478 478 # ip._showtraceback() is replaced in globalipapp.py.
479 479 # Call original method to test.
480 480 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF))
481 481
482 482 self.assertEqual(mocked_print.call_count, 2)
483 483 self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"])
484 484
485 485 def test_mktempfile(self):
486 486 filename = ip.mktempfile()
487 487 # Check that we can open the file again on Windows
488 with open(filename, 'w', encoding='utf-8') as f:
489 f.write('abc')
488 with open(filename, "w", encoding="utf-8") as f:
489 f.write("abc")
490 490
491 filename = ip.mktempfile(data='blah')
492 with open(filename, 'r', encoding='utf-8') as f:
493 self.assertEqual(f.read(), 'blah')
491 filename = ip.mktempfile(data="blah")
492 with open(filename, "r", encoding="utf-8") as f:
493 self.assertEqual(f.read(), "blah")
494 494
495 495 def test_new_main_mod(self):
496 496 # Smoketest to check that this accepts a unicode module name
497 497 name = u'jiefmw'
498 498 mod = ip.new_main_mod(u'%s.py' % name, name)
499 499 self.assertEqual(mod.__name__, name)
500 500
501 501 def test_get_exception_only(self):
502 502 try:
503 503 raise KeyboardInterrupt
504 504 except KeyboardInterrupt:
505 505 msg = ip.get_exception_only()
506 506 self.assertEqual(msg, 'KeyboardInterrupt\n')
507 507
508 508 try:
509 509 raise DerivedInterrupt("foo")
510 510 except KeyboardInterrupt:
511 511 msg = ip.get_exception_only()
512 512 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
513 513
514 514 def test_inspect_text(self):
515 515 ip.run_cell('a = 5')
516 516 text = ip.object_inspect_text('a')
517 517 self.assertIsInstance(text, str)
518 518
519 519 def test_last_execution_result(self):
520 520 """ Check that last execution result gets set correctly (GH-10702) """
521 521 result = ip.run_cell('a = 5; a')
522 522 self.assertTrue(ip.last_execution_succeeded)
523 523 self.assertEqual(ip.last_execution_result.result, 5)
524 524
525 525 result = ip.run_cell('a = x_invalid_id_x')
526 526 self.assertFalse(ip.last_execution_succeeded)
527 527 self.assertFalse(ip.last_execution_result.success)
528 528 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
529 529
530 530 def test_reset_aliasing(self):
531 531 """ Check that standard posix aliases work after %reset. """
532 532 if os.name != 'posix':
533 533 return
534 534
535 535 ip.reset()
536 536 for cmd in ('clear', 'more', 'less', 'man'):
537 537 res = ip.run_cell('%' + cmd)
538 538 self.assertEqual(res.success, True)
539 539
540 540
541 541 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
542 542
543 543 @onlyif_unicode_paths
544 544 def setUp(self):
545 545 self.BASETESTDIR = tempfile.mkdtemp()
546 546 self.TESTDIR = join(self.BASETESTDIR, u"åäö")
547 547 os.mkdir(self.TESTDIR)
548 with open(join(self.TESTDIR, u"åäötestscript.py"), "w", encoding='utf-8') as sfile:
548 with open(
549 join(self.TESTDIR, u"åäötestscript.py"), "w", encoding="utf-8"
550 ) as sfile:
549 551 sfile.write("pass\n")
550 552 self.oldpath = os.getcwd()
551 553 os.chdir(self.TESTDIR)
552 554 self.fname = u"åäötestscript.py"
553 555
554 556 def tearDown(self):
555 557 os.chdir(self.oldpath)
556 558 shutil.rmtree(self.BASETESTDIR)
557 559
558 560 @onlyif_unicode_paths
559 561 def test_1(self):
560 562 """Test safe_execfile with non-ascii path
561 563 """
562 564 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
563 565
564 566 class ExitCodeChecks(tt.TempFileMixin):
565 567
566 568 def setUp(self):
567 569 self.system = ip.system_raw
568 570
569 571 def test_exit_code_ok(self):
570 572 self.system('exit 0')
571 573 self.assertEqual(ip.user_ns['_exit_code'], 0)
572 574
573 575 def test_exit_code_error(self):
574 576 self.system('exit 1')
575 577 self.assertEqual(ip.user_ns['_exit_code'], 1)
576 578
577 579 @skipif(not hasattr(signal, 'SIGALRM'))
578 580 def test_exit_code_signal(self):
579 581 self.mktmp("import signal, time\n"
580 582 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
581 583 "time.sleep(1)\n")
582 584 self.system("%s %s" % (sys.executable, self.fname))
583 585 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
584 586
585 587 @onlyif_cmds_exist("csh")
586 588 def test_exit_code_signal_csh(self):
587 589 SHELL = os.environ.get('SHELL', None)
588 590 os.environ['SHELL'] = find_cmd("csh")
589 591 try:
590 592 self.test_exit_code_signal()
591 593 finally:
592 594 if SHELL is not None:
593 595 os.environ['SHELL'] = SHELL
594 596 else:
595 597 del os.environ['SHELL']
596 598
597 599
598 600 class TestSystemRaw(ExitCodeChecks):
599 601
600 602 def setUp(self):
601 603 super().setUp()
602 604 self.system = ip.system_raw
603 605
604 606 @onlyif_unicode_paths
605 607 def test_1(self):
606 608 """Test system_raw with non-ascii cmd
607 609 """
608 610 cmd = u'''python -c "'åäö'" '''
609 611 ip.system_raw(cmd)
610 612
611 613 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
612 614 @mock.patch('os.system', side_effect=KeyboardInterrupt)
613 615 def test_control_c(self, *mocks):
614 616 try:
615 617 self.system("sleep 1 # wont happen")
616 618 except KeyboardInterrupt:
617 619 self.fail(
618 620 "system call should intercept "
619 621 "keyboard interrupt from subprocess.call"
620 622 )
621 623 self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT)
622 624
623 625 def test_magic_warnings(self):
624 626 for magic_cmd in ("ls", "pip", "conda", "cd"):
625 627 with self.assertWarnsRegex(Warning, "You executed the system command"):
626 628 ip.system_raw(magic_cmd)
627 629
628 630 # TODO: Exit codes are currently ignored on Windows.
629 631 class TestSystemPipedExitCode(ExitCodeChecks):
630 632
631 633 def setUp(self):
632 634 super().setUp()
633 635 self.system = ip.system_piped
634 636
635 637 @skip_win32
636 638 def test_exit_code_ok(self):
637 639 ExitCodeChecks.test_exit_code_ok(self)
638 640
639 641 @skip_win32
640 642 def test_exit_code_error(self):
641 643 ExitCodeChecks.test_exit_code_error(self)
642 644
643 645 @skip_win32
644 646 def test_exit_code_signal(self):
645 647 ExitCodeChecks.test_exit_code_signal(self)
646 648
647 649 class TestModules(tt.TempFileMixin):
648 650 def test_extraneous_loads(self):
649 651 """Test we're not loading modules on startup that we shouldn't.
650 652 """
651 653 self.mktmp("import sys\n"
652 654 "print('numpy' in sys.modules)\n"
653 655 "print('ipyparallel' in sys.modules)\n"
654 656 "print('ipykernel' in sys.modules)\n"
655 657 )
656 658 out = "False\nFalse\nFalse\n"
657 659 tt.ipexec_validate(self.fname, out)
658 660
659 661 class Negator(ast.NodeTransformer):
660 662 """Negates all number literals in an AST."""
661 663
662 664 # for python 3.7 and earlier
663 665 def visit_Num(self, node):
664 666 node.n = -node.n
665 667 return node
666 668
667 669 # for python 3.8+
668 670 def visit_Constant(self, node):
669 671 if isinstance(node.value, int):
670 672 return self.visit_Num(node)
671 673 return node
672 674
673 675 class TestAstTransform(unittest.TestCase):
674 676 def setUp(self):
675 677 self.negator = Negator()
676 678 ip.ast_transformers.append(self.negator)
677 679
678 680 def tearDown(self):
679 681 ip.ast_transformers.remove(self.negator)
680 682
681 683 def test_run_cell(self):
682 684 with tt.AssertPrints('-34'):
683 685 ip.run_cell('print (12 + 22)')
684 686
685 687 # A named reference to a number shouldn't be transformed.
686 688 ip.user_ns['n'] = 55
687 689 with tt.AssertNotPrints('-55'):
688 690 ip.run_cell('print (n)')
689 691
690 692 def test_timeit(self):
691 693 called = set()
692 694 def f(x):
693 695 called.add(x)
694 696 ip.push({'f':f})
695 697
696 698 with tt.AssertPrints("std. dev. of"):
697 699 ip.run_line_magic("timeit", "-n1 f(1)")
698 700 self.assertEqual(called, {-1})
699 701 called.clear()
700 702
701 703 with tt.AssertPrints("std. dev. of"):
702 704 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
703 705 self.assertEqual(called, {-2, -3})
704 706
705 707 def test_time(self):
706 708 called = []
707 709 def f(x):
708 710 called.append(x)
709 711 ip.push({'f':f})
710 712
711 713 # Test with an expression
712 714 with tt.AssertPrints("Wall time: "):
713 715 ip.run_line_magic("time", "f(5+9)")
714 716 self.assertEqual(called, [-14])
715 717 called[:] = []
716 718
717 719 # Test with a statement (different code path)
718 720 with tt.AssertPrints("Wall time: "):
719 721 ip.run_line_magic("time", "a = f(-3 + -2)")
720 722 self.assertEqual(called, [5])
721 723
722 724 def test_macro(self):
723 725 ip.push({'a':10})
724 726 # The AST transformation makes this do a+=-1
725 727 ip.define_macro("amacro", "a+=1\nprint(a)")
726 728
727 729 with tt.AssertPrints("9"):
728 730 ip.run_cell("amacro")
729 731 with tt.AssertPrints("8"):
730 732 ip.run_cell("amacro")
731 733
732 734 class TestMiscTransform(unittest.TestCase):
733 735
734 736
735 737 def test_transform_only_once(self):
736 738 cleanup = 0
737 739 line_t = 0
738 740 def count_cleanup(lines):
739 741 nonlocal cleanup
740 742 cleanup += 1
741 743 return lines
742 744
743 745 def count_line_t(lines):
744 746 nonlocal line_t
745 747 line_t += 1
746 748 return lines
747 749
748 750 ip.input_transformer_manager.cleanup_transforms.append(count_cleanup)
749 751 ip.input_transformer_manager.line_transforms.append(count_line_t)
750 752
751 753 ip.run_cell('1')
752 754
753 755 assert cleanup == 1
754 756 assert line_t == 1
755 757
756 758 class IntegerWrapper(ast.NodeTransformer):
757 759 """Wraps all integers in a call to Integer()"""
758 760
759 761 # for Python 3.7 and earlier
760 762
761 763 # for Python 3.7 and earlier
762 764 def visit_Num(self, node):
763 765 if isinstance(node.n, int):
764 766 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
765 767 args=[node], keywords=[])
766 768 return node
767 769
768 770 # For Python 3.8+
769 771 def visit_Constant(self, node):
770 772 if isinstance(node.value, int):
771 773 return self.visit_Num(node)
772 774 return node
773 775
774 776
775 777 class TestAstTransform2(unittest.TestCase):
776 778 def setUp(self):
777 779 self.intwrapper = IntegerWrapper()
778 780 ip.ast_transformers.append(self.intwrapper)
779 781
780 782 self.calls = []
781 783 def Integer(*args):
782 784 self.calls.append(args)
783 785 return args
784 786 ip.push({"Integer": Integer})
785 787
786 788 def tearDown(self):
787 789 ip.ast_transformers.remove(self.intwrapper)
788 790 del ip.user_ns['Integer']
789 791
790 792 def test_run_cell(self):
791 793 ip.run_cell("n = 2")
792 794 self.assertEqual(self.calls, [(2,)])
793 795
794 796 # This shouldn't throw an error
795 797 ip.run_cell("o = 2.0")
796 798 self.assertEqual(ip.user_ns['o'], 2.0)
797 799
798 800 def test_timeit(self):
799 801 called = set()
800 802 def f(x):
801 803 called.add(x)
802 804 ip.push({'f':f})
803 805
804 806 with tt.AssertPrints("std. dev. of"):
805 807 ip.run_line_magic("timeit", "-n1 f(1)")
806 808 self.assertEqual(called, {(1,)})
807 809 called.clear()
808 810
809 811 with tt.AssertPrints("std. dev. of"):
810 812 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
811 813 self.assertEqual(called, {(2,), (3,)})
812 814
813 815 class ErrorTransformer(ast.NodeTransformer):
814 816 """Throws an error when it sees a number."""
815 817
816 818 # for Python 3.7 and earlier
817 819 def visit_Num(self, node):
818 820 raise ValueError("test")
819 821
820 822 # for Python 3.8+
821 823 def visit_Constant(self, node):
822 824 if isinstance(node.value, int):
823 825 return self.visit_Num(node)
824 826 return node
825 827
826 828
827 829 class TestAstTransformError(unittest.TestCase):
828 830 def test_unregistering(self):
829 831 err_transformer = ErrorTransformer()
830 832 ip.ast_transformers.append(err_transformer)
831 833
832 834 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
833 835 ip.run_cell("1 + 2")
834 836
835 837 # This should have been removed.
836 838 self.assertNotIn(err_transformer, ip.ast_transformers)
837 839
838 840
839 841 class StringRejector(ast.NodeTransformer):
840 842 """Throws an InputRejected when it sees a string literal.
841 843
842 844 Used to verify that NodeTransformers can signal that a piece of code should
843 845 not be executed by throwing an InputRejected.
844 846 """
845 847
846 848 #for python 3.7 and earlier
847 849 def visit_Str(self, node):
848 850 raise InputRejected("test")
849 851
850 852 # 3.8 only
851 853 def visit_Constant(self, node):
852 854 if isinstance(node.value, str):
853 855 raise InputRejected("test")
854 856 return node
855 857
856 858
857 859 class TestAstTransformInputRejection(unittest.TestCase):
858 860
859 861 def setUp(self):
860 862 self.transformer = StringRejector()
861 863 ip.ast_transformers.append(self.transformer)
862 864
863 865 def tearDown(self):
864 866 ip.ast_transformers.remove(self.transformer)
865 867
866 868 def test_input_rejection(self):
867 869 """Check that NodeTransformers can reject input."""
868 870
869 871 expect_exception_tb = tt.AssertPrints("InputRejected: test")
870 872 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
871 873
872 874 # Run the same check twice to verify that the transformer is not
873 875 # disabled after raising.
874 876 with expect_exception_tb, expect_no_cell_output:
875 877 ip.run_cell("'unsafe'")
876 878
877 879 with expect_exception_tb, expect_no_cell_output:
878 880 res = ip.run_cell("'unsafe'")
879 881
880 882 self.assertIsInstance(res.error_before_exec, InputRejected)
881 883
882 884 def test__IPYTHON__():
883 885 # This shouldn't raise a NameError, that's all
884 886 __IPYTHON__
885 887
886 888
887 889 class DummyRepr(object):
888 890 def __repr__(self):
889 891 return "DummyRepr"
890 892
891 893 def _repr_html_(self):
892 894 return "<b>dummy</b>"
893 895
894 896 def _repr_javascript_(self):
895 897 return "console.log('hi');", {'key': 'value'}
896 898
897 899
898 900 def test_user_variables():
899 901 # enable all formatters
900 902 ip.display_formatter.active_types = ip.display_formatter.format_types
901 903
902 904 ip.user_ns['dummy'] = d = DummyRepr()
903 905 keys = {'dummy', 'doesnotexist'}
904 906 r = ip.user_expressions({ key:key for key in keys})
905 907
906 908 assert keys == set(r.keys())
907 909 dummy = r["dummy"]
908 910 assert {"status", "data", "metadata"} == set(dummy.keys())
909 911 assert dummy["status"] == "ok"
910 912 data = dummy["data"]
911 913 metadata = dummy["metadata"]
912 914 assert data.get("text/html") == d._repr_html_()
913 915 js, jsmd = d._repr_javascript_()
914 916 assert data.get("application/javascript") == js
915 917 assert metadata.get("application/javascript") == jsmd
916 918
917 919 dne = r["doesnotexist"]
918 920 assert dne["status"] == "error"
919 921 assert dne["ename"] == "NameError"
920 922
921 923 # back to text only
922 924 ip.display_formatter.active_types = ['text/plain']
923 925
924 926 def test_user_expression():
925 927 # enable all formatters
926 928 ip.display_formatter.active_types = ip.display_formatter.format_types
927 929 query = {
928 930 'a' : '1 + 2',
929 931 'b' : '1/0',
930 932 }
931 933 r = ip.user_expressions(query)
932 934 import pprint
933 935 pprint.pprint(r)
934 936 assert set(r.keys()) == set(query.keys())
935 937 a = r["a"]
936 938 assert {"status", "data", "metadata"} == set(a.keys())
937 939 assert a["status"] == "ok"
938 940 data = a["data"]
939 941 metadata = a["metadata"]
940 942 assert data.get("text/plain") == "3"
941 943
942 944 b = r["b"]
943 945 assert b["status"] == "error"
944 946 assert b["ename"] == "ZeroDivisionError"
945 947
946 948 # back to text only
947 949 ip.display_formatter.active_types = ['text/plain']
948 950
949 951
950 952 class TestSyntaxErrorTransformer(unittest.TestCase):
951 953 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
952 954
953 955 @staticmethod
954 956 def transformer(lines):
955 957 for line in lines:
956 958 pos = line.find('syntaxerror')
957 959 if pos >= 0:
958 960 e = SyntaxError('input contains "syntaxerror"')
959 961 e.text = line
960 962 e.offset = pos + 1
961 963 raise e
962 964 return lines
963 965
964 966 def setUp(self):
965 967 ip.input_transformers_post.append(self.transformer)
966 968
967 969 def tearDown(self):
968 970 ip.input_transformers_post.remove(self.transformer)
969 971
970 972 def test_syntaxerror_input_transformer(self):
971 973 with tt.AssertPrints('1234'):
972 974 ip.run_cell('1234')
973 975 with tt.AssertPrints('SyntaxError: invalid syntax'):
974 976 ip.run_cell('1 2 3') # plain python syntax error
975 977 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
976 978 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
977 979 with tt.AssertPrints('3456'):
978 980 ip.run_cell('3456')
979 981
980 982
981 983 class TestWarningSuppression(unittest.TestCase):
982 984 def test_warning_suppression(self):
983 985 ip.run_cell("import warnings")
984 986 try:
985 987 with self.assertWarnsRegex(UserWarning, "asdf"):
986 988 ip.run_cell("warnings.warn('asdf')")
987 989 # Here's the real test -- if we run that again, we should get the
988 990 # warning again. Traditionally, each warning was only issued once per
989 991 # IPython session (approximately), even if the user typed in new and
990 992 # different code that should have also triggered the warning, leading
991 993 # to much confusion.
992 994 with self.assertWarnsRegex(UserWarning, "asdf"):
993 995 ip.run_cell("warnings.warn('asdf')")
994 996 finally:
995 997 ip.run_cell("del warnings")
996 998
997 999
998 1000 def test_deprecation_warning(self):
999 1001 ip.run_cell("""
1000 1002 import warnings
1001 1003 def wrn():
1002 1004 warnings.warn(
1003 1005 "I AM A WARNING",
1004 1006 DeprecationWarning
1005 1007 )
1006 1008 """)
1007 1009 try:
1008 1010 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
1009 1011 ip.run_cell("wrn()")
1010 1012 finally:
1011 1013 ip.run_cell("del warnings")
1012 1014 ip.run_cell("del wrn")
1013 1015
1014 1016
1015 1017 class TestImportNoDeprecate(tt.TempFileMixin):
1016 1018
1017 1019 def setUp(self):
1018 1020 """Make a valid python temp file."""
1019 1021 self.mktmp("""
1020 1022 import warnings
1021 1023 def wrn():
1022 1024 warnings.warn(
1023 1025 "I AM A WARNING",
1024 1026 DeprecationWarning
1025 1027 )
1026 1028 """)
1027 1029 super().setUp()
1028 1030
1029 1031 def test_no_dep(self):
1030 1032 """
1031 1033 No deprecation warning should be raised from imported functions
1032 1034 """
1033 1035 ip.run_cell("from {} import wrn".format(self.fname))
1034 1036
1035 1037 with tt.AssertNotPrints("I AM A WARNING"):
1036 1038 ip.run_cell("wrn()")
1037 1039 ip.run_cell("del wrn")
1038 1040
1039 1041
1040 1042 def test_custom_exc_count():
1041 1043 hook = mock.Mock(return_value=None)
1042 1044 ip.set_custom_exc((SyntaxError,), hook)
1043 1045 before = ip.execution_count
1044 1046 ip.run_cell("def foo()", store_history=True)
1045 1047 # restore default excepthook
1046 1048 ip.set_custom_exc((), None)
1047 1049 assert hook.call_count == 1
1048 1050 assert ip.execution_count == before + 1
1049 1051
1050 1052
1051 1053 def test_run_cell_async():
1052 1054 ip.run_cell("import asyncio")
1053 1055 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
1054 1056 assert asyncio.iscoroutine(coro)
1055 1057 loop = asyncio.new_event_loop()
1056 1058 result = loop.run_until_complete(coro)
1057 1059 assert isinstance(result, interactiveshell.ExecutionResult)
1058 1060 assert result.result == 5
1059 1061
1060 1062
1061 1063 def test_run_cell_await():
1062 1064 ip.run_cell("import asyncio")
1063 1065 result = ip.run_cell("await asyncio.sleep(0.01); 10")
1064 1066 assert ip.user_ns["_"] == 10
1065 1067
1066 1068
1067 1069 def test_run_cell_asyncio_run():
1068 1070 ip.run_cell("import asyncio")
1069 1071 result = ip.run_cell("await asyncio.sleep(0.01); 1")
1070 1072 assert ip.user_ns["_"] == 1
1071 1073 result = ip.run_cell("asyncio.run(asyncio.sleep(0.01)); 2")
1072 1074 assert ip.user_ns["_"] == 2
1073 1075 result = ip.run_cell("await asyncio.sleep(0.01); 3")
1074 1076 assert ip.user_ns["_"] == 3
1075 1077
1076 1078
1077 1079 def test_should_run_async():
1078 1080 assert not ip.should_run_async("a = 5")
1079 1081 assert ip.should_run_async("await x")
1080 1082 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
1081 1083
1082 1084
1083 1085 def test_set_custom_completer():
1084 1086 num_completers = len(ip.Completer.matchers)
1085 1087
1086 1088 def foo(*args, **kwargs):
1087 1089 return "I'm a completer!"
1088 1090
1089 1091 ip.set_custom_completer(foo, 0)
1090 1092
1091 1093 # check that we've really added a new completer
1092 1094 assert len(ip.Completer.matchers) == num_completers + 1
1093 1095
1094 1096 # check that the first completer is the function we defined
1095 1097 assert ip.Completer.matchers[0]() == "I'm a completer!"
1096 1098
1097 1099 # clean up
1098 1100 ip.Completer.custom_matchers.pop()
@@ -1,1366 +1,1408 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for various magic functions."""
3 3
4 4 import asyncio
5 5 import gc
6 6 import io
7 7 import os
8 8 import re
9 9 import shlex
10 10 import sys
11 11 import warnings
12 12 from importlib import invalidate_caches
13 13 from io import StringIO
14 14 from pathlib import Path
15 15 from textwrap import dedent
16 16 from unittest import TestCase, mock
17 17
18 18 import pytest
19 19
20 20 from IPython import get_ipython
21 21 from IPython.core import magic
22 22 from IPython.core.error import UsageError
23 23 from IPython.core.magic import (
24 24 Magics,
25 25 cell_magic,
26 26 line_magic,
27 27 magics_class,
28 28 register_cell_magic,
29 29 register_line_magic,
30 30 )
31 31 from IPython.core.magics import code, execution, logging, osm, script
32 32 from IPython.testing import decorators as dec
33 33 from IPython.testing import tools as tt
34 34 from IPython.utils.io import capture_output
35 35 from IPython.utils.process import find_cmd
36 36 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
37 37
38 38 from .test_debugger import PdbTestInput
39 39
40 40
41 41 @magic.magics_class
42 42 class DummyMagics(magic.Magics): pass
43 43
44 44 def test_extract_code_ranges():
45 45 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
46 46 expected = [
47 47 (0, 1),
48 48 (2, 3),
49 49 (4, 6),
50 50 (6, 9),
51 51 (9, 14),
52 52 (16, None),
53 53 (None, 9),
54 54 (9, None),
55 55 (None, 13),
56 56 (None, None),
57 57 ]
58 58 actual = list(code.extract_code_ranges(instr))
59 59 assert actual == expected
60 60
61 61 def test_extract_symbols():
62 62 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
63 63 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
64 64 expected = [([], ['a']),
65 65 (["def b():\n return 42\n"], []),
66 66 (["class A: pass\n"], []),
67 67 (["class A: pass\n", "def b():\n return 42\n"], []),
68 68 (["class A: pass\n"], ['a']),
69 69 ([], ['z'])]
70 70 for symbols, exp in zip(symbols_args, expected):
71 71 assert code.extract_symbols(source, symbols) == exp
72 72
73 73
74 74 def test_extract_symbols_raises_exception_with_non_python_code():
75 75 source = ("=begin A Ruby program :)=end\n"
76 76 "def hello\n"
77 77 "puts 'Hello world'\n"
78 78 "end")
79 79 with pytest.raises(SyntaxError):
80 80 code.extract_symbols(source, "hello")
81 81
82 82
83 83 def test_magic_not_found():
84 84 # magic not found raises UsageError
85 85 with pytest.raises(UsageError):
86 86 _ip.magic('doesntexist')
87 87
88 88 # ensure result isn't success when a magic isn't found
89 89 result = _ip.run_cell('%doesntexist')
90 90 assert isinstance(result.error_in_exec, UsageError)
91 91
92 92
93 93 def test_cell_magic_not_found():
94 94 # magic not found raises UsageError
95 95 with pytest.raises(UsageError):
96 96 _ip.run_cell_magic('doesntexist', 'line', 'cell')
97 97
98 98 # ensure result isn't success when a magic isn't found
99 99 result = _ip.run_cell('%%doesntexist')
100 100 assert isinstance(result.error_in_exec, UsageError)
101 101
102 102
103 103 def test_magic_error_status():
104 104 def fail(shell):
105 105 1/0
106 106 _ip.register_magic_function(fail)
107 107 result = _ip.run_cell('%fail')
108 108 assert isinstance(result.error_in_exec, ZeroDivisionError)
109 109
110 110
111 111 def test_config():
112 112 """ test that config magic does not raise
113 113 can happen if Configurable init is moved too early into
114 114 Magics.__init__ as then a Config object will be registered as a
115 115 magic.
116 116 """
117 117 ## should not raise.
118 118 _ip.magic('config')
119 119
120 120 def test_config_available_configs():
121 121 """ test that config magic prints available configs in unique and
122 122 sorted order. """
123 123 with capture_output() as captured:
124 124 _ip.magic('config')
125 125
126 126 stdout = captured.stdout
127 127 config_classes = stdout.strip().split('\n')[1:]
128 128 assert config_classes == sorted(set(config_classes))
129 129
130 130 def test_config_print_class():
131 131 """ test that config with a classname prints the class's options. """
132 132 with capture_output() as captured:
133 133 _ip.magic('config TerminalInteractiveShell')
134 134
135 135 stdout = captured.stdout
136 136 assert re.match(
137 137 "TerminalInteractiveShell.* options", stdout.splitlines()[0]
138 138 ), f"{stdout}\n\n1st line of stdout not like 'TerminalInteractiveShell.* options'"
139 139
140 140
141 141 def test_rehashx():
142 142 # clear up everything
143 143 _ip.alias_manager.clear_aliases()
144 144 del _ip.db['syscmdlist']
145 145
146 146 _ip.magic('rehashx')
147 147 # Practically ALL ipython development systems will have more than 10 aliases
148 148
149 149 assert len(_ip.alias_manager.aliases) > 10
150 150 for name, cmd in _ip.alias_manager.aliases:
151 151 # we must strip dots from alias names
152 152 assert "." not in name
153 153
154 154 # rehashx must fill up syscmdlist
155 155 scoms = _ip.db['syscmdlist']
156 156 assert len(scoms) > 10
157 157
158 158
159 159 def test_magic_parse_options():
160 160 """Test that we don't mangle paths when parsing magic options."""
161 161 ip = get_ipython()
162 162 path = 'c:\\x'
163 163 m = DummyMagics(ip)
164 164 opts = m.parse_options('-f %s' % path,'f:')[0]
165 165 # argv splitting is os-dependent
166 166 if os.name == 'posix':
167 167 expected = 'c:x'
168 168 else:
169 169 expected = path
170 170 assert opts["f"] == expected
171 171
172 172
173 173 def test_magic_parse_long_options():
174 174 """Magic.parse_options can handle --foo=bar long options"""
175 175 ip = get_ipython()
176 176 m = DummyMagics(ip)
177 177 opts, _ = m.parse_options("--foo --bar=bubble", "a", "foo", "bar=")
178 178 assert "foo" in opts
179 179 assert "bar" in opts
180 180 assert opts["bar"] == "bubble"
181 181
182 182
183 183 def doctest_hist_f():
184 184 """Test %hist -f with temporary filename.
185 185
186 186 In [9]: import tempfile
187 187
188 188 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
189 189
190 190 In [11]: %hist -nl -f $tfile 3
191 191
192 192 In [13]: import os; os.unlink(tfile)
193 193 """
194 194
195 195
196 196 def doctest_hist_op():
197 197 """Test %hist -op
198 198
199 199 In [1]: class b(float):
200 200 ...: pass
201 201 ...:
202 202
203 203 In [2]: class s(object):
204 204 ...: def __str__(self):
205 205 ...: return 's'
206 206 ...:
207 207
208 208 In [3]:
209 209
210 210 In [4]: class r(b):
211 211 ...: def __repr__(self):
212 212 ...: return 'r'
213 213 ...:
214 214
215 215 In [5]: class sr(s,r): pass
216 216 ...:
217 217
218 218 In [6]:
219 219
220 220 In [7]: bb=b()
221 221
222 222 In [8]: ss=s()
223 223
224 224 In [9]: rr=r()
225 225
226 226 In [10]: ssrr=sr()
227 227
228 228 In [11]: 4.5
229 229 Out[11]: 4.5
230 230
231 231 In [12]: str(ss)
232 232 Out[12]: 's'
233 233
234 234 In [13]:
235 235
236 236 In [14]: %hist -op
237 237 >>> class b:
238 238 ... pass
239 239 ...
240 240 >>> class s(b):
241 241 ... def __str__(self):
242 242 ... return 's'
243 243 ...
244 244 >>>
245 245 >>> class r(b):
246 246 ... def __repr__(self):
247 247 ... return 'r'
248 248 ...
249 249 >>> class sr(s,r): pass
250 250 >>>
251 251 >>> bb=b()
252 252 >>> ss=s()
253 253 >>> rr=r()
254 254 >>> ssrr=sr()
255 255 >>> 4.5
256 256 4.5
257 257 >>> str(ss)
258 258 's'
259 259 >>>
260 260 """
261 261
262 262 def test_hist_pof():
263 263 ip = get_ipython()
264 264 ip.run_cell("1+2", store_history=True)
265 265 #raise Exception(ip.history_manager.session_number)
266 266 #raise Exception(list(ip.history_manager._get_range_session()))
267 267 with TemporaryDirectory() as td:
268 268 tf = os.path.join(td, 'hist.py')
269 269 ip.run_line_magic('history', '-pof %s' % tf)
270 270 assert os.path.isfile(tf)
271 271
272 272
273 273 def test_macro():
274 274 ip = get_ipython()
275 275 ip.history_manager.reset() # Clear any existing history.
276 276 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
277 277 for i, cmd in enumerate(cmds, start=1):
278 278 ip.history_manager.store_inputs(i, cmd)
279 279 ip.magic("macro test 1-3")
280 280 assert ip.user_ns["test"].value == "\n".join(cmds) + "\n"
281 281
282 282 # List macros
283 283 assert "test" in ip.magic("macro")
284 284
285 285
286 286 def test_macro_run():
287 287 """Test that we can run a multi-line macro successfully."""
288 288 ip = get_ipython()
289 289 ip.history_manager.reset()
290 290 cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"]
291 291 for cmd in cmds:
292 292 ip.run_cell(cmd, store_history=True)
293 293 assert ip.user_ns["test"].value == "a+=1\nprint(a)\n"
294 294 with tt.AssertPrints("12"):
295 295 ip.run_cell("test")
296 296 with tt.AssertPrints("13"):
297 297 ip.run_cell("test")
298 298
299 299
300 300 def test_magic_magic():
301 301 """Test %magic"""
302 302 ip = get_ipython()
303 303 with capture_output() as captured:
304 304 ip.magic("magic")
305 305
306 306 stdout = captured.stdout
307 307 assert "%magic" in stdout
308 308 assert "IPython" in stdout
309 309 assert "Available" in stdout
310 310
311 311
312 312 @dec.skipif_not_numpy
313 313 def test_numpy_reset_array_undec():
314 314 "Test '%reset array' functionality"
315 315 _ip.ex("import numpy as np")
316 316 _ip.ex("a = np.empty(2)")
317 317 assert "a" in _ip.user_ns
318 318 _ip.magic("reset -f array")
319 319 assert "a" not in _ip.user_ns
320 320
321 321
322 322 def test_reset_out():
323 323 "Test '%reset out' magic"
324 324 _ip.run_cell("parrot = 'dead'", store_history=True)
325 325 # test '%reset -f out', make an Out prompt
326 326 _ip.run_cell("parrot", store_history=True)
327 327 assert "dead" in [_ip.user_ns[x] for x in ("_", "__", "___")]
328 328 _ip.magic("reset -f out")
329 329 assert "dead" not in [_ip.user_ns[x] for x in ("_", "__", "___")]
330 330 assert len(_ip.user_ns["Out"]) == 0
331 331
332 332
333 333 def test_reset_in():
334 334 "Test '%reset in' magic"
335 335 # test '%reset -f in'
336 336 _ip.run_cell("parrot", store_history=True)
337 337 assert "parrot" in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
338 338 _ip.magic("%reset -f in")
339 339 assert "parrot" not in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
340 340 assert len(set(_ip.user_ns["In"])) == 1
341 341
342 342
343 343 def test_reset_dhist():
344 344 "Test '%reset dhist' magic"
345 345 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
346 346 _ip.magic("cd " + os.path.dirname(pytest.__file__))
347 347 _ip.magic("cd -")
348 348 assert len(_ip.user_ns["_dh"]) > 0
349 349 _ip.magic("reset -f dhist")
350 350 assert len(_ip.user_ns["_dh"]) == 0
351 351 _ip.run_cell("_dh = [d for d in tmp]") # restore
352 352
353 353
354 354 def test_reset_in_length():
355 355 "Test that '%reset in' preserves In[] length"
356 356 _ip.run_cell("print 'foo'")
357 357 _ip.run_cell("reset -f in")
358 358 assert len(_ip.user_ns["In"]) == _ip.displayhook.prompt_count + 1
359 359
360 360
361 361 class TestResetErrors(TestCase):
362 362
363 363 def test_reset_redefine(self):
364 364
365 365 @magics_class
366 366 class KernelMagics(Magics):
367 367 @line_magic
368 368 def less(self, shell): pass
369 369
370 370 _ip.register_magics(KernelMagics)
371 371
372 372 with self.assertLogs() as cm:
373 373 # hack, we want to just capture logs, but assertLogs fails if not
374 374 # logs get produce.
375 375 # so log one things we ignore.
376 376 import logging as log_mod
377 377 log = log_mod.getLogger()
378 378 log.info('Nothing')
379 379 # end hack.
380 380 _ip.run_cell("reset -f")
381 381
382 382 assert len(cm.output) == 1
383 383 for out in cm.output:
384 384 assert "Invalid alias" not in out
385 385
386 386 def test_tb_syntaxerror():
387 387 """test %tb after a SyntaxError"""
388 388 ip = get_ipython()
389 389 ip.run_cell("for")
390 390
391 391 # trap and validate stdout
392 392 save_stdout = sys.stdout
393 393 try:
394 394 sys.stdout = StringIO()
395 395 ip.run_cell("%tb")
396 396 out = sys.stdout.getvalue()
397 397 finally:
398 398 sys.stdout = save_stdout
399 399 # trim output, and only check the last line
400 400 last_line = out.rstrip().splitlines()[-1].strip()
401 401 assert last_line == "SyntaxError: invalid syntax"
402 402
403 403
404 404 def test_time():
405 405 ip = get_ipython()
406 406
407 407 with tt.AssertPrints("Wall time: "):
408 408 ip.run_cell("%time None")
409 409
410 410 ip.run_cell("def f(kmjy):\n"
411 411 " %time print (2*kmjy)")
412 412
413 413 with tt.AssertPrints("Wall time: "):
414 414 with tt.AssertPrints("hihi", suppress=False):
415 415 ip.run_cell("f('hi')")
416 416
417 417 def test_time_last_not_expression():
418 418 ip.run_cell("%%time\n"
419 419 "var_1 = 1\n"
420 420 "var_2 = 2\n")
421 421 assert ip.user_ns['var_1'] == 1
422 422 del ip.user_ns['var_1']
423 423 assert ip.user_ns['var_2'] == 2
424 424 del ip.user_ns['var_2']
425 425
426 426
427 427 @dec.skip_win32
428 428 def test_time2():
429 429 ip = get_ipython()
430 430
431 431 with tt.AssertPrints("CPU times: user "):
432 432 ip.run_cell("%time None")
433 433
434 434 def test_time3():
435 435 """Erroneous magic function calls, issue gh-3334"""
436 436 ip = get_ipython()
437 437 ip.user_ns.pop('run', None)
438 438
439 439 with tt.AssertNotPrints("not found", channel='stderr'):
440 440 ip.run_cell("%%time\n"
441 441 "run = 0\n"
442 442 "run += 1")
443 443
444 444 def test_multiline_time():
445 445 """Make sure last statement from time return a value."""
446 446 ip = get_ipython()
447 447 ip.user_ns.pop('run', None)
448 448
449 449 ip.run_cell(dedent("""\
450 450 %%time
451 451 a = "ho"
452 452 b = "hey"
453 453 a+b
454 454 """
455 455 )
456 456 )
457 457 assert ip.user_ns_hidden["_"] == "hohey"
458 458
459 459
460 460 def test_time_local_ns():
461 461 """
462 462 Test that local_ns is actually global_ns when running a cell magic
463 463 """
464 464 ip = get_ipython()
465 465 ip.run_cell("%%time\n" "myvar = 1")
466 466 assert ip.user_ns["myvar"] == 1
467 467 del ip.user_ns["myvar"]
468 468
469 469
470 470 def test_doctest_mode():
471 471 "Toggle doctest_mode twice, it should be a no-op and run without error"
472 472 _ip.magic('doctest_mode')
473 473 _ip.magic('doctest_mode')
474 474
475 475
476 476 def test_parse_options():
477 477 """Tests for basic options parsing in magics."""
478 478 # These are only the most minimal of tests, more should be added later. At
479 479 # the very least we check that basic text/unicode calls work OK.
480 480 m = DummyMagics(_ip)
481 481 assert m.parse_options("foo", "")[1] == "foo"
482 482 assert m.parse_options("foo", "")[1] == "foo"
483 483
484 484
485 485 def test_parse_options_preserve_non_option_string():
486 486 """Test to assert preservation of non-option part of magic-block, while parsing magic options."""
487 487 m = DummyMagics(_ip)
488 488 opts, stmt = m.parse_options(
489 489 " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True
490 490 )
491 491 assert opts == {"n": "1", "r": "13"}
492 492 assert stmt == "_ = 314 + foo"
493 493
494 494
495 495 def test_run_magic_preserve_code_block():
496 496 """Test to assert preservation of non-option part of magic-block, while running magic."""
497 497 _ip.user_ns["spaces"] = []
498 498 _ip.magic("timeit -n1 -r1 spaces.append([s.count(' ') for s in ['document']])")
499 499 assert _ip.user_ns["spaces"] == [[0]]
500 500
501 501
502 502 def test_dirops():
503 503 """Test various directory handling operations."""
504 504 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
505 505 curpath = os.getcwd
506 506 startdir = os.getcwd()
507 507 ipdir = os.path.realpath(_ip.ipython_dir)
508 508 try:
509 509 _ip.magic('cd "%s"' % ipdir)
510 510 assert curpath() == ipdir
511 511 _ip.magic('cd -')
512 512 assert curpath() == startdir
513 513 _ip.magic('pushd "%s"' % ipdir)
514 514 assert curpath() == ipdir
515 515 _ip.magic('popd')
516 516 assert curpath() == startdir
517 517 finally:
518 518 os.chdir(startdir)
519 519
520 520
521 521 def test_cd_force_quiet():
522 522 """Test OSMagics.cd_force_quiet option"""
523 523 _ip.config.OSMagics.cd_force_quiet = True
524 524 osmagics = osm.OSMagics(shell=_ip)
525 525
526 526 startdir = os.getcwd()
527 527 ipdir = os.path.realpath(_ip.ipython_dir)
528 528
529 529 try:
530 530 with tt.AssertNotPrints(ipdir):
531 531 osmagics.cd('"%s"' % ipdir)
532 532 with tt.AssertNotPrints(startdir):
533 533 osmagics.cd('-')
534 534 finally:
535 535 os.chdir(startdir)
536 536
537 537
538 538 def test_xmode():
539 539 # Calling xmode three times should be a no-op
540 540 xmode = _ip.InteractiveTB.mode
541 541 for i in range(4):
542 542 _ip.magic("xmode")
543 543 assert _ip.InteractiveTB.mode == xmode
544 544
545 545 def test_reset_hard():
546 546 monitor = []
547 547 class A(object):
548 548 def __del__(self):
549 549 monitor.append(1)
550 550 def __repr__(self):
551 551 return "<A instance>"
552 552
553 553 _ip.user_ns["a"] = A()
554 554 _ip.run_cell("a")
555 555
556 556 assert monitor == []
557 557 _ip.magic("reset -f")
558 558 assert monitor == [1]
559 559
560 560 class TestXdel(tt.TempFileMixin):
561 561 def test_xdel(self):
562 562 """Test that references from %run are cleared by xdel."""
563 563 src = ("class A(object):\n"
564 564 " monitor = []\n"
565 565 " def __del__(self):\n"
566 566 " self.monitor.append(1)\n"
567 567 "a = A()\n")
568 568 self.mktmp(src)
569 569 # %run creates some hidden references...
570 570 _ip.magic("run %s" % self.fname)
571 571 # ... as does the displayhook.
572 572 _ip.run_cell("a")
573 573
574 574 monitor = _ip.user_ns["A"].monitor
575 575 assert monitor == []
576 576
577 577 _ip.magic("xdel a")
578 578
579 579 # Check that a's __del__ method has been called.
580 580 gc.collect(0)
581 581 assert monitor == [1]
582 582
583 583 def doctest_who():
584 584 """doctest for %who
585 585
586 586 In [1]: %reset -sf
587 587
588 588 In [2]: alpha = 123
589 589
590 590 In [3]: beta = 'beta'
591 591
592 592 In [4]: %who int
593 593 alpha
594 594
595 595 In [5]: %who str
596 596 beta
597 597
598 598 In [6]: %whos
599 599 Variable Type Data/Info
600 600 ----------------------------
601 601 alpha int 123
602 602 beta str beta
603 603
604 604 In [7]: %who_ls
605 605 Out[7]: ['alpha', 'beta']
606 606 """
607 607
608 608 def test_whos():
609 609 """Check that whos is protected against objects where repr() fails."""
610 610 class A(object):
611 611 def __repr__(self):
612 612 raise Exception()
613 613 _ip.user_ns['a'] = A()
614 614 _ip.magic("whos")
615 615
616 616 def doctest_precision():
617 617 """doctest for %precision
618 618
619 619 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
620 620
621 621 In [2]: %precision 5
622 622 Out[2]: '%.5f'
623 623
624 624 In [3]: f.float_format
625 625 Out[3]: '%.5f'
626 626
627 627 In [4]: %precision %e
628 628 Out[4]: '%e'
629 629
630 630 In [5]: f(3.1415927)
631 631 Out[5]: '3.141593e+00'
632 632 """
633 633
634 634 def test_debug_magic():
635 635 """Test debugging a small code with %debug
636 636
637 637 In [1]: with PdbTestInput(['c']):
638 638 ...: %debug print("a b") #doctest: +ELLIPSIS
639 639 ...:
640 640 ...
641 641 ipdb> c
642 642 a b
643 643 In [2]:
644 644 """
645 645
646 646 def test_psearch():
647 647 with tt.AssertPrints("dict.fromkeys"):
648 648 _ip.run_cell("dict.fr*?")
649 649 with tt.AssertPrints("Ï€.is_integer"):
650 650 _ip.run_cell("π = 3.14;\nπ.is_integ*?")
651 651
652 652 def test_timeit_shlex():
653 653 """test shlex issues with timeit (#1109)"""
654 654 _ip.ex("def f(*a,**kw): pass")
655 655 _ip.magic('timeit -n1 "this is a bug".count(" ")')
656 656 _ip.magic('timeit -r1 -n1 f(" ", 1)')
657 657 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
658 658 _ip.magic('timeit -r1 -n1 ("a " + "b")')
659 659 _ip.magic('timeit -r1 -n1 f("a " + "b")')
660 660 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
661 661
662 662
663 663 def test_timeit_special_syntax():
664 664 "Test %%timeit with IPython special syntax"
665 665 @register_line_magic
666 666 def lmagic(line):
667 667 ip = get_ipython()
668 668 ip.user_ns['lmagic_out'] = line
669 669
670 670 # line mode test
671 671 _ip.run_line_magic("timeit", "-n1 -r1 %lmagic my line")
672 672 assert _ip.user_ns["lmagic_out"] == "my line"
673 673 # cell mode test
674 674 _ip.run_cell_magic("timeit", "-n1 -r1", "%lmagic my line2")
675 675 assert _ip.user_ns["lmagic_out"] == "my line2"
676 676
677 677
678 678 def test_timeit_return():
679 679 """
680 680 test whether timeit -o return object
681 681 """
682 682
683 683 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
684 684 assert(res is not None)
685 685
686 686 def test_timeit_quiet():
687 687 """
688 688 test quiet option of timeit magic
689 689 """
690 690 with tt.AssertNotPrints("loops"):
691 691 _ip.run_cell("%timeit -n1 -r1 -q 1")
692 692
693 693 def test_timeit_return_quiet():
694 694 with tt.AssertNotPrints("loops"):
695 695 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
696 696 assert (res is not None)
697 697
698 698 def test_timeit_invalid_return():
699 699 with pytest.raises(SyntaxError):
700 700 _ip.run_line_magic('timeit', 'return')
701 701
702 702 @dec.skipif(execution.profile is None)
703 703 def test_prun_special_syntax():
704 704 "Test %%prun with IPython special syntax"
705 705 @register_line_magic
706 706 def lmagic(line):
707 707 ip = get_ipython()
708 708 ip.user_ns['lmagic_out'] = line
709 709
710 710 # line mode test
711 711 _ip.run_line_magic("prun", "-q %lmagic my line")
712 712 assert _ip.user_ns["lmagic_out"] == "my line"
713 713 # cell mode test
714 714 _ip.run_cell_magic("prun", "-q", "%lmagic my line2")
715 715 assert _ip.user_ns["lmagic_out"] == "my line2"
716 716
717 717
718 718 @dec.skipif(execution.profile is None)
719 719 def test_prun_quotes():
720 720 "Test that prun does not clobber string escapes (GH #1302)"
721 721 _ip.magic(r"prun -q x = '\t'")
722 722 assert _ip.user_ns["x"] == "\t"
723 723
724 724
725 725 def test_extension():
726 726 # Debugging information for failures of this test
727 727 print('sys.path:')
728 728 for p in sys.path:
729 729 print(' ', p)
730 730 print('CWD', os.getcwd())
731 731
732 732 pytest.raises(ImportError, _ip.magic, "load_ext daft_extension")
733 733 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
734 734 sys.path.insert(0, daft_path)
735 735 try:
736 736 _ip.user_ns.pop('arq', None)
737 737 invalidate_caches() # Clear import caches
738 738 _ip.magic("load_ext daft_extension")
739 739 assert _ip.user_ns["arq"] == 185
740 740 _ip.magic("unload_ext daft_extension")
741 741 assert 'arq' not in _ip.user_ns
742 742 finally:
743 743 sys.path.remove(daft_path)
744 744
745 745
746 746 def test_notebook_export_json():
747 747 pytest.importorskip("nbformat")
748 748 _ip = get_ipython()
749 749 _ip.history_manager.reset() # Clear any existing history.
750 750 cmds = ["a=1", "def b():\n return a**2", "print('noël, été', b())"]
751 751 for i, cmd in enumerate(cmds, start=1):
752 752 _ip.history_manager.store_inputs(i, cmd)
753 753 with TemporaryDirectory() as td:
754 754 outfile = os.path.join(td, "nb.ipynb")
755 755 _ip.magic("notebook %s" % outfile)
756 756
757 757
758 758 class TestEnv(TestCase):
759 759
760 760 def test_env(self):
761 761 env = _ip.magic("env")
762 762 self.assertTrue(isinstance(env, dict))
763 763
764 764 def test_env_secret(self):
765 765 env = _ip.magic("env")
766 766 hidden = "<hidden>"
767 767 with mock.patch.dict(
768 768 os.environ,
769 769 {
770 770 "API_KEY": "abc123",
771 771 "SECRET_THING": "ssshhh",
772 772 "JUPYTER_TOKEN": "",
773 773 "VAR": "abc"
774 774 }
775 775 ):
776 776 env = _ip.magic("env")
777 777 assert env["API_KEY"] == hidden
778 778 assert env["SECRET_THING"] == hidden
779 779 assert env["JUPYTER_TOKEN"] == hidden
780 780 assert env["VAR"] == "abc"
781 781
782 782 def test_env_get_set_simple(self):
783 783 env = _ip.magic("env var val1")
784 784 self.assertEqual(env, None)
785 785 self.assertEqual(os.environ['var'], 'val1')
786 786 self.assertEqual(_ip.magic("env var"), 'val1')
787 787 env = _ip.magic("env var=val2")
788 788 self.assertEqual(env, None)
789 789 self.assertEqual(os.environ['var'], 'val2')
790 790
791 791 def test_env_get_set_complex(self):
792 792 env = _ip.magic("env var 'val1 '' 'val2")
793 793 self.assertEqual(env, None)
794 794 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
795 795 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
796 796 env = _ip.magic('env var=val2 val3="val4')
797 797 self.assertEqual(env, None)
798 798 self.assertEqual(os.environ['var'], 'val2 val3="val4')
799 799
800 800 def test_env_set_bad_input(self):
801 801 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
802 802
803 803 def test_env_set_whitespace(self):
804 804 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
805 805
806 806
807 807 class CellMagicTestCase(TestCase):
808 808
809 809 def check_ident(self, magic):
810 810 # Manually called, we get the result
811 811 out = _ip.run_cell_magic(magic, "a", "b")
812 812 assert out == ("a", "b")
813 813 # Via run_cell, it goes into the user's namespace via displayhook
814 814 _ip.run_cell("%%" + magic + " c\nd\n")
815 815 assert _ip.user_ns["_"] == ("c", "d\n")
816 816
817 817 def test_cell_magic_func_deco(self):
818 818 "Cell magic using simple decorator"
819 819 @register_cell_magic
820 820 def cellm(line, cell):
821 821 return line, cell
822 822
823 823 self.check_ident('cellm')
824 824
825 825 def test_cell_magic_reg(self):
826 826 "Cell magic manually registered"
827 827 def cellm(line, cell):
828 828 return line, cell
829 829
830 830 _ip.register_magic_function(cellm, 'cell', 'cellm2')
831 831 self.check_ident('cellm2')
832 832
833 833 def test_cell_magic_class(self):
834 834 "Cell magics declared via a class"
835 835 @magics_class
836 836 class MyMagics(Magics):
837 837
838 838 @cell_magic
839 839 def cellm3(self, line, cell):
840 840 return line, cell
841 841
842 842 _ip.register_magics(MyMagics)
843 843 self.check_ident('cellm3')
844 844
845 845 def test_cell_magic_class2(self):
846 846 "Cell magics declared via a class, #2"
847 847 @magics_class
848 848 class MyMagics2(Magics):
849 849
850 850 @cell_magic('cellm4')
851 851 def cellm33(self, line, cell):
852 852 return line, cell
853 853
854 854 _ip.register_magics(MyMagics2)
855 855 self.check_ident('cellm4')
856 856 # Check that nothing is registered as 'cellm33'
857 857 c33 = _ip.find_cell_magic('cellm33')
858 858 assert c33 == None
859 859
860 860 def test_file():
861 861 """Basic %%writefile"""
862 862 ip = get_ipython()
863 863 with TemporaryDirectory() as td:
864 fname = os.path.join(td, 'file1')
865 ip.run_cell_magic("writefile", fname, u'\n'.join([
866 'line1',
867 'line2',
868 ]))
869 s = Path(fname).read_text(encoding='utf-8')
864 fname = os.path.join(td, "file1")
865 ip.run_cell_magic(
866 "writefile",
867 fname,
868 "\n".join(
869 [
870 "line1",
871 "line2",
872 ]
873 ),
874 )
875 s = Path(fname).read_text(encoding="utf-8")
870 876 assert "line1\n" in s
871 877 assert "line2" in s
872 878
873 879
874 880 @dec.skip_win32
875 881 def test_file_single_quote():
876 882 """Basic %%writefile with embedded single quotes"""
877 883 ip = get_ipython()
878 884 with TemporaryDirectory() as td:
879 fname = os.path.join(td, '\'file1\'')
880 ip.run_cell_magic("writefile", fname, u'\n'.join([
881 'line1',
882 'line2',
883 ]))
884 s = Path(fname).read_text(encoding='utf-8')
885 fname = os.path.join(td, "'file1'")
886 ip.run_cell_magic(
887 "writefile",
888 fname,
889 "\n".join(
890 [
891 "line1",
892 "line2",
893 ]
894 ),
895 )
896 s = Path(fname).read_text(encoding="utf-8")
885 897 assert "line1\n" in s
886 898 assert "line2" in s
887 899
888 900
889 901 @dec.skip_win32
890 902 def test_file_double_quote():
891 903 """Basic %%writefile with embedded double quotes"""
892 904 ip = get_ipython()
893 905 with TemporaryDirectory() as td:
894 906 fname = os.path.join(td, '"file1"')
895 ip.run_cell_magic("writefile", fname, u'\n'.join([
896 'line1',
897 'line2',
898 ]))
899 s = Path(fname).read_text(encoding='utf-8')
907 ip.run_cell_magic(
908 "writefile",
909 fname,
910 "\n".join(
911 [
912 "line1",
913 "line2",
914 ]
915 ),
916 )
917 s = Path(fname).read_text(encoding="utf-8")
900 918 assert "line1\n" in s
901 919 assert "line2" in s
902 920
903 921
904 922 def test_file_var_expand():
905 923 """%%writefile $filename"""
906 924 ip = get_ipython()
907 925 with TemporaryDirectory() as td:
908 fname = os.path.join(td, 'file1')
909 ip.user_ns['filename'] = fname
910 ip.run_cell_magic("writefile", '$filename', u'\n'.join([
911 'line1',
912 'line2',
913 ]))
914 s = Path(fname).read_text(encoding='utf-8')
926 fname = os.path.join(td, "file1")
927 ip.user_ns["filename"] = fname
928 ip.run_cell_magic(
929 "writefile",
930 "$filename",
931 "\n".join(
932 [
933 "line1",
934 "line2",
935 ]
936 ),
937 )
938 s = Path(fname).read_text(encoding="utf-8")
915 939 assert "line1\n" in s
916 940 assert "line2" in s
917 941
918 942
919 943 def test_file_unicode():
920 944 """%%writefile with unicode cell"""
921 945 ip = get_ipython()
922 946 with TemporaryDirectory() as td:
923 947 fname = os.path.join(td, 'file1')
924 948 ip.run_cell_magic("writefile", fname, u'\n'.join([
925 949 u'liné1',
926 950 u'liné2',
927 951 ]))
928 952 with io.open(fname, encoding='utf-8') as f:
929 953 s = f.read()
930 954 assert "liné1\n" in s
931 955 assert "liné2" in s
932 956
933 957
934 958 def test_file_amend():
935 959 """%%writefile -a amends files"""
936 960 ip = get_ipython()
937 961 with TemporaryDirectory() as td:
938 fname = os.path.join(td, 'file2')
939 ip.run_cell_magic("writefile", fname, u'\n'.join([
940 'line1',
941 'line2',
942 ]))
943 ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([
944 'line3',
945 'line4',
946 ]))
947 s = Path(fname).read_text(encoding='utf-8')
962 fname = os.path.join(td, "file2")
963 ip.run_cell_magic(
964 "writefile",
965 fname,
966 "\n".join(
967 [
968 "line1",
969 "line2",
970 ]
971 ),
972 )
973 ip.run_cell_magic(
974 "writefile",
975 "-a %s" % fname,
976 "\n".join(
977 [
978 "line3",
979 "line4",
980 ]
981 ),
982 )
983 s = Path(fname).read_text(encoding="utf-8")
948 984 assert "line1\n" in s
949 985 assert "line3\n" in s
950 986
951 987
952 988 def test_file_spaces():
953 989 """%%file with spaces in filename"""
954 990 ip = get_ipython()
955 991 with TemporaryWorkingDirectory() as td:
956 992 fname = "file name"
957 ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([
958 'line1',
959 'line2',
960 ]))
961 s = Path(fname).read_text(encoding='utf-8')
993 ip.run_cell_magic(
994 "file",
995 '"%s"' % fname,
996 "\n".join(
997 [
998 "line1",
999 "line2",
1000 ]
1001 ),
1002 )
1003 s = Path(fname).read_text(encoding="utf-8")
962 1004 assert "line1\n" in s
963 1005 assert "line2" in s
964 1006
965 1007
966 1008 def test_script_config():
967 1009 ip = get_ipython()
968 1010 ip.config.ScriptMagics.script_magics = ['whoda']
969 1011 sm = script.ScriptMagics(shell=ip)
970 1012 assert "whoda" in sm.magics["cell"]
971 1013
972 1014
973 1015 def test_script_out():
974 1016 ip = get_ipython()
975 1017 ip.run_cell_magic("script", f"--out output {sys.executable}", "print('hi')")
976 1018 assert ip.user_ns["output"].strip() == "hi"
977 1019
978 1020
979 1021 def test_script_err():
980 1022 ip = get_ipython()
981 1023 ip.run_cell_magic(
982 1024 "script",
983 1025 f"--err error {sys.executable}",
984 1026 "import sys; print('hello', file=sys.stderr)",
985 1027 )
986 1028 assert ip.user_ns["error"].strip() == "hello"
987 1029
988 1030
989 1031 def test_script_out_err():
990 1032
991 1033 ip = get_ipython()
992 1034 ip.run_cell_magic(
993 1035 "script",
994 1036 f"--out output --err error {sys.executable}",
995 1037 "\n".join(
996 1038 [
997 1039 "import sys",
998 1040 "print('hi')",
999 1041 "print('hello', file=sys.stderr)",
1000 1042 ]
1001 1043 ),
1002 1044 )
1003 1045 assert ip.user_ns["output"].strip() == "hi"
1004 1046 assert ip.user_ns["error"].strip() == "hello"
1005 1047
1006 1048
1007 1049 async def test_script_bg_out():
1008 1050 ip = get_ipython()
1009 1051 ip.run_cell_magic("script", f"--bg --out output {sys.executable}", "print('hi')")
1010 1052 assert (await ip.user_ns["output"].read()).strip() == b"hi"
1011 1053 assert ip.user_ns["output"].at_eof()
1012 1054
1013 1055
1014 1056 async def test_script_bg_err():
1015 1057 ip = get_ipython()
1016 1058 ip.run_cell_magic(
1017 1059 "script",
1018 1060 f"--bg --err error {sys.executable}",
1019 1061 "import sys; print('hello', file=sys.stderr)",
1020 1062 )
1021 1063 assert (await ip.user_ns["error"].read()).strip() == b"hello"
1022 1064 assert ip.user_ns["error"].at_eof()
1023 1065
1024 1066
1025 1067 async def test_script_bg_out_err():
1026 1068 ip = get_ipython()
1027 1069 ip.run_cell_magic(
1028 1070 "script",
1029 1071 f"--bg --out output --err error {sys.executable}",
1030 1072 "\n".join(
1031 1073 [
1032 1074 "import sys",
1033 1075 "print('hi')",
1034 1076 "print('hello', file=sys.stderr)",
1035 1077 ]
1036 1078 ),
1037 1079 )
1038 1080 assert (await ip.user_ns["output"].read()).strip() == b"hi"
1039 1081 assert (await ip.user_ns["error"].read()).strip() == b"hello"
1040 1082 assert ip.user_ns["output"].at_eof()
1041 1083 assert ip.user_ns["error"].at_eof()
1042 1084
1043 1085
1044 1086 async def test_script_bg_proc():
1045 1087 ip = get_ipython()
1046 1088 ip.run_cell_magic(
1047 1089 "script",
1048 1090 f"--bg --out output --proc p {sys.executable}",
1049 1091 "\n".join(
1050 1092 [
1051 1093 "import sys",
1052 1094 "print('hi')",
1053 1095 "print('hello', file=sys.stderr)",
1054 1096 ]
1055 1097 ),
1056 1098 )
1057 1099 p = ip.user_ns["p"]
1058 1100 await p.wait()
1059 1101 assert p.returncode == 0
1060 1102 assert (await p.stdout.read()).strip() == b"hi"
1061 1103 # not captured, so empty
1062 1104 assert (await p.stderr.read()) == b""
1063 1105 assert p.stdout.at_eof()
1064 1106 assert p.stderr.at_eof()
1065 1107
1066 1108
1067 1109 def test_script_defaults():
1068 1110 ip = get_ipython()
1069 1111 for cmd in ['sh', 'bash', 'perl', 'ruby']:
1070 1112 try:
1071 1113 find_cmd(cmd)
1072 1114 except Exception:
1073 1115 pass
1074 1116 else:
1075 1117 assert cmd in ip.magics_manager.magics["cell"]
1076 1118
1077 1119
1078 1120 @magics_class
1079 1121 class FooFoo(Magics):
1080 1122 """class with both %foo and %%foo magics"""
1081 1123 @line_magic('foo')
1082 1124 def line_foo(self, line):
1083 1125 "I am line foo"
1084 1126 pass
1085 1127
1086 1128 @cell_magic("foo")
1087 1129 def cell_foo(self, line, cell):
1088 1130 "I am cell foo, not line foo"
1089 1131 pass
1090 1132
1091 1133 def test_line_cell_info():
1092 1134 """%%foo and %foo magics are distinguishable to inspect"""
1093 1135 ip = get_ipython()
1094 1136 ip.magics_manager.register(FooFoo)
1095 1137 oinfo = ip.object_inspect("foo")
1096 1138 assert oinfo["found"] is True
1097 1139 assert oinfo["ismagic"] is True
1098 1140
1099 1141 oinfo = ip.object_inspect("%%foo")
1100 1142 assert oinfo["found"] is True
1101 1143 assert oinfo["ismagic"] is True
1102 1144 assert oinfo["docstring"] == FooFoo.cell_foo.__doc__
1103 1145
1104 1146 oinfo = ip.object_inspect("%foo")
1105 1147 assert oinfo["found"] is True
1106 1148 assert oinfo["ismagic"] is True
1107 1149 assert oinfo["docstring"] == FooFoo.line_foo.__doc__
1108 1150
1109 1151
1110 1152 def test_multiple_magics():
1111 1153 ip = get_ipython()
1112 1154 foo1 = FooFoo(ip)
1113 1155 foo2 = FooFoo(ip)
1114 1156 mm = ip.magics_manager
1115 1157 mm.register(foo1)
1116 1158 assert mm.magics["line"]["foo"].__self__ is foo1
1117 1159 mm.register(foo2)
1118 1160 assert mm.magics["line"]["foo"].__self__ is foo2
1119 1161
1120 1162
1121 1163 def test_alias_magic():
1122 1164 """Test %alias_magic."""
1123 1165 ip = get_ipython()
1124 1166 mm = ip.magics_manager
1125 1167
1126 1168 # Basic operation: both cell and line magics are created, if possible.
1127 1169 ip.run_line_magic("alias_magic", "timeit_alias timeit")
1128 1170 assert "timeit_alias" in mm.magics["line"]
1129 1171 assert "timeit_alias" in mm.magics["cell"]
1130 1172
1131 1173 # --cell is specified, line magic not created.
1132 1174 ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit")
1133 1175 assert "timeit_cell_alias" not in mm.magics["line"]
1134 1176 assert "timeit_cell_alias" in mm.magics["cell"]
1135 1177
1136 1178 # Test that line alias is created successfully.
1137 1179 ip.run_line_magic("alias_magic", "--line env_alias env")
1138 1180 assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "")
1139 1181
1140 1182 # Test that line alias with parameters passed in is created successfully.
1141 1183 ip.run_line_magic(
1142 1184 "alias_magic", "--line history_alias history --params " + shlex.quote("3")
1143 1185 )
1144 1186 assert "history_alias" in mm.magics["line"]
1145 1187
1146 1188
1147 1189 def test_save():
1148 1190 """Test %save."""
1149 1191 ip = get_ipython()
1150 1192 ip.history_manager.reset() # Clear any existing history.
1151 1193 cmds = ["a=1", "def b():\n return a**2", "print(a, b())"]
1152 1194 for i, cmd in enumerate(cmds, start=1):
1153 1195 ip.history_manager.store_inputs(i, cmd)
1154 1196 with TemporaryDirectory() as tmpdir:
1155 1197 file = os.path.join(tmpdir, "testsave.py")
1156 1198 ip.run_line_magic("save", "%s 1-10" % file)
1157 content = Path(file).read_text(encoding='utf-8')
1199 content = Path(file).read_text(encoding="utf-8")
1158 1200 assert content.count(cmds[0]) == 1
1159 1201 assert "coding: utf-8" in content
1160 1202 ip.run_line_magic("save", "-a %s 1-10" % file)
1161 content = Path(file).read_text(encoding='utf-8')
1203 content = Path(file).read_text(encoding="utf-8")
1162 1204 assert content.count(cmds[0]) == 2
1163 1205 assert "coding: utf-8" in content
1164 1206
1165 1207
1166 1208 def test_save_with_no_args():
1167 1209 ip = get_ipython()
1168 1210 ip.history_manager.reset() # Clear any existing history.
1169 1211 cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"]
1170 1212 for i, cmd in enumerate(cmds, start=1):
1171 1213 ip.history_manager.store_inputs(i, cmd)
1172 1214
1173 1215 with TemporaryDirectory() as tmpdir:
1174 1216 path = os.path.join(tmpdir, "testsave.py")
1175 1217 ip.run_line_magic("save", path)
1176 content = Path(path).read_text(encoding='utf-8')
1218 content = Path(path).read_text(encoding="utf-8")
1177 1219 expected_content = dedent(
1178 1220 """\
1179 1221 # coding: utf-8
1180 1222 a=1
1181 1223 def b():
1182 1224 return a**2
1183 1225 print(a, b())
1184 1226 """
1185 1227 )
1186 1228 assert content == expected_content
1187 1229
1188 1230
1189 1231 def test_store():
1190 1232 """Test %store."""
1191 1233 ip = get_ipython()
1192 1234 ip.run_line_magic('load_ext', 'storemagic')
1193 1235
1194 1236 # make sure the storage is empty
1195 1237 ip.run_line_magic("store", "-z")
1196 1238 ip.user_ns["var"] = 42
1197 1239 ip.run_line_magic("store", "var")
1198 1240 ip.user_ns["var"] = 39
1199 1241 ip.run_line_magic("store", "-r")
1200 1242 assert ip.user_ns["var"] == 42
1201 1243
1202 1244 ip.run_line_magic("store", "-d var")
1203 1245 ip.user_ns["var"] = 39
1204 1246 ip.run_line_magic("store", "-r")
1205 1247 assert ip.user_ns["var"] == 39
1206 1248
1207 1249
1208 1250 def _run_edit_test(arg_s, exp_filename=None,
1209 1251 exp_lineno=-1,
1210 1252 exp_contents=None,
1211 1253 exp_is_temp=None):
1212 1254 ip = get_ipython()
1213 1255 M = code.CodeMagics(ip)
1214 1256 last_call = ['','']
1215 1257 opts,args = M.parse_options(arg_s,'prxn:')
1216 1258 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1217 1259
1218 1260 if exp_filename is not None:
1219 1261 assert exp_filename == filename
1220 1262 if exp_contents is not None:
1221 1263 with io.open(filename, 'r', encoding='utf-8') as f:
1222 1264 contents = f.read()
1223 1265 assert exp_contents == contents
1224 1266 if exp_lineno != -1:
1225 1267 assert exp_lineno == lineno
1226 1268 if exp_is_temp is not None:
1227 1269 assert exp_is_temp == is_temp
1228 1270
1229 1271
1230 1272 def test_edit_interactive():
1231 1273 """%edit on interactively defined objects"""
1232 1274 ip = get_ipython()
1233 1275 n = ip.execution_count
1234 1276 ip.run_cell("def foo(): return 1", store_history=True)
1235 1277
1236 1278 with pytest.raises(code.InteractivelyDefined) as e:
1237 1279 _run_edit_test("foo")
1238 1280 assert e.value.index == n
1239 1281
1240 1282
1241 1283 def test_edit_cell():
1242 1284 """%edit [cell id]"""
1243 1285 ip = get_ipython()
1244 1286
1245 1287 ip.run_cell("def foo(): return 1", store_history=True)
1246 1288
1247 1289 # test
1248 1290 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1249 1291
1250 1292 def test_edit_fname():
1251 1293 """%edit file"""
1252 1294 # test
1253 1295 _run_edit_test("test file.py", exp_filename="test file.py")
1254 1296
1255 1297 def test_bookmark():
1256 1298 ip = get_ipython()
1257 1299 ip.run_line_magic('bookmark', 'bmname')
1258 1300 with tt.AssertPrints('bmname'):
1259 1301 ip.run_line_magic('bookmark', '-l')
1260 1302 ip.run_line_magic('bookmark', '-d bmname')
1261 1303
1262 1304 def test_ls_magic():
1263 1305 ip = get_ipython()
1264 1306 json_formatter = ip.display_formatter.formatters['application/json']
1265 1307 json_formatter.enabled = True
1266 1308 lsmagic = ip.magic('lsmagic')
1267 1309 with warnings.catch_warnings(record=True) as w:
1268 1310 j = json_formatter(lsmagic)
1269 1311 assert sorted(j) == ["cell", "line"]
1270 1312 assert w == [] # no warnings
1271 1313
1272 1314
1273 1315 def test_strip_initial_indent():
1274 1316 def sii(s):
1275 1317 lines = s.splitlines()
1276 1318 return '\n'.join(code.strip_initial_indent(lines))
1277 1319
1278 1320 assert sii(" a = 1\nb = 2") == "a = 1\nb = 2"
1279 1321 assert sii(" a\n b\nc") == "a\n b\nc"
1280 1322 assert sii("a\n b") == "a\n b"
1281 1323
1282 1324 def test_logging_magic_quiet_from_arg():
1283 1325 _ip.config.LoggingMagics.quiet = False
1284 1326 lm = logging.LoggingMagics(shell=_ip)
1285 1327 with TemporaryDirectory() as td:
1286 1328 try:
1287 1329 with tt.AssertNotPrints(re.compile("Activating.*")):
1288 1330 lm.logstart('-q {}'.format(
1289 1331 os.path.join(td, "quiet_from_arg.log")))
1290 1332 finally:
1291 1333 _ip.logger.logstop()
1292 1334
1293 1335 def test_logging_magic_quiet_from_config():
1294 1336 _ip.config.LoggingMagics.quiet = True
1295 1337 lm = logging.LoggingMagics(shell=_ip)
1296 1338 with TemporaryDirectory() as td:
1297 1339 try:
1298 1340 with tt.AssertNotPrints(re.compile("Activating.*")):
1299 1341 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1300 1342 finally:
1301 1343 _ip.logger.logstop()
1302 1344
1303 1345
1304 1346 def test_logging_magic_not_quiet():
1305 1347 _ip.config.LoggingMagics.quiet = False
1306 1348 lm = logging.LoggingMagics(shell=_ip)
1307 1349 with TemporaryDirectory() as td:
1308 1350 try:
1309 1351 with tt.AssertPrints(re.compile("Activating.*")):
1310 1352 lm.logstart(os.path.join(td, "not_quiet.log"))
1311 1353 finally:
1312 1354 _ip.logger.logstop()
1313 1355
1314 1356
1315 1357 def test_time_no_var_expand():
1316 1358 _ip.user_ns['a'] = 5
1317 1359 _ip.user_ns['b'] = []
1318 1360 _ip.magic('time b.append("{a}")')
1319 1361 assert _ip.user_ns['b'] == ['{a}']
1320 1362
1321 1363
1322 1364 # this is slow, put at the end for local testing.
1323 1365 def test_timeit_arguments():
1324 1366 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1325 1367 _ip.magic("timeit -n1 -r1 a=('#')")
1326 1368
1327 1369
1328 1370 TEST_MODULE = """
1329 1371 print('Loaded my_tmp')
1330 1372 if __name__ == "__main__":
1331 1373 print('I just ran a script')
1332 1374 """
1333 1375
1334 1376
1335 1377 def test_run_module_from_import_hook():
1336 1378 "Test that a module can be loaded via an import hook"
1337 1379 with TemporaryDirectory() as tmpdir:
1338 fullpath = os.path.join(tmpdir, 'my_tmp.py')
1339 Path(fullpath).write_text(TEST_MODULE, encoding='utf-8')
1380 fullpath = os.path.join(tmpdir, "my_tmp.py")
1381 Path(fullpath).write_text(TEST_MODULE, encoding="utf-8")
1340 1382
1341 1383 import importlib.abc
1342 1384 import importlib.util
1343 1385
1344 1386 class MyTempImporter(importlib.abc.MetaPathFinder, importlib.abc.SourceLoader):
1345 1387 def find_spec(self, fullname, path, target=None):
1346 1388 if fullname == "my_tmp":
1347 1389 return importlib.util.spec_from_loader(fullname, self)
1348 1390
1349 1391 def get_filename(self, fullname):
1350 1392 assert fullname == "my_tmp"
1351 1393 return fullpath
1352 1394
1353 1395 def get_data(self, path):
1354 1396 assert Path(path).samefile(fullpath)
1355 return Path(fullpath).read_text(encoding='utf-8')
1397 return Path(fullpath).read_text(encoding="utf-8")
1356 1398
1357 1399 sys.meta_path.insert(0, MyTempImporter())
1358 1400
1359 1401 with capture_output() as captured:
1360 1402 _ip.magic("run -m my_tmp")
1361 1403 _ip.run_cell("import my_tmp")
1362 1404
1363 1405 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1364 1406 assert output == captured.stdout
1365 1407
1366 1408 sys.meta_path.pop(0)
@@ -1,156 +1,156 b''
1 1 # coding: utf-8
2 2 """Tests for profile-related functions.
3 3
4 4 Currently only the startup-dir functionality is tested, but more tests should
5 5 be added for:
6 6
7 7 * ipython profile create
8 8 * ipython profile list
9 9 * ipython profile create --parallel
10 10 * security dir permissions
11 11
12 12 Authors
13 13 -------
14 14
15 15 * MinRK
16 16
17 17 """
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Imports
21 21 #-----------------------------------------------------------------------------
22 22
23 23 import shutil
24 24 import sys
25 25 import tempfile
26 26
27 27 from pathlib import Path
28 28 from unittest import TestCase
29 29
30 30 from IPython.core.profileapp import list_profiles_in, list_bundled_profiles
31 31 from IPython.core.profiledir import ProfileDir
32 32
33 33 from IPython.testing import decorators as dec
34 34 from IPython.testing import tools as tt
35 35 from IPython.utils.process import getoutput
36 36 from IPython.utils.tempdir import TemporaryDirectory
37 37
38 38 #-----------------------------------------------------------------------------
39 39 # Globals
40 40 #-----------------------------------------------------------------------------
41 41 TMP_TEST_DIR = Path(tempfile.mkdtemp())
42 42 HOME_TEST_DIR = TMP_TEST_DIR / "home_test_dir"
43 43 IP_TEST_DIR = HOME_TEST_DIR / ".ipython"
44 44
45 45 #
46 46 # Setup/teardown functions/decorators
47 47 #
48 48
49 49 def setup_module():
50 50 """Setup test environment for the module:
51 51
52 52 - Adds dummy home dir tree
53 53 """
54 54 # Do not mask exceptions here. In particular, catching WindowsError is a
55 55 # problem because that exception is only defined on Windows...
56 56 (Path.cwd() / IP_TEST_DIR).mkdir(parents=True)
57 57
58 58
59 59 def teardown_module():
60 60 """Teardown test environment for the module:
61 61
62 62 - Remove dummy home dir tree
63 63 """
64 64 # Note: we remove the parent test dir, which is the root of all test
65 65 # subdirs we may have created. Use shutil instead of os.removedirs, so
66 66 # that non-empty directories are all recursively removed.
67 67 shutil.rmtree(TMP_TEST_DIR)
68 68
69 69
70 70 #-----------------------------------------------------------------------------
71 71 # Test functions
72 72 #-----------------------------------------------------------------------------
73 73 class ProfileStartupTest(TestCase):
74 74 def setUp(self):
75 75 # create profile dir
76 76 self.pd = ProfileDir.create_profile_dir_by_name(IP_TEST_DIR, "test")
77 77 self.options = ["--ipython-dir", IP_TEST_DIR, "--profile", "test"]
78 78 self.fname = TMP_TEST_DIR / "test.py"
79 79
80 80 def tearDown(self):
81 81 # We must remove this profile right away so its presence doesn't
82 82 # confuse other tests.
83 83 shutil.rmtree(self.pd.location)
84 84
85 85 def init(self, startup_file, startup, test):
86 86 # write startup python file
87 with open(Path(self.pd.startup_dir) / startup_file, "w", encoding='utf-8') as f:
87 with open(Path(self.pd.startup_dir) / startup_file, "w", encoding="utf-8") as f:
88 88 f.write(startup)
89 89 # write simple test file, to check that the startup file was run
90 with open(self.fname, 'w', encoding='utf-8') as f:
90 with open(self.fname, "w", encoding="utf-8") as f:
91 91 f.write(test)
92 92
93 93 def validate(self, output):
94 94 tt.ipexec_validate(self.fname, output, "", options=self.options)
95 95
96 96 def test_startup_py(self):
97 97 self.init('00-start.py', 'zzz=123\n', 'print(zzz)\n')
98 98 self.validate('123')
99 99
100 100 def test_startup_ipy(self):
101 101 self.init('00-start.ipy', '%xmode plain\n', '')
102 102 self.validate('Exception reporting mode: Plain')
103 103
104 104
105 105 def test_list_profiles_in():
106 106 # No need to remove these directories and files, as they will get nuked in
107 107 # the module-level teardown.
108 108 td = Path(tempfile.mkdtemp(dir=TMP_TEST_DIR))
109 109 for name in ("profile_foo", "profile_hello", "not_a_profile"):
110 110 Path(td / name).mkdir(parents=True)
111 111 if dec.unicode_paths:
112 112 Path(td / u"profile_ünicode").mkdir(parents=True)
113 113
114 with open(td / "profile_file", "w", encoding='utf-8') as f:
114 with open(td / "profile_file", "w", encoding="utf-8") as f:
115 115 f.write("I am not a profile directory")
116 116 profiles = list_profiles_in(td)
117 117
118 118 # unicode normalization can turn u'ünicode' into u'u\0308nicode',
119 119 # so only check for *nicode, and that creating a ProfileDir from the
120 120 # name remains valid
121 121 found_unicode = False
122 122 for p in list(profiles):
123 123 if p.endswith('nicode'):
124 124 pd = ProfileDir.find_profile_dir_by_name(td, p)
125 125 profiles.remove(p)
126 126 found_unicode = True
127 127 break
128 128 if dec.unicode_paths:
129 129 assert found_unicode is True
130 130 assert set(profiles) == {"foo", "hello"}
131 131
132 132
133 133 def test_list_bundled_profiles():
134 134 # This variable will need to be updated when a new profile gets bundled
135 135 bundled = sorted(list_bundled_profiles())
136 136 assert bundled == []
137 137
138 138
139 139 def test_profile_create_ipython_dir():
140 140 """ipython profile create respects --ipython-dir"""
141 141 with TemporaryDirectory() as td:
142 142 getoutput(
143 143 [
144 144 sys.executable,
145 145 "-m",
146 146 "IPython",
147 147 "profile",
148 148 "create",
149 149 "foo",
150 150 "--ipython-dir=%s" % td,
151 151 ]
152 152 )
153 153 profile_dir = Path(td) / "profile_foo"
154 154 assert Path(profile_dir).exists()
155 155 ipython_config = profile_dir / "ipython_config.py"
156 156 assert Path(ipython_config).exists()
@@ -1,612 +1,620 b''
1 1 # encoding: utf-8
2 2 """Tests for code execution (%run and related), which is particularly tricky.
3 3
4 4 Because of how %run manages namespaces, and the fact that we are trying here to
5 5 verify subtle object deletion and reference counting issues, the %run tests
6 6 will be kept in this separate file. This makes it easier to aggregate in one
7 7 place the tricks needed to handle it; most other magics are much easier to test
8 8 and we do so in a common test_magic file.
9 9
10 10 Note that any test using `run -i` should make sure to do a `reset` afterwards,
11 11 as otherwise it may influence later tests.
12 12 """
13 13
14 14 # Copyright (c) IPython Development Team.
15 15 # Distributed under the terms of the Modified BSD License.
16 16
17 17
18 18
19 19 import functools
20 20 import os
21 21 import platform
22 22 from os.path import join as pjoin
23 23 import random
24 24 import string
25 25 import sys
26 26 import textwrap
27 27 import unittest
28 28 from unittest.mock import patch
29 29
30 30 import pytest
31 31
32 32 from IPython.testing import decorators as dec
33 33 from IPython.testing import tools as tt
34 34 from IPython.utils.io import capture_output
35 35 from IPython.utils.tempdir import TemporaryDirectory
36 36 from IPython.core import debugger
37 37
38 38 def doctest_refbug():
39 39 """Very nasty problem with references held by multiple runs of a script.
40 40 See: https://github.com/ipython/ipython/issues/141
41 41
42 42 In [1]: _ip.clear_main_mod_cache()
43 43 # random
44 44
45 45 In [2]: %run refbug
46 46
47 47 In [3]: call_f()
48 48 lowercased: hello
49 49
50 50 In [4]: %run refbug
51 51
52 52 In [5]: call_f()
53 53 lowercased: hello
54 54 lowercased: hello
55 55 """
56 56
57 57
58 58 def doctest_run_builtins():
59 59 r"""Check that %run doesn't damage __builtins__.
60 60
61 61 In [1]: import tempfile
62 62
63 63 In [2]: bid1 = id(__builtins__)
64 64
65 65 In [3]: fname = tempfile.mkstemp('.py')[1]
66 66
67 67 In [3]: f = open(fname, 'w', encoding='utf-8')
68 68
69 69 In [4]: dummy= f.write('pass\n')
70 70
71 71 In [5]: f.flush()
72 72
73 73 In [6]: t1 = type(__builtins__)
74 74
75 75 In [7]: %run $fname
76 76
77 77 In [7]: f.close()
78 78
79 79 In [8]: bid2 = id(__builtins__)
80 80
81 81 In [9]: t2 = type(__builtins__)
82 82
83 83 In [10]: t1 == t2
84 84 Out[10]: True
85 85
86 86 In [10]: bid1 == bid2
87 87 Out[10]: True
88 88
89 89 In [12]: try:
90 90 ....: os.unlink(fname)
91 91 ....: except:
92 92 ....: pass
93 93 ....:
94 94 """
95 95
96 96
97 97 def doctest_run_option_parser():
98 98 r"""Test option parser in %run.
99 99
100 100 In [1]: %run print_argv.py
101 101 []
102 102
103 103 In [2]: %run print_argv.py print*.py
104 104 ['print_argv.py']
105 105
106 106 In [3]: %run -G print_argv.py print*.py
107 107 ['print*.py']
108 108
109 109 """
110 110
111 111
112 112 @dec.skip_win32
113 113 def doctest_run_option_parser_for_posix():
114 114 r"""Test option parser in %run (Linux/OSX specific).
115 115
116 116 You need double quote to escape glob in POSIX systems:
117 117
118 118 In [1]: %run print_argv.py print\\*.py
119 119 ['print*.py']
120 120
121 121 You can't use quote to escape glob in POSIX systems:
122 122
123 123 In [2]: %run print_argv.py 'print*.py'
124 124 ['print_argv.py']
125 125
126 126 """
127 127
128 128
129 129 doctest_run_option_parser_for_posix.__skip_doctest__ = sys.platform == "win32"
130 130
131 131
132 132 @dec.skip_if_not_win32
133 133 def doctest_run_option_parser_for_windows():
134 134 r"""Test option parser in %run (Windows specific).
135 135
136 136 In Windows, you can't escape ``*` `by backslash:
137 137
138 138 In [1]: %run print_argv.py print\\*.py
139 139 ['print\\\\*.py']
140 140
141 141 You can use quote to escape glob:
142 142
143 143 In [2]: %run print_argv.py 'print*.py'
144 144 ["'print*.py'"]
145 145
146 146 """
147 147
148 148
149 149 doctest_run_option_parser_for_windows.__skip_doctest__ = sys.platform != "win32"
150 150
151 151
152 152 def doctest_reset_del():
153 153 """Test that resetting doesn't cause errors in __del__ methods.
154 154
155 155 In [2]: class A(object):
156 156 ...: def __del__(self):
157 157 ...: print(str("Hi"))
158 158 ...:
159 159
160 160 In [3]: a = A()
161 161
162 162 In [4]: get_ipython().reset(); import gc; x = gc.collect(0)
163 163 Hi
164 164
165 165 In [5]: 1+1
166 166 Out[5]: 2
167 167 """
168 168
169 169 # For some tests, it will be handy to organize them in a class with a common
170 170 # setup that makes a temp file
171 171
172 172 class TestMagicRunPass(tt.TempFileMixin):
173 173
174 174 def setUp(self):
175 175 content = "a = [1,2,3]\nb = 1"
176 176 self.mktmp(content)
177 177
178 178 def run_tmpfile(self):
179 179 _ip = get_ipython()
180 180 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
181 181 # See below and ticket https://bugs.launchpad.net/bugs/366353
182 182 _ip.magic('run %s' % self.fname)
183 183
184 184 def run_tmpfile_p(self):
185 185 _ip = get_ipython()
186 186 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
187 187 # See below and ticket https://bugs.launchpad.net/bugs/366353
188 188 _ip.magic('run -p %s' % self.fname)
189 189
190 190 def test_builtins_id(self):
191 191 """Check that %run doesn't damage __builtins__ """
192 192 _ip = get_ipython()
193 193 # Test that the id of __builtins__ is not modified by %run
194 194 bid1 = id(_ip.user_ns['__builtins__'])
195 195 self.run_tmpfile()
196 196 bid2 = id(_ip.user_ns['__builtins__'])
197 197 assert bid1 == bid2
198 198
199 199 def test_builtins_type(self):
200 200 """Check that the type of __builtins__ doesn't change with %run.
201 201
202 202 However, the above could pass if __builtins__ was already modified to
203 203 be a dict (it should be a module) by a previous use of %run. So we
204 204 also check explicitly that it really is a module:
205 205 """
206 206 _ip = get_ipython()
207 207 self.run_tmpfile()
208 208 assert type(_ip.user_ns["__builtins__"]) == type(sys)
209 209
210 210 def test_run_profile(self):
211 211 """Test that the option -p, which invokes the profiler, do not
212 212 crash by invoking execfile"""
213 213 self.run_tmpfile_p()
214 214
215 215 def test_run_debug_twice(self):
216 216 # https://github.com/ipython/ipython/issues/10028
217 217 _ip = get_ipython()
218 218 with tt.fake_input(['c']):
219 219 _ip.magic('run -d %s' % self.fname)
220 220 with tt.fake_input(['c']):
221 221 _ip.magic('run -d %s' % self.fname)
222 222
223 223 def test_run_debug_twice_with_breakpoint(self):
224 224 """Make a valid python temp file."""
225 225 _ip = get_ipython()
226 226 with tt.fake_input(['b 2', 'c', 'c']):
227 227 _ip.magic('run -d %s' % self.fname)
228 228
229 229 with tt.fake_input(['c']):
230 230 with tt.AssertNotPrints('KeyError'):
231 231 _ip.magic('run -d %s' % self.fname)
232 232
233 233
234 234 class TestMagicRunSimple(tt.TempFileMixin):
235 235
236 236 def test_simpledef(self):
237 237 """Test that simple class definitions work."""
238 238 src = ("class foo: pass\n"
239 239 "def f(): return foo()")
240 240 self.mktmp(src)
241 241 _ip.magic("run %s" % self.fname)
242 242 _ip.run_cell("t = isinstance(f(), foo)")
243 243 assert _ip.user_ns["t"] is True
244 244
245 245 @pytest.mark.xfail(
246 246 platform.python_implementation() == "PyPy",
247 247 reason="expecting __del__ call on exit is unreliable and doesn't happen on PyPy",
248 248 )
249 249 def test_obj_del(self):
250 250 """Test that object's __del__ methods are called on exit."""
251 251 src = ("class A(object):\n"
252 252 " def __del__(self):\n"
253 253 " print('object A deleted')\n"
254 254 "a = A()\n")
255 255 self.mktmp(src)
256 256 err = None
257 257 tt.ipexec_validate(self.fname, 'object A deleted', err)
258 258
259 259 def test_aggressive_namespace_cleanup(self):
260 260 """Test that namespace cleanup is not too aggressive GH-238
261 261
262 262 Returning from another run magic deletes the namespace"""
263 263 # see ticket https://github.com/ipython/ipython/issues/238
264 264
265 265 with tt.TempFileMixin() as empty:
266 266 empty.mktmp("")
267 267 # On Windows, the filename will have \users in it, so we need to use the
268 268 # repr so that the \u becomes \\u.
269 269 src = (
270 270 "ip = get_ipython()\n"
271 271 "for i in range(5):\n"
272 272 " try:\n"
273 273 " ip.magic(%r)\n"
274 274 " except NameError as e:\n"
275 275 " print(i)\n"
276 276 " break\n" % ("run " + empty.fname)
277 277 )
278 278 self.mktmp(src)
279 279 _ip.magic("run %s" % self.fname)
280 280 _ip.run_cell("ip == get_ipython()")
281 281 assert _ip.user_ns["i"] == 4
282 282
283 283 def test_run_second(self):
284 284 """Test that running a second file doesn't clobber the first, gh-3547"""
285 285 self.mktmp("avar = 1\n" "def afunc():\n" " return avar\n")
286 286
287 287 with tt.TempFileMixin() as empty:
288 288 empty.mktmp("")
289 289
290 290 _ip.magic("run %s" % self.fname)
291 291 _ip.magic("run %s" % empty.fname)
292 292 assert _ip.user_ns["afunc"]() == 1
293 293
294 294 def test_tclass(self):
295 295 mydir = os.path.dirname(__file__)
296 296 tc = os.path.join(mydir, "tclass")
297 297 src = f"""\
298 298 import gc
299 299 %run "{tc}" C-first
300 300 gc.collect(0)
301 301 %run "{tc}" C-second
302 302 gc.collect(0)
303 303 %run "{tc}" C-third
304 304 gc.collect(0)
305 305 %reset -f
306 306 """
307 307 self.mktmp(src, ".ipy")
308 308 out = """\
309 309 ARGV 1-: ['C-first']
310 310 ARGV 1-: ['C-second']
311 311 tclass.py: deleting object: C-first
312 312 ARGV 1-: ['C-third']
313 313 tclass.py: deleting object: C-second
314 314 tclass.py: deleting object: C-third
315 315 """
316 316 err = None
317 317 tt.ipexec_validate(self.fname, out, err)
318 318
319 319 def test_run_i_after_reset(self):
320 320 """Check that %run -i still works after %reset (gh-693)"""
321 321 src = "yy = zz\n"
322 322 self.mktmp(src)
323 323 _ip.run_cell("zz = 23")
324 324 try:
325 325 _ip.magic("run -i %s" % self.fname)
326 326 assert _ip.user_ns["yy"] == 23
327 327 finally:
328 328 _ip.magic('reset -f')
329 329
330 330 _ip.run_cell("zz = 23")
331 331 try:
332 332 _ip.magic("run -i %s" % self.fname)
333 333 assert _ip.user_ns["yy"] == 23
334 334 finally:
335 335 _ip.magic('reset -f')
336 336
337 337 def test_unicode(self):
338 338 """Check that files in odd encodings are accepted."""
339 339 mydir = os.path.dirname(__file__)
340 340 na = os.path.join(mydir, 'nonascii.py')
341 341 _ip.magic('run "%s"' % na)
342 342 assert _ip.user_ns["u"] == "Ўт№Ф"
343 343
344 344 def test_run_py_file_attribute(self):
345 345 """Test handling of `__file__` attribute in `%run <file>.py`."""
346 346 src = "t = __file__\n"
347 347 self.mktmp(src)
348 348 _missing = object()
349 349 file1 = _ip.user_ns.get('__file__', _missing)
350 350 _ip.magic('run %s' % self.fname)
351 351 file2 = _ip.user_ns.get('__file__', _missing)
352 352
353 353 # Check that __file__ was equal to the filename in the script's
354 354 # namespace.
355 355 assert _ip.user_ns["t"] == self.fname
356 356
357 357 # Check that __file__ was not leaked back into user_ns.
358 358 assert file1 == file2
359 359
360 360 def test_run_ipy_file_attribute(self):
361 361 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
362 362 src = "t = __file__\n"
363 363 self.mktmp(src, ext='.ipy')
364 364 _missing = object()
365 365 file1 = _ip.user_ns.get('__file__', _missing)
366 366 _ip.magic('run %s' % self.fname)
367 367 file2 = _ip.user_ns.get('__file__', _missing)
368 368
369 369 # Check that __file__ was equal to the filename in the script's
370 370 # namespace.
371 371 assert _ip.user_ns["t"] == self.fname
372 372
373 373 # Check that __file__ was not leaked back into user_ns.
374 374 assert file1 == file2
375 375
376 376 def test_run_formatting(self):
377 377 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
378 378 src = "pass"
379 379 self.mktmp(src)
380 380 _ip.magic('run -t -N 1 %s' % self.fname)
381 381 _ip.magic('run -t -N 10 %s' % self.fname)
382 382
383 383 def test_ignore_sys_exit(self):
384 384 """Test the -e option to ignore sys.exit()"""
385 385 src = "import sys; sys.exit(1)"
386 386 self.mktmp(src)
387 387 with tt.AssertPrints('SystemExit'):
388 388 _ip.magic('run %s' % self.fname)
389 389
390 390 with tt.AssertNotPrints('SystemExit'):
391 391 _ip.magic('run -e %s' % self.fname)
392 392
393 393 def test_run_nb(self):
394 394 """Test %run notebook.ipynb"""
395 395 pytest.importorskip("nbformat")
396 396 from nbformat import v4, writes
397 397 nb = v4.new_notebook(
398 398 cells=[
399 399 v4.new_markdown_cell("The Ultimate Question of Everything"),
400 400 v4.new_code_cell("answer=42")
401 401 ]
402 402 )
403 403 src = writes(nb, version=4)
404 404 self.mktmp(src, ext='.ipynb')
405 405
406 406 _ip.magic("run %s" % self.fname)
407 407
408 408 assert _ip.user_ns["answer"] == 42
409 409
410 410 def test_run_nb_error(self):
411 411 """Test %run notebook.ipynb error"""
412 412 pytest.importorskip("nbformat")
413 413 from nbformat import v4, writes
414 414 # %run when a file name isn't provided
415 415 pytest.raises(Exception, _ip.magic, "run")
416 416
417 417 # %run when a file doesn't exist
418 418 pytest.raises(Exception, _ip.magic, "run foobar.ipynb")
419 419
420 420 # %run on a notebook with an error
421 421 nb = v4.new_notebook(
422 422 cells=[
423 423 v4.new_code_cell("0/0")
424 424 ]
425 425 )
426 426 src = writes(nb, version=4)
427 427 self.mktmp(src, ext='.ipynb')
428 428 pytest.raises(Exception, _ip.magic, "run %s" % self.fname)
429 429
430 430 def test_file_options(self):
431 431 src = ('import sys\n'
432 432 'a = " ".join(sys.argv[1:])\n')
433 433 self.mktmp(src)
434 434 test_opts = "-x 3 --verbose"
435 435 _ip.run_line_magic("run", "{0} {1}".format(self.fname, test_opts))
436 436 assert _ip.user_ns["a"] == test_opts
437 437
438 438
439 439 class TestMagicRunWithPackage(unittest.TestCase):
440 440
441 441 def writefile(self, name, content):
442 442 path = os.path.join(self.tempdir.name, name)
443 443 d = os.path.dirname(path)
444 444 if not os.path.isdir(d):
445 445 os.makedirs(d)
446 with open(path, 'w', encoding='utf-8') as f:
446 with open(path, "w", encoding="utf-8") as f:
447 447 f.write(textwrap.dedent(content))
448 448
449 449 def setUp(self):
450 450 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
451 451 """Temporary (probably) valid python package name."""
452 452
453 453 self.value = int(random.random() * 10000)
454 454
455 455 self.tempdir = TemporaryDirectory()
456 456 self.__orig_cwd = os.getcwd()
457 457 sys.path.insert(0, self.tempdir.name)
458 458
459 459 self.writefile(os.path.join(package, '__init__.py'), '')
460 460 self.writefile(os.path.join(package, 'sub.py'), """
461 461 x = {0!r}
462 462 """.format(self.value))
463 463 self.writefile(os.path.join(package, 'relative.py'), """
464 464 from .sub import x
465 465 """)
466 466 self.writefile(os.path.join(package, 'absolute.py'), """
467 467 from {0}.sub import x
468 468 """.format(package))
469 469 self.writefile(os.path.join(package, 'args.py'), """
470 470 import sys
471 471 a = " ".join(sys.argv[1:])
472 472 """.format(package))
473 473
474 474 def tearDown(self):
475 475 os.chdir(self.__orig_cwd)
476 476 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
477 477 self.tempdir.cleanup()
478 478
479 479 def check_run_submodule(self, submodule, opts=''):
480 480 _ip.user_ns.pop('x', None)
481 481 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
482 482 self.assertEqual(_ip.user_ns['x'], self.value,
483 483 'Variable `x` is not loaded from module `{0}`.'
484 484 .format(submodule))
485 485
486 486 def test_run_submodule_with_absolute_import(self):
487 487 self.check_run_submodule('absolute')
488 488
489 489 def test_run_submodule_with_relative_import(self):
490 490 """Run submodule that has a relative import statement (#2727)."""
491 491 self.check_run_submodule('relative')
492 492
493 493 def test_prun_submodule_with_absolute_import(self):
494 494 self.check_run_submodule('absolute', '-p')
495 495
496 496 def test_prun_submodule_with_relative_import(self):
497 497 self.check_run_submodule('relative', '-p')
498 498
499 499 def with_fake_debugger(func):
500 500 @functools.wraps(func)
501 501 def wrapper(*args, **kwds):
502 502 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
503 503 return func(*args, **kwds)
504 504 return wrapper
505 505
506 506 @with_fake_debugger
507 507 def test_debug_run_submodule_with_absolute_import(self):
508 508 self.check_run_submodule('absolute', '-d')
509 509
510 510 @with_fake_debugger
511 511 def test_debug_run_submodule_with_relative_import(self):
512 512 self.check_run_submodule('relative', '-d')
513 513
514 514 def test_module_options(self):
515 515 _ip.user_ns.pop("a", None)
516 516 test_opts = "-x abc -m test"
517 517 _ip.run_line_magic("run", "-m {0}.args {1}".format(self.package, test_opts))
518 518 assert _ip.user_ns["a"] == test_opts
519 519
520 520 def test_module_options_with_separator(self):
521 521 _ip.user_ns.pop("a", None)
522 522 test_opts = "-x abc -m test"
523 523 _ip.run_line_magic("run", "-m {0}.args -- {1}".format(self.package, test_opts))
524 524 assert _ip.user_ns["a"] == test_opts
525 525
526 526
527 527 def test_run__name__():
528 528 with TemporaryDirectory() as td:
529 path = pjoin(td, 'foo.py')
530 with open(path, 'w', encoding='utf-8') as f:
529 path = pjoin(td, "foo.py")
530 with open(path, "w", encoding="utf-8") as f:
531 531 f.write("q = __name__")
532 532
533 533 _ip.user_ns.pop("q", None)
534 534 _ip.magic("run {}".format(path))
535 535 assert _ip.user_ns.pop("q") == "__main__"
536 536
537 537 _ip.magic("run -n {}".format(path))
538 538 assert _ip.user_ns.pop("q") == "foo"
539 539
540 540 try:
541 541 _ip.magic("run -i -n {}".format(path))
542 542 assert _ip.user_ns.pop("q") == "foo"
543 543 finally:
544 544 _ip.magic('reset -f')
545 545
546 546
547 547 def test_run_tb():
548 548 """Test traceback offset in %run"""
549 549 with TemporaryDirectory() as td:
550 path = pjoin(td, 'foo.py')
551 with open(path, 'w', encoding='utf-8') as f:
552 f.write('\n'.join([
553 "def foo():",
554 " return bar()",
555 "def bar():",
556 " raise RuntimeError('hello!')",
557 "foo()",
558 ]))
550 path = pjoin(td, "foo.py")
551 with open(path, "w", encoding="utf-8") as f:
552 f.write(
553 "\n".join(
554 [
555 "def foo():",
556 " return bar()",
557 "def bar():",
558 " raise RuntimeError('hello!')",
559 "foo()",
560 ]
561 )
562 )
559 563 with capture_output() as io:
560 564 _ip.magic('run {}'.format(path))
561 565 out = io.stdout
562 566 assert "execfile" not in out
563 567 assert "RuntimeError" in out
564 568 assert out.count("---->") == 3
565 569 del ip.user_ns['bar']
566 570 del ip.user_ns['foo']
567 571
568 572
569 573 def test_multiprocessing_run():
570 574 """Set we can run mutiprocesgin without messing up up main namespace
571 575
572 576 Note that import `nose.tools as nt` mdify the value s
573 577 sys.module['__mp_main__'] so we need to temporarily set it to None to test
574 578 the issue.
575 579 """
576 580 with TemporaryDirectory() as td:
577 581 mpm = sys.modules.get('__mp_main__')
578 582 sys.modules['__mp_main__'] = None
579 583 try:
580 path = pjoin(td, 'test.py')
581 with open(path, 'w', encoding='utf-8') as f:
584 path = pjoin(td, "test.py")
585 with open(path, "w", encoding="utf-8") as f:
582 586 f.write("import multiprocessing\nprint('hoy')")
583 587 with capture_output() as io:
584 588 _ip.run_line_magic('run', path)
585 589 _ip.run_cell("i_m_undefined")
586 590 out = io.stdout
587 591 assert "hoy" in out
588 592 assert "AttributeError" not in out
589 593 assert "NameError" in out
590 594 assert out.count("---->") == 1
591 595 except:
592 596 raise
593 597 finally:
594 598 sys.modules['__mp_main__'] = mpm
595 599
596 600
597 601 def test_script_tb():
598 602 """Test traceback offset in `ipython script.py`"""
599 603 with TemporaryDirectory() as td:
600 path = pjoin(td, 'foo.py')
601 with open(path, 'w', encoding='utf-8') as f:
602 f.write('\n'.join([
603 "def foo():",
604 " return bar()",
605 "def bar():",
606 " raise RuntimeError('hello!')",
607 "foo()",
608 ]))
604 path = pjoin(td, "foo.py")
605 with open(path, "w", encoding="utf-8") as f:
606 f.write(
607 "\n".join(
608 [
609 "def foo():",
610 " return bar()",
611 "def bar():",
612 " raise RuntimeError('hello!')",
613 "foo()",
614 ]
615 )
616 )
609 617 out, err = tt.ipexec(path)
610 618 assert "execfile" not in out
611 619 assert "RuntimeError" in out
612 620 assert out.count("---->") == 3
@@ -1,410 +1,410 b''
1 1 # encoding: utf-8
2 2 """Tests for IPython.core.ultratb
3 3 """
4 4 import io
5 5 import logging
6 6 import platform
7 7 import re
8 8 import sys
9 9 import os.path
10 10 from textwrap import dedent
11 11 import traceback
12 12 import unittest
13 13
14 14 from IPython.core.ultratb import ColorTB, VerboseTB
15 15
16 16
17 17 from IPython.testing import tools as tt
18 18 from IPython.testing.decorators import onlyif_unicode_paths
19 19 from IPython.utils.syspathcontext import prepended_to_syspath
20 20 from IPython.utils.tempdir import TemporaryDirectory
21 21
22 22 file_1 = """1
23 23 2
24 24 3
25 25 def f():
26 26 1/0
27 27 """
28 28
29 29 file_2 = """def f():
30 30 1/0
31 31 """
32 32
33 33
34 34 def recursionlimit(frames):
35 35 """
36 36 decorator to set the recursion limit temporarily
37 37 """
38 38
39 39 def inner(test_function):
40 40 def wrapper(*args, **kwargs):
41 41 rl = sys.getrecursionlimit()
42 42 sys.setrecursionlimit(frames)
43 43 try:
44 44 return test_function(*args, **kwargs)
45 45 finally:
46 46 sys.setrecursionlimit(rl)
47 47
48 48 return wrapper
49 49
50 50 return inner
51 51
52 52
53 53 class ChangedPyFileTest(unittest.TestCase):
54 54 def test_changing_py_file(self):
55 55 """Traceback produced if the line where the error occurred is missing?
56 56
57 57 https://github.com/ipython/ipython/issues/1456
58 58 """
59 59 with TemporaryDirectory() as td:
60 60 fname = os.path.join(td, "foo.py")
61 with open(fname, "w", encoding='utf-8') as f:
61 with open(fname, "w", encoding="utf-8") as f:
62 62 f.write(file_1)
63 63
64 64 with prepended_to_syspath(td):
65 65 ip.run_cell("import foo")
66 66
67 67 with tt.AssertPrints("ZeroDivisionError"):
68 68 ip.run_cell("foo.f()")
69 69
70 70 # Make the file shorter, so the line of the error is missing.
71 with open(fname, "w", encoding='utf-8') as f:
71 with open(fname, "w", encoding="utf-8") as f:
72 72 f.write(file_2)
73 73
74 74 # For some reason, this was failing on the *second* call after
75 75 # changing the file, so we call f() twice.
76 76 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
77 77 with tt.AssertPrints("ZeroDivisionError"):
78 78 ip.run_cell("foo.f()")
79 79 with tt.AssertPrints("ZeroDivisionError"):
80 80 ip.run_cell("foo.f()")
81 81
82 82 iso_8859_5_file = u'''# coding: iso-8859-5
83 83
84 84 def fail():
85 85 """дбИЖ"""
86 86 1/0 # дбИЖ
87 87 '''
88 88
89 89 class NonAsciiTest(unittest.TestCase):
90 90 @onlyif_unicode_paths
91 91 def test_nonascii_path(self):
92 92 # Non-ascii directory name as well.
93 93 with TemporaryDirectory(suffix=u'é') as td:
94 94 fname = os.path.join(td, u"fooé.py")
95 with open(fname, "w", encoding='utf-8') as f:
95 with open(fname, "w", encoding="utf-8") as f:
96 96 f.write(file_1)
97 97
98 98 with prepended_to_syspath(td):
99 99 ip.run_cell("import foo")
100 100
101 101 with tt.AssertPrints("ZeroDivisionError"):
102 102 ip.run_cell("foo.f()")
103 103
104 104 def test_iso8859_5(self):
105 105 with TemporaryDirectory() as td:
106 106 fname = os.path.join(td, 'dfghjkl.py')
107 107
108 108 with io.open(fname, 'w', encoding='iso-8859-5') as f:
109 109 f.write(iso_8859_5_file)
110 110
111 111 with prepended_to_syspath(td):
112 112 ip.run_cell("from dfghjkl import fail")
113 113
114 114 with tt.AssertPrints("ZeroDivisionError"):
115 115 with tt.AssertPrints(u'дбИЖ', suppress=False):
116 116 ip.run_cell('fail()')
117 117
118 118 def test_nonascii_msg(self):
119 119 cell = u"raise Exception('é')"
120 120 expected = u"Exception('é')"
121 121 ip.run_cell("%xmode plain")
122 122 with tt.AssertPrints(expected):
123 123 ip.run_cell(cell)
124 124
125 125 ip.run_cell("%xmode verbose")
126 126 with tt.AssertPrints(expected):
127 127 ip.run_cell(cell)
128 128
129 129 ip.run_cell("%xmode context")
130 130 with tt.AssertPrints(expected):
131 131 ip.run_cell(cell)
132 132
133 133 ip.run_cell("%xmode minimal")
134 134 with tt.AssertPrints(u"Exception: é"):
135 135 ip.run_cell(cell)
136 136
137 137 # Put this back into Context mode for later tests.
138 138 ip.run_cell("%xmode context")
139 139
140 140 class NestedGenExprTestCase(unittest.TestCase):
141 141 """
142 142 Regression test for the following issues:
143 143 https://github.com/ipython/ipython/issues/8293
144 144 https://github.com/ipython/ipython/issues/8205
145 145 """
146 146 def test_nested_genexpr(self):
147 147 code = dedent(
148 148 """\
149 149 class SpecificException(Exception):
150 150 pass
151 151
152 152 def foo(x):
153 153 raise SpecificException("Success!")
154 154
155 155 sum(sum(foo(x) for _ in [0]) for x in [0])
156 156 """
157 157 )
158 158 with tt.AssertPrints('SpecificException: Success!', suppress=False):
159 159 ip.run_cell(code)
160 160
161 161
162 162 indentationerror_file = """if True:
163 163 zoon()
164 164 """
165 165
166 166 class IndentationErrorTest(unittest.TestCase):
167 167 def test_indentationerror_shows_line(self):
168 168 # See issue gh-2398
169 169 with tt.AssertPrints("IndentationError"):
170 170 with tt.AssertPrints("zoon()", suppress=False):
171 171 ip.run_cell(indentationerror_file)
172 172
173 173 with TemporaryDirectory() as td:
174 174 fname = os.path.join(td, "foo.py")
175 with open(fname, "w", encoding='utf-8') as f:
175 with open(fname, "w", encoding="utf-8") as f:
176 176 f.write(indentationerror_file)
177 177
178 178 with tt.AssertPrints("IndentationError"):
179 179 with tt.AssertPrints("zoon()", suppress=False):
180 180 ip.magic('run %s' % fname)
181 181
182 182 se_file_1 = """1
183 183 2
184 184 7/
185 185 """
186 186
187 187 se_file_2 = """7/
188 188 """
189 189
190 190 class SyntaxErrorTest(unittest.TestCase):
191 191
192 192 def test_syntaxerror_no_stacktrace_at_compile_time(self):
193 193 syntax_error_at_compile_time = """
194 194 def foo():
195 195 ..
196 196 """
197 197 with tt.AssertPrints("SyntaxError"):
198 198 ip.run_cell(syntax_error_at_compile_time)
199 199
200 200 with tt.AssertNotPrints("foo()"):
201 201 ip.run_cell(syntax_error_at_compile_time)
202 202
203 203 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
204 204 syntax_error_at_runtime = """
205 205 def foo():
206 206 eval("..")
207 207
208 208 def bar():
209 209 foo()
210 210
211 211 bar()
212 212 """
213 213 with tt.AssertPrints("SyntaxError"):
214 214 ip.run_cell(syntax_error_at_runtime)
215 215 # Assert syntax error during runtime generate stacktrace
216 216 with tt.AssertPrints(["foo()", "bar()"]):
217 217 ip.run_cell(syntax_error_at_runtime)
218 218 del ip.user_ns['bar']
219 219 del ip.user_ns['foo']
220 220
221 221 def test_changing_py_file(self):
222 222 with TemporaryDirectory() as td:
223 223 fname = os.path.join(td, "foo.py")
224 with open(fname, 'w', encoding='utf-8') as f:
224 with open(fname, "w", encoding="utf-8") as f:
225 225 f.write(se_file_1)
226 226
227 227 with tt.AssertPrints(["7/", "SyntaxError"]):
228 228 ip.magic("run " + fname)
229 229
230 230 # Modify the file
231 with open(fname, 'w', encoding='utf-8') as f:
231 with open(fname, "w", encoding="utf-8") as f:
232 232 f.write(se_file_2)
233 233
234 234 # The SyntaxError should point to the correct line
235 235 with tt.AssertPrints(["7/", "SyntaxError"]):
236 236 ip.magic("run " + fname)
237 237
238 238 def test_non_syntaxerror(self):
239 239 # SyntaxTB may be called with an error other than a SyntaxError
240 240 # See e.g. gh-4361
241 241 try:
242 242 raise ValueError('QWERTY')
243 243 except ValueError:
244 244 with tt.AssertPrints('QWERTY'):
245 245 ip.showsyntaxerror()
246 246
247 247 import sys
248 248
249 249 if sys.version_info < (3, 9) and platform.python_implementation() != "PyPy":
250 250 """
251 251 New 3.9 Pgen Parser does not raise Memory error, except on failed malloc.
252 252 """
253 253 class MemoryErrorTest(unittest.TestCase):
254 254 def test_memoryerror(self):
255 255 memoryerror_code = "(" * 200 + ")" * 200
256 256 with tt.AssertPrints("MemoryError"):
257 257 ip.run_cell(memoryerror_code)
258 258
259 259
260 260 class Python3ChainedExceptionsTest(unittest.TestCase):
261 261 DIRECT_CAUSE_ERROR_CODE = """
262 262 try:
263 263 x = 1 + 2
264 264 print(not_defined_here)
265 265 except Exception as e:
266 266 x += 55
267 267 x - 1
268 268 y = {}
269 269 raise KeyError('uh') from e
270 270 """
271 271
272 272 EXCEPTION_DURING_HANDLING_CODE = """
273 273 try:
274 274 x = 1 + 2
275 275 print(not_defined_here)
276 276 except Exception as e:
277 277 x += 55
278 278 x - 1
279 279 y = {}
280 280 raise KeyError('uh')
281 281 """
282 282
283 283 SUPPRESS_CHAINING_CODE = """
284 284 try:
285 285 1/0
286 286 except Exception:
287 287 raise ValueError("Yikes") from None
288 288 """
289 289
290 290 def test_direct_cause_error(self):
291 291 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
292 292 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
293 293
294 294 def test_exception_during_handling_error(self):
295 295 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
296 296 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
297 297
298 298 def test_suppress_exception_chaining(self):
299 299 with tt.AssertNotPrints("ZeroDivisionError"), \
300 300 tt.AssertPrints("ValueError", suppress=False):
301 301 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
302 302
303 303 def test_plain_direct_cause_error(self):
304 304 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
305 305 ip.run_cell("%xmode Plain")
306 306 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
307 307 ip.run_cell("%xmode Verbose")
308 308
309 309 def test_plain_exception_during_handling_error(self):
310 310 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
311 311 ip.run_cell("%xmode Plain")
312 312 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
313 313 ip.run_cell("%xmode Verbose")
314 314
315 315 def test_plain_suppress_exception_chaining(self):
316 316 with tt.AssertNotPrints("ZeroDivisionError"), \
317 317 tt.AssertPrints("ValueError", suppress=False):
318 318 ip.run_cell("%xmode Plain")
319 319 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
320 320 ip.run_cell("%xmode Verbose")
321 321
322 322
323 323 class RecursionTest(unittest.TestCase):
324 324 DEFINITIONS = """
325 325 def non_recurs():
326 326 1/0
327 327
328 328 def r1():
329 329 r1()
330 330
331 331 def r3a():
332 332 r3b()
333 333
334 334 def r3b():
335 335 r3c()
336 336
337 337 def r3c():
338 338 r3a()
339 339
340 340 def r3o1():
341 341 r3a()
342 342
343 343 def r3o2():
344 344 r3o1()
345 345 """
346 346 def setUp(self):
347 347 ip.run_cell(self.DEFINITIONS)
348 348
349 349 def test_no_recursion(self):
350 350 with tt.AssertNotPrints("skipping similar frames"):
351 351 ip.run_cell("non_recurs()")
352 352
353 353 @recursionlimit(200)
354 354 def test_recursion_one_frame(self):
355 355 with tt.AssertPrints(re.compile(
356 356 r"\[\.\.\. skipping similar frames: r1 at line 5 \(\d{2,3} times\)\]")
357 357 ):
358 358 ip.run_cell("r1()")
359 359
360 360 @recursionlimit(160)
361 361 def test_recursion_three_frames(self):
362 362 with tt.AssertPrints("[... skipping similar frames: "), \
363 363 tt.AssertPrints(re.compile(r"r3a at line 8 \(\d{2} times\)"), suppress=False), \
364 364 tt.AssertPrints(re.compile(r"r3b at line 11 \(\d{2} times\)"), suppress=False), \
365 365 tt.AssertPrints(re.compile(r"r3c at line 14 \(\d{2} times\)"), suppress=False):
366 366 ip.run_cell("r3o2()")
367 367
368 368
369 369 #----------------------------------------------------------------------------
370 370
371 371 # module testing (minimal)
372 372 def test_handlers():
373 373 def spam(c, d_e):
374 374 (d, e) = d_e
375 375 x = c + d
376 376 y = c * d
377 377 foo(x, y)
378 378
379 379 def foo(a, b, bar=1):
380 380 eggs(a, b + bar)
381 381
382 382 def eggs(f, g, z=globals()):
383 383 h = f + g
384 384 i = f - g
385 385 return h / i
386 386
387 387 buff = io.StringIO()
388 388
389 389 buff.write('')
390 390 buff.write('*** Before ***')
391 391 try:
392 392 buff.write(spam(1, (2, 3)))
393 393 except:
394 394 traceback.print_exc(file=buff)
395 395
396 396 handler = ColorTB(ostream=buff)
397 397 buff.write('*** ColorTB ***')
398 398 try:
399 399 buff.write(spam(1, (2, 3)))
400 400 except:
401 401 handler(*sys.exc_info())
402 402 buff.write('')
403 403
404 404 handler = VerboseTB(ostream=buff)
405 405 buff.write('*** VerboseTB ***')
406 406 try:
407 407 buff.write(spam(1, (2, 3)))
408 408 except:
409 409 handler(*sys.exc_info())
410 410 buff.write('')
@@ -1,236 +1,236 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 %store magic for lightweight persistence.
4 4
5 5 Stores variables, aliases and macros in IPython's database.
6 6
7 7 To automatically restore stored variables at startup, add this to your
8 8 :file:`ipython_config.py` file::
9 9
10 10 c.StoreMagics.autorestore = True
11 11 """
12 12
13 13 # Copyright (c) IPython Development Team.
14 14 # Distributed under the terms of the Modified BSD License.
15 15
16 16 import inspect, os, sys, textwrap
17 17
18 18 from IPython.core.error import UsageError
19 19 from IPython.core.magic import Magics, magics_class, line_magic
20 20 from IPython.testing.skipdoctest import skip_doctest
21 21 from traitlets import Bool
22 22
23 23
24 24 def restore_aliases(ip, alias=None):
25 25 staliases = ip.db.get('stored_aliases', {})
26 26 if alias is None:
27 27 for k,v in staliases.items():
28 28 #print "restore alias",k,v # dbg
29 29 #self.alias_table[k] = v
30 30 ip.alias_manager.define_alias(k,v)
31 31 else:
32 32 ip.alias_manager.define_alias(alias, staliases[alias])
33 33
34 34
35 35 def refresh_variables(ip):
36 36 db = ip.db
37 37 for key in db.keys('autorestore/*'):
38 38 # strip autorestore
39 39 justkey = os.path.basename(key)
40 40 try:
41 41 obj = db[key]
42 42 except KeyError:
43 43 print("Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey)
44 44 print("The error was:", sys.exc_info()[0])
45 45 else:
46 46 #print "restored",justkey,"=",obj #dbg
47 47 ip.user_ns[justkey] = obj
48 48
49 49
50 50 def restore_dhist(ip):
51 51 ip.user_ns['_dh'] = ip.db.get('dhist',[])
52 52
53 53
54 54 def restore_data(ip):
55 55 refresh_variables(ip)
56 56 restore_aliases(ip)
57 57 restore_dhist(ip)
58 58
59 59
60 60 @magics_class
61 61 class StoreMagics(Magics):
62 62 """Lightweight persistence for python variables.
63 63
64 64 Provides the %store magic."""
65 65
66 66 autorestore = Bool(False, help=
67 67 """If True, any %store-d variables will be automatically restored
68 68 when IPython starts.
69 69 """
70 70 ).tag(config=True)
71 71
72 72 def __init__(self, shell):
73 73 super(StoreMagics, self).__init__(shell=shell)
74 74 self.shell.configurables.append(self)
75 75 if self.autorestore:
76 76 restore_data(self.shell)
77 77
78 78 @skip_doctest
79 79 @line_magic
80 80 def store(self, parameter_s=''):
81 81 """Lightweight persistence for python variables.
82 82
83 83 Example::
84 84
85 85 In [1]: l = ['hello',10,'world']
86 86 In [2]: %store l
87 87 Stored 'l' (list)
88 88 In [3]: exit
89 89
90 90 (IPython session is closed and started again...)
91 91
92 92 ville@badger:~$ ipython
93 93 In [1]: l
94 94 NameError: name 'l' is not defined
95 95 In [2]: %store -r
96 96 In [3]: l
97 97 Out[3]: ['hello', 10, 'world']
98 98
99 99 Usage:
100 100
101 101 * ``%store`` - Show list of all variables and their current
102 102 values
103 103 * ``%store spam bar`` - Store the *current* value of the variables spam
104 104 and bar to disk
105 105 * ``%store -d spam`` - Remove the variable and its value from storage
106 106 * ``%store -z`` - Remove all variables from storage
107 107 * ``%store -r`` - Refresh all variables, aliases and directory history
108 108 from store (overwrite current vals)
109 109 * ``%store -r spam bar`` - Refresh specified variables and aliases from store
110 110 (delete current val)
111 111 * ``%store foo >a.txt`` - Store value of foo to new file a.txt
112 112 * ``%store foo >>a.txt`` - Append value of foo to file a.txt
113 113
114 114 It should be noted that if you change the value of a variable, you
115 115 need to %store it again if you want to persist the new value.
116 116
117 117 Note also that the variables will need to be pickleable; most basic
118 118 python types can be safely %store'd.
119 119
120 120 Also aliases can be %store'd across sessions.
121 121 To remove an alias from the storage, use the %unalias magic.
122 122 """
123 123
124 124 opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
125 125 args = argsl.split()
126 126 ip = self.shell
127 127 db = ip.db
128 128 # delete
129 129 if 'd' in opts:
130 130 try:
131 131 todel = args[0]
132 132 except IndexError as e:
133 133 raise UsageError('You must provide the variable to forget') from e
134 134 else:
135 135 try:
136 136 del db['autorestore/' + todel]
137 137 except BaseException as e:
138 138 raise UsageError("Can't delete variable '%s'" % todel) from e
139 139 # reset
140 140 elif 'z' in opts:
141 141 for k in db.keys('autorestore/*'):
142 142 del db[k]
143 143
144 144 elif 'r' in opts:
145 145 if args:
146 146 for arg in args:
147 147 try:
148 148 obj = db['autorestore/' + arg]
149 149 except KeyError:
150 150 try:
151 151 restore_aliases(ip, alias=arg)
152 152 except KeyError:
153 153 print("no stored variable or alias %s" % arg)
154 154 else:
155 155 ip.user_ns[arg] = obj
156 156 else:
157 157 restore_data(ip)
158 158
159 159 # run without arguments -> list variables & values
160 160 elif not args:
161 161 vars = db.keys('autorestore/*')
162 162 vars.sort()
163 163 if vars:
164 164 size = max(map(len, vars))
165 165 else:
166 166 size = 0
167 167
168 168 print('Stored variables and their in-db values:')
169 169 fmt = '%-'+str(size)+'s -> %s'
170 170 get = db.get
171 171 for var in vars:
172 172 justkey = os.path.basename(var)
173 173 # print 30 first characters from every var
174 174 print(fmt % (justkey, repr(get(var, '<unavailable>'))[:50]))
175 175
176 176 # default action - store the variable
177 177 else:
178 178 # %store foo >file.txt or >>file.txt
179 if len(args) > 1 and args[1].startswith('>'):
180 fnam = os.path.expanduser(args[1].lstrip('>').lstrip())
181 if args[1].startswith('>>'):
182 fil = open(fnam, 'a', encoding='utf-8')
179 if len(args) > 1 and args[1].startswith(">"):
180 fnam = os.path.expanduser(args[1].lstrip(">").lstrip())
181 if args[1].startswith(">>"):
182 fil = open(fnam, "a", encoding="utf-8")
183 183 else:
184 fil = open(fnam, 'w', encoding='utf-8')
184 fil = open(fnam, "w", encoding="utf-8")
185 185 with fil:
186 186 obj = ip.ev(args[0])
187 187 print("Writing '%s' (%s) to file '%s'." % (args[0],
188 188 obj.__class__.__name__, fnam))
189 189
190 190 if not isinstance (obj, str):
191 191 from pprint import pprint
192 192 pprint(obj, fil)
193 193 else:
194 194 fil.write(obj)
195 195 if not obj.endswith('\n'):
196 196 fil.write('\n')
197 197
198 198 return
199 199
200 200 # %store foo
201 201 for arg in args:
202 202 try:
203 203 obj = ip.user_ns[arg]
204 204 except KeyError:
205 205 # it might be an alias
206 206 name = arg
207 207 try:
208 208 cmd = ip.alias_manager.retrieve_alias(name)
209 209 except ValueError as e:
210 210 raise UsageError("Unknown variable '%s'" % name) from e
211 211
212 212 staliases = db.get('stored_aliases',{})
213 213 staliases[name] = cmd
214 214 db['stored_aliases'] = staliases
215 215 print("Alias stored: %s (%s)" % (name, cmd))
216 216 return
217 217
218 218 else:
219 219 modname = getattr(inspect.getmodule(obj), '__name__', '')
220 220 if modname == '__main__':
221 221 print(textwrap.dedent("""\
222 222 Warning:%s is %s
223 223 Proper storage of interactively declared classes (or instances
224 224 of those classes) is not possible! Only instances
225 225 of classes in real modules on file system can be %%store'd.
226 226 """ % (arg, obj) ))
227 227 return
228 228 #pickled = pickle.dumps(obj)
229 229 db[ 'autorestore/' + arg ] = obj
230 230 print("Stored '%s' (%s)" % (arg, obj.__class__.__name__))
231 231
232 232
233 233 def load_ipython_extension(ip):
234 234 """Load the extension in IPython."""
235 235 ip.register_magics(StoreMagics)
236 236
@@ -1,597 +1,597 b''
1 1 """Tests for autoreload extension.
2 2 """
3 3 # -----------------------------------------------------------------------------
4 4 # Copyright (c) 2012 IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 # -----------------------------------------------------------------------------
10 10
11 11 # -----------------------------------------------------------------------------
12 12 # Imports
13 13 # -----------------------------------------------------------------------------
14 14
15 15 import os
16 16 import platform
17 17 import pytest
18 18 import sys
19 19 import tempfile
20 20 import textwrap
21 21 import shutil
22 22 import random
23 23 import time
24 24 from io import StringIO
25 25
26 26 import IPython.testing.tools as tt
27 27
28 28 from unittest import TestCase
29 29
30 30 from IPython.extensions.autoreload import AutoreloadMagics
31 31 from IPython.core.events import EventManager, pre_run_cell
32 32 from IPython.testing.decorators import skipif_not_numpy
33 33
34 34 if platform.python_implementation() == "PyPy":
35 35 pytest.skip(
36 36 "Current autoreload implementation is extremly slow on PyPy",
37 37 allow_module_level=True,
38 38 )
39 39
40 40 # -----------------------------------------------------------------------------
41 41 # Test fixture
42 42 # -----------------------------------------------------------------------------
43 43
44 44 noop = lambda *a, **kw: None
45 45
46 46
47 47 class FakeShell:
48 48 def __init__(self):
49 49 self.ns = {}
50 50 self.user_ns = self.ns
51 51 self.user_ns_hidden = {}
52 52 self.events = EventManager(self, {"pre_run_cell", pre_run_cell})
53 53 self.auto_magics = AutoreloadMagics(shell=self)
54 54 self.events.register("pre_run_cell", self.auto_magics.pre_run_cell)
55 55
56 56 register_magics = set_hook = noop
57 57
58 58 def run_code(self, code):
59 59 self.events.trigger("pre_run_cell")
60 60 exec(code, self.user_ns)
61 61 self.auto_magics.post_execute_hook()
62 62
63 63 def push(self, items):
64 64 self.ns.update(items)
65 65
66 66 def magic_autoreload(self, parameter):
67 67 self.auto_magics.autoreload(parameter)
68 68
69 69 def magic_aimport(self, parameter, stream=None):
70 70 self.auto_magics.aimport(parameter, stream=stream)
71 71 self.auto_magics.post_execute_hook()
72 72
73 73
74 74 class Fixture(TestCase):
75 75 """Fixture for creating test module files"""
76 76
77 77 test_dir = None
78 78 old_sys_path = None
79 79 filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
80 80
81 81 def setUp(self):
82 82 self.test_dir = tempfile.mkdtemp()
83 83 self.old_sys_path = list(sys.path)
84 84 sys.path.insert(0, self.test_dir)
85 85 self.shell = FakeShell()
86 86
87 87 def tearDown(self):
88 88 shutil.rmtree(self.test_dir)
89 89 sys.path = self.old_sys_path
90 90
91 91 self.test_dir = None
92 92 self.old_sys_path = None
93 93 self.shell = None
94 94
95 95 def get_module(self):
96 96 module_name = "tmpmod_" + "".join(random.sample(self.filename_chars, 20))
97 97 if module_name in sys.modules:
98 98 del sys.modules[module_name]
99 99 file_name = os.path.join(self.test_dir, module_name + ".py")
100 100 return module_name, file_name
101 101
102 102 def write_file(self, filename, content):
103 103 """
104 104 Write a file, and force a timestamp difference of at least one second
105 105
106 106 Notes
107 107 -----
108 108 Python's .pyc files record the timestamp of their compilation
109 109 with a time resolution of one second.
110 110
111 111 Therefore, we need to force a timestamp difference between .py
112 112 and .pyc, without having the .py file be timestamped in the
113 113 future, and without changing the timestamp of the .pyc file
114 114 (because that is stored in the file). The only reliable way
115 115 to achieve this seems to be to sleep.
116 116 """
117 117 content = textwrap.dedent(content)
118 118 # Sleep one second + eps
119 119 time.sleep(1.05)
120 120
121 121 # Write
122 with open(filename, "w", encoding='utf-8') as f:
122 with open(filename, "w", encoding="utf-8") as f:
123 123 f.write(content)
124 124
125 125 def new_module(self, code):
126 126 code = textwrap.dedent(code)
127 127 mod_name, mod_fn = self.get_module()
128 with open(mod_fn, "w", encoding='utf-8') as f:
128 with open(mod_fn, "w", encoding="utf-8") as f:
129 129 f.write(code)
130 130 return mod_name, mod_fn
131 131
132 132
133 133 # -----------------------------------------------------------------------------
134 134 # Test automatic reloading
135 135 # -----------------------------------------------------------------------------
136 136
137 137
138 138 def pickle_get_current_class(obj):
139 139 """
140 140 Original issue comes from pickle; hence the name.
141 141 """
142 142 name = obj.__class__.__name__
143 143 module_name = getattr(obj, "__module__", None)
144 144 obj2 = sys.modules[module_name]
145 145 for subpath in name.split("."):
146 146 obj2 = getattr(obj2, subpath)
147 147 return obj2
148 148
149 149
150 150 class TestAutoreload(Fixture):
151 151 def test_reload_enums(self):
152 152 mod_name, mod_fn = self.new_module(
153 153 textwrap.dedent(
154 154 """
155 155 from enum import Enum
156 156 class MyEnum(Enum):
157 157 A = 'A'
158 158 B = 'B'
159 159 """
160 160 )
161 161 )
162 162 self.shell.magic_autoreload("2")
163 163 self.shell.magic_aimport(mod_name)
164 164 self.write_file(
165 165 mod_fn,
166 166 textwrap.dedent(
167 167 """
168 168 from enum import Enum
169 169 class MyEnum(Enum):
170 170 A = 'A'
171 171 B = 'B'
172 172 C = 'C'
173 173 """
174 174 ),
175 175 )
176 176 with tt.AssertNotPrints(
177 177 ("[autoreload of %s failed:" % mod_name), channel="stderr"
178 178 ):
179 179 self.shell.run_code("pass") # trigger another reload
180 180
181 181 def test_reload_class_type(self):
182 182 self.shell.magic_autoreload("2")
183 183 mod_name, mod_fn = self.new_module(
184 184 """
185 185 class Test():
186 186 def meth(self):
187 187 return "old"
188 188 """
189 189 )
190 190 assert "test" not in self.shell.ns
191 191 assert "result" not in self.shell.ns
192 192
193 193 self.shell.run_code("from %s import Test" % mod_name)
194 194 self.shell.run_code("test = Test()")
195 195
196 196 self.write_file(
197 197 mod_fn,
198 198 """
199 199 class Test():
200 200 def meth(self):
201 201 return "new"
202 202 """,
203 203 )
204 204
205 205 test_object = self.shell.ns["test"]
206 206
207 207 # important to trigger autoreload logic !
208 208 self.shell.run_code("pass")
209 209
210 210 test_class = pickle_get_current_class(test_object)
211 211 assert isinstance(test_object, test_class)
212 212
213 213 # extra check.
214 214 self.shell.run_code("import pickle")
215 215 self.shell.run_code("p = pickle.dumps(test)")
216 216
217 217 def test_reload_class_attributes(self):
218 218 self.shell.magic_autoreload("2")
219 219 mod_name, mod_fn = self.new_module(
220 220 textwrap.dedent(
221 221 """
222 222 class MyClass:
223 223
224 224 def __init__(self, a=10):
225 225 self.a = a
226 226 self.b = 22
227 227 # self.toto = 33
228 228
229 229 def square(self):
230 230 print('compute square')
231 231 return self.a*self.a
232 232 """
233 233 )
234 234 )
235 235 self.shell.run_code("from %s import MyClass" % mod_name)
236 236 self.shell.run_code("first = MyClass(5)")
237 237 self.shell.run_code("first.square()")
238 238 with self.assertRaises(AttributeError):
239 239 self.shell.run_code("first.cube()")
240 240 with self.assertRaises(AttributeError):
241 241 self.shell.run_code("first.power(5)")
242 242 self.shell.run_code("first.b")
243 243 with self.assertRaises(AttributeError):
244 244 self.shell.run_code("first.toto")
245 245
246 246 # remove square, add power
247 247
248 248 self.write_file(
249 249 mod_fn,
250 250 textwrap.dedent(
251 251 """
252 252 class MyClass:
253 253
254 254 def __init__(self, a=10):
255 255 self.a = a
256 256 self.b = 11
257 257
258 258 def power(self, p):
259 259 print('compute power '+str(p))
260 260 return self.a**p
261 261 """
262 262 ),
263 263 )
264 264
265 265 self.shell.run_code("second = MyClass(5)")
266 266
267 267 for object_name in {"first", "second"}:
268 268 self.shell.run_code(f"{object_name}.power(5)")
269 269 with self.assertRaises(AttributeError):
270 270 self.shell.run_code(f"{object_name}.cube()")
271 271 with self.assertRaises(AttributeError):
272 272 self.shell.run_code(f"{object_name}.square()")
273 273 self.shell.run_code(f"{object_name}.b")
274 274 self.shell.run_code(f"{object_name}.a")
275 275 with self.assertRaises(AttributeError):
276 276 self.shell.run_code(f"{object_name}.toto")
277 277
278 278 @skipif_not_numpy
279 279 def test_comparing_numpy_structures(self):
280 280 self.shell.magic_autoreload("2")
281 281 mod_name, mod_fn = self.new_module(
282 282 textwrap.dedent(
283 283 """
284 284 import numpy as np
285 285 class MyClass:
286 286 a = (np.array((.1, .2)),
287 287 np.array((.2, .3)))
288 288 """
289 289 )
290 290 )
291 291 self.shell.run_code("from %s import MyClass" % mod_name)
292 292 self.shell.run_code("first = MyClass()")
293 293
294 294 # change property `a`
295 295 self.write_file(
296 296 mod_fn,
297 297 textwrap.dedent(
298 298 """
299 299 import numpy as np
300 300 class MyClass:
301 301 a = (np.array((.3, .4)),
302 302 np.array((.5, .6)))
303 303 """
304 304 ),
305 305 )
306 306
307 307 with tt.AssertNotPrints(
308 308 ("[autoreload of %s failed:" % mod_name), channel="stderr"
309 309 ):
310 310 self.shell.run_code("pass") # trigger another reload
311 311
312 312 def test_autoload_newly_added_objects(self):
313 313 self.shell.magic_autoreload("3")
314 314 mod_code = """
315 315 def func1(): pass
316 316 """
317 317 mod_name, mod_fn = self.new_module(textwrap.dedent(mod_code))
318 318 self.shell.run_code(f"from {mod_name} import *")
319 319 self.shell.run_code("func1()")
320 320 with self.assertRaises(NameError):
321 321 self.shell.run_code("func2()")
322 322 with self.assertRaises(NameError):
323 323 self.shell.run_code("t = Test()")
324 324 with self.assertRaises(NameError):
325 325 self.shell.run_code("number")
326 326
327 327 # ----------- TEST NEW OBJ LOADED --------------------------
328 328
329 329 new_code = """
330 330 def func1(): pass
331 331 def func2(): pass
332 332 class Test: pass
333 333 number = 0
334 334 from enum import Enum
335 335 class TestEnum(Enum):
336 336 A = 'a'
337 337 """
338 338 self.write_file(mod_fn, textwrap.dedent(new_code))
339 339
340 340 # test function now exists in shell's namespace namespace
341 341 self.shell.run_code("func2()")
342 342 # test function now exists in module's dict
343 343 self.shell.run_code(f"import sys; sys.modules['{mod_name}'].func2()")
344 344 # test class now exists
345 345 self.shell.run_code("t = Test()")
346 346 # test global built-in var now exists
347 347 self.shell.run_code("number")
348 348 # test the enumerations gets loaded successfully
349 349 self.shell.run_code("TestEnum.A")
350 350
351 351 # ----------- TEST NEW OBJ CAN BE CHANGED --------------------
352 352
353 353 new_code = """
354 354 def func1(): return 'changed'
355 355 def func2(): return 'changed'
356 356 class Test:
357 357 def new_func(self):
358 358 return 'changed'
359 359 number = 1
360 360 from enum import Enum
361 361 class TestEnum(Enum):
362 362 A = 'a'
363 363 B = 'added'
364 364 """
365 365 self.write_file(mod_fn, textwrap.dedent(new_code))
366 366 self.shell.run_code("assert func1() == 'changed'")
367 367 self.shell.run_code("assert func2() == 'changed'")
368 368 self.shell.run_code("t = Test(); assert t.new_func() == 'changed'")
369 369 self.shell.run_code("assert number == 1")
370 370 self.shell.run_code("assert TestEnum.B.value == 'added'")
371 371
372 372 # ----------- TEST IMPORT FROM MODULE --------------------------
373 373
374 374 new_mod_code = """
375 375 from enum import Enum
376 376 class Ext(Enum):
377 377 A = 'ext'
378 378 def ext_func():
379 379 return 'ext'
380 380 class ExtTest:
381 381 def meth(self):
382 382 return 'ext'
383 383 ext_int = 2
384 384 """
385 385 new_mod_name, new_mod_fn = self.new_module(textwrap.dedent(new_mod_code))
386 386 current_mod_code = f"""
387 387 from {new_mod_name} import *
388 388 """
389 389 self.write_file(mod_fn, textwrap.dedent(current_mod_code))
390 390 self.shell.run_code("assert Ext.A.value == 'ext'")
391 391 self.shell.run_code("assert ext_func() == 'ext'")
392 392 self.shell.run_code("t = ExtTest(); assert t.meth() == 'ext'")
393 393 self.shell.run_code("assert ext_int == 2")
394 394
395 395 def _check_smoketest(self, use_aimport=True):
396 396 """
397 397 Functional test for the automatic reloader using either
398 398 '%autoreload 1' or '%autoreload 2'
399 399 """
400 400
401 401 mod_name, mod_fn = self.new_module(
402 402 """
403 403 x = 9
404 404
405 405 z = 123 # this item will be deleted
406 406
407 407 def foo(y):
408 408 return y + 3
409 409
410 410 class Baz(object):
411 411 def __init__(self, x):
412 412 self.x = x
413 413 def bar(self, y):
414 414 return self.x + y
415 415 @property
416 416 def quux(self):
417 417 return 42
418 418 def zzz(self):
419 419 '''This method will be deleted below'''
420 420 return 99
421 421
422 422 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
423 423 def foo(self):
424 424 return 1
425 425 """
426 426 )
427 427
428 428 #
429 429 # Import module, and mark for reloading
430 430 #
431 431 if use_aimport:
432 432 self.shell.magic_autoreload("1")
433 433 self.shell.magic_aimport(mod_name)
434 434 stream = StringIO()
435 435 self.shell.magic_aimport("", stream=stream)
436 436 self.assertIn(("Modules to reload:\n%s" % mod_name), stream.getvalue())
437 437
438 438 with self.assertRaises(ImportError):
439 439 self.shell.magic_aimport("tmpmod_as318989e89ds")
440 440 else:
441 441 self.shell.magic_autoreload("2")
442 442 self.shell.run_code("import %s" % mod_name)
443 443 stream = StringIO()
444 444 self.shell.magic_aimport("", stream=stream)
445 445 self.assertTrue(
446 446 "Modules to reload:\nall-except-skipped" in stream.getvalue()
447 447 )
448 448 self.assertIn(mod_name, self.shell.ns)
449 449
450 450 mod = sys.modules[mod_name]
451 451
452 452 #
453 453 # Test module contents
454 454 #
455 455 old_foo = mod.foo
456 456 old_obj = mod.Baz(9)
457 457 old_obj2 = mod.Bar()
458 458
459 459 def check_module_contents():
460 460 self.assertEqual(mod.x, 9)
461 461 self.assertEqual(mod.z, 123)
462 462
463 463 self.assertEqual(old_foo(0), 3)
464 464 self.assertEqual(mod.foo(0), 3)
465 465
466 466 obj = mod.Baz(9)
467 467 self.assertEqual(old_obj.bar(1), 10)
468 468 self.assertEqual(obj.bar(1), 10)
469 469 self.assertEqual(obj.quux, 42)
470 470 self.assertEqual(obj.zzz(), 99)
471 471
472 472 obj2 = mod.Bar()
473 473 self.assertEqual(old_obj2.foo(), 1)
474 474 self.assertEqual(obj2.foo(), 1)
475 475
476 476 check_module_contents()
477 477
478 478 #
479 479 # Simulate a failed reload: no reload should occur and exactly
480 480 # one error message should be printed
481 481 #
482 482 self.write_file(
483 483 mod_fn,
484 484 """
485 485 a syntax error
486 486 """,
487 487 )
488 488
489 489 with tt.AssertPrints(
490 490 ("[autoreload of %s failed:" % mod_name), channel="stderr"
491 491 ):
492 492 self.shell.run_code("pass") # trigger reload
493 493 with tt.AssertNotPrints(
494 494 ("[autoreload of %s failed:" % mod_name), channel="stderr"
495 495 ):
496 496 self.shell.run_code("pass") # trigger another reload
497 497 check_module_contents()
498 498
499 499 #
500 500 # Rewrite module (this time reload should succeed)
501 501 #
502 502 self.write_file(
503 503 mod_fn,
504 504 """
505 505 x = 10
506 506
507 507 def foo(y):
508 508 return y + 4
509 509
510 510 class Baz(object):
511 511 def __init__(self, x):
512 512 self.x = x
513 513 def bar(self, y):
514 514 return self.x + y + 1
515 515 @property
516 516 def quux(self):
517 517 return 43
518 518
519 519 class Bar: # old-style class
520 520 def foo(self):
521 521 return 2
522 522 """,
523 523 )
524 524
525 525 def check_module_contents():
526 526 self.assertEqual(mod.x, 10)
527 527 self.assertFalse(hasattr(mod, "z"))
528 528
529 529 self.assertEqual(old_foo(0), 4) # superreload magic!
530 530 self.assertEqual(mod.foo(0), 4)
531 531
532 532 obj = mod.Baz(9)
533 533 self.assertEqual(old_obj.bar(1), 11) # superreload magic!
534 534 self.assertEqual(obj.bar(1), 11)
535 535
536 536 self.assertEqual(old_obj.quux, 43)
537 537 self.assertEqual(obj.quux, 43)
538 538
539 539 self.assertFalse(hasattr(old_obj, "zzz"))
540 540 self.assertFalse(hasattr(obj, "zzz"))
541 541
542 542 obj2 = mod.Bar()
543 543 self.assertEqual(old_obj2.foo(), 2)
544 544 self.assertEqual(obj2.foo(), 2)
545 545
546 546 self.shell.run_code("pass") # trigger reload
547 547 check_module_contents()
548 548
549 549 #
550 550 # Another failure case: deleted file (shouldn't reload)
551 551 #
552 552 os.unlink(mod_fn)
553 553
554 554 self.shell.run_code("pass") # trigger reload
555 555 check_module_contents()
556 556
557 557 #
558 558 # Disable autoreload and rewrite module: no reload should occur
559 559 #
560 560 if use_aimport:
561 561 self.shell.magic_aimport("-" + mod_name)
562 562 stream = StringIO()
563 563 self.shell.magic_aimport("", stream=stream)
564 564 self.assertTrue(("Modules to skip:\n%s" % mod_name) in stream.getvalue())
565 565
566 566 # This should succeed, although no such module exists
567 567 self.shell.magic_aimport("-tmpmod_as318989e89ds")
568 568 else:
569 569 self.shell.magic_autoreload("0")
570 570
571 571 self.write_file(
572 572 mod_fn,
573 573 """
574 574 x = -99
575 575 """,
576 576 )
577 577
578 578 self.shell.run_code("pass") # trigger reload
579 579 self.shell.run_code("pass")
580 580 check_module_contents()
581 581
582 582 #
583 583 # Re-enable autoreload: reload should now occur
584 584 #
585 585 if use_aimport:
586 586 self.shell.magic_aimport(mod_name)
587 587 else:
588 588 self.shell.magic_autoreload("")
589 589
590 590 self.shell.run_code("pass") # trigger reload
591 591 self.assertEqual(mod.x, -99)
592 592
593 593 def test_smoketest_aimport(self):
594 594 self._check_smoketest(use_aimport=True)
595 595
596 596 def test_smoketest_autoreload(self):
597 597 self._check_smoketest(use_aimport=False)
@@ -1,672 +1,672 b''
1 1 """Module for interactive demos using IPython.
2 2
3 3 This module implements a few classes for running Python scripts interactively
4 4 in IPython for demonstrations. With very simple markup (a few tags in
5 5 comments), you can control points where the script stops executing and returns
6 6 control to IPython.
7 7
8 8
9 9 Provided classes
10 10 ----------------
11 11
12 12 The classes are (see their docstrings for further details):
13 13
14 14 - Demo: pure python demos
15 15
16 16 - IPythonDemo: demos with input to be processed by IPython as if it had been
17 17 typed interactively (so magics work, as well as any other special syntax you
18 18 may have added via input prefilters).
19 19
20 20 - LineDemo: single-line version of the Demo class. These demos are executed
21 21 one line at a time, and require no markup.
22 22
23 23 - IPythonLineDemo: IPython version of the LineDemo class (the demo is
24 24 executed a line at a time, but processed via IPython).
25 25
26 26 - ClearMixin: mixin to make Demo classes with less visual clutter. It
27 27 declares an empty marquee and a pre_cmd that clears the screen before each
28 28 block (see Subclassing below).
29 29
30 30 - ClearDemo, ClearIPDemo: mixin-enabled versions of the Demo and IPythonDemo
31 31 classes.
32 32
33 33 Inheritance diagram:
34 34
35 35 .. inheritance-diagram:: IPython.lib.demo
36 36 :parts: 3
37 37
38 38 Subclassing
39 39 -----------
40 40
41 41 The classes here all include a few methods meant to make customization by
42 42 subclassing more convenient. Their docstrings below have some more details:
43 43
44 44 - highlight(): format every block and optionally highlight comments and
45 45 docstring content.
46 46
47 47 - marquee(): generates a marquee to provide visible on-screen markers at each
48 48 block start and end.
49 49
50 50 - pre_cmd(): run right before the execution of each block.
51 51
52 52 - post_cmd(): run right after the execution of each block. If the block
53 53 raises an exception, this is NOT called.
54 54
55 55
56 56 Operation
57 57 ---------
58 58
59 59 The file is run in its own empty namespace (though you can pass it a string of
60 60 arguments as if in a command line environment, and it will see those as
61 61 sys.argv). But at each stop, the global IPython namespace is updated with the
62 62 current internal demo namespace, so you can work interactively with the data
63 63 accumulated so far.
64 64
65 65 By default, each block of code is printed (with syntax highlighting) before
66 66 executing it and you have to confirm execution. This is intended to show the
67 67 code to an audience first so you can discuss it, and only proceed with
68 68 execution once you agree. There are a few tags which allow you to modify this
69 69 behavior.
70 70
71 71 The supported tags are:
72 72
73 73 # <demo> stop
74 74
75 75 Defines block boundaries, the points where IPython stops execution of the
76 76 file and returns to the interactive prompt.
77 77
78 78 You can optionally mark the stop tag with extra dashes before and after the
79 79 word 'stop', to help visually distinguish the blocks in a text editor:
80 80
81 81 # <demo> --- stop ---
82 82
83 83
84 84 # <demo> silent
85 85
86 86 Make a block execute silently (and hence automatically). Typically used in
87 87 cases where you have some boilerplate or initialization code which you need
88 88 executed but do not want to be seen in the demo.
89 89
90 90 # <demo> auto
91 91
92 92 Make a block execute automatically, but still being printed. Useful for
93 93 simple code which does not warrant discussion, since it avoids the extra
94 94 manual confirmation.
95 95
96 96 # <demo> auto_all
97 97
98 98 This tag can _only_ be in the first block, and if given it overrides the
99 99 individual auto tags to make the whole demo fully automatic (no block asks
100 100 for confirmation). It can also be given at creation time (or the attribute
101 101 set later) to override what's in the file.
102 102
103 103 While _any_ python file can be run as a Demo instance, if there are no stop
104 104 tags the whole file will run in a single block (no different that calling
105 105 first %pycat and then %run). The minimal markup to make this useful is to
106 106 place a set of stop tags; the other tags are only there to let you fine-tune
107 107 the execution.
108 108
109 109 This is probably best explained with the simple example file below. You can
110 110 copy this into a file named ex_demo.py, and try running it via::
111 111
112 112 from IPython.lib.demo import Demo
113 113 d = Demo('ex_demo.py')
114 114 d()
115 115
116 116 Each time you call the demo object, it runs the next block. The demo object
117 117 has a few useful methods for navigation, like again(), edit(), jump(), seek()
118 118 and back(). It can be reset for a new run via reset() or reloaded from disk
119 119 (in case you've edited the source) via reload(). See their docstrings below.
120 120
121 121 Note: To make this simpler to explore, a file called "demo-exercizer.py" has
122 122 been added to the "docs/examples/core" directory. Just cd to this directory in
123 123 an IPython session, and type::
124 124
125 125 %run demo-exercizer.py
126 126
127 127 and then follow the directions.
128 128
129 129 Example
130 130 -------
131 131
132 132 The following is a very simple example of a valid demo file.
133 133
134 134 ::
135 135
136 136 #################### EXAMPLE DEMO <ex_demo.py> ###############################
137 137 '''A simple interactive demo to illustrate the use of IPython's Demo class.'''
138 138
139 139 print 'Hello, welcome to an interactive IPython demo.'
140 140
141 141 # The mark below defines a block boundary, which is a point where IPython will
142 142 # stop execution and return to the interactive prompt. The dashes are actually
143 143 # optional and used only as a visual aid to clearly separate blocks while
144 144 # editing the demo code.
145 145 # <demo> stop
146 146
147 147 x = 1
148 148 y = 2
149 149
150 150 # <demo> stop
151 151
152 152 # the mark below makes this block as silent
153 153 # <demo> silent
154 154
155 155 print 'This is a silent block, which gets executed but not printed.'
156 156
157 157 # <demo> stop
158 158 # <demo> auto
159 159 print 'This is an automatic block.'
160 160 print 'It is executed without asking for confirmation, but printed.'
161 161 z = x+y
162 162
163 163 print 'z=',x
164 164
165 165 # <demo> stop
166 166 # This is just another normal block.
167 167 print 'z is now:', z
168 168
169 169 print 'bye!'
170 170 ################### END EXAMPLE DEMO <ex_demo.py> ############################
171 171 """
172 172
173 173
174 174 #*****************************************************************************
175 175 # Copyright (C) 2005-2006 Fernando Perez. <Fernando.Perez@colorado.edu>
176 176 #
177 177 # Distributed under the terms of the BSD License. The full license is in
178 178 # the file COPYING, distributed as part of this software.
179 179 #
180 180 #*****************************************************************************
181 181
182 182 import os
183 183 import re
184 184 import shlex
185 185 import sys
186 186 import pygments
187 187 from pathlib import Path
188 188
189 189 from IPython.utils.text import marquee
190 190 from IPython.utils import openpy
191 191 from IPython.utils import py3compat
192 192 __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError']
193 193
194 194 class DemoError(Exception): pass
195 195
196 196 def re_mark(mark):
197 197 return re.compile(r'^\s*#\s+<demo>\s+%s\s*$' % mark,re.MULTILINE)
198 198
199 199 class Demo(object):
200 200
201 201 re_stop = re_mark(r'-*\s?stop\s?-*')
202 202 re_silent = re_mark('silent')
203 203 re_auto = re_mark('auto')
204 204 re_auto_all = re_mark('auto_all')
205 205
206 206 def __init__(self,src,title='',arg_str='',auto_all=None, format_rst=False,
207 207 formatter='terminal', style='default'):
208 208 """Make a new demo object. To run the demo, simply call the object.
209 209
210 210 See the module docstring for full details and an example (you can use
211 211 IPython.Demo? in IPython to see it).
212 212
213 213 Inputs:
214 214
215 215 - src is either a file, or file-like object, or a
216 216 string that can be resolved to a filename.
217 217
218 218 Optional inputs:
219 219
220 220 - title: a string to use as the demo name. Of most use when the demo
221 221 you are making comes from an object that has no filename, or if you
222 222 want an alternate denotation distinct from the filename.
223 223
224 224 - arg_str(''): a string of arguments, internally converted to a list
225 225 just like sys.argv, so the demo script can see a similar
226 226 environment.
227 227
228 228 - auto_all(None): global flag to run all blocks automatically without
229 229 confirmation. This attribute overrides the block-level tags and
230 230 applies to the whole demo. It is an attribute of the object, and
231 231 can be changed at runtime simply by reassigning it to a boolean
232 232 value.
233 233
234 234 - format_rst(False): a bool to enable comments and doc strings
235 235 formatting with pygments rst lexer
236 236
237 237 - formatter('terminal'): a string of pygments formatter name to be
238 238 used. Useful values for terminals: terminal, terminal256,
239 239 terminal16m
240 240
241 241 - style('default'): a string of pygments style name to be used.
242 242 """
243 243 if hasattr(src, "read"):
244 244 # It seems to be a file or a file-like object
245 245 self.fname = "from a file-like object"
246 246 if title == '':
247 247 self.title = "from a file-like object"
248 248 else:
249 249 self.title = title
250 250 else:
251 251 # Assume it's a string or something that can be converted to one
252 252 self.fname = src
253 253 if title == '':
254 254 (filepath, filename) = os.path.split(src)
255 255 self.title = filename
256 256 else:
257 257 self.title = title
258 258 self.sys_argv = [src] + shlex.split(arg_str)
259 259 self.auto_all = auto_all
260 260 self.src = src
261 261
262 262 try:
263 263 ip = get_ipython() # this is in builtins whenever IPython is running
264 264 self.inside_ipython = True
265 265 except NameError:
266 266 self.inside_ipython = False
267 267
268 268 if self.inside_ipython:
269 269 # get a few things from ipython. While it's a bit ugly design-wise,
270 270 # it ensures that things like color scheme and the like are always in
271 271 # sync with the ipython mode being used. This class is only meant to
272 272 # be used inside ipython anyways, so it's OK.
273 273 self.ip_ns = ip.user_ns
274 274 self.ip_colorize = ip.pycolorize
275 275 self.ip_showtb = ip.showtraceback
276 276 self.ip_run_cell = ip.run_cell
277 277 self.shell = ip
278 278
279 279 self.formatter = pygments.formatters.get_formatter_by_name(formatter,
280 280 style=style)
281 281 self.python_lexer = pygments.lexers.get_lexer_by_name("py3")
282 282 self.format_rst = format_rst
283 283 if format_rst:
284 284 self.rst_lexer = pygments.lexers.get_lexer_by_name("rst")
285 285
286 286 # load user data and initialize data structures
287 287 self.reload()
288 288
289 289 def fload(self):
290 290 """Load file object."""
291 291 # read data and parse into blocks
292 292 if hasattr(self, 'fobj') and self.fobj is not None:
293 293 self.fobj.close()
294 294 if hasattr(self.src, "read"):
295 295 # It seems to be a file or a file-like object
296 296 self.fobj = self.src
297 297 else:
298 298 # Assume it's a string or something that can be converted to one
299 299 self.fobj = openpy.open(self.fname)
300 300
301 301 def reload(self):
302 302 """Reload source from disk and initialize state."""
303 303 self.fload()
304 304
305 305 self.src = "".join(openpy.strip_encoding_cookie(self.fobj))
306 306 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
307 307 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
308 308 self._auto = [bool(self.re_auto.findall(b)) for b in src_b]
309 309
310 310 # if auto_all is not given (def. None), we read it from the file
311 311 if self.auto_all is None:
312 312 self.auto_all = bool(self.re_auto_all.findall(src_b[0]))
313 313 else:
314 314 self.auto_all = bool(self.auto_all)
315 315
316 316 # Clean the sources from all markup so it doesn't get displayed when
317 317 # running the demo
318 318 src_blocks = []
319 319 auto_strip = lambda s: self.re_auto.sub('',s)
320 320 for i,b in enumerate(src_b):
321 321 if self._auto[i]:
322 322 src_blocks.append(auto_strip(b))
323 323 else:
324 324 src_blocks.append(b)
325 325 # remove the auto_all marker
326 326 src_blocks[0] = self.re_auto_all.sub('',src_blocks[0])
327 327
328 328 self.nblocks = len(src_blocks)
329 329 self.src_blocks = src_blocks
330 330
331 331 # also build syntax-highlighted source
332 332 self.src_blocks_colored = list(map(self.highlight,self.src_blocks))
333 333
334 334 # ensure clean namespace and seek offset
335 335 self.reset()
336 336
337 337 def reset(self):
338 338 """Reset the namespace and seek pointer to restart the demo"""
339 339 self.user_ns = {}
340 340 self.finished = False
341 341 self.block_index = 0
342 342
343 343 def _validate_index(self,index):
344 344 if index<0 or index>=self.nblocks:
345 345 raise ValueError('invalid block index %s' % index)
346 346
347 347 def _get_index(self,index):
348 348 """Get the current block index, validating and checking status.
349 349
350 350 Returns None if the demo is finished"""
351 351
352 352 if index is None:
353 353 if self.finished:
354 354 print('Demo finished. Use <demo_name>.reset() if you want to rerun it.')
355 355 return None
356 356 index = self.block_index
357 357 else:
358 358 self._validate_index(index)
359 359 return index
360 360
361 361 def seek(self,index):
362 362 """Move the current seek pointer to the given block.
363 363
364 364 You can use negative indices to seek from the end, with identical
365 365 semantics to those of Python lists."""
366 366 if index<0:
367 367 index = self.nblocks + index
368 368 self._validate_index(index)
369 369 self.block_index = index
370 370 self.finished = False
371 371
372 372 def back(self,num=1):
373 373 """Move the seek pointer back num blocks (default is 1)."""
374 374 self.seek(self.block_index-num)
375 375
376 376 def jump(self,num=1):
377 377 """Jump a given number of blocks relative to the current one.
378 378
379 379 The offset can be positive or negative, defaults to 1."""
380 380 self.seek(self.block_index+num)
381 381
382 382 def again(self):
383 383 """Move the seek pointer back one block and re-execute."""
384 384 self.back(1)
385 385 self()
386 386
387 387 def edit(self,index=None):
388 388 """Edit a block.
389 389
390 390 If no number is given, use the last block executed.
391 391
392 392 This edits the in-memory copy of the demo, it does NOT modify the
393 393 original source file. If you want to do that, simply open the file in
394 394 an editor and use reload() when you make changes to the file. This
395 395 method is meant to let you change a block during a demonstration for
396 396 explanatory purposes, without damaging your original script."""
397 397
398 398 index = self._get_index(index)
399 399 if index is None:
400 400 return
401 401 # decrease the index by one (unless we're at the very beginning), so
402 402 # that the default demo.edit() call opens up the sblock we've last run
403 403 if index>0:
404 404 index -= 1
405 405
406 406 filename = self.shell.mktempfile(self.src_blocks[index])
407 407 self.shell.hooks.editor(filename, 1)
408 with open(Path(filename), "r", encoding='utf-8') as f:
408 with open(Path(filename), "r", encoding="utf-8") as f:
409 409 new_block = f.read()
410 410 # update the source and colored block
411 411 self.src_blocks[index] = new_block
412 412 self.src_blocks_colored[index] = self.highlight(new_block)
413 413 self.block_index = index
414 414 # call to run with the newly edited index
415 415 self()
416 416
417 417 def show(self,index=None):
418 418 """Show a single block on screen"""
419 419
420 420 index = self._get_index(index)
421 421 if index is None:
422 422 return
423 423
424 424 print(self.marquee('<%s> block # %s (%s remaining)' %
425 425 (self.title,index,self.nblocks-index-1)))
426 426 print(self.src_blocks_colored[index])
427 427 sys.stdout.flush()
428 428
429 429 def show_all(self):
430 430 """Show entire demo on screen, block by block"""
431 431
432 432 fname = self.title
433 433 title = self.title
434 434 nblocks = self.nblocks
435 435 silent = self._silent
436 436 marquee = self.marquee
437 437 for index,block in enumerate(self.src_blocks_colored):
438 438 if silent[index]:
439 439 print(marquee('<%s> SILENT block # %s (%s remaining)' %
440 440 (title,index,nblocks-index-1)))
441 441 else:
442 442 print(marquee('<%s> block # %s (%s remaining)' %
443 443 (title,index,nblocks-index-1)))
444 444 print(block, end=' ')
445 445 sys.stdout.flush()
446 446
447 447 def run_cell(self,source):
448 448 """Execute a string with one or more lines of code"""
449 449
450 450 exec(source, self.user_ns)
451 451
452 452 def __call__(self,index=None):
453 453 """run a block of the demo.
454 454
455 455 If index is given, it should be an integer >=1 and <= nblocks. This
456 456 means that the calling convention is one off from typical Python
457 457 lists. The reason for the inconsistency is that the demo always
458 458 prints 'Block n/N, and N is the total, so it would be very odd to use
459 459 zero-indexing here."""
460 460
461 461 index = self._get_index(index)
462 462 if index is None:
463 463 return
464 464 try:
465 465 marquee = self.marquee
466 466 next_block = self.src_blocks[index]
467 467 self.block_index += 1
468 468 if self._silent[index]:
469 469 print(marquee('Executing silent block # %s (%s remaining)' %
470 470 (index,self.nblocks-index-1)))
471 471 else:
472 472 self.pre_cmd()
473 473 self.show(index)
474 474 if self.auto_all or self._auto[index]:
475 475 print(marquee('output:'))
476 476 else:
477 477 print(marquee('Press <q> to quit, <Enter> to execute...'), end=' ')
478 478 ans = py3compat.input().strip()
479 479 if ans:
480 480 print(marquee('Block NOT executed'))
481 481 return
482 482 try:
483 483 save_argv = sys.argv
484 484 sys.argv = self.sys_argv
485 485 self.run_cell(next_block)
486 486 self.post_cmd()
487 487 finally:
488 488 sys.argv = save_argv
489 489
490 490 except:
491 491 if self.inside_ipython:
492 492 self.ip_showtb(filename=self.fname)
493 493 else:
494 494 if self.inside_ipython:
495 495 self.ip_ns.update(self.user_ns)
496 496
497 497 if self.block_index == self.nblocks:
498 498 mq1 = self.marquee('END OF DEMO')
499 499 if mq1:
500 500 # avoid spurious print if empty marquees are used
501 501 print()
502 502 print(mq1)
503 503 print(self.marquee('Use <demo_name>.reset() if you want to rerun it.'))
504 504 self.finished = True
505 505
506 506 # These methods are meant to be overridden by subclasses who may wish to
507 507 # customize the behavior of of their demos.
508 508 def marquee(self,txt='',width=78,mark='*'):
509 509 """Return the input string centered in a 'marquee'."""
510 510 return marquee(txt,width,mark)
511 511
512 512 def pre_cmd(self):
513 513 """Method called before executing each block."""
514 514 pass
515 515
516 516 def post_cmd(self):
517 517 """Method called after executing each block."""
518 518 pass
519 519
520 520 def highlight(self, block):
521 521 """Method called on each block to highlight it content"""
522 522 tokens = pygments.lex(block, self.python_lexer)
523 523 if self.format_rst:
524 524 from pygments.token import Token
525 525 toks = []
526 526 for token in tokens:
527 527 if token[0] == Token.String.Doc and len(token[1]) > 6:
528 528 toks += pygments.lex(token[1][:3], self.python_lexer)
529 529 # parse doc string content by rst lexer
530 530 toks += pygments.lex(token[1][3:-3], self.rst_lexer)
531 531 toks += pygments.lex(token[1][-3:], self.python_lexer)
532 532 elif token[0] == Token.Comment.Single:
533 533 toks.append((Token.Comment.Single, token[1][0]))
534 534 # parse comment content by rst lexer
535 535 # remove the extra newline added by rst lexer
536 536 toks += list(pygments.lex(token[1][1:], self.rst_lexer))[:-1]
537 537 else:
538 538 toks.append(token)
539 539 tokens = toks
540 540 return pygments.format(tokens, self.formatter)
541 541
542 542
543 543 class IPythonDemo(Demo):
544 544 """Class for interactive demos with IPython's input processing applied.
545 545
546 546 This subclasses Demo, but instead of executing each block by the Python
547 547 interpreter (via exec), it actually calls IPython on it, so that any input
548 548 filters which may be in place are applied to the input block.
549 549
550 550 If you have an interactive environment which exposes special input
551 551 processing, you can use this class instead to write demo scripts which
552 552 operate exactly as if you had typed them interactively. The default Demo
553 553 class requires the input to be valid, pure Python code.
554 554 """
555 555
556 556 def run_cell(self,source):
557 557 """Execute a string with one or more lines of code"""
558 558
559 559 self.shell.run_cell(source)
560 560
561 561 class LineDemo(Demo):
562 562 """Demo where each line is executed as a separate block.
563 563
564 564 The input script should be valid Python code.
565 565
566 566 This class doesn't require any markup at all, and it's meant for simple
567 567 scripts (with no nesting or any kind of indentation) which consist of
568 568 multiple lines of input to be executed, one at a time, as if they had been
569 569 typed in the interactive prompt.
570 570
571 571 Note: the input can not have *any* indentation, which means that only
572 572 single-lines of input are accepted, not even function definitions are
573 573 valid."""
574 574
575 575 def reload(self):
576 576 """Reload source from disk and initialize state."""
577 577 # read data and parse into blocks
578 578 self.fload()
579 579 lines = self.fobj.readlines()
580 580 src_b = [l for l in lines if l.strip()]
581 581 nblocks = len(src_b)
582 582 self.src = ''.join(lines)
583 583 self._silent = [False]*nblocks
584 584 self._auto = [True]*nblocks
585 585 self.auto_all = True
586 586 self.nblocks = nblocks
587 587 self.src_blocks = src_b
588 588
589 589 # also build syntax-highlighted source
590 590 self.src_blocks_colored = list(map(self.highlight,self.src_blocks))
591 591
592 592 # ensure clean namespace and seek offset
593 593 self.reset()
594 594
595 595
596 596 class IPythonLineDemo(IPythonDemo,LineDemo):
597 597 """Variant of the LineDemo class whose input is processed by IPython."""
598 598 pass
599 599
600 600
601 601 class ClearMixin(object):
602 602 """Use this mixin to make Demo classes with less visual clutter.
603 603
604 604 Demos using this mixin will clear the screen before every block and use
605 605 blank marquees.
606 606
607 607 Note that in order for the methods defined here to actually override those
608 608 of the classes it's mixed with, it must go /first/ in the inheritance
609 609 tree. For example:
610 610
611 611 class ClearIPDemo(ClearMixin,IPythonDemo): pass
612 612
613 613 will provide an IPythonDemo class with the mixin's features.
614 614 """
615 615
616 616 def marquee(self,txt='',width=78,mark='*'):
617 617 """Blank marquee that returns '' no matter what the input."""
618 618 return ''
619 619
620 620 def pre_cmd(self):
621 621 """Method called before executing each block.
622 622
623 623 This one simply clears the screen."""
624 624 from IPython.utils.terminal import _term_clear
625 625 _term_clear()
626 626
627 627 class ClearDemo(ClearMixin,Demo):
628 628 pass
629 629
630 630
631 631 class ClearIPDemo(ClearMixin,IPythonDemo):
632 632 pass
633 633
634 634
635 635 def slide(file_path, noclear=False, format_rst=True, formatter="terminal",
636 636 style="native", auto_all=False, delimiter='...'):
637 637 if noclear:
638 638 demo_class = Demo
639 639 else:
640 640 demo_class = ClearDemo
641 641 demo = demo_class(file_path, format_rst=format_rst, formatter=formatter,
642 642 style=style, auto_all=auto_all)
643 643 while not demo.finished:
644 644 demo()
645 645 try:
646 646 py3compat.input('\n' + delimiter)
647 647 except KeyboardInterrupt:
648 648 exit(1)
649 649
650 650 if __name__ == '__main__':
651 651 import argparse
652 652 parser = argparse.ArgumentParser(description='Run python demos')
653 653 parser.add_argument('--noclear', '-C', action='store_true',
654 654 help='Do not clear terminal on each slide')
655 655 parser.add_argument('--rst', '-r', action='store_true',
656 656 help='Highlight comments and dostrings as rst')
657 657 parser.add_argument('--formatter', '-f', default='terminal',
658 658 help='pygments formatter name could be: terminal, '
659 659 'terminal256, terminal16m')
660 660 parser.add_argument('--style', '-s', default='default',
661 661 help='pygments style name')
662 662 parser.add_argument('--auto', '-a', action='store_true',
663 663 help='Run all blocks automatically without'
664 664 'confirmation')
665 665 parser.add_argument('--delimiter', '-d', default='...',
666 666 help='slides delimiter added after each slide run')
667 667 parser.add_argument('file', nargs=1,
668 668 help='python demo file')
669 669 args = parser.parse_args()
670 670 slide(args.file[0], noclear=args.noclear, format_rst=args.rst,
671 671 formatter=args.formatter, style=args.style, auto_all=args.auto,
672 672 delimiter=args.delimiter)
@@ -1,56 +1,56 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Test suite for the deepreload module."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 import pytest
8 8 import types
9 9
10 10 from pathlib import Path
11 11
12 12 from IPython.utils.syspathcontext import prepended_to_syspath
13 13 from IPython.utils.tempdir import TemporaryDirectory
14 14 from IPython.lib.deepreload import reload as dreload, modules_reloading
15 15
16 16
17 17 def test_deepreload():
18 18 "Test that dreload does deep reloads and skips excluded modules."
19 19 with TemporaryDirectory() as tmpdir:
20 20 with prepended_to_syspath(tmpdir):
21 21 tmpdirpath = Path(tmpdir)
22 with open(tmpdirpath / "A.py", "w", encoding='utf-8') as f:
22 with open(tmpdirpath / "A.py", "w", encoding="utf-8") as f:
23 23 f.write("class Object:\n pass\nok = True\n")
24 with open(tmpdirpath / "B.py", "w", encoding='utf-8') as f:
24 with open(tmpdirpath / "B.py", "w", encoding="utf-8") as f:
25 25 f.write("import A\nassert A.ok, 'we are fine'\n")
26 26 import A
27 27 import B
28 28
29 29 # Test that A is not reloaded.
30 30 obj = A.Object()
31 31 dreload(B, exclude=["A"])
32 32 assert isinstance(obj, A.Object) is True
33 33
34 34 # Test that an import failure will not blow-up us.
35 35 A.ok = False
36 36 with pytest.raises(AssertionError, match="we are fine"):
37 37 dreload(B, exclude=["A"])
38 38 assert len(modules_reloading) == 0
39 39 assert not A.ok
40 40
41 41 # Test that A is reloaded.
42 42 obj = A.Object()
43 43 A.ok = False
44 44 dreload(B)
45 45 assert A.ok
46 46 assert isinstance(obj, A.Object) is False
47 47
48 48
49 49 def test_not_module():
50 50 pytest.raises(TypeError, dreload, "modulename")
51 51
52 52
53 53 def test_not_in_sys_modules():
54 54 fake_module = types.ModuleType("fake_module")
55 55 with pytest.raises(ImportError, match="not in sys.modules"):
56 56 dreload(fake_module)
@@ -1,133 +1,133 b''
1 1 # encoding: utf-8
2 2 """
3 3 Tests for testing.tools
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
17 17 import os
18 18 import unittest
19 19
20 20 from IPython.testing import decorators as dec
21 21 from IPython.testing import tools as tt
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Tests
25 25 #-----------------------------------------------------------------------------
26 26
27 27 @dec.skip_win32
28 28 def test_full_path_posix():
29 29 spath = "/foo/bar.py"
30 30 result = tt.full_path(spath, ["a.txt", "b.txt"])
31 31 assert result, ["/foo/a.txt" == "/foo/b.txt"]
32 32 spath = "/foo"
33 33 result = tt.full_path(spath, ["a.txt", "b.txt"])
34 34 assert result, ["/a.txt" == "/b.txt"]
35 35 result = tt.full_path(spath, "a.txt")
36 36 assert result == ["/a.txt"]
37 37
38 38
39 39 @dec.skip_if_not_win32
40 40 def test_full_path_win32():
41 41 spath = "c:\\foo\\bar.py"
42 42 result = tt.full_path(spath, ["a.txt", "b.txt"])
43 43 assert result, ["c:\\foo\\a.txt" == "c:\\foo\\b.txt"]
44 44 spath = "c:\\foo"
45 45 result = tt.full_path(spath, ["a.txt", "b.txt"])
46 46 assert result, ["c:\\a.txt" == "c:\\b.txt"]
47 47 result = tt.full_path(spath, "a.txt")
48 48 assert result == ["c:\\a.txt"]
49 49
50 50
51 51 def test_parser():
52 52 err = ("FAILED (errors=1)", 1, 0)
53 53 fail = ("FAILED (failures=1)", 0, 1)
54 54 both = ("FAILED (errors=1, failures=1)", 1, 1)
55 55 for txt, nerr, nfail in [err, fail, both]:
56 56 nerr1, nfail1 = tt.parse_test_output(txt)
57 57 assert nerr == nerr1
58 58 assert nfail == nfail1
59 59
60 60
61 61 def test_temp_pyfile():
62 62 src = 'pass\n'
63 63 fname = tt.temp_pyfile(src)
64 64 assert os.path.isfile(fname)
65 with open(fname, encoding='utf-8') as fh2:
65 with open(fname, encoding="utf-8") as fh2:
66 66 src2 = fh2.read()
67 67 assert src2 == src
68 68
69 69 class TestAssertPrints(unittest.TestCase):
70 70 def test_passing(self):
71 71 with tt.AssertPrints("abc"):
72 72 print("abcd")
73 73 print("def")
74 74 print(b"ghi")
75 75
76 76 def test_failing(self):
77 77 def func():
78 78 with tt.AssertPrints("abc"):
79 79 print("acd")
80 80 print("def")
81 81 print(b"ghi")
82 82
83 83 self.assertRaises(AssertionError, func)
84 84
85 85
86 86 class Test_ipexec_validate(tt.TempFileMixin):
87 87 def test_main_path(self):
88 88 """Test with only stdout results.
89 89 """
90 90 self.mktmp("print('A')\n"
91 91 "print('B')\n"
92 92 )
93 93 out = "A\nB"
94 94 tt.ipexec_validate(self.fname, out)
95 95
96 96 def test_main_path2(self):
97 97 """Test with only stdout results, expecting windows line endings.
98 98 """
99 99 self.mktmp("print('A')\n"
100 100 "print('B')\n"
101 101 )
102 102 out = "A\r\nB"
103 103 tt.ipexec_validate(self.fname, out)
104 104
105 105 def test_exception_path(self):
106 106 """Test exception path in exception_validate.
107 107 """
108 108 self.mktmp("import sys\n"
109 109 "print('A')\n"
110 110 "print('B')\n"
111 111 "print('C', file=sys.stderr)\n"
112 112 "print('D', file=sys.stderr)\n"
113 113 )
114 114 out = "A\nB"
115 115 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\nD")
116 116
117 117 def test_exception_path2(self):
118 118 """Test exception path in exception_validate, expecting windows line endings.
119 119 """
120 120 self.mktmp("import sys\n"
121 121 "print('A')\n"
122 122 "print('B')\n"
123 123 "print('C', file=sys.stderr)\n"
124 124 "print('D', file=sys.stderr)\n"
125 125 )
126 126 out = "A\r\nB"
127 127 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\r\nD")
128 128
129 129
130 130 def tearDown(self):
131 131 # tear down correctly the mixin,
132 132 # unittest.TestCase.tearDown does nothing
133 133 tt.TempFileMixin.tearDown(self)
@@ -1,477 +1,476 b''
1 1 """Generic testing tools.
2 2
3 3 Authors
4 4 -------
5 5 - Fernando Perez <Fernando.Perez@berkeley.edu>
6 6 """
7 7
8 8
9 9 # Copyright (c) IPython Development Team.
10 10 # Distributed under the terms of the Modified BSD License.
11 11
12 12 import os
13 13 from pathlib import Path
14 14 import re
15 15 import sys
16 16 import tempfile
17 17 import unittest
18 18
19 19 from contextlib import contextmanager
20 20 from io import StringIO
21 21 from subprocess import Popen, PIPE
22 22 from unittest.mock import patch
23 23
24 24 from traitlets.config.loader import Config
25 25 from IPython.utils.process import get_output_error_code
26 26 from IPython.utils.text import list_strings
27 27 from IPython.utils.io import temp_pyfile, Tee
28 28 from IPython.utils import py3compat
29 29
30 30 from . import decorators as dec
31 31 from . import skipdoctest
32 32
33 33
34 34 # The docstring for full_path doctests differently on win32 (different path
35 35 # separator) so just skip the doctest there. The example remains informative.
36 36 doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco
37 37
38 38 @doctest_deco
39 39 def full_path(startPath,files):
40 40 """Make full paths for all the listed files, based on startPath.
41 41
42 42 Only the base part of startPath is kept, since this routine is typically
43 43 used with a script's ``__file__`` variable as startPath. The base of startPath
44 44 is then prepended to all the listed files, forming the output list.
45 45
46 46 Parameters
47 47 ----------
48 48 startPath : string
49 49 Initial path to use as the base for the results. This path is split
50 50 using os.path.split() and only its first component is kept.
51 51
52 52 files : string or list
53 53 One or more files.
54 54
55 55 Examples
56 56 --------
57 57
58 58 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
59 59 ['/foo/a.txt', '/foo/b.txt']
60 60
61 61 >>> full_path('/foo',['a.txt','b.txt'])
62 62 ['/a.txt', '/b.txt']
63 63
64 64 If a single file is given, the output is still a list::
65 65
66 66 >>> full_path('/foo','a.txt')
67 67 ['/a.txt']
68 68 """
69 69
70 70 files = list_strings(files)
71 71 base = os.path.split(startPath)[0]
72 72 return [ os.path.join(base,f) for f in files ]
73 73
74 74
75 75 def parse_test_output(txt):
76 76 """Parse the output of a test run and return errors, failures.
77 77
78 78 Parameters
79 79 ----------
80 80 txt : str
81 81 Text output of a test run, assumed to contain a line of one of the
82 82 following forms::
83 83
84 84 'FAILED (errors=1)'
85 85 'FAILED (failures=1)'
86 86 'FAILED (errors=1, failures=1)'
87 87
88 88 Returns
89 89 -------
90 90 nerr, nfail
91 91 number of errors and failures.
92 92 """
93 93
94 94 err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE)
95 95 if err_m:
96 96 nerr = int(err_m.group(1))
97 97 nfail = 0
98 98 return nerr, nfail
99 99
100 100 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
101 101 if fail_m:
102 102 nerr = 0
103 103 nfail = int(fail_m.group(1))
104 104 return nerr, nfail
105 105
106 106 both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt,
107 107 re.MULTILINE)
108 108 if both_m:
109 109 nerr = int(both_m.group(1))
110 110 nfail = int(both_m.group(2))
111 111 return nerr, nfail
112 112
113 113 # If the input didn't match any of these forms, assume no error/failures
114 114 return 0, 0
115 115
116 116
117 117 # So nose doesn't think this is a test
118 118 parse_test_output.__test__ = False
119 119
120 120
121 121 def default_argv():
122 122 """Return a valid default argv for creating testing instances of ipython"""
123 123
124 124 return ['--quick', # so no config file is loaded
125 125 # Other defaults to minimize side effects on stdout
126 126 '--colors=NoColor', '--no-term-title','--no-banner',
127 127 '--autocall=0']
128 128
129 129
130 130 def default_config():
131 131 """Return a config object with good defaults for testing."""
132 132 config = Config()
133 133 config.TerminalInteractiveShell.colors = 'NoColor'
134 134 config.TerminalTerminalInteractiveShell.term_title = False,
135 135 config.TerminalInteractiveShell.autocall = 0
136 136 f = tempfile.NamedTemporaryFile(suffix=u'test_hist.sqlite', delete=False)
137 137 config.HistoryManager.hist_file = Path(f.name)
138 138 f.close()
139 139 config.HistoryManager.db_cache_size = 10000
140 140 return config
141 141
142 142
143 143 def get_ipython_cmd(as_string=False):
144 144 """
145 145 Return appropriate IPython command line name. By default, this will return
146 146 a list that can be used with subprocess.Popen, for example, but passing
147 147 `as_string=True` allows for returning the IPython command as a string.
148 148
149 149 Parameters
150 150 ----------
151 151 as_string: bool
152 152 Flag to allow to return the command as a string.
153 153 """
154 154 ipython_cmd = [sys.executable, "-m", "IPython"]
155 155
156 156 if as_string:
157 157 ipython_cmd = " ".join(ipython_cmd)
158 158
159 159 return ipython_cmd
160 160
161 161 def ipexec(fname, options=None, commands=()):
162 162 """Utility to call 'ipython filename'.
163 163
164 164 Starts IPython with a minimal and safe configuration to make startup as fast
165 165 as possible.
166 166
167 167 Note that this starts IPython in a subprocess!
168 168
169 169 Parameters
170 170 ----------
171 171 fname : str, Path
172 172 Name of file to be executed (should have .py or .ipy extension).
173 173
174 174 options : optional, list
175 175 Extra command-line flags to be passed to IPython.
176 176
177 177 commands : optional, list
178 178 Commands to send in on stdin
179 179
180 180 Returns
181 181 -------
182 182 ``(stdout, stderr)`` of ipython subprocess.
183 183 """
184 184 __tracebackhide__ = True
185 185
186 186 if options is None:
187 187 options = []
188 188
189 189 cmdargs = default_argv() + options
190 190
191 191 test_dir = os.path.dirname(__file__)
192 192
193 193 ipython_cmd = get_ipython_cmd()
194 194 # Absolute path for filename
195 195 full_fname = os.path.join(test_dir, fname)
196 196 full_cmd = ipython_cmd + cmdargs + ['--', full_fname]
197 197 env = os.environ.copy()
198 198 # FIXME: ignore all warnings in ipexec while we have shims
199 199 # should we keep suppressing warnings here, even after removing shims?
200 200 env['PYTHONWARNINGS'] = 'ignore'
201 201 # env.pop('PYTHONWARNINGS', None) # Avoid extraneous warnings appearing on stderr
202 202 # Prevent coloring under PyCharm ("\x1b[0m" at the end of the stdout)
203 203 env.pop("PYCHARM_HOSTED", None)
204 204 for k, v in env.items():
205 205 # Debug a bizarre failure we've seen on Windows:
206 206 # TypeError: environment can only contain strings
207 207 if not isinstance(v, str):
208 208 print(k, v)
209 209 p = Popen(full_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env)
210 210 out, err = p.communicate(input=py3compat.encode('\n'.join(commands)) or None)
211 211 out, err = py3compat.decode(out), py3compat.decode(err)
212 212 # `import readline` causes 'ESC[?1034h' to be output sometimes,
213 213 # so strip that out before doing comparisons
214 214 if out:
215 215 out = re.sub(r'\x1b\[[^h]+h', '', out)
216 216 return out, err
217 217
218 218
219 219 def ipexec_validate(fname, expected_out, expected_err='',
220 220 options=None, commands=()):
221 221 """Utility to call 'ipython filename' and validate output/error.
222 222
223 223 This function raises an AssertionError if the validation fails.
224 224
225 225 Note that this starts IPython in a subprocess!
226 226
227 227 Parameters
228 228 ----------
229 229 fname : str, Path
230 230 Name of the file to be executed (should have .py or .ipy extension).
231 231
232 232 expected_out : str
233 233 Expected stdout of the process.
234 234
235 235 expected_err : optional, str
236 236 Expected stderr of the process.
237 237
238 238 options : optional, list
239 239 Extra command-line flags to be passed to IPython.
240 240
241 241 Returns
242 242 -------
243 243 None
244 244 """
245 245 __tracebackhide__ = True
246 246
247 247 out, err = ipexec(fname, options, commands)
248 248 #print 'OUT', out # dbg
249 249 #print 'ERR', err # dbg
250 250 # If there are any errors, we must check those before stdout, as they may be
251 251 # more informative than simply having an empty stdout.
252 252 if err:
253 253 if expected_err:
254 254 assert "\n".join(err.strip().splitlines()) == "\n".join(
255 255 expected_err.strip().splitlines()
256 256 )
257 257 else:
258 258 raise ValueError('Running file %r produced error: %r' %
259 259 (fname, err))
260 260 # If no errors or output on stderr was expected, match stdout
261 261 assert "\n".join(out.strip().splitlines()) == "\n".join(
262 262 expected_out.strip().splitlines()
263 263 )
264 264
265 265
266 266 class TempFileMixin(unittest.TestCase):
267 267 """Utility class to create temporary Python/IPython files.
268 268
269 269 Meant as a mixin class for test cases."""
270 270
271 271 def mktmp(self, src, ext='.py'):
272 272 """Make a valid python temp file."""
273 273 fname = temp_pyfile(src, ext)
274 274 if not hasattr(self, 'tmps'):
275 275 self.tmps=[]
276 276 self.tmps.append(fname)
277 277 self.fname = fname
278 278
279 279 def tearDown(self):
280 280 # If the tmpfile wasn't made because of skipped tests, like in
281 281 # win32, there's nothing to cleanup.
282 282 if hasattr(self, 'tmps'):
283 283 for fname in self.tmps:
284 284 # If the tmpfile wasn't made because of skipped tests, like in
285 285 # win32, there's nothing to cleanup.
286 286 try:
287 287 os.unlink(fname)
288 288 except:
289 289 # On Windows, even though we close the file, we still can't
290 290 # delete it. I have no clue why
291 291 pass
292 292
293 293 def __enter__(self):
294 294 return self
295 295
296 296 def __exit__(self, exc_type, exc_value, traceback):
297 297 self.tearDown()
298 298
299 299
300 300 pair_fail_msg = ("Testing {0}\n\n"
301 301 "In:\n"
302 302 " {1!r}\n"
303 303 "Expected:\n"
304 304 " {2!r}\n"
305 305 "Got:\n"
306 306 " {3!r}\n")
307 307 def check_pairs(func, pairs):
308 308 """Utility function for the common case of checking a function with a
309 309 sequence of input/output pairs.
310 310
311 311 Parameters
312 312 ----------
313 313 func : callable
314 314 The function to be tested. Should accept a single argument.
315 315 pairs : iterable
316 316 A list of (input, expected_output) tuples.
317 317
318 318 Returns
319 319 -------
320 320 None. Raises an AssertionError if any output does not match the expected
321 321 value.
322 322 """
323 323 __tracebackhide__ = True
324 324
325 325 name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>"))
326 326 for inp, expected in pairs:
327 327 out = func(inp)
328 328 assert out == expected, pair_fail_msg.format(name, inp, expected, out)
329 329
330 330
331 331 MyStringIO = StringIO
332 332
333 333 _re_type = type(re.compile(r''))
334 334
335 335 notprinted_msg = """Did not find {0!r} in printed output (on {1}):
336 336 -------
337 337 {2!s}
338 338 -------
339 339 """
340 340
341 341 class AssertPrints(object):
342 342 """Context manager for testing that code prints certain text.
343 343
344 344 Examples
345 345 --------
346 346 >>> with AssertPrints("abc", suppress=False):
347 347 ... print("abcd")
348 348 ... print("def")
349 349 ...
350 350 abcd
351 351 def
352 352 """
353 353 def __init__(self, s, channel='stdout', suppress=True):
354 354 self.s = s
355 355 if isinstance(self.s, (str, _re_type)):
356 356 self.s = [self.s]
357 357 self.channel = channel
358 358 self.suppress = suppress
359 359
360 360 def __enter__(self):
361 361 self.orig_stream = getattr(sys, self.channel)
362 362 self.buffer = MyStringIO()
363 363 self.tee = Tee(self.buffer, channel=self.channel)
364 364 setattr(sys, self.channel, self.buffer if self.suppress else self.tee)
365 365
366 366 def __exit__(self, etype, value, traceback):
367 367 __tracebackhide__ = True
368 368
369 369 try:
370 370 if value is not None:
371 371 # If an error was raised, don't check anything else
372 372 return False
373 373 self.tee.flush()
374 374 setattr(sys, self.channel, self.orig_stream)
375 375 printed = self.buffer.getvalue()
376 376 for s in self.s:
377 377 if isinstance(s, _re_type):
378 378 assert s.search(printed), notprinted_msg.format(s.pattern, self.channel, printed)
379 379 else:
380 380 assert s in printed, notprinted_msg.format(s, self.channel, printed)
381 381 return False
382 382 finally:
383 383 self.tee.close()
384 384
385 385 printed_msg = """Found {0!r} in printed output (on {1}):
386 386 -------
387 387 {2!s}
388 388 -------
389 389 """
390 390
391 391 class AssertNotPrints(AssertPrints):
392 392 """Context manager for checking that certain output *isn't* produced.
393 393
394 394 Counterpart of AssertPrints"""
395 395 def __exit__(self, etype, value, traceback):
396 396 __tracebackhide__ = True
397 397
398 398 try:
399 399 if value is not None:
400 400 # If an error was raised, don't check anything else
401 401 self.tee.close()
402 402 return False
403 403 self.tee.flush()
404 404 setattr(sys, self.channel, self.orig_stream)
405 405 printed = self.buffer.getvalue()
406 406 for s in self.s:
407 407 if isinstance(s, _re_type):
408 408 assert not s.search(printed),printed_msg.format(
409 409 s.pattern, self.channel, printed)
410 410 else:
411 411 assert s not in printed, printed_msg.format(
412 412 s, self.channel, printed)
413 413 return False
414 414 finally:
415 415 self.tee.close()
416 416
417 417 @contextmanager
418 418 def mute_warn():
419 419 from IPython.utils import warn
420 420 save_warn = warn.warn
421 421 warn.warn = lambda *a, **kw: None
422 422 try:
423 423 yield
424 424 finally:
425 425 warn.warn = save_warn
426 426
427 427 @contextmanager
428 428 def make_tempfile(name):
429 """ Create an empty, named, temporary file for the duration of the context.
430 """
431 open(name, 'w', encoding='utf-8').close()
429 """Create an empty, named, temporary file for the duration of the context."""
430 open(name, "w", encoding="utf-8").close()
432 431 try:
433 432 yield
434 433 finally:
435 434 os.unlink(name)
436 435
437 436 def fake_input(inputs):
438 437 """Temporarily replace the input() function to return the given values
439 438
440 439 Use as a context manager:
441 440
442 441 with fake_input(['result1', 'result2']):
443 442 ...
444 443
445 444 Values are returned in order. If input() is called again after the last value
446 445 was used, EOFError is raised.
447 446 """
448 447 it = iter(inputs)
449 448 def mock_input(prompt=''):
450 449 try:
451 450 return next(it)
452 451 except StopIteration as e:
453 452 raise EOFError('No more inputs given') from e
454 453
455 454 return patch('builtins.input', mock_input)
456 455
457 456 def help_output_test(subcommand=''):
458 457 """test that `ipython [subcommand] -h` works"""
459 458 cmd = get_ipython_cmd() + [subcommand, '-h']
460 459 out, err, rc = get_output_error_code(cmd)
461 460 assert rc == 0, err
462 461 assert "Traceback" not in err
463 462 assert "Options" in out
464 463 assert "--help-all" in out
465 464 return out, err
466 465
467 466
468 467 def help_all_output_test(subcommand=''):
469 468 """test that `ipython [subcommand] --help-all` works"""
470 469 cmd = get_ipython_cmd() + [subcommand, '--help-all']
471 470 out, err, rc = get_output_error_code(cmd)
472 471 assert rc == 0, err
473 472 assert "Traceback" not in err
474 473 assert "Options" in out
475 474 assert "Class" in out
476 475 return out, err
477 476
@@ -1,65 +1,67 b''
1 1 """
2 2 Test that CVEs stay fixed.
3 3 """
4 4
5 5 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
6 6 from pathlib import Path
7 7 import random
8 8 import sys
9 9 import os
10 10 import string
11 11 import subprocess
12 12 import time
13 13
14 14
15 15 def test_cve_2022_21699():
16 16 """
17 17 Here we test CVE-2022-21699.
18 18
19 19 We create a temporary directory, cd into it.
20 20 Make a profile file that should not be executed and start IPython in a subprocess,
21 21 checking for the value.
22 22
23 23
24 24
25 25 """
26 26
27 27 dangerous_profile_dir = Path("profile_default")
28 28
29 29 dangerous_startup_dir = dangerous_profile_dir / "startup"
30 30 dangerous_expected = "CVE-2022-21699-" + "".join(
31 31 [random.choice(string.ascii_letters) for i in range(10)]
32 32 )
33 33
34 34 with TemporaryWorkingDirectory() as t:
35 35 dangerous_startup_dir.mkdir(parents=True)
36 (dangerous_startup_dir / "foo.py").write_text(f'print("{dangerous_expected}")', encoding='utf-8')
36 (dangerous_startup_dir / "foo.py").write_text(
37 f'print("{dangerous_expected}")', encoding="utf-8"
38 )
37 39 # 1 sec to make sure FS is flushed.
38 40 # time.sleep(1)
39 41 cmd = [sys.executable, "-m", "IPython"]
40 42 env = os.environ.copy()
41 43 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
42 44
43 45 # First we fake old behavior, making sure the profile is/was actually dangerous
44 46 p_dangerous = subprocess.Popen(
45 47 cmd + [f"--profile-dir={dangerous_profile_dir}"],
46 48 env=env,
47 49 stdin=subprocess.PIPE,
48 50 stdout=subprocess.PIPE,
49 51 stderr=subprocess.PIPE,
50 52 )
51 53 out_dangerous, err_dangerouns = p_dangerous.communicate(b"exit\r")
52 54 assert dangerous_expected in out_dangerous.decode()
53 55
54 56 # Now that we know it _would_ have been dangerous, we test it's not loaded
55 57 p = subprocess.Popen(
56 58 cmd,
57 59 env=env,
58 60 stdin=subprocess.PIPE,
59 61 stdout=subprocess.PIPE,
60 62 stderr=subprocess.PIPE,
61 63 )
62 64 out, err = p.communicate(b"exit\r")
63 65 assert b"IPython" in out
64 66 assert dangerous_expected not in out.decode()
65 67 assert err == b""
@@ -1,157 +1,157 b''
1 1 # encoding: utf-8
2 2 """
3 3 IO related utilities.
4 4 """
5 5
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 9
10 10
11 11 import atexit
12 12 import os
13 13 import sys
14 14 import tempfile
15 15 import warnings
16 16 from pathlib import Path
17 17 from warnings import warn
18 18
19 19 from IPython.utils.decorators import undoc
20 20 from .capture import CapturedIO, capture_output
21 21
22 22 # setup stdin/stdout/stderr to sys.stdin/sys.stdout/sys.stderr
23 devnull = open(os.devnull, 'w', encoding='utf-8')
23 devnull = open(os.devnull, "w", encoding="utf-8")
24 24 atexit.register(devnull.close)
25 25
26 26
27 27 class Tee(object):
28 28 """A class to duplicate an output stream to stdout/err.
29 29
30 30 This works in a manner very similar to the Unix 'tee' command.
31 31
32 32 When the object is closed or deleted, it closes the original file given to
33 33 it for duplication.
34 34 """
35 35 # Inspired by:
36 36 # http://mail.python.org/pipermail/python-list/2007-May/442737.html
37 37
38 38 def __init__(self, file_or_name, mode="w", channel='stdout'):
39 39 """Construct a new Tee object.
40 40
41 41 Parameters
42 42 ----------
43 43 file_or_name : filename or open filehandle (writable)
44 44 File that will be duplicated
45 45 mode : optional, valid mode for open().
46 46 If a filename was give, open with this mode.
47 47 channel : str, one of ['stdout', 'stderr']
48 48 """
49 49 if channel not in ['stdout', 'stderr']:
50 50 raise ValueError('Invalid channel spec %s' % channel)
51 51
52 52 if hasattr(file_or_name, 'write') and hasattr(file_or_name, 'seek'):
53 53 self.file = file_or_name
54 54 else:
55 encoding = None if 'b' in mode else 'utf-8'
55 encoding = None if "b" in mode else "utf-8"
56 56 self.file = open(file_or_name, mode, encoding=encoding)
57 57 self.channel = channel
58 58 self.ostream = getattr(sys, channel)
59 59 setattr(sys, channel, self)
60 60 self._closed = False
61 61
62 62 def close(self):
63 63 """Close the file and restore the channel."""
64 64 self.flush()
65 65 setattr(sys, self.channel, self.ostream)
66 66 self.file.close()
67 67 self._closed = True
68 68
69 69 def write(self, data):
70 70 """Write data to both channels."""
71 71 self.file.write(data)
72 72 self.ostream.write(data)
73 73 self.ostream.flush()
74 74
75 75 def flush(self):
76 76 """Flush both channels."""
77 77 self.file.flush()
78 78 self.ostream.flush()
79 79
80 80 def __del__(self):
81 81 if not self._closed:
82 82 self.close()
83 83
84 84
85 85 def ask_yes_no(prompt, default=None, interrupt=None):
86 86 """Asks a question and returns a boolean (y/n) answer.
87 87
88 88 If default is given (one of 'y','n'), it is used if the user input is
89 89 empty. If interrupt is given (one of 'y','n'), it is used if the user
90 90 presses Ctrl-C. Otherwise the question is repeated until an answer is
91 91 given.
92 92
93 93 An EOF is treated as the default answer. If there is no default, an
94 94 exception is raised to prevent infinite loops.
95 95
96 96 Valid answers are: y/yes/n/no (match is not case sensitive)."""
97 97
98 98 answers = {'y':True,'n':False,'yes':True,'no':False}
99 99 ans = None
100 100 while ans not in answers.keys():
101 101 try:
102 102 ans = input(prompt+' ').lower()
103 103 if not ans: # response was an empty string
104 104 ans = default
105 105 except KeyboardInterrupt:
106 106 if interrupt:
107 107 ans = interrupt
108 108 print("\r")
109 109 except EOFError:
110 110 if default in answers.keys():
111 111 ans = default
112 112 print()
113 113 else:
114 114 raise
115 115
116 116 return answers[ans]
117 117
118 118
119 119 def temp_pyfile(src, ext='.py'):
120 120 """Make a temporary python file, return filename and filehandle.
121 121
122 122 Parameters
123 123 ----------
124 124 src : string or list of strings (no need for ending newlines if list)
125 125 Source code to be written to the file.
126 126 ext : optional, string
127 127 Extension for the generated file.
128 128
129 129 Returns
130 130 -------
131 131 (filename, open filehandle)
132 132 It is the caller's responsibility to close the open file and unlink it.
133 133 """
134 134 fname = tempfile.mkstemp(ext)[1]
135 with open(Path(fname), "w", encoding='utf-8') as f:
135 with open(Path(fname), "w", encoding="utf-8") as f:
136 136 f.write(src)
137 137 f.flush()
138 138 return fname
139 139
140 140
141 141 @undoc
142 142 def raw_print(*args, **kw):
143 143 """DEPRECATED: Raw print to sys.__stdout__, otherwise identical interface to print()."""
144 144 warn("IPython.utils.io.raw_print has been deprecated since IPython 7.0", DeprecationWarning, stacklevel=2)
145 145
146 146 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
147 147 file=sys.__stdout__)
148 148 sys.__stdout__.flush()
149 149
150 150 @undoc
151 151 def raw_print_err(*args, **kw):
152 152 """DEPRECATED: Raw print to sys.__stderr__, otherwise identical interface to print()."""
153 153 warn("IPython.utils.io.raw_print_err has been deprecated since IPython 7.0", DeprecationWarning, stacklevel=2)
154 154
155 155 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
156 156 file=sys.__stderr__)
157 157 sys.__stderr__.flush()
@@ -1,59 +1,59 b''
1 1 """ This module contains classes - NamedFileInTemporaryDirectory, TemporaryWorkingDirectory.
2 2
3 3 These classes add extra features such as creating a named file in temporary directory and
4 4 creating a context manager for the working directory which is also temporary.
5 5 """
6 6
7 7 import os as _os
8 8 from pathlib import Path
9 9 from tempfile import TemporaryDirectory
10 10
11 11
12 12 class NamedFileInTemporaryDirectory(object):
13 13
14 14 def __init__(self, filename, mode='w+b', bufsize=-1, **kwds):
15 15 """
16 16 Open a file named `filename` in a temporary directory.
17 17
18 18 This context manager is preferred over `NamedTemporaryFile` in
19 19 stdlib `tempfile` when one needs to reopen the file.
20 20
21 21 Arguments `mode` and `bufsize` are passed to `open`.
22 22 Rest of the arguments are passed to `TemporaryDirectory`.
23 23
24 24 """
25 25 self._tmpdir = TemporaryDirectory(**kwds)
26 26 path = Path(self._tmpdir.name) / filename
27 encoding = None if 'b' in mode else 'utf-8'
27 encoding = None if "b" in mode else "utf-8"
28 28 self.file = open(path, mode, bufsize, encoding=encoding)
29 29
30 30 def cleanup(self):
31 31 self.file.close()
32 32 self._tmpdir.cleanup()
33 33
34 34 __del__ = cleanup
35 35
36 36 def __enter__(self):
37 37 return self.file
38 38
39 39 def __exit__(self, type, value, traceback):
40 40 self.cleanup()
41 41
42 42
43 43 class TemporaryWorkingDirectory(TemporaryDirectory):
44 44 """
45 45 Creates a temporary directory and sets the cwd to that directory.
46 46 Automatically reverts to previous cwd upon cleanup.
47 47 Usage example:
48 48
49 49 with TemporaryWorkingDirectory() as tmpdir:
50 50 ...
51 51 """
52 52 def __enter__(self):
53 53 self.old_wd = Path.cwd()
54 54 _os.chdir(self.name)
55 55 return super(TemporaryWorkingDirectory, self).__enter__()
56 56
57 57 def __exit__(self, exc, value, tb):
58 58 _os.chdir(self.old_wd)
59 59 return super(TemporaryWorkingDirectory, self).__exit__(exc, value, tb)
@@ -1,109 +1,109 b''
1 1 # encoding: utf-8
2 2 """Tests for IPython.utils.module_paths.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 import shutil
16 16 import sys
17 17 import tempfile
18 18
19 19 from pathlib import Path
20 20
21 21 from IPython.testing.tools import make_tempfile
22 22
23 23 import IPython.utils.module_paths as mp
24 24
25 25 TEST_FILE_PATH = Path(__file__).resolve().parent
26 26
27 27 TMP_TEST_DIR = Path(tempfile.mkdtemp(suffix="with.dot"))
28 28 #
29 29 # Setup/teardown functions/decorators
30 30 #
31 31
32 32 old_syspath = sys.path
33 33
34 34 def make_empty_file(fname):
35 open(fname, 'w', encoding='utf-8').close()
35 open(fname, "w", encoding="utf-8").close()
36 36
37 37
38 38 def setup_module():
39 39 """Setup testenvironment for the module:
40 40
41 41 """
42 42 # Do not mask exceptions here. In particular, catching WindowsError is a
43 43 # problem because that exception is only defined on Windows...
44 44 Path(TMP_TEST_DIR / "xmod").mkdir(parents=True)
45 45 Path(TMP_TEST_DIR / "nomod").mkdir(parents=True)
46 46 make_empty_file(TMP_TEST_DIR / "xmod/__init__.py")
47 47 make_empty_file(TMP_TEST_DIR / "xmod/sub.py")
48 48 make_empty_file(TMP_TEST_DIR / "pack.py")
49 49 make_empty_file(TMP_TEST_DIR / "packpyc.pyc")
50 50 sys.path = [str(TMP_TEST_DIR)]
51 51
52 52 def teardown_module():
53 53 """Teardown testenvironment for the module:
54 54
55 55 - Remove tempdir
56 56 - restore sys.path
57 57 """
58 58 # Note: we remove the parent test dir, which is the root of all test
59 59 # subdirs we may have created. Use shutil instead of os.removedirs, so
60 60 # that non-empty directories are all recursively removed.
61 61 shutil.rmtree(TMP_TEST_DIR)
62 62 sys.path = old_syspath
63 63
64 64 def test_tempdir():
65 65 """
66 66 Ensure the test are done with a temporary file that have a dot somewhere.
67 67 """
68 68 assert "." in str(TMP_TEST_DIR)
69 69
70 70
71 71 def test_find_mod_1():
72 72 """
73 73 Search for a directory's file path.
74 74 Expected output: a path to that directory's __init__.py file.
75 75 """
76 76 modpath = TMP_TEST_DIR / "xmod" / "__init__.py"
77 77 assert Path(mp.find_mod("xmod")) == modpath
78 78
79 79 def test_find_mod_2():
80 80 """
81 81 Search for a directory's file path.
82 82 Expected output: a path to that directory's __init__.py file.
83 83 TODO: Confirm why this is a duplicate test.
84 84 """
85 85 modpath = TMP_TEST_DIR / "xmod" / "__init__.py"
86 86 assert Path(mp.find_mod("xmod")) == modpath
87 87
88 88 def test_find_mod_3():
89 89 """
90 90 Search for a directory + a filename without its .py extension
91 91 Expected output: full path with .py extension.
92 92 """
93 93 modpath = TMP_TEST_DIR / "xmod" / "sub.py"
94 94 assert Path(mp.find_mod("xmod.sub")) == modpath
95 95
96 96 def test_find_mod_4():
97 97 """
98 98 Search for a filename without its .py extension
99 99 Expected output: full path with .py extension
100 100 """
101 101 modpath = TMP_TEST_DIR / "pack.py"
102 102 assert Path(mp.find_mod("pack")) == modpath
103 103
104 104 def test_find_mod_5():
105 105 """
106 106 Search for a filename with a .pyc extension
107 107 Expected output: TODO: do we exclude or include .pyc files?
108 108 """
109 109 assert mp.find_mod("packpyc") == None
@@ -1,509 +1,509 b''
1 1 # encoding: utf-8
2 2 """Tests for IPython.utils.path.py"""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 import os
8 8 import shutil
9 9 import sys
10 10 import tempfile
11 11 import unittest
12 12 from contextlib import contextmanager
13 13 from unittest.mock import patch
14 14 from os.path import join, abspath
15 15 from importlib import reload
16 16
17 17 import pytest
18 18
19 19 import IPython
20 20 from IPython import paths
21 21 from IPython.testing import decorators as dec
22 22 from IPython.testing.decorators import (
23 23 skip_if_not_win32,
24 24 skip_win32,
25 25 onlyif_unicode_paths,
26 26 )
27 27 from IPython.testing.tools import make_tempfile
28 28 from IPython.utils import path
29 29 from IPython.utils.tempdir import TemporaryDirectory
30 30
31 31
32 32 # Platform-dependent imports
33 33 try:
34 34 import winreg as wreg
35 35 except ImportError:
36 36 #Fake _winreg module on non-windows platforms
37 37 import types
38 38 wr_name = "winreg"
39 39 sys.modules[wr_name] = types.ModuleType(wr_name)
40 40 try:
41 41 import winreg as wreg
42 42 except ImportError:
43 43 import _winreg as wreg
44 44 #Add entries that needs to be stubbed by the testing code
45 45 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
46 46
47 47 #-----------------------------------------------------------------------------
48 48 # Globals
49 49 #-----------------------------------------------------------------------------
50 50 env = os.environ
51 51 TMP_TEST_DIR = tempfile.mkdtemp()
52 52 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
53 53 #
54 54 # Setup/teardown functions/decorators
55 55 #
56 56
57 57 def setup_module():
58 58 """Setup testenvironment for the module:
59 59
60 60 - Adds dummy home dir tree
61 61 """
62 62 # Do not mask exceptions here. In particular, catching WindowsError is a
63 63 # problem because that exception is only defined on Windows...
64 64 os.makedirs(os.path.join(HOME_TEST_DIR, 'ipython'))
65 65
66 66
67 67 def teardown_module():
68 68 """Teardown testenvironment for the module:
69 69
70 70 - Remove dummy home dir tree
71 71 """
72 72 # Note: we remove the parent test dir, which is the root of all test
73 73 # subdirs we may have created. Use shutil instead of os.removedirs, so
74 74 # that non-empty directories are all recursively removed.
75 75 shutil.rmtree(TMP_TEST_DIR)
76 76
77 77
78 78 def setup_environment():
79 79 """Setup testenvironment for some functions that are tested
80 80 in this module. In particular this functions stores attributes
81 81 and other things that we need to stub in some test functions.
82 82 This needs to be done on a function level and not module level because
83 83 each testfunction needs a pristine environment.
84 84 """
85 85 global oldstuff, platformstuff
86 86 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
87 87
88 88 def teardown_environment():
89 89 """Restore things that were remembered by the setup_environment function
90 90 """
91 91 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
92 92 os.chdir(old_wd)
93 93 reload(path)
94 94
95 95 for key in list(env):
96 96 if key not in oldenv:
97 97 del env[key]
98 98 env.update(oldenv)
99 99 if hasattr(sys, 'frozen'):
100 100 del sys.frozen
101 101
102 102
103 103 # Build decorator that uses the setup_environment/setup_environment
104 104 @pytest.fixture
105 105 def environment():
106 106 setup_environment()
107 107 yield
108 108 teardown_environment()
109 109
110 110
111 111 with_environment = pytest.mark.usefixtures("environment")
112 112
113 113
114 114 @skip_if_not_win32
115 115 @with_environment
116 116 def test_get_home_dir_1():
117 117 """Testcase for py2exe logic, un-compressed lib
118 118 """
119 119 unfrozen = path.get_home_dir()
120 120 sys.frozen = True
121 121
122 122 #fake filename for IPython.__init__
123 123 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
124 124
125 125 home_dir = path.get_home_dir()
126 126 assert home_dir == unfrozen
127 127
128 128
129 129 @skip_if_not_win32
130 130 @with_environment
131 131 def test_get_home_dir_2():
132 132 """Testcase for py2exe logic, compressed lib
133 133 """
134 134 unfrozen = path.get_home_dir()
135 135 sys.frozen = True
136 136 #fake filename for IPython.__init__
137 137 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
138 138
139 139 home_dir = path.get_home_dir(True)
140 140 assert home_dir == unfrozen
141 141
142 142
143 143 @skip_win32
144 144 @with_environment
145 145 def test_get_home_dir_3():
146 146 """get_home_dir() uses $HOME if set"""
147 147 env["HOME"] = HOME_TEST_DIR
148 148 home_dir = path.get_home_dir(True)
149 149 # get_home_dir expands symlinks
150 150 assert home_dir == os.path.realpath(env["HOME"])
151 151
152 152
153 153 @with_environment
154 154 def test_get_home_dir_4():
155 155 """get_home_dir() still works if $HOME is not set"""
156 156
157 157 if 'HOME' in env: del env['HOME']
158 158 # this should still succeed, but we don't care what the answer is
159 159 home = path.get_home_dir(False)
160 160
161 161 @skip_win32
162 162 @with_environment
163 163 def test_get_home_dir_5():
164 164 """raise HomeDirError if $HOME is specified, but not a writable dir"""
165 165 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
166 166 # set os.name = posix, to prevent My Documents fallback on Windows
167 167 os.name = 'posix'
168 168 pytest.raises(path.HomeDirError, path.get_home_dir, True)
169 169
170 170 # Should we stub wreg fully so we can run the test on all platforms?
171 171 @skip_if_not_win32
172 172 @with_environment
173 173 def test_get_home_dir_8():
174 174 """Using registry hack for 'My Documents', os=='nt'
175 175
176 176 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
177 177 """
178 178 os.name = 'nt'
179 179 # Remove from stub environment all keys that may be set
180 180 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
181 181 env.pop(key, None)
182 182
183 183 class key:
184 184 def __enter__(self):
185 185 pass
186 186 def Close(self):
187 187 pass
188 188 def __exit__(*args, **kwargs):
189 189 pass
190 190
191 191 with patch.object(wreg, 'OpenKey', return_value=key()), \
192 192 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
193 193 home_dir = path.get_home_dir()
194 194 assert home_dir == abspath(HOME_TEST_DIR)
195 195
196 196 @with_environment
197 197 def test_get_xdg_dir_0():
198 198 """test_get_xdg_dir_0, check xdg_dir"""
199 199 reload(path)
200 200 path._writable_dir = lambda path: True
201 201 path.get_home_dir = lambda : 'somewhere'
202 202 os.name = "posix"
203 203 sys.platform = "linux2"
204 204 env.pop('IPYTHON_DIR', None)
205 205 env.pop('IPYTHONDIR', None)
206 206 env.pop('XDG_CONFIG_HOME', None)
207 207
208 208 assert path.get_xdg_dir() == os.path.join("somewhere", ".config")
209 209
210 210
211 211 @with_environment
212 212 def test_get_xdg_dir_1():
213 213 """test_get_xdg_dir_1, check nonexistent xdg_dir"""
214 214 reload(path)
215 215 path.get_home_dir = lambda : HOME_TEST_DIR
216 216 os.name = "posix"
217 217 sys.platform = "linux2"
218 218 env.pop('IPYTHON_DIR', None)
219 219 env.pop('IPYTHONDIR', None)
220 220 env.pop('XDG_CONFIG_HOME', None)
221 221 assert path.get_xdg_dir() is None
222 222
223 223 @with_environment
224 224 def test_get_xdg_dir_2():
225 225 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
226 226 reload(path)
227 227 path.get_home_dir = lambda : HOME_TEST_DIR
228 228 os.name = "posix"
229 229 sys.platform = "linux2"
230 230 env.pop('IPYTHON_DIR', None)
231 231 env.pop('IPYTHONDIR', None)
232 232 env.pop('XDG_CONFIG_HOME', None)
233 233 cfgdir=os.path.join(path.get_home_dir(), '.config')
234 234 if not os.path.exists(cfgdir):
235 235 os.makedirs(cfgdir)
236 236
237 237 assert path.get_xdg_dir() == cfgdir
238 238
239 239 @with_environment
240 240 def test_get_xdg_dir_3():
241 241 """test_get_xdg_dir_3, check xdg_dir not used on non-posix systems"""
242 242 reload(path)
243 243 path.get_home_dir = lambda : HOME_TEST_DIR
244 244 os.name = "nt"
245 245 sys.platform = "win32"
246 246 env.pop('IPYTHON_DIR', None)
247 247 env.pop('IPYTHONDIR', None)
248 248 env.pop('XDG_CONFIG_HOME', None)
249 249 cfgdir=os.path.join(path.get_home_dir(), '.config')
250 250 os.makedirs(cfgdir, exist_ok=True)
251 251
252 252 assert path.get_xdg_dir() is None
253 253
254 254 def test_filefind():
255 255 """Various tests for filefind"""
256 256 f = tempfile.NamedTemporaryFile()
257 257 # print 'fname:',f.name
258 258 alt_dirs = paths.get_ipython_dir()
259 259 t = path.filefind(f.name, alt_dirs)
260 260 # print 'found:',t
261 261
262 262
263 263 @dec.skip_if_not_win32
264 264 def test_get_long_path_name_win32():
265 265 with TemporaryDirectory() as tmpdir:
266 266
267 267 # Make a long path. Expands the path of tmpdir prematurely as it may already have a long
268 268 # path component, so ensure we include the long form of it
269 269 long_path = os.path.join(path.get_long_path_name(tmpdir), 'this is my long path name')
270 270 os.makedirs(long_path)
271 271
272 272 # Test to see if the short path evaluates correctly.
273 273 short_path = os.path.join(tmpdir, 'THISIS~1')
274 274 evaluated_path = path.get_long_path_name(short_path)
275 275 assert evaluated_path.lower() == long_path.lower()
276 276
277 277
278 278 @dec.skip_win32
279 279 def test_get_long_path_name():
280 280 p = path.get_long_path_name("/usr/local")
281 281 assert p == "/usr/local"
282 282
283 283
284 284 class TestRaiseDeprecation(unittest.TestCase):
285 285
286 286 @dec.skip_win32 # can't create not-user-writable dir on win
287 287 @with_environment
288 288 def test_not_writable_ipdir(self):
289 289 tmpdir = tempfile.mkdtemp()
290 290 os.name = "posix"
291 291 env.pop('IPYTHON_DIR', None)
292 292 env.pop('IPYTHONDIR', None)
293 293 env.pop('XDG_CONFIG_HOME', None)
294 294 env['HOME'] = tmpdir
295 295 ipdir = os.path.join(tmpdir, '.ipython')
296 296 os.mkdir(ipdir, 0o555)
297 297 try:
298 open(os.path.join(ipdir, "_foo_"), 'w', encoding='utf-8').close()
298 open(os.path.join(ipdir, "_foo_"), "w", encoding="utf-8").close()
299 299 except IOError:
300 300 pass
301 301 else:
302 302 # I can still write to an unwritable dir,
303 303 # assume I'm root and skip the test
304 304 pytest.skip("I can't create directories that I can't write to")
305 305
306 306 with self.assertWarnsRegex(UserWarning, 'is not a writable location'):
307 307 ipdir = paths.get_ipython_dir()
308 308 env.pop('IPYTHON_DIR', None)
309 309
310 310 @with_environment
311 311 def test_get_py_filename():
312 312 os.chdir(TMP_TEST_DIR)
313 313 with make_tempfile("foo.py"):
314 314 assert path.get_py_filename("foo.py") == "foo.py"
315 315 assert path.get_py_filename("foo") == "foo.py"
316 316 with make_tempfile("foo"):
317 317 assert path.get_py_filename("foo") == "foo"
318 318 pytest.raises(IOError, path.get_py_filename, "foo.py")
319 319 pytest.raises(IOError, path.get_py_filename, "foo")
320 320 pytest.raises(IOError, path.get_py_filename, "foo.py")
321 321 true_fn = "foo with spaces.py"
322 322 with make_tempfile(true_fn):
323 323 assert path.get_py_filename("foo with spaces") == true_fn
324 324 assert path.get_py_filename("foo with spaces.py") == true_fn
325 325 pytest.raises(IOError, path.get_py_filename, '"foo with spaces.py"')
326 326 pytest.raises(IOError, path.get_py_filename, "'foo with spaces.py'")
327 327
328 328 @onlyif_unicode_paths
329 329 def test_unicode_in_filename():
330 330 """When a file doesn't exist, the exception raised should be safe to call
331 331 str() on - i.e. in Python 2 it must only have ASCII characters.
332 332
333 333 https://github.com/ipython/ipython/issues/875
334 334 """
335 335 try:
336 336 # these calls should not throw unicode encode exceptions
337 337 path.get_py_filename('fooéè.py')
338 338 except IOError as ex:
339 339 str(ex)
340 340
341 341
342 342 class TestShellGlob(unittest.TestCase):
343 343
344 344 @classmethod
345 345 def setUpClass(cls):
346 346 cls.filenames_start_with_a = ['a0', 'a1', 'a2']
347 347 cls.filenames_end_with_b = ['0b', '1b', '2b']
348 348 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
349 349 cls.tempdir = TemporaryDirectory()
350 350 td = cls.tempdir.name
351 351
352 352 with cls.in_tempdir():
353 353 # Create empty files
354 354 for fname in cls.filenames:
355 open(os.path.join(td, fname), 'w', encoding='utf-8').close()
355 open(os.path.join(td, fname), "w", encoding="utf-8").close()
356 356
357 357 @classmethod
358 358 def tearDownClass(cls):
359 359 cls.tempdir.cleanup()
360 360
361 361 @classmethod
362 362 @contextmanager
363 363 def in_tempdir(cls):
364 364 save = os.getcwd()
365 365 try:
366 366 os.chdir(cls.tempdir.name)
367 367 yield
368 368 finally:
369 369 os.chdir(save)
370 370
371 371 def check_match(self, patterns, matches):
372 372 with self.in_tempdir():
373 373 # glob returns unordered list. that's why sorted is required.
374 374 assert sorted(path.shellglob(patterns)) == sorted(matches)
375 375
376 376 def common_cases(self):
377 377 return [
378 378 (['*'], self.filenames),
379 379 (['a*'], self.filenames_start_with_a),
380 380 (['*c'], ['*c']),
381 381 (['*', 'a*', '*b', '*c'], self.filenames
382 382 + self.filenames_start_with_a
383 383 + self.filenames_end_with_b
384 384 + ['*c']),
385 385 (['a[012]'], self.filenames_start_with_a),
386 386 ]
387 387
388 388 @skip_win32
389 389 def test_match_posix(self):
390 390 for (patterns, matches) in self.common_cases() + [
391 391 ([r'\*'], ['*']),
392 392 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
393 393 ([r'a\[012]'], ['a[012]']),
394 394 ]:
395 395 self.check_match(patterns, matches)
396 396
397 397 @skip_if_not_win32
398 398 def test_match_windows(self):
399 399 for (patterns, matches) in self.common_cases() + [
400 400 # In windows, backslash is interpreted as path
401 401 # separator. Therefore, you can't escape glob
402 402 # using it.
403 403 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
404 404 ([r'a\[012]'], [r'a\[012]']),
405 405 ]:
406 406 self.check_match(patterns, matches)
407 407
408 408
409 409 @pytest.mark.parametrize(
410 410 "globstr, unescaped_globstr",
411 411 [
412 412 (r"\*\[\!\]\?", "*[!]?"),
413 413 (r"\\*", r"\*"),
414 414 (r"\\\*", r"\*"),
415 415 (r"\\a", r"\a"),
416 416 (r"\a", r"\a"),
417 417 ],
418 418 )
419 419 def test_unescape_glob(globstr, unescaped_globstr):
420 420 assert path.unescape_glob(globstr) == unescaped_globstr
421 421
422 422
423 423 @onlyif_unicode_paths
424 424 def test_ensure_dir_exists():
425 425 with TemporaryDirectory() as td:
426 426 d = os.path.join(td, '∂ir')
427 427 path.ensure_dir_exists(d) # create it
428 428 assert os.path.isdir(d)
429 path.ensure_dir_exists(d) # no-op
430 f = os.path.join(td, 'Æ’ile')
431 open(f, 'w', encoding='utf-8').close() # touch
429 path.ensure_dir_exists(d) # no-op
430 f = os.path.join(td, "Æ’ile")
431 open(f, "w", encoding="utf-8").close() # touch
432 432 with pytest.raises(IOError):
433 433 path.ensure_dir_exists(f)
434 434
435 435 class TestLinkOrCopy(unittest.TestCase):
436 436 def setUp(self):
437 437 self.tempdir = TemporaryDirectory()
438 438 self.src = self.dst("src")
439 with open(self.src, "w", encoding='utf-8') as f:
439 with open(self.src, "w", encoding="utf-8") as f:
440 440 f.write("Hello, world!")
441 441
442 442 def tearDown(self):
443 443 self.tempdir.cleanup()
444 444
445 445 def dst(self, *args):
446 446 return os.path.join(self.tempdir.name, *args)
447 447
448 448 def assert_inode_not_equal(self, a, b):
449 449 assert (
450 450 os.stat(a).st_ino != os.stat(b).st_ino
451 451 ), "%r and %r do reference the same indoes" % (a, b)
452 452
453 453 def assert_inode_equal(self, a, b):
454 454 assert (
455 455 os.stat(a).st_ino == os.stat(b).st_ino
456 456 ), "%r and %r do not reference the same indoes" % (a, b)
457 457
458 458 def assert_content_equal(self, a, b):
459 with open(a, 'rb') as a_f:
460 with open(b, 'rb') as b_f:
459 with open(a, "rb") as a_f:
460 with open(b, "rb") as b_f:
461 461 assert a_f.read() == b_f.read()
462 462
463 463 @skip_win32
464 464 def test_link_successful(self):
465 465 dst = self.dst("target")
466 466 path.link_or_copy(self.src, dst)
467 467 self.assert_inode_equal(self.src, dst)
468 468
469 469 @skip_win32
470 470 def test_link_into_dir(self):
471 471 dst = self.dst("some_dir")
472 472 os.mkdir(dst)
473 473 path.link_or_copy(self.src, dst)
474 474 expected_dst = self.dst("some_dir", os.path.basename(self.src))
475 475 self.assert_inode_equal(self.src, expected_dst)
476 476
477 477 @skip_win32
478 478 def test_target_exists(self):
479 479 dst = self.dst("target")
480 open(dst, "w", encoding='utf-8').close()
480 open(dst, "w", encoding="utf-8").close()
481 481 path.link_or_copy(self.src, dst)
482 482 self.assert_inode_equal(self.src, dst)
483 483
484 484 @skip_win32
485 485 def test_no_link(self):
486 486 real_link = os.link
487 487 try:
488 488 del os.link
489 489 dst = self.dst("target")
490 490 path.link_or_copy(self.src, dst)
491 491 self.assert_content_equal(self.src, dst)
492 492 self.assert_inode_not_equal(self.src, dst)
493 493 finally:
494 494 os.link = real_link
495 495
496 496 @skip_if_not_win32
497 497 def test_windows(self):
498 498 dst = self.dst("target")
499 499 path.link_or_copy(self.src, dst)
500 500 self.assert_content_equal(self.src, dst)
501 501
502 502 def test_link_twice(self):
503 503 # Linking the same file twice shouldn't leave duplicates around.
504 504 # See https://github.com/ipython/ipython/issues/6450
505 505 dst = self.dst('target')
506 506 path.link_or_copy(self.src, dst)
507 507 path.link_or_copy(self.src, dst)
508 508 self.assert_inode_equal(self.src, dst)
509 509 assert sorted(os.listdir(self.tempdir.name)) == ["src", "target"]
@@ -1,126 +1,126 b''
1 1 #!/usr/bin/env python
2 2
3 3 import inspect
4 4 from pathlib import Path
5 5 from IPython.terminal.ipapp import TerminalIPythonApp
6 6 from ipykernel.kernelapp import IPKernelApp
7 7 from traitlets import Undefined
8 8 from collections import defaultdict
9 9
10 10 here = (Path(__file__)).parent
11 11 options = here / "source" / "config" / "options"
12 12 generated = options / "config-generated.txt"
13 13
14 14 import textwrap
15 15 indent = lambda text,n: textwrap.indent(text,n*' ')
16 16
17 17
18 18 def interesting_default_value(dv):
19 19 if (dv is None) or (dv is Undefined):
20 20 return False
21 21 if isinstance(dv, (str, list, tuple, dict, set)):
22 22 return bool(dv)
23 23 return True
24 24
25 25 def format_aliases(aliases):
26 26 fmted = []
27 27 for a in aliases:
28 28 dashes = '-' if len(a) == 1 else '--'
29 29 fmted.append('``%s%s``' % (dashes, a))
30 30 return ', '.join(fmted)
31 31
32 32 def class_config_rst_doc(cls, trait_aliases):
33 33 """Generate rST documentation for this class' config options.
34 34
35 35 Excludes traits defined on parent classes.
36 36 """
37 37 lines = []
38 38 classname = cls.__name__
39 39 for k, trait in sorted(cls.class_traits(config=True).items()):
40 40 ttype = trait.__class__.__name__
41 41
42 42 fullname = classname + '.' + trait.name
43 43 lines += ['.. configtrait:: ' + fullname,
44 44 ''
45 45 ]
46 46
47 47 help = trait.help.rstrip() or 'No description'
48 48 lines.append(indent(inspect.cleandoc(help), 4) + '\n')
49 49
50 50 # Choices or type
51 51 if 'Enum' in ttype:
52 52 # include Enum choices
53 53 lines.append(indent(
54 54 ':options: ' + ', '.join('``%r``' % x for x in trait.values), 4))
55 55 else:
56 56 lines.append(indent(':trait type: ' + ttype, 4))
57 57
58 58 # Default value
59 59 # Ignore boring default values like None, [] or ''
60 60 if interesting_default_value(trait.default_value):
61 61 try:
62 62 dvr = trait.default_value_repr()
63 63 except Exception:
64 64 dvr = None # ignore defaults we can't construct
65 65 if dvr is not None:
66 66 if len(dvr) > 64:
67 67 dvr = dvr[:61] + '...'
68 68 # Double up backslashes, so they get to the rendered docs
69 69 dvr = dvr.replace('\\n', '\\\\n')
70 70 lines.append(indent(':default: ``%s``' % dvr, 4))
71 71
72 72 # Command line aliases
73 73 if trait_aliases[fullname]:
74 74 fmt_aliases = format_aliases(trait_aliases[fullname])
75 75 lines.append(indent(':CLI option: ' + fmt_aliases, 4))
76 76
77 77 # Blank line
78 78 lines.append('')
79 79
80 80 return '\n'.join(lines)
81 81
82 82 def reverse_aliases(app):
83 83 """Produce a mapping of trait names to lists of command line aliases.
84 84 """
85 85 res = defaultdict(list)
86 86 for alias, trait in app.aliases.items():
87 87 res[trait].append(alias)
88 88
89 89 # Flags also often act as aliases for a boolean trait.
90 90 # Treat flags which set one trait to True as aliases.
91 91 for flag, (cfg, _) in app.flags.items():
92 92 if len(cfg) == 1:
93 93 classname = list(cfg)[0]
94 94 cls_cfg = cfg[classname]
95 95 if len(cls_cfg) == 1:
96 96 traitname = list(cls_cfg)[0]
97 97 if cls_cfg[traitname] is True:
98 98 res[classname+'.'+traitname].append(flag)
99 99
100 100 return res
101 101
102 102 def write_doc(name, title, app, preamble=None):
103 103 trait_aliases = reverse_aliases(app)
104 104 filename = options / (name + ".rst")
105 with open(filename, "w", encoding='utf-8') as f:
105 with open(filename, "w", encoding="utf-8") as f:
106 106 f.write(title + "\n")
107 107 f.write(("=" * len(title)) + "\n")
108 108 f.write("\n")
109 109 if preamble is not None:
110 110 f.write(preamble + '\n\n')
111 111 #f.write(app.document_config_options())
112 112
113 113 for c in app._classes_inc_parents():
114 114 f.write(class_config_rst_doc(c, trait_aliases))
115 115 f.write('\n')
116 116
117 117
118 118 if __name__ == '__main__':
119 119 # Touch this file for the make target
120 Path(generated).write_text("", encoding='utf-8')
120 Path(generated).write_text("", encoding="utf-8")
121 121
122 122 write_doc('terminal', 'Terminal IPython options', TerminalIPythonApp())
123 123 write_doc('kernel', 'IPython kernel options', IPKernelApp(),
124 124 preamble=("These options can be used in :file:`ipython_kernel_config.py`. "
125 125 "The kernel also respects any options in `ipython_config.py`"),
126 126 )
@@ -1,66 +1,66 b''
1 1 from pathlib import Path
2 2 from IPython.core.alias import Alias
3 3 from IPython.core.interactiveshell import InteractiveShell
4 4 from IPython.core.magic import MagicAlias
5 5 from IPython.utils.text import dedent, indent
6 6
7 7 shell = InteractiveShell.instance()
8 8 magics = shell.magics_manager.magics
9 9
10 10 def _strip_underline(line):
11 11 chars = set(line.strip())
12 12 if len(chars) == 1 and ("-" in chars or "=" in chars):
13 13 return ""
14 14 else:
15 15 return line
16 16
17 17 def format_docstring(func):
18 18 docstring = (func.__doc__ or "Undocumented").rstrip()
19 19 docstring = indent(dedent(docstring))
20 20 # Sphinx complains if indented bits have rst headings in, so strip out
21 21 # any underlines in the docstring.
22 22 lines = [_strip_underline(l) for l in docstring.splitlines()]
23 23 return "\n".join(lines)
24 24
25 25 output = [
26 26 "Line magics",
27 27 "===========",
28 28 "",
29 29 ]
30 30
31 31 # Case insensitive sort by name
32 32 def sortkey(s): return s[0].lower()
33 33
34 34 for name, func in sorted(magics["line"].items(), key=sortkey):
35 35 if isinstance(func, Alias) or isinstance(func, MagicAlias):
36 36 # Aliases are magics, but shouldn't be documented here
37 37 # Also skip aliases to other magics
38 38 continue
39 39 output.extend([".. magic:: {}".format(name),
40 40 "",
41 41 format_docstring(func),
42 42 ""])
43 43
44 44 output.extend([
45 45 "Cell magics",
46 46 "===========",
47 47 "",
48 48 ])
49 49
50 50 for name, func in sorted(magics["cell"].items(), key=sortkey):
51 51 if name == "!":
52 52 # Special case - don't encourage people to use %%!
53 53 continue
54 54 if func == magics["line"].get(name, "QQQP"):
55 55 # Don't redocument line magics that double as cell magics
56 56 continue
57 57 if isinstance(func, MagicAlias):
58 58 continue
59 59 output.extend([".. cellmagic:: {}".format(name),
60 60 "",
61 61 format_docstring(func),
62 62 ""])
63 63
64 64 src_path = Path(__file__).parent
65 65 dest = src_path.joinpath("source", "interactive", "magics-generated.txt")
66 dest.write_text("\n".join(output), encoding='utf-8')
66 dest.write_text("\n".join(output), encoding="utf-8")
@@ -1,94 +1,96 b''
1 1 from pathlib import Path
2 2
3 3 from IPython.terminal.shortcuts import create_ipython_shortcuts
4 4
5 5 def name(c):
6 6 s = c.__class__.__name__
7 7 if s == '_Invert':
8 8 return '(Not: %s)' % name(c.filter)
9 9 if s in log_filters.keys():
10 10 return '(%s: %s)' % (log_filters[s], ', '.join(name(x) for x in c.filters))
11 11 return log_filters[s] if s in log_filters.keys() else s
12 12
13 13
14 14 def sentencize(s):
15 15 """Extract first sentence
16 16 """
17 17 s = s.replace('\n', ' ').strip().split('.')
18 18 s = s[0] if len(s) else s
19 19 try:
20 20 return " ".join(s.split())
21 21 except AttributeError:
22 22 return s
23 23
24 24
25 25 def most_common(lst, n=3):
26 26 """Most common elements occurring more then `n` times
27 27 """
28 28 from collections import Counter
29 29
30 30 c = Counter(lst)
31 31 return [k for (k, v) in c.items() if k and v > n]
32 32
33 33
34 34 def multi_filter_str(flt):
35 35 """Yield readable conditional filter
36 36 """
37 37 assert hasattr(flt, 'filters'), 'Conditional filter required'
38 38 yield name(flt)
39 39
40 40
41 41 log_filters = {'_AndList': 'And', '_OrList': 'Or'}
42 42 log_invert = {'_Invert'}
43 43
44 44 class _DummyTerminal:
45 45 """Used as a buffer to get prompt_toolkit bindings
46 46 """
47 47 handle_return = None
48 48 input_transformer_manager = None
49 49 display_completions = None
50 50 editing_mode = "emacs"
51 51
52 52
53 53 ipy_bindings = create_ipython_shortcuts(_DummyTerminal()).bindings
54 54
55 55 dummy_docs = [] # ignore bindings without proper documentation
56 56
57 57 common_docs = most_common([kb.handler.__doc__ for kb in ipy_bindings])
58 58 if common_docs:
59 59 dummy_docs.extend(common_docs)
60 60
61 61 dummy_docs = list(set(dummy_docs))
62 62
63 63 single_filter = {}
64 64 multi_filter = {}
65 65 for kb in ipy_bindings:
66 66 doc = kb.handler.__doc__
67 67 if not doc or doc in dummy_docs:
68 68 continue
69 69
70 70 shortcut = ' '.join([k if isinstance(k, str) else k.name for k in kb.keys])
71 71 shortcut += shortcut.endswith('\\') and '\\' or ''
72 72 if hasattr(kb.filter, 'filters'):
73 73 flt = ' '.join(multi_filter_str(kb.filter))
74 74 multi_filter[(shortcut, flt)] = sentencize(doc)
75 75 else:
76 76 single_filter[(shortcut, name(kb.filter))] = sentencize(doc)
77 77
78 78
79 79 if __name__ == '__main__':
80 80 here = Path(__file__).parent
81 81 dest = here / "source" / "config" / "shortcuts"
82 82
83 83 def sort_key(item):
84 84 k, v = item
85 85 shortcut, flt = k
86 86 return (str(shortcut), str(flt))
87 87
88 88 for filters, output_filename in [
89 89 (single_filter, "single_filtered"),
90 90 (multi_filter, "multi_filtered"),
91 91 ]:
92 with (dest / "{}.csv".format(output_filename)).open("w", encoding='utf-8') as csv:
92 with (dest / "{}.csv".format(output_filename)).open(
93 "w", encoding="utf-8"
94 ) as csv:
93 95 for (shortcut, flt), v in sorted(filters.items(), key=sort_key):
94 96 csv.write(":kbd:`{}`\t{}\t{}\n".format(shortcut, flt, v))
@@ -1,316 +1,326 b''
1 1 # -*- coding: utf-8 -*-
2 2 #
3 3 # IPython documentation build configuration file.
4 4
5 5 # NOTE: This file has been edited manually from the auto-generated one from
6 6 # sphinx. Do NOT delete and re-generate. If any changes from sphinx are
7 7 # needed, generate a scratch one and merge by hand any new fields needed.
8 8
9 9 #
10 10 # This file is execfile()d with the current directory set to its containing dir.
11 11 #
12 12 # The contents of this file are pickled, so don't put values in the namespace
13 13 # that aren't pickleable (module imports are okay, they're removed automatically).
14 14 #
15 15 # All configuration values have a default value; values that are commented out
16 16 # serve to show the default value.
17 17
18 18 import sys, os
19 19 from pathlib import Path
20 20
21 21 # https://read-the-docs.readthedocs.io/en/latest/faq.html
22 22 ON_RTD = os.environ.get('READTHEDOCS', None) == 'True'
23 23
24 24 if ON_RTD:
25 25 tags.add('rtd')
26 26
27 27 # RTD doesn't use the Makefile, so re-run autogen_{things}.py here.
28 28 for name in ("config", "api", "magics", "shortcuts"):
29 29 fname = Path("autogen_{}.py".format(name))
30 30 fpath = (Path(__file__).parent).joinpath("..", fname)
31 with open(fpath, encoding='utf-8') as f:
32 exec(compile(f.read(), fname, 'exec'), {
33 '__file__': fpath,
34 '__name__': '__main__',
35 })
31 with open(fpath, encoding="utf-8") as f:
32 exec(
33 compile(f.read(), fname, "exec"),
34 {
35 "__file__": fpath,
36 "__name__": "__main__",
37 },
38 )
36 39 else:
37 40 import sphinx_rtd_theme
38 41 html_theme = "sphinx_rtd_theme"
39 42 html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
40 43
41 44 # If your extensions are in another directory, add it here. If the directory
42 45 # is relative to the documentation root, use os.path.abspath to make it
43 46 # absolute, like shown here.
44 47 sys.path.insert(0, os.path.abspath('../sphinxext'))
45 48
46 49 # We load the ipython release info into a dict by explicit execution
47 50 iprelease = {}
48 exec(compile(open('../../IPython/core/release.py', encoding='utf-8').read(), '../../IPython/core/release.py', 'exec'),iprelease)
51 exec(
52 compile(
53 open("../../IPython/core/release.py", encoding="utf-8").read(),
54 "../../IPython/core/release.py",
55 "exec",
56 ),
57 iprelease,
58 )
49 59
50 60 # General configuration
51 61 # ---------------------
52 62
53 63 # Add any Sphinx extension module names here, as strings. They can be extensions
54 64 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
55 65 extensions = [
56 66 'sphinx.ext.autodoc',
57 67 'sphinx.ext.autosummary',
58 68 'sphinx.ext.doctest',
59 69 'sphinx.ext.inheritance_diagram',
60 70 'sphinx.ext.intersphinx',
61 71 'sphinx.ext.graphviz',
62 72 'IPython.sphinxext.ipython_console_highlighting',
63 73 'IPython.sphinxext.ipython_directive',
64 74 'sphinx.ext.napoleon', # to preprocess docstrings
65 75 'github', # for easy GitHub links
66 76 'magics',
67 77 'configtraits',
68 78 ]
69 79
70 80 # Add any paths that contain templates here, relative to this directory.
71 81 templates_path = ['_templates']
72 82
73 83 # The suffix of source filenames.
74 84 source_suffix = '.rst'
75 85
76 86 rst_prolog = ''
77 87
78 88 def is_stable(extra):
79 89 for ext in {'dev', 'b', 'rc'}:
80 90 if ext in extra:
81 91 return False
82 92 return True
83 93
84 94 if is_stable(iprelease['_version_extra']):
85 95 tags.add('ipystable')
86 96 print('Adding Tag: ipystable')
87 97 else:
88 98 tags.add('ipydev')
89 99 print('Adding Tag: ipydev')
90 100 rst_prolog += """
91 101 .. warning::
92 102
93 103 This documentation covers a development version of IPython. The development
94 104 version may differ significantly from the latest stable release.
95 105 """
96 106
97 107 rst_prolog += """
98 108 .. important::
99 109
100 110 This documentation covers IPython versions 6.0 and higher. Beginning with
101 111 version 6.0, IPython stopped supporting compatibility with Python versions
102 112 lower than 3.3 including all versions of Python 2.7.
103 113
104 114 If you are looking for an IPython version compatible with Python 2.7,
105 115 please use the IPython 5.x LTS release and refer to its documentation (LTS
106 116 is the long term support release).
107 117
108 118 """
109 119
110 120 # The master toctree document.
111 121 master_doc = 'index'
112 122
113 123 # General substitutions.
114 124 project = 'IPython'
115 125 copyright = 'The IPython Development Team'
116 126
117 127 # ghissue config
118 128 github_project_url = "https://github.com/ipython/ipython"
119 129
120 130 # numpydoc config
121 131 numpydoc_show_class_members = False # Otherwise Sphinx emits thousands of warnings
122 132 numpydoc_class_members_toctree = False
123 133 warning_is_error = True
124 134
125 135 import logging
126 136
127 137 class ConfigtraitFilter(logging.Filter):
128 138 """
129 139 This is a filter to remove in sphinx 3+ the error about config traits being duplicated.
130 140
131 141 As we autogenerate configuration traits from, subclasses have lots of
132 142 duplication and we want to silence them. Indeed we build on travis with
133 143 warnings-as-error set to True, so those duplicate items make the build fail.
134 144 """
135 145
136 146 def filter(self, record):
137 147 if record.args and record.args[0] == 'configtrait' and 'duplicate' in record.msg:
138 148 return False
139 149 return True
140 150
141 151 ct_filter = ConfigtraitFilter()
142 152
143 153 import sphinx.util
144 154 logger = sphinx.util.logging.getLogger('sphinx.domains.std').logger
145 155
146 156 logger.addFilter(ct_filter)
147 157
148 158 # The default replacements for |version| and |release|, also used in various
149 159 # other places throughout the built documents.
150 160 #
151 161 # The full version, including alpha/beta/rc tags.
152 162 release = "%s" % iprelease['version']
153 163 # Just the X.Y.Z part, no '-dev'
154 164 version = iprelease['version'].split('-', 1)[0]
155 165
156 166
157 167 # There are two options for replacing |today|: either, you set today to some
158 168 # non-false value, then it is used:
159 169 #today = ''
160 170 # Else, today_fmt is used as the format for a strftime call.
161 171 today_fmt = '%B %d, %Y'
162 172
163 173 # List of documents that shouldn't be included in the build.
164 174 #unused_docs = []
165 175
166 176 # Exclude these glob-style patterns when looking for source files. They are
167 177 # relative to the source/ directory.
168 178 exclude_patterns = []
169 179
170 180
171 181 # If true, '()' will be appended to :func: etc. cross-reference text.
172 182 #add_function_parentheses = True
173 183
174 184 # If true, the current module name will be prepended to all description
175 185 # unit titles (such as .. function::).
176 186 #add_module_names = True
177 187
178 188 # If true, sectionauthor and moduleauthor directives will be shown in the
179 189 # output. They are ignored by default.
180 190 #show_authors = False
181 191
182 192 # The name of the Pygments (syntax highlighting) style to use.
183 193 pygments_style = 'sphinx'
184 194
185 195 # Set the default role so we can use `foo` instead of ``foo``
186 196 default_role = 'literal'
187 197
188 198 # Options for HTML output
189 199 # -----------------------
190 200
191 201 # The style sheet to use for HTML and HTML Help pages. A file of that name
192 202 # must exist either in Sphinx' static/ path, or in one of the custom paths
193 203 # given in html_static_path.
194 204 # html_style = 'default.css'
195 205
196 206
197 207 # The name for this set of Sphinx documents. If None, it defaults to
198 208 # "<project> v<release> documentation".
199 209 #html_title = None
200 210
201 211 # The name of an image file (within the static path) to place at the top of
202 212 # the sidebar.
203 213 #html_logo = None
204 214
205 215 # Add any paths that contain custom static files (such as style sheets) here,
206 216 # relative to this directory. They are copied after the builtin static files,
207 217 # so a file named "default.css" will overwrite the builtin "default.css".
208 218 html_static_path = ['_static']
209 219
210 220 # Favicon needs the directory name
211 221 html_favicon = '_static/favicon.ico'
212 222 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
213 223 # using the given strftime format.
214 224 html_last_updated_fmt = '%b %d, %Y'
215 225
216 226 # If true, SmartyPants will be used to convert quotes and dashes to
217 227 # typographically correct entities.
218 228 #html_use_smartypants = True
219 229
220 230 # Custom sidebar templates, maps document names to template names.
221 231 #html_sidebars = {}
222 232
223 233 # Additional templates that should be rendered to pages, maps page names to
224 234 # template names.
225 235 html_additional_pages = {
226 236 'interactive/htmlnotebook': 'notebook_redirect.html',
227 237 'interactive/notebook': 'notebook_redirect.html',
228 238 'interactive/nbconvert': 'notebook_redirect.html',
229 239 'interactive/public_server': 'notebook_redirect.html',
230 240 }
231 241
232 242 # If false, no module index is generated.
233 243 #html_use_modindex = True
234 244
235 245 # If true, the reST sources are included in the HTML build as _sources/<name>.
236 246 #html_copy_source = True
237 247
238 248 # If true, an OpenSearch description file will be output, and all pages will
239 249 # contain a <link> tag referring to it. The value of this option must be the
240 250 # base URL from which the finished HTML is served.
241 251 #html_use_opensearch = ''
242 252
243 253 # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
244 254 #html_file_suffix = ''
245 255
246 256 # Output file base name for HTML help builder.
247 257 htmlhelp_basename = 'ipythondoc'
248 258
249 259 intersphinx_mapping = {'python': ('https://docs.python.org/3/', None),
250 260 'rpy2': ('https://rpy2.github.io/doc/latest/html/', None),
251 261 'jupyterclient': ('https://jupyter-client.readthedocs.io/en/latest/', None),
252 262 'jupyter': ('https://jupyter.readthedocs.io/en/latest/', None),
253 263 'jedi': ('https://jedi.readthedocs.io/en/latest/', None),
254 264 'traitlets': ('https://traitlets.readthedocs.io/en/latest/', None),
255 265 'ipykernel': ('https://ipykernel.readthedocs.io/en/latest/', None),
256 266 'prompt_toolkit' : ('https://python-prompt-toolkit.readthedocs.io/en/stable/', None),
257 267 'ipywidgets': ('https://ipywidgets.readthedocs.io/en/stable/', None),
258 268 'ipyparallel': ('https://ipyparallel.readthedocs.io/en/stable/', None),
259 269 'pip': ('https://pip.pypa.io/en/stable/', None)
260 270 }
261 271
262 272 # Options for LaTeX output
263 273 # ------------------------
264 274
265 275 # The font size ('10pt', '11pt' or '12pt').
266 276 latex_font_size = '11pt'
267 277
268 278 # Grouping the document tree into LaTeX files. List of tuples
269 279 # (source start file, target name, title, author, document class [howto/manual]).
270 280
271 281 latex_documents = [
272 282 ('index', 'ipython.tex', 'IPython Documentation',
273 283 u"""The IPython Development Team""", 'manual', True),
274 284 ('parallel/winhpc_index', 'winhpc_whitepaper.tex',
275 285 'Using IPython on Windows HPC Server 2008',
276 286 u"Brian E. Granger", 'manual', True)
277 287 ]
278 288
279 289 # The name of an image file (relative to this directory) to place at the top of
280 290 # the title page.
281 291 #latex_logo = None
282 292
283 293 # For "manual" documents, if this is true, then toplevel headings are parts,
284 294 # not chapters.
285 295 #latex_use_parts = False
286 296
287 297 # Additional stuff for the LaTeX preamble.
288 298 #latex_preamble = ''
289 299
290 300 # Documents to append as an appendix to all manuals.
291 301 #latex_appendices = []
292 302
293 303 # If false, no module index is generated.
294 304 latex_use_modindex = True
295 305
296 306
297 307 # Options for texinfo output
298 308 # --------------------------
299 309
300 310 texinfo_documents = [
301 311 (master_doc, 'ipython', 'IPython Documentation',
302 312 'The IPython Development Team',
303 313 'IPython',
304 314 'IPython Documentation',
305 315 'Programming',
306 316 1),
307 317 ]
308 318
309 319 modindex_common_prefix = ['IPython.']
310 320
311 321
312 322 # Cleanup
313 323 # -------
314 324 # delete release info to avoid pickling errors from sphinx
315 325
316 326 del iprelease
@@ -1,453 +1,452 b''
1 1 """Attempt to generate templates for module reference with Sphinx
2 2
3 3 XXX - we exclude extension modules
4 4
5 5 To include extension modules, first identify them as valid in the
6 6 ``_uri2path`` method, then handle them in the ``_parse_module`` script.
7 7
8 8 We get functions and classes by parsing the text of .py files.
9 9 Alternatively we could import the modules for discovery, and we'd have
10 10 to do that for extension modules. This would involve changing the
11 11 ``_parse_module`` method to work via import and introspection, and
12 12 might involve changing ``discover_modules`` (which determines which
13 13 files are modules, and therefore which module URIs will be passed to
14 14 ``_parse_module``).
15 15
16 16 NOTE: this is a modified version of a script originally shipped with the
17 17 PyMVPA project, which we've adapted for NIPY use. PyMVPA is an MIT-licensed
18 18 project."""
19 19
20 20
21 21 # Stdlib imports
22 22 import ast
23 23 import inspect
24 24 import os
25 25 import re
26 26 from importlib import import_module
27 27
28 28
29 29 class Obj(object):
30 30 '''Namespace to hold arbitrary information.'''
31 31 def __init__(self, **kwargs):
32 32 for k, v in kwargs.items():
33 33 setattr(self, k, v)
34 34
35 35 class FuncClsScanner(ast.NodeVisitor):
36 36 """Scan a module for top-level functions and classes.
37 37
38 38 Skips objects with an @undoc decorator, or a name starting with '_'.
39 39 """
40 40 def __init__(self):
41 41 ast.NodeVisitor.__init__(self)
42 42 self.classes = []
43 43 self.classes_seen = set()
44 44 self.functions = []
45 45
46 46 @staticmethod
47 47 def has_undoc_decorator(node):
48 48 return any(isinstance(d, ast.Name) and d.id == 'undoc' \
49 49 for d in node.decorator_list)
50 50
51 51 def visit_If(self, node):
52 52 if isinstance(node.test, ast.Compare) \
53 53 and isinstance(node.test.left, ast.Name) \
54 54 and node.test.left.id == '__name__':
55 55 return # Ignore classes defined in "if __name__ == '__main__':"
56 56
57 57 self.generic_visit(node)
58 58
59 59 def visit_FunctionDef(self, node):
60 60 if not (node.name.startswith('_') or self.has_undoc_decorator(node)) \
61 61 and node.name not in self.functions:
62 62 self.functions.append(node.name)
63 63
64 64 def visit_ClassDef(self, node):
65 65 if not (node.name.startswith('_') or self.has_undoc_decorator(node)) \
66 66 and node.name not in self.classes_seen:
67 67 cls = Obj(name=node.name)
68 68 cls.has_init = any(isinstance(n, ast.FunctionDef) and \
69 69 n.name=='__init__' for n in node.body)
70 70 self.classes.append(cls)
71 71 self.classes_seen.add(node.name)
72 72
73 73 def scan(self, mod):
74 74 self.visit(mod)
75 75 return self.functions, self.classes
76 76
77 77 # Functions and classes
78 78 class ApiDocWriter(object):
79 79 ''' Class for automatic detection and parsing of API docs
80 80 to Sphinx-parsable reST format'''
81 81
82 82 # only separating first two levels
83 83 rst_section_levels = ['*', '=', '-', '~', '^']
84 84
85 85 def __init__(self,
86 86 package_name,
87 87 rst_extension='.rst',
88 88 package_skip_patterns=None,
89 89 module_skip_patterns=None,
90 90 names_from__all__=None,
91 91 ):
92 92 ''' Initialize package for parsing
93 93
94 94 Parameters
95 95 ----------
96 96 package_name : string
97 97 Name of the top-level package. *package_name* must be the
98 98 name of an importable package
99 99 rst_extension : string, optional
100 100 Extension for reST files, default '.rst'
101 101 package_skip_patterns : None or sequence of {strings, regexps}
102 102 Sequence of strings giving URIs of packages to be excluded
103 103 Operates on the package path, starting at (including) the
104 104 first dot in the package path, after *package_name* - so,
105 105 if *package_name* is ``sphinx``, then ``sphinx.util`` will
106 106 result in ``.util`` being passed for earching by these
107 107 regexps. If is None, gives default. Default is:
108 108 ['\\.tests$']
109 109 module_skip_patterns : None or sequence
110 110 Sequence of strings giving URIs of modules to be excluded
111 111 Operates on the module name including preceding URI path,
112 112 back to the first dot after *package_name*. For example
113 113 ``sphinx.util.console`` results in the string to search of
114 114 ``.util.console``
115 115 If is None, gives default. Default is:
116 116 ['\\.setup$', '\\._']
117 117 names_from__all__ : set, optional
118 118 Modules listed in here will be scanned by doing ``from mod import *``,
119 119 rather than finding function and class definitions by scanning the
120 120 AST. This is intended for API modules which expose things defined in
121 121 other files. Modules listed here must define ``__all__`` to avoid
122 122 exposing everything they import.
123 123 '''
124 124 if package_skip_patterns is None:
125 125 package_skip_patterns = ['\\.tests$']
126 126 if module_skip_patterns is None:
127 127 module_skip_patterns = ['\\.setup$', '\\._']
128 128 self.package_name = package_name
129 129 self.rst_extension = rst_extension
130 130 self.package_skip_patterns = package_skip_patterns
131 131 self.module_skip_patterns = module_skip_patterns
132 132 self.names_from__all__ = names_from__all__ or set()
133 133
134 134 def get_package_name(self):
135 135 return self._package_name
136 136
137 137 def set_package_name(self, package_name):
138 138 ''' Set package_name
139 139
140 140 >>> docwriter = ApiDocWriter('sphinx')
141 141 >>> import sphinx
142 142 >>> docwriter.root_path == sphinx.__path__[0]
143 143 True
144 144 >>> docwriter.package_name = 'docutils'
145 145 >>> import docutils
146 146 >>> docwriter.root_path == docutils.__path__[0]
147 147 True
148 148 '''
149 149 # It's also possible to imagine caching the module parsing here
150 150 self._package_name = package_name
151 151 self.root_module = import_module(package_name)
152 152 self.root_path = self.root_module.__path__[0]
153 153 self.written_modules = None
154 154
155 155 package_name = property(get_package_name, set_package_name, None,
156 156 'get/set package_name')
157 157
158 158 def _uri2path(self, uri):
159 159 ''' Convert uri to absolute filepath
160 160
161 161 Parameters
162 162 ----------
163 163 uri : string
164 164 URI of python module to return path for
165 165
166 166 Returns
167 167 -------
168 168 path : None or string
169 169 Returns None if there is no valid path for this URI
170 170 Otherwise returns absolute file system path for URI
171 171
172 172 Examples
173 173 --------
174 174 >>> docwriter = ApiDocWriter('sphinx')
175 175 >>> import sphinx
176 176 >>> modpath = sphinx.__path__[0]
177 177 >>> res = docwriter._uri2path('sphinx.builder')
178 178 >>> res == os.path.join(modpath, 'builder.py')
179 179 True
180 180 >>> res = docwriter._uri2path('sphinx')
181 181 >>> res == os.path.join(modpath, '__init__.py')
182 182 True
183 183 >>> docwriter._uri2path('sphinx.does_not_exist')
184 184
185 185 '''
186 186 if uri == self.package_name:
187 187 return os.path.join(self.root_path, '__init__.py')
188 188 path = uri.replace('.', os.path.sep)
189 189 path = path.replace(self.package_name + os.path.sep, '')
190 190 path = os.path.join(self.root_path, path)
191 191 # XXX maybe check for extensions as well?
192 192 if os.path.exists(path + '.py'): # file
193 193 path += '.py'
194 194 elif os.path.exists(os.path.join(path, '__init__.py')):
195 195 path = os.path.join(path, '__init__.py')
196 196 else:
197 197 return None
198 198 return path
199 199
200 200 def _path2uri(self, dirpath):
201 201 ''' Convert directory path to uri '''
202 202 relpath = dirpath.replace(self.root_path, self.package_name)
203 203 if relpath.startswith(os.path.sep):
204 204 relpath = relpath[1:]
205 205 return relpath.replace(os.path.sep, '.')
206 206
207 207 def _parse_module(self, uri):
208 208 ''' Parse module defined in *uri* '''
209 209 filename = self._uri2path(uri)
210 210 if filename is None:
211 211 # nothing that we could handle here.
212 212 return ([],[])
213 213 with open(filename, 'rb') as f:
214 214 mod = ast.parse(f.read())
215 215 return FuncClsScanner().scan(mod)
216 216
217 217 def _import_funcs_classes(self, uri):
218 218 """Import * from uri, and separate out functions and classes."""
219 219 ns = {}
220 220 exec('from %s import *' % uri, ns)
221 221 funcs, classes = [], []
222 222 for name, obj in ns.items():
223 223 if inspect.isclass(obj):
224 224 cls = Obj(name=name, has_init='__init__' in obj.__dict__)
225 225 classes.append(cls)
226 226 elif inspect.isfunction(obj):
227 227 funcs.append(name)
228 228
229 229 return sorted(funcs), sorted(classes, key=lambda x: x.name)
230 230
231 231 def find_funcs_classes(self, uri):
232 232 """Find the functions and classes defined in the module ``uri``"""
233 233 if uri in self.names_from__all__:
234 234 # For API modules which expose things defined elsewhere, import them
235 235 return self._import_funcs_classes(uri)
236 236 else:
237 237 # For other modules, scan their AST to see what they define
238 238 return self._parse_module(uri)
239 239
240 240 def generate_api_doc(self, uri):
241 241 '''Make autodoc documentation template string for a module
242 242
243 243 Parameters
244 244 ----------
245 245 uri : string
246 246 python location of module - e.g 'sphinx.builder'
247 247
248 248 Returns
249 249 -------
250 250 S : string
251 251 Contents of API doc
252 252 '''
253 253 # get the names of all classes and functions
254 254 functions, classes = self.find_funcs_classes(uri)
255 255 if not len(functions) and not len(classes):
256 256 #print ('WARNING: Empty -', uri) # dbg
257 257 return ''
258 258
259 259 # Make a shorter version of the uri that omits the package name for
260 260 # titles
261 261 uri_short = re.sub(r'^%s\.' % self.package_name,'',uri)
262 262
263 263 ad = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n'
264 264
265 265 # Set the chapter title to read 'Module:' for all modules except for the
266 266 # main packages
267 267 if '.' in uri:
268 268 chap_title = 'Module: :mod:`' + uri_short + '`'
269 269 else:
270 270 chap_title = ':mod:`' + uri_short + '`'
271 271 ad += chap_title + '\n' + self.rst_section_levels[1] * len(chap_title)
272 272
273 273 ad += '\n.. automodule:: ' + uri + '\n'
274 274 ad += '\n.. currentmodule:: ' + uri + '\n'
275 275
276 276 if classes:
277 277 subhead = str(len(classes)) + (' Classes' if len(classes) > 1 else ' Class')
278 278 ad += '\n'+ subhead + '\n' + \
279 279 self.rst_section_levels[2] * len(subhead) + '\n'
280 280
281 281 for c in classes:
282 282 ad += '\n.. autoclass:: ' + c.name + '\n'
283 283 # must NOT exclude from index to keep cross-refs working
284 284 ad += ' :members:\n' \
285 285 ' :show-inheritance:\n'
286 286 if c.has_init:
287 287 ad += '\n .. automethod:: __init__\n'
288 288
289 289 if functions:
290 290 subhead = str(len(functions)) + (' Functions' if len(functions) > 1 else ' Function')
291 291 ad += '\n'+ subhead + '\n' + \
292 292 self.rst_section_levels[2] * len(subhead) + '\n'
293 293 for f in functions:
294 294 # must NOT exclude from index to keep cross-refs working
295 295 ad += '\n.. autofunction:: ' + uri + '.' + f + '\n\n'
296 296 return ad
297 297
298 298 def _survives_exclude(self, matchstr, match_type):
299 299 ''' Returns True if *matchstr* does not match patterns
300 300
301 301 ``self.package_name`` removed from front of string if present
302 302
303 303 Examples
304 304 --------
305 305 >>> dw = ApiDocWriter('sphinx')
306 306 >>> dw._survives_exclude('sphinx.okpkg', 'package')
307 307 True
308 308 >>> dw.package_skip_patterns.append('^\\.badpkg$')
309 309 >>> dw._survives_exclude('sphinx.badpkg', 'package')
310 310 False
311 311 >>> dw._survives_exclude('sphinx.badpkg', 'module')
312 312 True
313 313 >>> dw._survives_exclude('sphinx.badmod', 'module')
314 314 True
315 315 >>> dw.module_skip_patterns.append('^\\.badmod$')
316 316 >>> dw._survives_exclude('sphinx.badmod', 'module')
317 317 False
318 318 '''
319 319 if match_type == 'module':
320 320 patterns = self.module_skip_patterns
321 321 elif match_type == 'package':
322 322 patterns = self.package_skip_patterns
323 323 else:
324 324 raise ValueError('Cannot interpret match type "%s"'
325 325 % match_type)
326 326 # Match to URI without package name
327 327 L = len(self.package_name)
328 328 if matchstr[:L] == self.package_name:
329 329 matchstr = matchstr[L:]
330 330 for pat in patterns:
331 331 try:
332 332 pat.search
333 333 except AttributeError:
334 334 pat = re.compile(pat)
335 335 if pat.search(matchstr):
336 336 return False
337 337 return True
338 338
339 339 def discover_modules(self):
340 340 ''' Return module sequence discovered from ``self.package_name``
341 341
342 342
343 343 Parameters
344 344 ----------
345 345 None
346 346
347 347 Returns
348 348 -------
349 349 mods : sequence
350 350 Sequence of module names within ``self.package_name``
351 351
352 352 Examples
353 353 --------
354 354 >>> dw = ApiDocWriter('sphinx')
355 355 >>> mods = dw.discover_modules()
356 356 >>> 'sphinx.util' in mods
357 357 True
358 358 >>> dw.package_skip_patterns.append('\\.util$')
359 359 >>> 'sphinx.util' in dw.discover_modules()
360 360 False
361 361 >>>
362 362 '''
363 363 modules = [self.package_name]
364 364 # raw directory parsing
365 365 for dirpath, dirnames, filenames in os.walk(self.root_path):
366 366 # Check directory names for packages
367 367 root_uri = self._path2uri(os.path.join(self.root_path,
368 368 dirpath))
369 369 for dirname in dirnames[:]: # copy list - we modify inplace
370 370 package_uri = '.'.join((root_uri, dirname))
371 371 if (self._uri2path(package_uri) and
372 372 self._survives_exclude(package_uri, 'package')):
373 373 modules.append(package_uri)
374 374 else:
375 375 dirnames.remove(dirname)
376 376 # Check filenames for modules
377 377 for filename in filenames:
378 378 module_name = filename[:-3]
379 379 module_uri = '.'.join((root_uri, module_name))
380 380 if (self._uri2path(module_uri) and
381 381 self._survives_exclude(module_uri, 'module')):
382 382 modules.append(module_uri)
383 383 return sorted(modules)
384 384
385 385 def write_modules_api(self, modules,outdir):
386 386 # write the list
387 387 written_modules = []
388 388 for m in modules:
389 389 api_str = self.generate_api_doc(m)
390 390 if not api_str:
391 391 continue
392 392 # write out to file
393 outfile = os.path.join(outdir,
394 m + self.rst_extension)
395 with open(outfile, 'wt', encoding='utf-8') as fileobj:
393 outfile = os.path.join(outdir, m + self.rst_extension)
394 with open(outfile, "wt", encoding="utf-8") as fileobj:
396 395 fileobj.write(api_str)
397 396 written_modules.append(m)
398 397 self.written_modules = written_modules
399 398
400 399 def write_api_docs(self, outdir):
401 400 """Generate API reST files.
402 401
403 402 Parameters
404 403 ----------
405 404 outdir : string
406 405 Directory name in which to store files
407 406 We create automatic filenames for each module
408 407
409 408 Returns
410 409 -------
411 410 None
412 411
413 412 Notes
414 413 -----
415 414 Sets self.written_modules to list of written modules
416 415 """
417 416 if not os.path.exists(outdir):
418 417 os.mkdir(outdir)
419 418 # compose list of modules
420 419 modules = self.discover_modules()
421 420 self.write_modules_api(modules,outdir)
422 421
423 422 def write_index(self, outdir, path='gen.rst', relative_to=None):
424 423 """Make a reST API index file from written files
425 424
426 425 Parameters
427 426 ----------
428 427 outdir : string
429 428 Directory to which to write generated index file
430 429 path : string
431 430 Filename to write index to
432 431 relative_to : string
433 432 path to which written filenames are relative. This
434 433 component of the written file path will be removed from
435 434 outdir, in the generated index. Default is None, meaning,
436 435 leave path as it is.
437 436 """
438 437 if self.written_modules is None:
439 438 raise ValueError('No modules written')
440 439 # Get full filename path
441 440 path = os.path.join(outdir, path)
442 441 # Path written into index is relative to rootpath
443 442 if relative_to is not None:
444 443 relpath = outdir.replace(relative_to + os.path.sep, '')
445 444 else:
446 445 relpath = outdir
447 with open(path,'wt', encoding='utf-8') as idx:
446 with open(path, "wt", encoding="utf-8") as idx:
448 447 w = idx.write
449 448 w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
450 449 w('.. autosummary::\n'
451 450 ' :toctree: %s\n\n' % relpath)
452 451 for mod in self.written_modules:
453 452 w(' %s\n' % mod)
@@ -1,40 +1,40 b''
1 1 #!/usr/bin/env python
2 2 """Extract a session from the IPython input history.
3 3
4 4 Usage:
5 5 ipython-get-history.py sessionnumber [outputfile]
6 6
7 7 If outputfile is not given, the relevant history is written to stdout. If
8 8 outputfile has a .py extension, the translated history (without IPython's
9 9 special syntax) will be extracted.
10 10
11 11 Example:
12 12 ./ipython-get-history.py 57 record.ipy
13 13
14 14
15 15 This script is a simple demonstration of HistoryAccessor. It should be possible
16 16 to build much more flexible and powerful tools to browse and pull from the
17 17 history database.
18 18 """
19 19 import sys
20 20 from pathlib import Path
21 21
22 22 from IPython.core.history import HistoryAccessor
23 23
24 24 session_number = int(sys.argv[1])
25 25 if len(sys.argv) > 2:
26 26 filepath = Path(sys.argv[2])
27 dest = open(filepath, "w", encoding='utf-8')
27 dest = open(filepath, "w", encoding="utf-8")
28 28 raw = not filepath.name.endswith(".py")
29 29 else:
30 30 dest = sys.stdout
31 31 raw = True
32 32
33 33 with dest:
34 34 dest.write("# coding: utf-8\n")
35 35
36 36 # Profiles other than 'default' can be specified here with a profile= argument:
37 37 hist = HistoryAccessor()
38 38
39 39 for session, lineno, cell in hist.get_range(session=session_number, raw=raw):
40 40 dest.write(cell + '\n')
@@ -1,345 +1,348 b''
1 1 # encoding: utf-8
2 2 """
3 3 This module defines the things that are used in setup.py for building IPython
4 4
5 5 This includes:
6 6
7 7 * The basic arguments to setup
8 8 * Functions for finding things like packages, package data, etc.
9 9 * A function for checking dependencies.
10 10 """
11 11
12 12 # Copyright (c) IPython Development Team.
13 13 # Distributed under the terms of the Modified BSD License.
14 14
15 15 import os
16 16 import re
17 17 import sys
18 18 from glob import glob
19 19 from logging import log
20 20
21 21 from setuptools import Command
22 22 from setuptools.command.build_py import build_py
23 23
24 24 from setuptools.command.install import install
25 25 from setuptools.command.install_scripts import install_scripts
26 26
27 27
28 28 #-------------------------------------------------------------------------------
29 29 # Useful globals and utility functions
30 30 #-------------------------------------------------------------------------------
31 31
32 32 # A few handy globals
33 33 isfile = os.path.isfile
34 34 pjoin = os.path.join
35 35 repo_root = os.path.dirname(os.path.abspath(__file__))
36 36
37 37 def execfile(fname, globs, locs=None):
38 38 locs = locs or globs
39 with open(fname, encoding='utf-8') as f:
39 with open(fname, encoding="utf-8") as f:
40 40 exec(compile(f.read(), fname, "exec"), globs, locs)
41 41
42 42 # A little utility we'll need below, since glob() does NOT allow you to do
43 43 # exclusion on multiple endings!
44 44 def file_doesnt_endwith(test,endings):
45 45 """Return true if test is a file and its name does NOT end with any
46 46 of the strings listed in endings."""
47 47 if not isfile(test):
48 48 return False
49 49 for e in endings:
50 50 if test.endswith(e):
51 51 return False
52 52 return True
53 53
54 54 #---------------------------------------------------------------------------
55 55 # Basic project information
56 56 #---------------------------------------------------------------------------
57 57
58 58 # release.py contains version, authors, license, url, keywords, etc.
59 59 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
60 60
61 61 # Create a dict with the basic information
62 62 # This dict is eventually passed to setup after additional keys are added.
63 63 setup_args = dict(
64 64 author = author,
65 65 author_email = author_email,
66 66 license = license,
67 67 )
68 68
69 69
70 70 #---------------------------------------------------------------------------
71 71 # Find packages
72 72 #---------------------------------------------------------------------------
73 73
74 74 def find_packages():
75 75 """
76 76 Find all of IPython's packages.
77 77 """
78 78 excludes = ['deathrow', 'quarantine']
79 79 packages = []
80 80 for directory, subdirs, files in os.walk("IPython"):
81 81 package = directory.replace(os.path.sep, ".")
82 82 if any(package.startswith("IPython." + exc) for exc in excludes):
83 83 # package is to be excluded (e.g. deathrow)
84 84 continue
85 85 if '__init__.py' not in files:
86 86 # not a package
87 87 continue
88 88 packages.append(package)
89 89 return packages
90 90
91 91 #---------------------------------------------------------------------------
92 92 # Find package data
93 93 #---------------------------------------------------------------------------
94 94
95 95 def find_package_data():
96 96 """
97 97 Find IPython's package_data.
98 98 """
99 99 # This is not enough for these things to appear in an sdist.
100 100 # We need to muck with the MANIFEST to get this to work
101 101
102 102 package_data = {
103 103 'IPython.core' : ['profile/README*'],
104 104 'IPython.core.tests' : ['*.png', '*.jpg', 'daft_extension/*.py'],
105 105 'IPython.lib.tests' : ['*.wav'],
106 106 'IPython.testing.plugin' : ['*.txt'],
107 107 }
108 108
109 109 return package_data
110 110
111 111
112 112 def check_package_data(package_data):
113 113 """verify that package_data globs make sense"""
114 114 print("checking package data")
115 115 for pkg, data in package_data.items():
116 116 pkg_root = pjoin(*pkg.split('.'))
117 117 for d in data:
118 118 path = pjoin(pkg_root, d)
119 119 if '*' in path:
120 120 assert len(glob(path)) > 0, "No files match pattern %s" % path
121 121 else:
122 122 assert os.path.exists(path), "Missing package data: %s" % path
123 123
124 124
125 125 def check_package_data_first(command):
126 126 """decorator for checking package_data before running a given command
127 127
128 128 Probably only needs to wrap build_py
129 129 """
130 130 class DecoratedCommand(command):
131 131 def run(self):
132 132 check_package_data(self.package_data)
133 133 command.run(self)
134 134 return DecoratedCommand
135 135
136 136
137 137 #---------------------------------------------------------------------------
138 138 # Find data files
139 139 #---------------------------------------------------------------------------
140 140
141 141 def find_data_files():
142 142 """
143 143 Find IPython's data_files.
144 144
145 145 Just man pages at this point.
146 146 """
147 147
148 148 if "freebsd" in sys.platform:
149 149 manpagebase = pjoin('man', 'man1')
150 150 else:
151 151 manpagebase = pjoin('share', 'man', 'man1')
152 152
153 153 # Simple file lists can be made by hand
154 154 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
155 155 if not manpages:
156 156 # When running from a source tree, the manpages aren't gzipped
157 157 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
158 158
159 159 # And assemble the entire output list
160 160 data_files = [ (manpagebase, manpages) ]
161 161
162 162 return data_files
163 163
164 164
165 165 # The two functions below are copied from IPython.utils.path, so we don't need
166 166 # to import IPython during setup, which fails on Python 3.
167 167
168 168 def target_outdated(target,deps):
169 169 """Determine whether a target is out of date.
170 170
171 171 target_outdated(target,deps) -> 1/0
172 172
173 173 deps: list of filenames which MUST exist.
174 174 target: single filename which may or may not exist.
175 175
176 176 If target doesn't exist or is older than any file listed in deps, return
177 177 true, otherwise return false.
178 178 """
179 179 try:
180 180 target_time = os.path.getmtime(target)
181 181 except os.error:
182 182 return 1
183 183 for dep in deps:
184 184 dep_time = os.path.getmtime(dep)
185 185 if dep_time > target_time:
186 186 #print "For target",target,"Dep failed:",dep # dbg
187 187 #print "times (dep,tar):",dep_time,target_time # dbg
188 188 return 1
189 189 return 0
190 190
191 191
192 192 def target_update(target,deps,cmd):
193 193 """Update a target with a given command given a list of dependencies.
194 194
195 195 target_update(target,deps,cmd) -> runs cmd if target is outdated.
196 196
197 197 This is just a wrapper around target_outdated() which calls the given
198 198 command if target is outdated."""
199 199
200 200 if target_outdated(target,deps):
201 201 os.system(cmd)
202 202
203 203 #---------------------------------------------------------------------------
204 204 # Find scripts
205 205 #---------------------------------------------------------------------------
206 206
207 207 def find_entry_points():
208 208 """Defines the command line entry points for IPython
209 209
210 210 This always uses setuptools-style entry points. When setuptools is not in
211 211 use, our own build_scripts_entrypt class below parses these and builds
212 212 command line scripts.
213 213
214 214 Each of our entry points gets both a plain name, e.g. ipython, and one
215 215 suffixed with the Python major version number, e.g. ipython3.
216 216 """
217 217 ep = [
218 218 'ipython%s = IPython:start_ipython',
219 219 ]
220 220 suffix = str(sys.version_info[0])
221 221 return [e % '' for e in ep] + [e % suffix for e in ep]
222 222
223 223 class install_lib_symlink(Command):
224 224 user_options = [
225 225 ('install-dir=', 'd', "directory to install to"),
226 226 ]
227 227
228 228 def initialize_options(self):
229 229 self.install_dir = None
230 230
231 231 def finalize_options(self):
232 232 self.set_undefined_options('symlink',
233 233 ('install_lib', 'install_dir'),
234 234 )
235 235
236 236 def run(self):
237 237 if sys.platform == 'win32':
238 238 raise Exception("This doesn't work on Windows.")
239 239 pkg = os.path.join(os.getcwd(), 'IPython')
240 240 dest = os.path.join(self.install_dir, 'IPython')
241 241 if os.path.islink(dest):
242 242 print('removing existing symlink at %s' % dest)
243 243 os.unlink(dest)
244 244 print('symlinking %s -> %s' % (pkg, dest))
245 245 os.symlink(pkg, dest)
246 246
247 247 class unsymlink(install):
248 248 def run(self):
249 249 dest = os.path.join(self.install_lib, 'IPython')
250 250 if os.path.islink(dest):
251 251 print('removing symlink at %s' % dest)
252 252 os.unlink(dest)
253 253 else:
254 254 print('No symlink exists at %s' % dest)
255 255
256 256 class install_symlinked(install):
257 257 def run(self):
258 258 if sys.platform == 'win32':
259 259 raise Exception("This doesn't work on Windows.")
260 260
261 261 # Run all sub-commands (at least those that need to be run)
262 262 for cmd_name in self.get_sub_commands():
263 263 self.run_command(cmd_name)
264 264
265 265 # 'sub_commands': a list of commands this command might have to run to
266 266 # get its work done. See cmd.py for more info.
267 267 sub_commands = [('install_lib_symlink', lambda self:True),
268 268 ('install_scripts_sym', lambda self:True),
269 269 ]
270 270
271 271 class install_scripts_for_symlink(install_scripts):
272 272 """Redefined to get options from 'symlink' instead of 'install'.
273 273
274 274 I love distutils almost as much as I love setuptools.
275 275 """
276 276 def finalize_options(self):
277 277 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
278 278 self.set_undefined_options('symlink',
279 279 ('install_scripts', 'install_dir'),
280 280 ('force', 'force'),
281 281 ('skip_build', 'skip_build'),
282 282 )
283 283
284 284
285 285 #---------------------------------------------------------------------------
286 286 # VCS related
287 287 #---------------------------------------------------------------------------
288 288
289 289
290 290 def git_prebuild(pkg_dir, build_cmd=build_py):
291 291 """Return extended build or sdist command class for recording commit
292 292
293 293 records git commit in IPython.utils._sysinfo.commit
294 294
295 295 for use in IPython.utils.sysinfo.sys_info() calls after installation.
296 296 """
297 297
298 298 class MyBuildPy(build_cmd):
299 299 ''' Subclass to write commit data into installation tree '''
300 300 def run(self):
301 301 # loose as `.dev` is suppose to be invalid
302 302 print("check version number")
303 303 loose_pep440re = re.compile(r'^(\d+)\.(\d+)\.(\d+((a|b|rc)\d+)?)(\.post\d+)?(\.dev\d*)?$')
304 304 if not loose_pep440re.match(version):
305 305 raise ValueError("Version number '%s' is not valid (should match [N!]N(.N)*[{a|b|rc}N][.postN][.devN])" % version)
306 306
307 307
308 308 build_cmd.run(self)
309 309 # this one will only fire for build commands
310 310 if hasattr(self, 'build_lib'):
311 311 self._record_commit(self.build_lib)
312 312
313 313 def make_release_tree(self, base_dir, files):
314 314 # this one will fire for sdist
315 315 build_cmd.make_release_tree(self, base_dir, files)
316 316 self._record_commit(base_dir)
317 317
318 318 def _record_commit(self, base_dir):
319 319 import subprocess
320 320 proc = subprocess.Popen('git rev-parse --short HEAD',
321 321 stdout=subprocess.PIPE,
322 322 stderr=subprocess.PIPE,
323 323 shell=True)
324 324 repo_commit, _ = proc.communicate()
325 325 repo_commit = repo_commit.strip().decode("ascii")
326 326
327 327 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
328 328 if os.path.isfile(out_pth) and not repo_commit:
329 329 # nothing to write, don't clobber
330 330 return
331 331
332 332 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
333 333
334 334 # remove to avoid overwriting original via hard link
335 335 try:
336 336 os.remove(out_pth)
337 337 except (IOError, OSError):
338 338 pass
339 with open(out_pth, 'w', encoding='utf-8') as out_file:
340 out_file.writelines([
341 '# GENERATED BY setup.py\n',
342 'commit = u"%s"\n' % repo_commit,
343 ])
339 with open(out_pth, "w", encoding="utf-8") as out_file:
340 out_file.writelines(
341 [
342 "# GENERATED BY setup.py\n",
343 'commit = u"%s"\n' % repo_commit,
344 ]
345 )
346
344 347 return MyBuildPy
345 348
@@ -1,39 +1,39 b''
1 1 """
2 2 This tool is used during CI testing to make sure sphinx raise no error.
3 3
4 4 During development, we like to have whatsnew/pr/*.rst documents to track
5 5 individual new features. Unfortunately they other either:
6 6 - have no title (sphinx complains)
7 7 - are not included in any toctree (sphinx complain)
8 8
9 9 This fix-them up by "inventing" a title, before building the docs. At release
10 10 time, these title and files will anyway be rewritten into the actual release
11 11 notes.
12 12 """
13 13
14 14 from pathlib import Path
15 15
16 16 def main():
17 17 folder = Path("docs/source/whatsnew/pr/")
18 18 files = list(folder.glob("*.rst"))
19 19 print(files)
20 20
21 21 for filepath in files:
22 22 print("Adding pseudo-title to:", filepath.name)
23 23 title = filepath.name[:-4].split("/")[-1].replace("-", " ").capitalize()
24 24
25 data = filepath.read_text(encoding='utf-8')
25 data = filepath.read_text(encoding="utf-8")
26 26 try:
27 27 if data and data.splitlines()[1].startswith('='):
28 28 continue
29 29 except IndexError:
30 30 pass
31 31
32 with filepath.open("w", encoding='utf-8') as f:
32 with filepath.open("w", encoding="utf-8") as f:
33 33 f.write(title + "\n")
34 34 f.write("=" * len(title) + "\n\n")
35 35 f.write(data)
36 36
37 37 if __name__ == '__main__':
38 38 main()
39 39
@@ -1,48 +1,48 b''
1 1 """Various utilities common to IPython release and maintenance tools.
2 2 """
3 3
4 4 # Library imports
5 5 import os
6 6 import sys
7 7
8 8 # Useful shorthands
9 9 pjoin = os.path.join
10 10 cd = os.chdir
11 11
12 12 # Constants
13 13
14 14 # SSH root address of the archive site
15 15 archive_user = 'ipython@archive.ipython.org'
16 16 archive_dir = 'archive.ipython.org'
17 17 archive = '%s:%s' % (archive_user, archive_dir)
18 18
19 19 # Build commands
20 20 # Source dists
21 21 build_command = "{python} -m build".format(python=sys.executable)
22 22
23 23
24 24 # Utility functions
25 25 def sh(cmd):
26 26 """Run system command in shell, raise SystemExit if it returns an error."""
27 27 print("$", cmd)
28 28 stat = os.system(cmd)
29 29 #stat = 0 # Uncomment this and comment previous to run in debug mode
30 30 if stat:
31 31 raise SystemExit("Command %s failed with code: %s" % (cmd, stat))
32 32
33 33 def get_ipdir():
34 34 """Get IPython directory from command line, or assume it's the one above."""
35 35
36 36 # Initialize arguments and check location
37 37 ipdir = pjoin(os.path.dirname(__file__), os.pardir)
38 38
39 39 ipdir = os.path.abspath(ipdir)
40 40
41 41 cd(ipdir)
42 42 if not os.path.isdir('IPython') and os.path.isfile('setup.py'):
43 43 raise SystemExit('Invalid ipython directory: %s' % ipdir)
44 44 return ipdir
45 45
46 46 def execfile(fname, globs, locs=None):
47 47 locs = locs or globs
48 exec(compile(open(fname, encoding='utf-8').read(), fname, "exec"), globs, locs)
48 exec(compile(open(fname, encoding="utf-8").read(), fname, "exec"), globs, locs)
General Comments 0
You need to be logged in to leave comments. Login now