##// END OF EJS Templates
FIX CVE-2022-21699...
Matthias Bussonnier -
Show More
@@ -1,151 +1,155 b''
1 1 """
2 2 IPython: tools for interactive and parallel computing in Python.
3 3
4 4 https://ipython.org
5 5 """
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (c) 2008-2011, IPython Development Team.
8 8 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
9 9 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
10 10 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
11 11 #
12 12 # Distributed under the terms of the Modified BSD License.
13 13 #
14 14 # The full license is in the file COPYING.txt, distributed with this software.
15 15 #-----------------------------------------------------------------------------
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Imports
19 19 #-----------------------------------------------------------------------------
20 20
21 21 import os
22 22 import sys
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Setup everything
26 26 #-----------------------------------------------------------------------------
27 27
28 28 # Don't forget to also update setup.py when this changes!
29 29 if sys.version_info < (3, 8):
30 30 raise ImportError(
31 31 """
32 32 IPython 8+ supports Python 3.8 and above, following NEP 29.
33 33 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
34 34 Python 3.3 and 3.4 were supported up to IPython 6.x.
35 35 Python 3.5 was supported with IPython 7.0 to 7.9.
36 36 Python 3.6 was supported with IPython up to 7.16.
37 37 Python 3.7 was still supported with the 7.x branch.
38 38
39 39 See IPython `README.rst` file for more information:
40 40
41 41 https://github.com/ipython/ipython/blob/master/README.rst
42 42
43 43 """)
44 44
45 45 #-----------------------------------------------------------------------------
46 46 # Setup the top level names
47 47 #-----------------------------------------------------------------------------
48 48
49 49 from .core.getipython import get_ipython
50 50 from .core import release
51 51 from .core.application import Application
52 52 from .terminal.embed import embed
53 53
54 54 from .core.interactiveshell import InteractiveShell
55 55 from .utils.sysinfo import sys_info
56 56 from .utils.frame import extract_module_locals
57 57
58 58 # Release data
59 59 __author__ = '%s <%s>' % (release.author, release.author_email)
60 60 __license__ = release.license
61 61 __version__ = release.version
62 62 version_info = release.version_info
63 # list of CVEs that should have been patched in this release.
64 # this is informational and should not be relied upon.
65 __patched_cves__ = {"CVE-2022-21699"}
66
63 67
64 68 def embed_kernel(module=None, local_ns=None, **kwargs):
65 69 """Embed and start an IPython kernel in a given scope.
66 70
67 71 If you don't want the kernel to initialize the namespace
68 72 from the scope of the surrounding function,
69 73 and/or you want to load full IPython configuration,
70 74 you probably want `IPython.start_kernel()` instead.
71 75
72 76 Parameters
73 77 ----------
74 78 module : types.ModuleType, optional
75 79 The module to load into IPython globals (default: caller)
76 80 local_ns : dict, optional
77 81 The namespace to load into IPython user namespace (default: caller)
78 82 **kwargs : various, optional
79 83 Further keyword args are relayed to the IPKernelApp constructor,
80 84 allowing configuration of the Kernel. Will only have an effect
81 85 on the first embed_kernel call for a given process.
82 86 """
83 87
84 88 (caller_module, caller_locals) = extract_module_locals(1)
85 89 if module is None:
86 90 module = caller_module
87 91 if local_ns is None:
88 92 local_ns = caller_locals
89 93
90 94 # Only import .zmq when we really need it
91 95 from ipykernel.embed import embed_kernel as real_embed_kernel
92 96 real_embed_kernel(module=module, local_ns=local_ns, **kwargs)
93 97
94 98 def start_ipython(argv=None, **kwargs):
95 99 """Launch a normal IPython instance (as opposed to embedded)
96 100
97 101 `IPython.embed()` puts a shell in a particular calling scope,
98 102 such as a function or method for debugging purposes,
99 103 which is often not desirable.
100 104
101 105 `start_ipython()` does full, regular IPython initialization,
102 106 including loading startup files, configuration, etc.
103 107 much of which is skipped by `embed()`.
104 108
105 109 This is a public API method, and will survive implementation changes.
106 110
107 111 Parameters
108 112 ----------
109 113 argv : list or None, optional
110 114 If unspecified or None, IPython will parse command-line options from sys.argv.
111 115 To prevent any command-line parsing, pass an empty list: `argv=[]`.
112 116 user_ns : dict, optional
113 117 specify this dictionary to initialize the IPython user namespace with particular values.
114 118 **kwargs : various, optional
115 119 Any other kwargs will be passed to the Application constructor,
116 120 such as `config`.
117 121 """
118 122 from IPython.terminal.ipapp import launch_new_instance
119 123 return launch_new_instance(argv=argv, **kwargs)
120 124
121 125 def start_kernel(argv=None, **kwargs):
122 126 """Launch a normal IPython kernel instance (as opposed to embedded)
123 127
124 128 `IPython.embed_kernel()` puts a shell in a particular calling scope,
125 129 such as a function or method for debugging purposes,
126 130 which is often not desirable.
127 131
128 132 `start_kernel()` does full, regular IPython initialization,
129 133 including loading startup files, configuration, etc.
130 134 much of which is skipped by `embed()`.
131 135
132 136 Parameters
133 137 ----------
134 138 argv : list or None, optional
135 139 If unspecified or None, IPython will parse command-line options from sys.argv.
136 140 To prevent any command-line parsing, pass an empty list: `argv=[]`.
137 141 user_ns : dict, optional
138 142 specify this dictionary to initialize the IPython user namespace with particular values.
139 143 **kwargs : various, optional
140 144 Any other kwargs will be passed to the Application constructor,
141 145 such as `config`.
142 146 """
143 147 import warnings
144 148
145 149 warnings.warn(
146 150 "start_kernel is deprecated since IPython 8.0, use from `ipykernel.kernelapp.launch_new_instance`",
147 151 DeprecationWarning,
148 152 stacklevel=2,
149 153 )
150 154 from ipykernel.kernelapp import launch_new_instance
151 155 return launch_new_instance(argv=argv, **kwargs)
@@ -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 return [os.getcwd()]
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 473 config_file.write_text(s)
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,311 +1,312 b''
1 1 # encoding: utf-8
2 2 """
3 3 An application for managing IPython profiles.
4 4
5 5 To be invoked as the `ipython profile` subcommand.
6 6
7 7 Authors:
8 8
9 9 * Min RK
10 10
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Copyright (C) 2008 The IPython Development Team
15 15 #
16 16 # Distributed under the terms of the BSD License. The full license is in
17 17 # the file COPYING, distributed as part of this software.
18 18 #-----------------------------------------------------------------------------
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Imports
22 22 #-----------------------------------------------------------------------------
23 23
24 24 import os
25 25
26 26 from traitlets.config.application import Application
27 27 from IPython.core.application import (
28 28 BaseIPythonApplication, base_flags
29 29 )
30 30 from IPython.core.profiledir import ProfileDir
31 31 from IPython.utils.importstring import import_item
32 32 from IPython.paths import get_ipython_dir, get_ipython_package_dir
33 33 from traitlets import Unicode, Bool, Dict, observe
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Constants
37 37 #-----------------------------------------------------------------------------
38 38
39 39 create_help = """Create an IPython profile by name
40 40
41 41 Create an ipython profile directory by its name or
42 42 profile directory path. Profile directories contain
43 43 configuration, log and security related files and are named
44 44 using the convention 'profile_<name>'. By default they are
45 45 located in your ipython directory. Once created, you will
46 46 can edit the configuration files in the profile
47 47 directory to configure IPython. Most users will create a
48 48 profile directory by name,
49 49 `ipython profile create myprofile`, which will put the directory
50 50 in `<ipython_dir>/profile_myprofile`.
51 51 """
52 52 list_help = """List available IPython profiles
53 53
54 54 List all available profiles, by profile location, that can
55 55 be found in the current working directly or in the ipython
56 56 directory. Profile directories are named using the convention
57 57 'profile_<profile>'.
58 58 """
59 59 profile_help = """Manage IPython profiles
60 60
61 61 Profile directories contain
62 62 configuration, log and security related files and are named
63 63 using the convention 'profile_<name>'. By default they are
64 64 located in your ipython directory. You can create profiles
65 65 with `ipython profile create <name>`, or see the profiles you
66 66 already have with `ipython profile list`
67 67
68 68 To get started configuring IPython, simply do:
69 69
70 70 $> ipython profile create
71 71
72 72 and IPython will create the default profile in <ipython_dir>/profile_default,
73 73 where you can edit ipython_config.py to start configuring IPython.
74 74
75 75 """
76 76
77 77 _list_examples = "ipython profile list # list all profiles"
78 78
79 79 _create_examples = """
80 80 ipython profile create foo # create profile foo w/ default config files
81 81 ipython profile create foo --reset # restage default config files over current
82 82 ipython profile create foo --parallel # also stage parallel config files
83 83 """
84 84
85 85 _main_examples = """
86 86 ipython profile create -h # show the help string for the create subcommand
87 87 ipython profile list -h # show the help string for the list subcommand
88 88
89 89 ipython locate profile foo # print the path to the directory for profile 'foo'
90 90 """
91 91
92 92 #-----------------------------------------------------------------------------
93 93 # Profile Application Class (for `ipython profile` subcommand)
94 94 #-----------------------------------------------------------------------------
95 95
96 96
97 97 def list_profiles_in(path):
98 98 """list profiles in a given root directory"""
99 99 profiles = []
100 100
101 101 # for python 3.6+ rewrite to: with os.scandir(path) as dirlist:
102 102 files = os.scandir(path)
103 103 for f in files:
104 104 if f.is_dir() and f.name.startswith('profile_'):
105 105 profiles.append(f.name.split('_', 1)[-1])
106 106 return profiles
107 107
108 108
109 109 def list_bundled_profiles():
110 110 """list profiles that are bundled with IPython."""
111 111 path = os.path.join(get_ipython_package_dir(), u'core', u'profile')
112 112 profiles = []
113 113
114 114 # for python 3.6+ rewrite to: with os.scandir(path) as dirlist:
115 115 files = os.scandir(path)
116 116 for profile in files:
117 117 if profile.is_dir() and profile.name != "__pycache__":
118 118 profiles.append(profile.name)
119 119 return profiles
120 120
121 121
122 122 class ProfileLocate(BaseIPythonApplication):
123 123 description = """print the path to an IPython profile dir"""
124 124
125 125 def parse_command_line(self, argv=None):
126 126 super(ProfileLocate, self).parse_command_line(argv)
127 127 if self.extra_args:
128 128 self.profile = self.extra_args[0]
129 129
130 130 def start(self):
131 131 print(self.profile_dir.location)
132 132
133 133
134 134 class ProfileList(Application):
135 135 name = u'ipython-profile'
136 136 description = list_help
137 137 examples = _list_examples
138 138
139 139 aliases = Dict({
140 140 'ipython-dir' : 'ProfileList.ipython_dir',
141 141 'log-level' : 'Application.log_level',
142 142 })
143 143 flags = Dict(dict(
144 144 debug = ({'Application' : {'log_level' : 0}},
145 145 "Set Application.log_level to 0, maximizing log output."
146 146 )
147 147 ))
148 148
149 149 ipython_dir = Unicode(get_ipython_dir(),
150 150 help="""
151 151 The name of the IPython directory. This directory is used for logging
152 152 configuration (through profiles), history storage, etc. The default
153 153 is usually $HOME/.ipython. This options can also be specified through
154 154 the environment variable IPYTHONDIR.
155 155 """
156 156 ).tag(config=True)
157 157
158 158
159 159 def _print_profiles(self, profiles):
160 160 """print list of profiles, indented."""
161 161 for profile in profiles:
162 162 print(' %s' % profile)
163 163
164 164 def list_profile_dirs(self):
165 165 profiles = list_bundled_profiles()
166 166 if profiles:
167 167 print()
168 168 print("Available profiles in IPython:")
169 169 self._print_profiles(profiles)
170 170 print()
171 171 print(" The first request for a bundled profile will copy it")
172 172 print(" into your IPython directory (%s)," % self.ipython_dir)
173 173 print(" where you can customize it.")
174 174
175 175 profiles = list_profiles_in(self.ipython_dir)
176 176 if profiles:
177 177 print()
178 178 print("Available profiles in %s:" % self.ipython_dir)
179 179 self._print_profiles(profiles)
180 180
181 181 profiles = list_profiles_in(os.getcwd())
182 182 if profiles:
183 183 print()
184 print("Available profiles in current directory (%s):" % os.getcwd())
185 self._print_profiles(profiles)
186
184 print(
185 "Profiles from CWD have been removed for security reason, see CVE-2022-21699:"
186 )
187
187 188 print()
188 189 print("To use any of the above profiles, start IPython with:")
189 190 print(" ipython --profile=<name>")
190 191 print()
191 192
192 193 def start(self):
193 194 self.list_profile_dirs()
194 195
195 196
196 197 create_flags = {}
197 198 create_flags.update(base_flags)
198 199 # don't include '--init' flag, which implies running profile create in other apps
199 200 create_flags.pop('init')
200 201 create_flags['reset'] = ({'ProfileCreate': {'overwrite' : True}},
201 202 "reset config files in this profile to the defaults.")
202 203 create_flags['parallel'] = ({'ProfileCreate': {'parallel' : True}},
203 204 "Include the config files for parallel "
204 205 "computing apps (ipengine, ipcontroller, etc.)")
205 206
206 207
207 208 class ProfileCreate(BaseIPythonApplication):
208 209 name = u'ipython-profile'
209 210 description = create_help
210 211 examples = _create_examples
211 212 auto_create = Bool(True)
212 213 def _log_format_default(self):
213 214 return "[%(name)s] %(message)s"
214 215
215 216 def _copy_config_files_default(self):
216 217 return True
217 218
218 219 parallel = Bool(False,
219 220 help="whether to include parallel computing config files"
220 221 ).tag(config=True)
221 222
222 223 @observe('parallel')
223 224 def _parallel_changed(self, change):
224 225 parallel_files = [ 'ipcontroller_config.py',
225 226 'ipengine_config.py',
226 227 'ipcluster_config.py'
227 228 ]
228 229 if change['new']:
229 230 for cf in parallel_files:
230 231 self.config_files.append(cf)
231 232 else:
232 233 for cf in parallel_files:
233 234 if cf in self.config_files:
234 235 self.config_files.remove(cf)
235 236
236 237 def parse_command_line(self, argv):
237 238 super(ProfileCreate, self).parse_command_line(argv)
238 239 # accept positional arg as profile name
239 240 if self.extra_args:
240 241 self.profile = self.extra_args[0]
241 242
242 243 flags = Dict(create_flags)
243 244
244 245 classes = [ProfileDir]
245 246
246 247 def _import_app(self, app_path):
247 248 """import an app class"""
248 249 app = None
249 250 name = app_path.rsplit('.', 1)[-1]
250 251 try:
251 252 app = import_item(app_path)
252 253 except ImportError:
253 254 self.log.info("Couldn't import %s, config file will be excluded", name)
254 255 except Exception:
255 256 self.log.warning('Unexpected error importing %s', name, exc_info=True)
256 257 return app
257 258
258 259 def init_config_files(self):
259 260 super(ProfileCreate, self).init_config_files()
260 261 # use local imports, since these classes may import from here
261 262 from IPython.terminal.ipapp import TerminalIPythonApp
262 263 apps = [TerminalIPythonApp]
263 264 for app_path in (
264 265 'ipykernel.kernelapp.IPKernelApp',
265 266 ):
266 267 app = self._import_app(app_path)
267 268 if app is not None:
268 269 apps.append(app)
269 270 if self.parallel:
270 271 from ipyparallel.apps.ipcontrollerapp import IPControllerApp
271 272 from ipyparallel.apps.ipengineapp import IPEngineApp
272 273 from ipyparallel.apps.ipclusterapp import IPClusterStart
273 274 apps.extend([
274 275 IPControllerApp,
275 276 IPEngineApp,
276 277 IPClusterStart,
277 278 ])
278 279 for App in apps:
279 280 app = App()
280 281 app.config.update(self.config)
281 282 app.log = self.log
282 283 app.overwrite = self.overwrite
283 284 app.copy_config_files=True
284 285 app.ipython_dir=self.ipython_dir
285 286 app.profile_dir=self.profile_dir
286 287 app.init_config_files()
287 288
288 289 def stage_default_config_file(self):
289 290 pass
290 291
291 292
292 293 class ProfileApp(Application):
293 294 name = u'ipython profile'
294 295 description = profile_help
295 296 examples = _main_examples
296 297
297 298 subcommands = Dict(dict(
298 299 create = (ProfileCreate, ProfileCreate.description.splitlines()[0]),
299 300 list = (ProfileList, ProfileList.description.splitlines()[0]),
300 301 locate = (ProfileLocate, ProfileLocate.description.splitlines()[0]),
301 302 ))
302 303
303 304 def start(self):
304 305 if self.subapp is None:
305 306 print("No subcommand specified. Must specify one of: %s"%(self.subcommands.keys()))
306 307 print()
307 308 self.print_description()
308 309 self.print_subcommands()
309 310 self.exit(1)
310 311 else:
311 312 return self.subapp.start()
@@ -1,225 +1,225 b''
1 1 # encoding: utf-8
2 2 """An object for managing IPython profile directories."""
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 errno
10 10 from pathlib import Path
11 11
12 12 from traitlets.config.configurable import LoggingConfigurable
13 13 from ..paths import get_ipython_package_dir
14 14 from ..utils.path import expand_path, ensure_dir_exists
15 15 from traitlets import Unicode, Bool, observe
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Module errors
19 19 #-----------------------------------------------------------------------------
20 20
21 21 class ProfileDirError(Exception):
22 22 pass
23 23
24 24
25 25 #-----------------------------------------------------------------------------
26 26 # Class for managing profile directories
27 27 #-----------------------------------------------------------------------------
28 28
29 29 class ProfileDir(LoggingConfigurable):
30 30 """An object to manage the profile directory and its resources.
31 31
32 32 The profile directory is used by all IPython applications, to manage
33 33 configuration, logging and security.
34 34
35 35 This object knows how to find, create and manage these directories. This
36 36 should be used by any code that wants to handle profiles.
37 37 """
38 38
39 39 security_dir_name = Unicode('security')
40 40 log_dir_name = Unicode('log')
41 41 startup_dir_name = Unicode('startup')
42 42 pid_dir_name = Unicode('pid')
43 43 static_dir_name = Unicode('static')
44 44 security_dir = Unicode(u'')
45 45 log_dir = Unicode(u'')
46 46 startup_dir = Unicode(u'')
47 47 pid_dir = Unicode(u'')
48 48 static_dir = Unicode(u'')
49 49
50 50 location = Unicode(u'',
51 51 help="""Set the profile location directly. This overrides the logic used by the
52 52 `profile` option.""",
53 53 ).tag(config=True)
54 54
55 55 _location_isset = Bool(False) # flag for detecting multiply set location
56 56 @observe('location')
57 57 def _location_changed(self, change):
58 58 if self._location_isset:
59 59 raise RuntimeError("Cannot set profile location more than once.")
60 60 self._location_isset = True
61 61 new = change['new']
62 62 ensure_dir_exists(new)
63 63
64 64 # ensure config files exist:
65 65 self.security_dir = os.path.join(new, self.security_dir_name)
66 66 self.log_dir = os.path.join(new, self.log_dir_name)
67 67 self.startup_dir = os.path.join(new, self.startup_dir_name)
68 68 self.pid_dir = os.path.join(new, self.pid_dir_name)
69 69 self.static_dir = os.path.join(new, self.static_dir_name)
70 70 self.check_dirs()
71 71
72 72 def _mkdir(self, path, mode=None):
73 73 """ensure a directory exists at a given path
74 74
75 75 This is a version of os.mkdir, with the following differences:
76 76
77 77 - returns True if it created the directory, False otherwise
78 78 - ignores EEXIST, protecting against race conditions where
79 79 the dir may have been created in between the check and
80 80 the creation
81 81 - sets permissions if requested and the dir already exists
82 82 """
83 83 if os.path.exists(path):
84 84 if mode and os.stat(path).st_mode != mode:
85 85 try:
86 86 os.chmod(path, mode)
87 87 except OSError:
88 88 self.log.warning(
89 89 "Could not set permissions on %s",
90 90 path
91 91 )
92 92 return False
93 93 try:
94 94 if mode:
95 95 os.mkdir(path, mode)
96 96 else:
97 97 os.mkdir(path)
98 98 except OSError as e:
99 99 if e.errno == errno.EEXIST:
100 100 return False
101 101 else:
102 102 raise
103 103
104 104 return True
105 105
106 106 @observe('log_dir')
107 107 def check_log_dir(self, change=None):
108 108 self._mkdir(self.log_dir)
109 109
110 110 @observe('startup_dir')
111 111 def check_startup_dir(self, change=None):
112 112 self._mkdir(self.startup_dir)
113 113
114 114 readme = os.path.join(self.startup_dir, 'README')
115 115 src = os.path.join(get_ipython_package_dir(), u'core', u'profile', u'README_STARTUP')
116 116
117 117 if not os.path.exists(src):
118 118 self.log.warning("Could not copy README_STARTUP to startup dir. Source file %s does not exist.", src)
119 119
120 120 if os.path.exists(src) and not os.path.exists(readme):
121 121 shutil.copy(src, readme)
122 122
123 123 @observe('security_dir')
124 124 def check_security_dir(self, change=None):
125 125 self._mkdir(self.security_dir, 0o40700)
126 126
127 127 @observe('pid_dir')
128 128 def check_pid_dir(self, change=None):
129 129 self._mkdir(self.pid_dir, 0o40700)
130 130
131 131 def check_dirs(self):
132 132 self.check_security_dir()
133 133 self.check_log_dir()
134 134 self.check_pid_dir()
135 135 self.check_startup_dir()
136 136
137 137 def copy_config_file(self, config_file: str, path: Path, overwrite=False) -> bool:
138 138 """Copy a default config file into the active profile directory.
139 139
140 140 Default configuration files are kept in :mod:`IPython.core.profile`.
141 141 This function moves these from that location to the working profile
142 142 directory.
143 143 """
144 144 dst = Path(os.path.join(self.location, config_file))
145 145 if dst.exists() and not overwrite:
146 146 return False
147 147 if path is None:
148 148 path = os.path.join(get_ipython_package_dir(), u'core', u'profile', u'default')
149 149 assert isinstance(path, Path)
150 150 src = path / config_file
151 151 shutil.copy(src, dst)
152 152 return True
153 153
154 154 @classmethod
155 155 def create_profile_dir(cls, profile_dir, config=None):
156 156 """Create a new profile directory given a full path.
157 157
158 158 Parameters
159 159 ----------
160 160 profile_dir : str
161 161 The full path to the profile directory. If it does exist, it will
162 162 be used. If not, it will be created.
163 163 """
164 164 return cls(location=profile_dir, config=config)
165 165
166 166 @classmethod
167 167 def create_profile_dir_by_name(cls, path, name=u'default', config=None):
168 168 """Create a profile dir by profile name and path.
169 169
170 170 Parameters
171 171 ----------
172 172 path : unicode
173 173 The path (directory) to put the profile directory in.
174 174 name : unicode
175 175 The name of the profile. The name of the profile directory will
176 176 be "profile_<profile>".
177 177 """
178 178 if not os.path.isdir(path):
179 179 raise ProfileDirError('Directory not found: %s' % path)
180 180 profile_dir = os.path.join(path, u'profile_' + name)
181 181 return cls(location=profile_dir, config=config)
182 182
183 183 @classmethod
184 184 def find_profile_dir_by_name(cls, ipython_dir, name=u'default', config=None):
185 185 """Find an existing profile dir by profile name, return its ProfileDir.
186 186
187 187 This searches through a sequence of paths for a profile dir. If it
188 188 is not found, a :class:`ProfileDirError` exception will be raised.
189 189
190 190 The search path algorithm is:
191 1. ``os.getcwd()``
191 1. ``os.getcwd()`` # removed for security reason.
192 192 2. ``ipython_dir``
193 193
194 194 Parameters
195 195 ----------
196 196 ipython_dir : unicode or str
197 197 The IPython directory to use.
198 198 name : unicode or str
199 199 The name of the profile. The name of the profile directory
200 200 will be "profile_<profile>".
201 201 """
202 202 dirname = u'profile_' + name
203 paths = [os.getcwd(), ipython_dir]
203 paths = [ipython_dir]
204 204 for p in paths:
205 205 profile_dir = os.path.join(p, dirname)
206 206 if os.path.isdir(profile_dir):
207 207 return cls(location=profile_dir, config=config)
208 208 else:
209 209 raise ProfileDirError('Profile directory not found in paths: %s' % dirname)
210 210
211 211 @classmethod
212 212 def find_profile_dir(cls, profile_dir, config=None):
213 213 """Find/create a profile dir and return its ProfileDir.
214 214
215 215 This will create the profile directory if it doesn't exist.
216 216
217 217 Parameters
218 218 ----------
219 219 profile_dir : unicode or str
220 220 The path of the profile directory.
221 221 """
222 222 profile_dir = expand_path(profile_dir)
223 223 if not os.path.isdir(profile_dir):
224 224 raise ProfileDirError('Profile directory not found: %s' % profile_dir)
225 225 return cls(location=profile_dir, config=config)
@@ -1,897 +1,939 b''
1 1 ============
2 2 8.x Series
3 3 ============
4 4
5
6 IPython 8.0.1 (CVE-2022-21699)
7 ------------------------------
8
9 IPython 8.0.1, 7.31.1 and 5.11 are security releases that change some default
10 values in order to prevent potential Execution with Unnecessary Privileges.
11
12 Almost all version of IPython looks for configuration and profiles in current
13 working directory. Since IPython was developed before pip and environments
14 existed it was used a convenient way to load code/packages in a project
15 dependant way.
16
17 In 2022, it is not necessary anymore, and can lead to confusing behavior where
18 for example cloning a repository and starting IPython or loading a notebook from
19 any Jupyter-Compatible interface that has ipython set as a kernel can lead to
20 code execution.
21
22
23 I did not find any standard way for packaged to advertise CVEs they fix, I'm
24 thus trying to add a ``__patched_cves__`` attribute to the IPython module that
25 list the CVEs that should have been fixed. This attribute is informational only
26 as if a executable has a flaw, this value can always be changed by an attacker.
27
28 .. code::
29
30 In [1]: import IPython
31
32 In [2]: IPython.__patched_cves__
33 Out[2]: {'CVE-2022-21699'}
34
35 In [3]: 'CVE-2022-21699' in IPython.__patched_cves__
36 Out[3]: True
37
38 Thus starting with this version:
39
40 - The current working directory is not searched anymore for profiles or
41 configurations files.
42 - Added a ``__patched_cves__`` attribute (set of strings) to IPython module that contain
43 the list of fixed CVE. This is informational only.
44
45
46
5 47 IPython 8.0
6 48 -----------
7 49
8 50 IPython 8.0 is bringing a large number of new features and improvements to both the
9 51 user of the terminal and of the kernel via Jupyter. The removal of compatibility
10 52 with older version of Python is also the opportunity to do a couple of
11 53 performance improvement in particular with respect to startup time.
12 54 The 8.x branch started diverging from its predecessor around IPython 7.12
13 55 (January 2020).
14 56
15 57 This release contains 250+ pull requests, in addition to many of the features
16 58 and backports that have made it to the 7.x branch. Please see the
17 59 `8.0 milestone <https://github.com/ipython/ipython/milestone/73?closed=1>`__ for the full list of pull requests.
18 60
19 61 Please fell free to send pull requests to updates those notes after release,
20 62 I have likely forgotten a few things reviewing 250+ PRs.
21 63
22 64 Dependencies changes/downstream packaging
23 65 -----------------------------------------
24 66
25 67 Most of our building steps have been changed to be (mostly) declarative
26 68 and follow PEP 517. We are trying to completely remove ``setup.py`` (:ghpull:`13238`) and are
27 69 looking for help to do so.
28 70
29 71 - minimum supported ``traitlets`` version is now 5+
30 72 - we now require ``stack_data``
31 73 - minimal Python is now 3.8
32 74 - ``nose`` is not a testing requirement anymore
33 75 - ``pytest`` replaces nose.
34 76 - ``iptest``/``iptest3`` cli entrypoints do not exists anymore.
35 77 - minimum officially support ``numpy`` version has been bumped, but this should
36 78 not have much effect on packaging.
37 79
38 80
39 81 Deprecation and removal
40 82 -----------------------
41 83
42 84 We removed almost all features, arguments, functions, and modules that were
43 85 marked as deprecated between IPython 1.0 and 5.0. As a reminder, 5.0 was released
44 86 in 2016, and 1.0 in 2013. Last release of the 5 branch was 5.10.0, in May 2020.
45 87 The few remaining deprecated features we left have better deprecation warnings
46 88 or have been turned into explicit errors for better error messages.
47 89
48 90 I will use this occasion to add the following requests to anyone emitting a
49 91 deprecation warning:
50 92
51 93 - Please add at least ``stacklevel=2`` so that the warning is emitted into the
52 94 caller context, and not the callee one.
53 95 - Please add **since which version** something is deprecated.
54 96
55 97 As a side note, it is much easier to conditionally compare version
56 98 numbers rather than using ``try/except`` when functionality changes with a version.
57 99
58 100 I won't list all the removed features here, but modules like ``IPython.kernel``,
59 101 which was just a shim module around ``ipykernel`` for the past 8 years, have been
60 102 removed, and so many other similar things that pre-date the name **Jupyter**
61 103 itself.
62 104
63 105 We no longer need to add ``IPython.extensions`` to the PYTHONPATH because that is being
64 106 handled by ``load_extension``.
65 107
66 108 We are also removing ``Cythonmagic``, ``sympyprinting`` and ``rmagic`` as they are now in
67 109 other packages and no longer need to be inside IPython.
68 110
69 111
70 112 Documentation
71 113 -------------
72 114
73 115 The majority of our docstrings have now been reformatted and automatically fixed by
74 116 the experimental `VΓ©lin <https://pypi.org/project/velin/>`_ project to conform
75 117 to numpydoc.
76 118
77 119 Type annotations
78 120 ----------------
79 121
80 122 While IPython itself is highly dynamic and can't be completely typed, many of
81 123 the functions now have type annotations, and part of the codebase is now checked
82 124 by mypy.
83 125
84 126
85 127 Featured changes
86 128 ----------------
87 129
88 130 Here is a features list of changes in IPython 8.0. This is of course non-exhaustive.
89 131 Please note as well that many features have been added in the 7.x branch as well
90 132 (and hence why you want to read the 7.x what's new notes), in particular
91 133 features contributed by QuantStack (with respect to debugger protocol and Xeus
92 134 Python), as well as many debugger features that I was pleased to implement as
93 135 part of my work at QuanSight and sponsored by DE Shaw.
94 136
95 137 Traceback improvements
96 138 ~~~~~~~~~~~~~~~~~~~~~~
97 139
98 140 Previously, error tracebacks for errors happening in code cells were showing a
99 141 hash, the one used for compiling the Python AST::
100 142
101 143 In [1]: def foo():
102 144 ...: return 3 / 0
103 145 ...:
104 146
105 147 In [2]: foo()
106 148 ---------------------------------------------------------------------------
107 149 ZeroDivisionError Traceback (most recent call last)
108 150 <ipython-input-2-c19b6d9633cf> in <module>
109 151 ----> 1 foo()
110 152
111 153 <ipython-input-1-1595a74c32d5> in foo()
112 154 1 def foo():
113 155 ----> 2 return 3 / 0
114 156 3
115 157
116 158 ZeroDivisionError: division by zero
117 159
118 160 The error traceback is now correctly formatted, showing the cell number in which the error happened::
119 161
120 162 In [1]: def foo():
121 163 ...: return 3 / 0
122 164 ...:
123 165
124 166 Input In [2]: foo()
125 167 ---------------------------------------------------------------------------
126 168 ZeroDivisionError Traceback (most recent call last)
127 169 input In [2], in <module>
128 170 ----> 1 foo()
129 171
130 172 Input In [1], in foo()
131 173 1 def foo():
132 174 ----> 2 return 3 / 0
133 175
134 176 ZeroDivisionError: division by zero
135 177
136 178 The ``stack_data`` package has been integrated, which provides smarter information in the traceback;
137 179 in particular it will highlight the AST node where an error occurs which can help to quickly narrow down errors.
138 180
139 181 For example in the following snippet::
140 182
141 183 def foo(i):
142 184 x = [[[0]]]
143 185 return x[0][i][0]
144 186
145 187
146 188 def bar():
147 189 return foo(0) + foo(
148 190 1
149 191 ) + foo(2)
150 192
151 193
152 194 calling ``bar()`` would raise an ``IndexError`` on the return line of ``foo``,
153 195 and IPython 8.0 is capable of telling you where the index error occurs::
154 196
155 197
156 198 IndexError
157 199 Input In [2], in <module>
158 200 ----> 1 bar()
159 201 ^^^^^
160 202
161 203 Input In [1], in bar()
162 204 6 def bar():
163 205 ----> 7 return foo(0) + foo(
164 206 ^^^^
165 207 8 1
166 208 ^^^^^^^^
167 209 9 ) + foo(2)
168 210 ^^^^
169 211
170 212 Input In [1], in foo(i)
171 213 1 def foo(i):
172 214 2 x = [[[0]]]
173 215 ----> 3 return x[0][i][0]
174 216 ^^^^^^^
175 217
176 218 The corresponding locations marked here with ``^`` will show up highlighted in
177 219 the terminal and notebooks.
178 220
179 221 Finally, a colon ``::`` and line number is appended after a filename in
180 222 traceback::
181 223
182 224
183 225 ZeroDivisionError Traceback (most recent call last)
184 226 File ~/error.py:4, in <module>
185 227 1 def f():
186 228 2 1/0
187 229 ----> 4 f()
188 230
189 231 File ~/error.py:2, in f()
190 232 1 def f():
191 233 ----> 2 1/0
192 234
193 235 Many terminals and editors have integrations enabling you to directly jump to the
194 236 relevant file/line when this syntax is used, so this small addition may have a high
195 237 impact on productivity.
196 238
197 239
198 240 Autosuggestions
199 241 ~~~~~~~~~~~~~~~
200 242
201 243 Autosuggestion is a very useful feature available in `fish <https://fishshell.com/>`__, `zsh <https://en.wikipedia.org/wiki/Z_shell>`__, and `prompt-toolkit <https://python-prompt-toolkit.readthedocs.io/en/master/pages/asking_for_input.html#auto-suggestion>`__.
202 244
203 245 `Ptpython <https://github.com/prompt-toolkit/ptpython#ptpython>`__ allows users to enable this feature in
204 246 `ptpython/config.py <https://github.com/prompt-toolkit/ptpython/blob/master/examples/ptpython_config/config.py#L90>`__.
205 247
206 248 This feature allows users to accept autosuggestions with ctrl e, ctrl f,
207 249 or right arrow as described below.
208 250
209 251 1. Start ipython
210 252
211 253 .. image:: ../_images/8.0/auto_suggest_1_prompt_no_text.png
212 254
213 255 2. Run ``print("hello")``
214 256
215 257 .. image:: ../_images/8.0/auto_suggest_2_print_hello_suggest.png
216 258
217 259 3. start typing ``print`` again to see the autosuggestion
218 260
219 261 .. image:: ../_images/8.0/auto_suggest_3_print_hello_suggest.png
220 262
221 263 4. Press ``ctrl-f``, or ``ctrl-e``, or ``right-arrow`` to accept the suggestion
222 264
223 265 .. image:: ../_images/8.0/auto_suggest_4_print_hello.png
224 266
225 267 You can also complete word by word:
226 268
227 269 1. Run ``def say_hello(): print("hello")``
228 270
229 271 .. image:: ../_images/8.0/auto_suggest_second_prompt.png
230 272
231 273 2. Start typing the first letter if ``def`` to see the autosuggestion
232 274
233 275 .. image:: ../_images/8.0/auto_suggest_d_phantom.png
234 276
235 277 3. Press ``alt-f`` (or ``escape`` followed by ``f``), to accept the first word of the suggestion
236 278
237 279 .. image:: ../_images/8.0/auto_suggest_def_phantom.png
238 280
239 281 Importantly, this feature does not interfere with tab completion:
240 282
241 283 1. After running ``def say_hello(): print("hello")``, press d
242 284
243 285 .. image:: ../_images/8.0/auto_suggest_d_phantom.png
244 286
245 287 2. Press Tab to start tab completion
246 288
247 289 .. image:: ../_images/8.0/auto_suggest_d_completions.png
248 290
249 291 3A. Press Tab again to select the first option
250 292
251 293 .. image:: ../_images/8.0/auto_suggest_def_completions.png
252 294
253 295 3B. Press ``alt f`` (``escape``, ``f``) to accept to accept the first word of the suggestion
254 296
255 297 .. image:: ../_images/8.0/auto_suggest_def_phantom.png
256 298
257 299 3C. Press ``ctrl-f`` or ``ctrl-e`` to accept the entire suggestion
258 300
259 301 .. image:: ../_images/8.0/auto_suggest_match_parens.png
260 302
261 303
262 304 Currently, autosuggestions are only shown in the emacs or vi insert editing modes:
263 305
264 306 - The ctrl e, ctrl f, and alt f shortcuts work by default in emacs mode.
265 307 - To use these shortcuts in vi insert mode, you will have to create `custom keybindings in your config.py <https://github.com/mskar/setup/commit/2892fcee46f9f80ef7788f0749edc99daccc52f4/>`__.
266 308
267 309
268 310 Show pinfo information in ipdb using "?" and "??"
269 311 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
270 312
271 313 In IPDB, it is now possible to show the information about an object using "?"
272 314 and "??", in much the same way that it can be done when using the IPython prompt::
273 315
274 316 ipdb> partial?
275 317 Init signature: partial(self, /, *args, **kwargs)
276 318 Docstring:
277 319 partial(func, *args, **keywords) - new function with partial application
278 320 of the given arguments and keywords.
279 321 File: ~/.pyenv/versions/3.8.6/lib/python3.8/functools.py
280 322 Type: type
281 323 Subclasses:
282 324
283 325 Previously, ``pinfo`` or ``pinfo2`` command had to be used for this purpose.
284 326
285 327
286 328 Autoreload 3 feature
287 329 ~~~~~~~~~~~~~~~~~~~~
288 330
289 331 Example: When an IPython session is run with the 'autoreload' extension loaded,
290 332 you will now have the option '3' to select, which means the following:
291 333
292 334 1. replicate all functionality from option 2
293 335 2. autoload all new funcs/classes/enums/globals from the module when they are added
294 336 3. autoload all newly imported funcs/classes/enums/globals from external modules
295 337
296 338 Try ``%autoreload 3`` in an IPython session after running ``%load_ext autoreload``.
297 339
298 340 For more information please see the following unit test : ``extensions/tests/test_autoreload.py:test_autoload_newly_added_objects``
299 341
300 342 Auto formatting with black in the CLI
301 343 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
302 344
303 345 If ``black`` is installed in the same environment as IPython, terminal IPython
304 346 will now *by default* reformat the code in the CLI when possible. You can
305 347 disable this with ``--TerminalInteractiveShell.autoformatter=None``.
306 348
307 349 This feature was present in 7.x, but disabled by default.
308 350
309 351
310 352 History Range Glob feature
311 353 ~~~~~~~~~~~~~~~~~~~~~~~~~~
312 354
313 355 Previously, when using ``%history``, users could specify either
314 356 a range of sessions and lines, for example:
315 357
316 358 .. code-block:: python
317 359
318 360 ~8/1-~6/5 # see history from the first line of 8 sessions ago,
319 361 # to the fifth line of 6 sessions ago.``
320 362
321 363 Or users could specify a glob pattern:
322 364
323 365 .. code-block:: python
324 366
325 367 -g <pattern> # glob ALL history for the specified pattern.
326 368
327 369 However users could *not* specify both.
328 370
329 371 If a user *did* specify both a range and a glob pattern,
330 372 then the glob pattern would be used (globbing *all* history) *and the range would be ignored*.
331 373
332 374 With this enhancement, if a user specifies both a range and a glob pattern, then the glob pattern will be applied to the specified range of history.
333 375
334 376 Don't start a multi-line cell with sunken parenthesis
335 377 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
336 378
337 379 From now on, IPython will not ask for the next line of input when given a single
338 380 line with more closing than opening brackets. For example, this means that if
339 381 you (mis)type ``]]`` instead of ``[]``, a ``SyntaxError`` will show up, instead of
340 382 the ``...:`` prompt continuation.
341 383
342 384 IPython shell for ipdb interact
343 385 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
344 386
345 387 The ipdb ``interact`` starts an IPython shell instead of Python's built-in ``code.interact()``.
346 388
347 389 Automatic Vi prompt stripping
348 390 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
349 391
350 392 When pasting code into IPython, it will strip the leading prompt characters if
351 393 there are any. For example, you can paste the following code into the console -
352 394 it will still work, even though each line is prefixed with prompts (`In`,
353 395 `Out`)::
354 396
355 397 In [1]: 2 * 2 == 4
356 398 Out[1]: True
357 399
358 400 In [2]: print("This still works as pasted")
359 401
360 402
361 403 Previously, this was not the case for the Vi-mode prompts::
362 404
363 405 In [1]: [ins] In [13]: 2 * 2 == 4
364 406 ...: Out[13]: True
365 407 ...:
366 408 File "<ipython-input-1-727bb88eaf33>", line 1
367 409 [ins] In [13]: 2 * 2 == 4
368 410 ^
369 411 SyntaxError: invalid syntax
370 412
371 413 This is now fixed, and Vi prompt prefixes - ``[ins]`` and ``[nav]`` - are
372 414 skipped just as the normal ``In`` would be.
373 415
374 416 IPython shell can be started in the Vi mode using ``ipython --TerminalInteractiveShell.editing_mode=vi``,
375 417 You should be able to change mode dynamically with ``%config TerminalInteractiveShell.editing_mode='vi'``
376 418
377 419 Empty History Ranges
378 420 ~~~~~~~~~~~~~~~~~~~~
379 421
380 422 A number of magics that take history ranges can now be used with an empty
381 423 range. These magics are:
382 424
383 425 * ``%save``
384 426 * ``%load``
385 427 * ``%pastebin``
386 428 * ``%pycat``
387 429
388 430 Using them this way will make them take the history of the current session up
389 431 to the point of the magic call (such that the magic itself will not be
390 432 included).
391 433
392 434 Therefore it is now possible to save the whole history to a file using
393 435 ``%save <filename>``, load and edit it using ``%load`` (makes for a nice usage
394 436 when followed with :kbd:`F2`), send it to `dpaste.org <http://dpast.org>`_ using
395 437 ``%pastebin``, or view the whole thing syntax-highlighted with a single
396 438 ``%pycat``.
397 439
398 440
399 441 Windows timing implementation: Switch to process_time
400 442 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
401 443 Timing on Windows, for example with ``%%time``, was changed from being based on ``time.perf_counter``
402 444 (which counted time even when the process was sleeping) to being based on ``time.process_time`` instead
403 445 (which only counts CPU time). This brings it closer to the behavior on Linux. See :ghpull:`12984`.
404 446
405 447 Miscellaneous
406 448 ~~~~~~~~~~~~~
407 449 - Non-text formatters are not disabled in the terminal, which should simplify
408 450 writing extensions displaying images or other mimetypes in supporting terminals.
409 451 :ghpull:`12315`
410 452 - It is now possible to automatically insert matching brackets in Terminal IPython using the
411 453 ``TerminalInteractiveShell.auto_match=True`` option. :ghpull:`12586`
412 454 - We are thinking of deprecating the current ``%%javascript`` magic in favor of a better replacement. See :ghpull:`13376`.
413 455 - ``~`` is now expanded when part of a path in most magics :ghpull:`13385`
414 456 - ``%/%%timeit`` magic now adds a comma every thousands to make reading a long number easier :ghpull:`13379`
415 457 - ``"info"`` messages can now be customised to hide some fields :ghpull:`13343`
416 458 - ``collections.UserList`` now pretty-prints :ghpull:`13320`
417 459 - The debugger now has a persistent history, which should make it less
418 460 annoying to retype commands :ghpull:`13246`
419 461 - ``!pip`` ``!conda`` ``!cd`` or ``!ls`` are likely doing the wrong thing. We
420 462 now warn users if they use one of those commands. :ghpull:`12954`
421 463 - Make ``%precision`` work for ``numpy.float64`` type :ghpull:`12902`
422 464
423 465 Re-added support for XDG config directories
424 466 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
425 467
426 468 XDG support through the years comes and goes. There is a tension between having
427 469 an identical location for configuration in all platforms versus having simple instructions.
428 470 After initial failures a couple of years ago, IPython was modified to automatically migrate XDG
429 471 config files back into ``~/.ipython``. That migration code has now been removed.
430 472 IPython now checks the XDG locations, so if you _manually_ move your config
431 473 files to your preferred location, IPython will not move them back.
432 474
433 475
434 476 Preparing for Python 3.10
435 477 -------------------------
436 478
437 479 To prepare for Python 3.10, we have started working on removing reliance and
438 480 any dependency that is not compatible with Python 3.10. This includes migrating our
439 481 test suite to pytest and starting to remove nose. This also means that the
440 482 ``iptest`` command is now gone and all testing is via pytest.
441 483
442 484 This was in large part thanks to the NumFOCUS Small Developer grant, which enabled us to
443 485 allocate \$4000 to hire `Nikita Kniazev (@Kojoley) <https://github.com/Kojoley>`_,
444 486 who did a fantastic job at updating our code base, migrating to pytest, pushing
445 487 our coverage, and fixing a large number of bugs. I highly recommend contacting
446 488 them if you need help with C++ and Python projects.
447 489
448 490 You can find all relevant issues and PRs with the SDG 2021 tag `<https://github.com/ipython/ipython/issues?q=label%3A%22Numfocus+SDG+2021%22+>`__
449 491
450 492 Removing support for older Python versions
451 493 ------------------------------------------
452 494
453 495
454 496 We are removing support for Python up through 3.7, allowing internal code to use the more
455 497 efficient ``pathlib`` and to make better use of type annotations.
456 498
457 499 .. image:: ../_images/8.0/pathlib_pathlib_everywhere.jpg
458 500 :alt: "Meme image of Toy Story with Woody and Buzz, with the text 'pathlib, pathlib everywhere'"
459 501
460 502
461 503 We had about 34 PRs only to update some logic to update some functions from managing strings to
462 504 using Pathlib.
463 505
464 506 The completer has also seen significant updates and now makes use of newer Jedi APIs,
465 507 offering faster and more reliable tab completion.
466 508
467 509 Misc Statistics
468 510 ---------------
469 511
470 512 Here are some numbers::
471 513
472 514 7.x: 296 files, 12561 blank lines, 20282 comments, 35142 line of code.
473 515 8.0: 252 files, 12053 blank lines, 19232 comments, 34505 line of code.
474 516
475 517 $ git diff --stat 7.x...master | tail -1
476 518 340 files changed, 13399 insertions(+), 12421 deletions(-)
477 519
478 520 We have commits from 162 authors, who contributed 1916 commits in 23 month, excluding merges (to not bias toward
479 521 maintainers pushing buttons).::
480 522
481 523 $ git shortlog -s --no-merges 7.x...master | sort -nr
482 524 535 Matthias Bussonnier
483 525 86 Nikita Kniazev
484 526 69 Blazej Michalik
485 527 49 Samuel Gaist
486 528 27 Itamar Turner-Trauring
487 529 18 Spas Kalaydzhisyki
488 530 17 Thomas Kluyver
489 531 17 Quentin Peter
490 532 17 James Morris
491 533 17 Artur Svistunov
492 534 15 Bart Skowron
493 535 14 Alex Hall
494 536 13 rushabh-v
495 537 13 Terry Davis
496 538 13 Benjamin Ragan-Kelley
497 539 8 martinRenou
498 540 8 farisachugthai
499 541 7 dswij
500 542 7 Gal B
501 543 7 Corentin Cadiou
502 544 6 yuji96
503 545 6 Martin Skarzynski
504 546 6 Justin Palmer
505 547 6 Daniel Goldfarb
506 548 6 Ben Greiner
507 549 5 Sammy Al Hashemi
508 550 5 Paul Ivanov
509 551 5 Inception95
510 552 5 Eyenpi
511 553 5 Douglas Blank
512 554 5 Coco Mishra
513 555 5 Bibo Hao
514 556 5 AndrΓ© A. Gomes
515 557 5 Ahmed Fasih
516 558 4 takuya fujiwara
517 559 4 palewire
518 560 4 Thomas A Caswell
519 561 4 Talley Lambert
520 562 4 Scott Sanderson
521 563 4 Ram Rachum
522 564 4 Nick Muoh
523 565 4 Nathan Goldbaum
524 566 4 Mithil Poojary
525 567 4 Michael T
526 568 4 Jakub Klus
527 569 4 Ian Castleden
528 570 4 Eli Rykoff
529 571 4 Ashwin Vishnu
530 572 3 谭九鼎
531 573 3 sleeping
532 574 3 Sylvain Corlay
533 575 3 Peter Corke
534 576 3 Paul Bissex
535 577 3 Matthew Feickert
536 578 3 Fernando Perez
537 579 3 Eric Wieser
538 580 3 Daniel Mietchen
539 581 3 Aditya Sathe
540 582 3 007vedant
541 583 2 rchiodo
542 584 2 nicolaslazo
543 585 2 luttik
544 586 2 gorogoroumaru
545 587 2 foobarbyte
546 588 2 bar-hen
547 589 2 Theo Ouzhinski
548 590 2 Strawkage
549 591 2 Samreen Zarroug
550 592 2 Pete Blois
551 593 2 Meysam Azad
552 594 2 Matthieu Ancellin
553 595 2 Mark Schmitz
554 596 2 Maor Kleinberger
555 597 2 MRCWirtz
556 598 2 Lumir Balhar
557 599 2 Julien Rabinow
558 600 2 Juan Luis Cano RodrΓ­guez
559 601 2 Joyce Er
560 602 2 Jakub
561 603 2 Faris A Chugthai
562 604 2 Ethan Madden
563 605 2 Dimitri Papadopoulos
564 606 2 Diego Fernandez
565 607 2 Daniel Shimon
566 608 2 Coco Bennett
567 609 2 Carlos Cordoba
568 610 2 Boyuan Liu
569 611 2 BaoGiang HoangVu
570 612 2 Augusto
571 613 2 Arthur Svistunov
572 614 2 Arthur Moreira
573 615 2 Ali Nabipour
574 616 2 Adam Hackbarth
575 617 1 richard
576 618 1 linar-jether
577 619 1 lbennett
578 620 1 juacrumar
579 621 1 gpotter2
580 622 1 digitalvirtuoso
581 623 1 dalthviz
582 624 1 Yonatan Goldschmidt
583 625 1 Tomasz KΕ‚oczko
584 626 1 Tobias Bengfort
585 627 1 Timur Kushukov
586 628 1 Thomas
587 629 1 Snir Broshi
588 630 1 Shao Yang Hong
589 631 1 Sanjana-03
590 632 1 Romulo Filho
591 633 1 Rodolfo Carvalho
592 634 1 Richard Shadrach
593 635 1 Reilly Tucker Siemens
594 636 1 Rakessh Roshan
595 637 1 Piers Titus van der Torren
596 638 1 PhanatosZou
597 639 1 Pavel Safronov
598 640 1 Paulo S. Costa
599 641 1 Paul McCarthy
600 642 1 NotWearingPants
601 643 1 Naelson Douglas
602 644 1 Michael Tiemann
603 645 1 Matt Wozniski
604 646 1 Markus Wageringel
605 647 1 Marcus Wirtz
606 648 1 Marcio Mazza
607 649 1 LumΓ­r 'Frenzy' Balhar
608 650 1 Lightyagami1
609 651 1 Leon Anavi
610 652 1 LeafyLi
611 653 1 L0uisJ0shua
612 654 1 Kyle Cutler
613 655 1 Krzysztof Cybulski
614 656 1 Kevin Kirsche
615 657 1 KIU Shueng Chuan
616 658 1 Jonathan Slenders
617 659 1 Jay Qi
618 660 1 Jake VanderPlas
619 661 1 Iwan Briquemont
620 662 1 Hussaina Begum Nandyala
621 663 1 Gordon Ball
622 664 1 Gabriel Simonetto
623 665 1 Frank Tobia
624 666 1 Erik
625 667 1 Elliott Sales de Andrade
626 668 1 Daniel Hahler
627 669 1 Dan Green-Leipciger
628 670 1 Dan Green
629 671 1 Damian Yurzola
630 672 1 Coon, Ethan T
631 673 1 Carol Willing
632 674 1 Brian Lee
633 675 1 Brendan Gerrity
634 676 1 Blake Griffin
635 677 1 Bastian Ebeling
636 678 1 Bartosz Telenczuk
637 679 1 Ankitsingh6299
638 680 1 Andrew Port
639 681 1 Andrew J. Hesford
640 682 1 Albert Zhang
641 683 1 Adam Johnson
642 684
643 685 This does not, of course, represent non-code contributions, for which we are also grateful.
644 686
645 687
646 688 API Changes using Frappuccino
647 689 -----------------------------
648 690
649 691 This is an experimental exhaustive API difference using `Frappuccino <https://pypi.org/project/frappuccino/>`_
650 692
651 693
652 694 The following items are new in IPython 8.0 ::
653 695
654 696 + IPython.core.async_helpers.get_asyncio_loop()
655 697 + IPython.core.completer.Dict
656 698 + IPython.core.completer.Pattern
657 699 + IPython.core.completer.Sequence
658 700 + IPython.core.completer.__skip_doctest__
659 701 + IPython.core.debugger.Pdb.precmd(self, line)
660 702 + IPython.core.debugger.__skip_doctest__
661 703 + IPython.core.display.__getattr__(name)
662 704 + IPython.core.display.warn
663 705 + IPython.core.display_functions
664 706 + IPython.core.display_functions.DisplayHandle
665 707 + IPython.core.display_functions.DisplayHandle.display(self, obj, **kwargs)
666 708 + IPython.core.display_functions.DisplayHandle.update(self, obj, **kwargs)
667 709 + IPython.core.display_functions.__all__
668 710 + IPython.core.display_functions.__builtins__
669 711 + IPython.core.display_functions.__cached__
670 712 + IPython.core.display_functions.__doc__
671 713 + IPython.core.display_functions.__file__
672 714 + IPython.core.display_functions.__loader__
673 715 + IPython.core.display_functions.__name__
674 716 + IPython.core.display_functions.__package__
675 717 + IPython.core.display_functions.__spec__
676 718 + IPython.core.display_functions.b2a_hex
677 719 + IPython.core.display_functions.clear_output(wait=False)
678 720 + IPython.core.display_functions.display(*objs, include='None', exclude='None', metadata='None', transient='None', display_id='None', raw=False, clear=False, **kwargs)
679 721 + IPython.core.display_functions.publish_display_data(data, metadata='None', source='<deprecated>', *, transient='None', **kwargs)
680 722 + IPython.core.display_functions.update_display(obj, *, display_id, **kwargs)
681 723 + IPython.core.extensions.BUILTINS_EXTS
682 724 + IPython.core.inputtransformer2.has_sunken_brackets(tokens)
683 725 + IPython.core.interactiveshell.Callable
684 726 + IPython.core.interactiveshell.__annotations__
685 727 + IPython.core.ultratb.List
686 728 + IPython.core.ultratb.Tuple
687 729 + IPython.lib.pretty.CallExpression
688 730 + IPython.lib.pretty.CallExpression.factory(name)
689 731 + IPython.lib.pretty.RawStringLiteral
690 732 + IPython.lib.pretty.RawText
691 733 + IPython.terminal.debugger.TerminalPdb.do_interact(self, arg)
692 734 + IPython.terminal.embed.Set
693 735
694 736 The following items have been removed (or moved to superclass)::
695 737
696 738 - IPython.core.application.BaseIPythonApplication.initialize_subcommand
697 739 - IPython.core.completer.Sentinel
698 740 - IPython.core.completer.skip_doctest
699 741 - IPython.core.debugger.Tracer
700 742 - IPython.core.display.DisplayHandle
701 743 - IPython.core.display.DisplayHandle.display
702 744 - IPython.core.display.DisplayHandle.update
703 745 - IPython.core.display.b2a_hex
704 746 - IPython.core.display.clear_output
705 747 - IPython.core.display.display
706 748 - IPython.core.display.publish_display_data
707 749 - IPython.core.display.update_display
708 750 - IPython.core.excolors.Deprec
709 751 - IPython.core.excolors.ExceptionColors
710 752 - IPython.core.history.warn
711 753 - IPython.core.hooks.late_startup_hook
712 754 - IPython.core.hooks.pre_run_code_hook
713 755 - IPython.core.hooks.shutdown_hook
714 756 - IPython.core.interactiveshell.InteractiveShell.init_deprecation_warnings
715 757 - IPython.core.interactiveshell.InteractiveShell.init_readline
716 758 - IPython.core.interactiveshell.InteractiveShell.write
717 759 - IPython.core.interactiveshell.InteractiveShell.write_err
718 760 - IPython.core.interactiveshell.get_default_colors
719 761 - IPython.core.interactiveshell.removed_co_newlocals
720 762 - IPython.core.magics.execution.ExecutionMagics.profile_missing_notice
721 763 - IPython.core.magics.script.PIPE
722 764 - IPython.core.prefilter.PrefilterManager.init_transformers
723 765 - IPython.core.release.classifiers
724 766 - IPython.core.release.description
725 767 - IPython.core.release.keywords
726 768 - IPython.core.release.long_description
727 769 - IPython.core.release.name
728 770 - IPython.core.release.platforms
729 771 - IPython.core.release.url
730 772 - IPython.core.ultratb.VerboseTB.format_records
731 773 - IPython.core.ultratb.find_recursion
732 774 - IPython.core.ultratb.findsource
733 775 - IPython.core.ultratb.fix_frame_records_filenames
734 776 - IPython.core.ultratb.inspect_error
735 777 - IPython.core.ultratb.is_recursion_error
736 778 - IPython.core.ultratb.with_patch_inspect
737 779 - IPython.external.__all__
738 780 - IPython.external.__builtins__
739 781 - IPython.external.__cached__
740 782 - IPython.external.__doc__
741 783 - IPython.external.__file__
742 784 - IPython.external.__loader__
743 785 - IPython.external.__name__
744 786 - IPython.external.__package__
745 787 - IPython.external.__path__
746 788 - IPython.external.__spec__
747 789 - IPython.kernel.KernelConnectionInfo
748 790 - IPython.kernel.__builtins__
749 791 - IPython.kernel.__cached__
750 792 - IPython.kernel.__warningregistry__
751 793 - IPython.kernel.pkg
752 794 - IPython.kernel.protocol_version
753 795 - IPython.kernel.protocol_version_info
754 796 - IPython.kernel.src
755 797 - IPython.kernel.version_info
756 798 - IPython.kernel.warn
757 799 - IPython.lib.backgroundjobs
758 800 - IPython.lib.backgroundjobs.BackgroundJobBase
759 801 - IPython.lib.backgroundjobs.BackgroundJobBase.run
760 802 - IPython.lib.backgroundjobs.BackgroundJobBase.traceback
761 803 - IPython.lib.backgroundjobs.BackgroundJobExpr
762 804 - IPython.lib.backgroundjobs.BackgroundJobExpr.call
763 805 - IPython.lib.backgroundjobs.BackgroundJobFunc
764 806 - IPython.lib.backgroundjobs.BackgroundJobFunc.call
765 807 - IPython.lib.backgroundjobs.BackgroundJobManager
766 808 - IPython.lib.backgroundjobs.BackgroundJobManager.flush
767 809 - IPython.lib.backgroundjobs.BackgroundJobManager.new
768 810 - IPython.lib.backgroundjobs.BackgroundJobManager.remove
769 811 - IPython.lib.backgroundjobs.BackgroundJobManager.result
770 812 - IPython.lib.backgroundjobs.BackgroundJobManager.status
771 813 - IPython.lib.backgroundjobs.BackgroundJobManager.traceback
772 814 - IPython.lib.backgroundjobs.__builtins__
773 815 - IPython.lib.backgroundjobs.__cached__
774 816 - IPython.lib.backgroundjobs.__doc__
775 817 - IPython.lib.backgroundjobs.__file__
776 818 - IPython.lib.backgroundjobs.__loader__
777 819 - IPython.lib.backgroundjobs.__name__
778 820 - IPython.lib.backgroundjobs.__package__
779 821 - IPython.lib.backgroundjobs.__spec__
780 822 - IPython.lib.kernel.__builtins__
781 823 - IPython.lib.kernel.__cached__
782 824 - IPython.lib.kernel.__doc__
783 825 - IPython.lib.kernel.__file__
784 826 - IPython.lib.kernel.__loader__
785 827 - IPython.lib.kernel.__name__
786 828 - IPython.lib.kernel.__package__
787 829 - IPython.lib.kernel.__spec__
788 830 - IPython.lib.kernel.__warningregistry__
789 831 - IPython.paths.fs_encoding
790 832 - IPython.terminal.debugger.DEFAULT_BUFFER
791 833 - IPython.terminal.debugger.cursor_in_leading_ws
792 834 - IPython.terminal.debugger.emacs_insert_mode
793 835 - IPython.terminal.debugger.has_selection
794 836 - IPython.terminal.debugger.vi_insert_mode
795 837 - IPython.terminal.interactiveshell.DISPLAY_BANNER_DEPRECATED
796 838 - IPython.terminal.ipapp.TerminalIPythonApp.parse_command_line
797 839 - IPython.testing.test
798 840 - IPython.utils.contexts.NoOpContext
799 841 - IPython.utils.io.IOStream
800 842 - IPython.utils.io.IOStream.close
801 843 - IPython.utils.io.IOStream.write
802 844 - IPython.utils.io.IOStream.writelines
803 845 - IPython.utils.io.__warningregistry__
804 846 - IPython.utils.io.atomic_writing
805 847 - IPython.utils.io.stderr
806 848 - IPython.utils.io.stdin
807 849 - IPython.utils.io.stdout
808 850 - IPython.utils.io.unicode_std_stream
809 851 - IPython.utils.path.get_ipython_cache_dir
810 852 - IPython.utils.path.get_ipython_dir
811 853 - IPython.utils.path.get_ipython_module_path
812 854 - IPython.utils.path.get_ipython_package_dir
813 855 - IPython.utils.path.locate_profile
814 856 - IPython.utils.path.unquote_filename
815 857 - IPython.utils.py3compat.PY2
816 858 - IPython.utils.py3compat.PY3
817 859 - IPython.utils.py3compat.buffer_to_bytes
818 860 - IPython.utils.py3compat.builtin_mod_name
819 861 - IPython.utils.py3compat.cast_bytes
820 862 - IPython.utils.py3compat.getcwd
821 863 - IPython.utils.py3compat.isidentifier
822 864 - IPython.utils.py3compat.u_format
823 865
824 866 The following signatures differ between 7.x and 8.0::
825 867
826 868 - IPython.core.completer.IPCompleter.unicode_name_matches(self, text)
827 869 + IPython.core.completer.IPCompleter.unicode_name_matches(text)
828 870
829 871 - IPython.core.completer.match_dict_keys(keys, prefix, delims)
830 872 + IPython.core.completer.match_dict_keys(keys, prefix, delims, extra_prefix='None')
831 873
832 874 - IPython.core.interactiveshell.InteractiveShell.object_inspect_mime(self, oname, detail_level=0)
833 875 + IPython.core.interactiveshell.InteractiveShell.object_inspect_mime(self, oname, detail_level=0, omit_sections='()')
834 876
835 877 - IPython.core.interactiveshell.InteractiveShell.set_hook(self, name, hook, priority=50, str_key='None', re_key='None', _warn_deprecated=True)
836 878 + IPython.core.interactiveshell.InteractiveShell.set_hook(self, name, hook, priority=50, str_key='None', re_key='None')
837 879
838 880 - IPython.core.oinspect.Inspector.info(self, obj, oname='', formatter='None', info='None', detail_level=0)
839 881 + IPython.core.oinspect.Inspector.info(self, obj, oname='', info='None', detail_level=0)
840 882
841 883 - IPython.core.oinspect.Inspector.pinfo(self, obj, oname='', formatter='None', info='None', detail_level=0, enable_html_pager=True)
842 884 + IPython.core.oinspect.Inspector.pinfo(self, obj, oname='', formatter='None', info='None', detail_level=0, enable_html_pager=True, omit_sections='()')
843 885
844 886 - IPython.core.profiledir.ProfileDir.copy_config_file(self, config_file, path='None', overwrite=False)
845 887 + IPython.core.profiledir.ProfileDir.copy_config_file(self, config_file, path, overwrite=False)
846 888
847 889 - IPython.core.ultratb.VerboseTB.format_record(self, frame, file, lnum, func, lines, index)
848 890 + IPython.core.ultratb.VerboseTB.format_record(self, frame_info)
849 891
850 892 - IPython.terminal.embed.InteractiveShellEmbed.mainloop(self, local_ns='None', module='None', stack_depth=0, display_banner='None', global_ns='None', compile_flags='None')
851 893 + IPython.terminal.embed.InteractiveShellEmbed.mainloop(self, local_ns='None', module='None', stack_depth=0, compile_flags='None')
852 894
853 895 - IPython.terminal.embed.embed(**kwargs)
854 896 + IPython.terminal.embed.embed(*, header='', compile_flags='None', **kwargs)
855 897
856 898 - IPython.terminal.interactiveshell.TerminalInteractiveShell.interact(self, display_banner='<object object at 0xffffff>')
857 899 + IPython.terminal.interactiveshell.TerminalInteractiveShell.interact(self)
858 900
859 901 - IPython.terminal.interactiveshell.TerminalInteractiveShell.mainloop(self, display_banner='<object object at 0xffffff>')
860 902 + IPython.terminal.interactiveshell.TerminalInteractiveShell.mainloop(self)
861 903
862 904 - IPython.utils.path.get_py_filename(name, force_win32='None')
863 905 + IPython.utils.path.get_py_filename(name)
864 906
865 907 The following are new attributes (that might be inherited)::
866 908
867 909 + IPython.core.completer.IPCompleter.unicode_names
868 910 + IPython.core.debugger.InterruptiblePdb.precmd
869 911 + IPython.core.debugger.Pdb.precmd
870 912 + IPython.core.ultratb.AutoFormattedTB.has_colors
871 913 + IPython.core.ultratb.ColorTB.has_colors
872 914 + IPython.core.ultratb.FormattedTB.has_colors
873 915 + IPython.core.ultratb.ListTB.has_colors
874 916 + IPython.core.ultratb.SyntaxTB.has_colors
875 917 + IPython.core.ultratb.TBTools.has_colors
876 918 + IPython.core.ultratb.VerboseTB.has_colors
877 919 + IPython.terminal.debugger.TerminalPdb.do_interact
878 920 + IPython.terminal.debugger.TerminalPdb.precmd
879 921
880 922 The following attribute/methods have been removed::
881 923
882 924 - IPython.core.application.BaseIPythonApplication.deprecated_subcommands
883 925 - IPython.core.ultratb.AutoFormattedTB.format_records
884 926 - IPython.core.ultratb.ColorTB.format_records
885 927 - IPython.core.ultratb.FormattedTB.format_records
886 928 - IPython.terminal.embed.InteractiveShellEmbed.init_deprecation_warnings
887 929 - IPython.terminal.embed.InteractiveShellEmbed.init_readline
888 930 - IPython.terminal.embed.InteractiveShellEmbed.write
889 931 - IPython.terminal.embed.InteractiveShellEmbed.write_err
890 932 - IPython.terminal.interactiveshell.TerminalInteractiveShell.init_deprecation_warnings
891 933 - IPython.terminal.interactiveshell.TerminalInteractiveShell.init_readline
892 934 - IPython.terminal.interactiveshell.TerminalInteractiveShell.write
893 935 - IPython.terminal.interactiveshell.TerminalInteractiveShell.write_err
894 936 - IPython.terminal.ipapp.LocateIPythonApp.deprecated_subcommands
895 937 - IPython.terminal.ipapp.LocateIPythonApp.initialize_subcommand
896 938 - IPython.terminal.ipapp.TerminalIPythonApp.deprecated_subcommands
897 939 - IPython.terminal.ipapp.TerminalIPythonApp.initialize_subcommand
General Comments 0
You need to be logged in to leave comments. Login now