mehhl commited on
Commit
b7940a9
·
verified ·
1 Parent(s): 09826d0

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. temp_venv/lib/python3.13/site-packages/IPython/core/__init__.py +0 -0
  2. temp_venv/lib/python3.13/site-packages/IPython/core/application.py +492 -0
  3. temp_venv/lib/python3.13/site-packages/IPython/core/autocall.py +70 -0
  4. temp_venv/lib/python3.13/site-packages/IPython/core/builtin_trap.py +93 -0
  5. temp_venv/lib/python3.13/site-packages/IPython/core/compilerop.py +192 -0
  6. temp_venv/lib/python3.13/site-packages/IPython/core/completer.py +0 -0
  7. temp_venv/lib/python3.13/site-packages/IPython/core/crashhandler.py +244 -0
  8. temp_venv/lib/python3.13/site-packages/IPython/core/debugger.py +1240 -0
  9. temp_venv/lib/python3.13/site-packages/IPython/core/display.py +1266 -0
  10. temp_venv/lib/python3.13/site-packages/IPython/core/display_functions.py +371 -0
  11. temp_venv/lib/python3.13/site-packages/IPython/core/displayhook.py +346 -0
  12. temp_venv/lib/python3.13/site-packages/IPython/core/displaypub.py +183 -0
  13. temp_venv/lib/python3.13/site-packages/IPython/core/doctb.py +444 -0
  14. temp_venv/lib/python3.13/site-packages/IPython/core/events.py +158 -0
  15. temp_venv/lib/python3.13/site-packages/IPython/core/extensions.py +135 -0
  16. temp_venv/lib/python3.13/site-packages/IPython/core/formatters.py +1090 -0
  17. temp_venv/lib/python3.13/site-packages/IPython/core/getipython.py +23 -0
  18. temp_venv/lib/python3.13/site-packages/IPython/core/guarded_eval.py +893 -0
  19. temp_venv/lib/python3.13/site-packages/IPython/core/history.py +1218 -0
  20. temp_venv/lib/python3.13/site-packages/IPython/core/inputtransformer2.py +808 -0
  21. temp_venv/lib/python3.13/site-packages/IPython/core/interactiveshell.py +0 -0
  22. temp_venv/lib/python3.13/site-packages/IPython/core/latex_symbols.py +1306 -0
  23. temp_venv/lib/python3.13/site-packages/IPython/core/logger.py +231 -0
  24. temp_venv/lib/python3.13/site-packages/IPython/core/macro.py +53 -0
  25. temp_venv/lib/python3.13/site-packages/IPython/core/magic_arguments.py +310 -0
  26. temp_venv/lib/python3.13/site-packages/IPython/core/oinspect.py +1216 -0
  27. temp_venv/lib/python3.13/site-packages/IPython/core/page.py +348 -0
  28. temp_venv/lib/python3.13/site-packages/IPython/core/payload.py +54 -0
  29. temp_venv/lib/python3.13/site-packages/IPython/core/payloadpage.py +41 -0
  30. temp_venv/lib/python3.13/site-packages/IPython/core/profileapp.py +315 -0
  31. temp_venv/lib/python3.13/site-packages/IPython/core/pylabtools.py +515 -0
  32. temp_venv/lib/python3.13/site-packages/IPython/core/shellapp.py +496 -0
  33. temp_venv/lib/python3.13/site-packages/IPython/core/splitinput.py +145 -0
  34. temp_venv/lib/python3.13/site-packages/IPython/core/tbtools.py +554 -0
  35. temp_venv/lib/python3.13/site-packages/IPython/core/tips.py +118 -0
  36. temp_venv/lib/python3.13/site-packages/IPython/core/ultratb.py +1252 -0
  37. temp_venv/lib/python3.13/site-packages/IPython/core/usage.py +341 -0
  38. temp_venv/lib/python3.13/site-packages/IPython/sphinxext/__init__.py +0 -0
  39. temp_venv/lib/python3.13/site-packages/IPython/sphinxext/custom_doctests.py +155 -0
  40. temp_venv/lib/python3.13/site-packages/IPython/sphinxext/ipython_console_highlighting.py +28 -0
  41. temp_venv/lib/python3.13/site-packages/IPython/sphinxext/ipython_directive.py +1278 -0
  42. temp_venv/lib/python3.13/site-packages/IPython/terminal/__init__.py +0 -0
  43. temp_venv/lib/python3.13/site-packages/IPython/terminal/debugger.py +181 -0
  44. temp_venv/lib/python3.13/site-packages/IPython/terminal/embed.py +436 -0
  45. temp_venv/lib/python3.13/site-packages/IPython/terminal/interactiveshell.py +1119 -0
  46. temp_venv/lib/python3.13/site-packages/IPython/terminal/ipapp.py +346 -0
  47. temp_venv/lib/python3.13/site-packages/IPython/terminal/magics.py +214 -0
  48. temp_venv/lib/python3.13/site-packages/IPython/terminal/prompts.py +141 -0
  49. temp_venv/lib/python3.13/site-packages/IPython/terminal/ptutils.py +230 -0
  50. temp_venv/lib/python3.13/site-packages/IPython/utils/__init__.py +0 -0
temp_venv/lib/python3.13/site-packages/IPython/core/__init__.py ADDED
File without changes
temp_venv/lib/python3.13/site-packages/IPython/core/application.py ADDED
@@ -0,0 +1,492 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # encoding: utf-8
2
+ """
3
+ An application for IPython.
4
+
5
+ All top-level applications should use the classes in this module for
6
+ handling configuration and creating configurables.
7
+
8
+ The job of an :class:`Application` is to create the master configuration
9
+ object and then create the configurable objects, passing the config to them.
10
+ """
11
+
12
+ # Copyright (c) IPython Development Team.
13
+ # Distributed under the terms of the Modified BSD License.
14
+
15
+ import atexit
16
+ from copy import deepcopy
17
+ import logging
18
+ import os
19
+ import shutil
20
+ import sys
21
+
22
+ from pathlib import Path
23
+
24
+ from traitlets.config.application import Application, catch_config_error
25
+ from traitlets.config.loader import ConfigFileNotFound, PyFileConfigLoader
26
+ from IPython.core import release, crashhandler
27
+ from IPython.core.profiledir import ProfileDir, ProfileDirError
28
+ from IPython.paths import get_ipython_dir, get_ipython_package_dir
29
+ from IPython.utils.path import ensure_dir_exists
30
+ from traitlets import (
31
+ List, Unicode, Type, Bool, Set, Instance, Undefined,
32
+ default, observe,
33
+ )
34
+
35
+ if os.name == "nt":
36
+ programdata = os.environ.get("PROGRAMDATA", None)
37
+ if programdata is not None:
38
+ SYSTEM_CONFIG_DIRS = [str(Path(programdata) / "ipython")]
39
+ else: # PROGRAMDATA is not defined by default on XP.
40
+ SYSTEM_CONFIG_DIRS = []
41
+ else:
42
+ SYSTEM_CONFIG_DIRS = [
43
+ "/usr/local/etc/ipython",
44
+ "/etc/ipython",
45
+ ]
46
+
47
+
48
+ ENV_CONFIG_DIRS = []
49
+ _env_config_dir = os.path.join(sys.prefix, 'etc', 'ipython')
50
+ if _env_config_dir not in SYSTEM_CONFIG_DIRS:
51
+ # only add ENV_CONFIG if sys.prefix is not already included
52
+ ENV_CONFIG_DIRS.append(_env_config_dir)
53
+
54
+
55
+ _envvar = os.environ.get('IPYTHON_SUPPRESS_CONFIG_ERRORS')
56
+ if _envvar in {None, ''}:
57
+ IPYTHON_SUPPRESS_CONFIG_ERRORS = None
58
+ else:
59
+ if _envvar.lower() in {'1','true'}:
60
+ IPYTHON_SUPPRESS_CONFIG_ERRORS = True
61
+ elif _envvar.lower() in {'0','false'} :
62
+ IPYTHON_SUPPRESS_CONFIG_ERRORS = False
63
+ else:
64
+ sys.exit("Unsupported value for environment variable: 'IPYTHON_SUPPRESS_CONFIG_ERRORS' is set to '%s' which is none of {'0', '1', 'false', 'true', ''}."% _envvar )
65
+
66
+ # aliases and flags
67
+
68
+ base_aliases = {}
69
+ if isinstance(Application.aliases, dict):
70
+ # traitlets 5
71
+ base_aliases.update(Application.aliases)
72
+ base_aliases.update(
73
+ {
74
+ "profile-dir": "ProfileDir.location",
75
+ "profile": "BaseIPythonApplication.profile",
76
+ "ipython-dir": "BaseIPythonApplication.ipython_dir",
77
+ "log-level": "Application.log_level",
78
+ "config": "BaseIPythonApplication.extra_config_file",
79
+ }
80
+ )
81
+
82
+ base_flags = dict()
83
+ if isinstance(Application.flags, dict):
84
+ # traitlets 5
85
+ base_flags.update(Application.flags)
86
+ base_flags.update(
87
+ dict(
88
+ debug=(
89
+ {"Application": {"log_level": logging.DEBUG}},
90
+ "set log level to logging.DEBUG (maximize logging output)",
91
+ ),
92
+ quiet=(
93
+ {"Application": {"log_level": logging.CRITICAL}},
94
+ "set log level to logging.CRITICAL (minimize logging output)",
95
+ ),
96
+ init=(
97
+ {
98
+ "BaseIPythonApplication": {
99
+ "copy_config_files": True,
100
+ "auto_create": True,
101
+ }
102
+ },
103
+ """Initialize profile with default config files. This is equivalent
104
+ to running `ipython profile create <profile>` prior to startup.
105
+ """,
106
+ ),
107
+ )
108
+ )
109
+
110
+
111
+ class ProfileAwareConfigLoader(PyFileConfigLoader):
112
+ """A Python file config loader that is aware of IPython profiles."""
113
+ def load_subconfig(self, fname, path=None, profile=None):
114
+ if profile is not None:
115
+ try:
116
+ profile_dir = ProfileDir.find_profile_dir_by_name(
117
+ get_ipython_dir(),
118
+ profile,
119
+ )
120
+ except ProfileDirError:
121
+ return
122
+ path = profile_dir.location
123
+ return super(ProfileAwareConfigLoader, self).load_subconfig(fname, path=path)
124
+
125
+ class BaseIPythonApplication(Application):
126
+ name = "ipython"
127
+ description = "IPython: an enhanced interactive Python shell."
128
+ version = Unicode(release.version)
129
+
130
+ aliases = base_aliases
131
+ flags = base_flags
132
+ classes = List([ProfileDir])
133
+
134
+ # enable `load_subconfig('cfg.py', profile='name')`
135
+ python_config_loader_class = ProfileAwareConfigLoader
136
+
137
+ # Track whether the config_file has changed,
138
+ # because some logic happens only if we aren't using the default.
139
+ config_file_specified = Set()
140
+
141
+ config_file_name = Unicode()
142
+ @default('config_file_name')
143
+ def _config_file_name_default(self):
144
+ return self.name.replace('-','_') + u'_config.py'
145
+ @observe('config_file_name')
146
+ def _config_file_name_changed(self, change):
147
+ if change['new'] != change['old']:
148
+ self.config_file_specified.add(change['new'])
149
+
150
+ # The directory that contains IPython's builtin profiles.
151
+ builtin_profile_dir = Unicode(
152
+ os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
153
+ )
154
+
155
+ config_file_paths = List(Unicode())
156
+ @default('config_file_paths')
157
+ def _config_file_paths_default(self):
158
+ return []
159
+
160
+ extra_config_file = Unicode(
161
+ help="""Path to an extra config file to load.
162
+
163
+ If specified, load this config file in addition to any other IPython config.
164
+ """).tag(config=True)
165
+ @observe('extra_config_file')
166
+ def _extra_config_file_changed(self, change):
167
+ old = change['old']
168
+ new = change['new']
169
+ try:
170
+ self.config_files.remove(old)
171
+ except ValueError:
172
+ pass
173
+ self.config_file_specified.add(new)
174
+ self.config_files.append(new)
175
+
176
+ profile = Unicode(u'default',
177
+ help="""The IPython profile to use."""
178
+ ).tag(config=True)
179
+
180
+ @observe('profile')
181
+ def _profile_changed(self, change):
182
+ self.builtin_profile_dir = os.path.join(
183
+ get_ipython_package_dir(), u'config', u'profile', change['new']
184
+ )
185
+
186
+ add_ipython_dir_to_sys_path = Bool(
187
+ False,
188
+ """Should the IPython profile directory be added to sys path ?
189
+
190
+ This option was non-existing before IPython 8.0, and ipython_dir was added to
191
+ sys path to allow import of extensions present there. This was historical
192
+ baggage from when pip did not exist. This now default to false,
193
+ but can be set to true for legacy reasons.
194
+ """,
195
+ ).tag(config=True)
196
+
197
+ ipython_dir = Unicode(
198
+ help="""
199
+ The name of the IPython directory. This directory is used for logging
200
+ configuration (through profiles), history storage, etc. The default
201
+ is usually $HOME/.ipython. This option can also be specified through
202
+ the environment variable IPYTHONDIR.
203
+ """
204
+ ).tag(config=True)
205
+ @default('ipython_dir')
206
+ def _ipython_dir_default(self):
207
+ d = get_ipython_dir()
208
+ self._ipython_dir_changed({
209
+ 'name': 'ipython_dir',
210
+ 'old': d,
211
+ 'new': d,
212
+ })
213
+ return d
214
+
215
+ _in_init_profile_dir = False
216
+
217
+ profile_dir = Instance(ProfileDir, allow_none=True)
218
+
219
+ @default('profile_dir')
220
+ def _profile_dir_default(self):
221
+ # avoid recursion
222
+ if self._in_init_profile_dir:
223
+ return
224
+ # profile_dir requested early, force initialization
225
+ self.init_profile_dir()
226
+ return self.profile_dir
227
+
228
+ overwrite = Bool(False,
229
+ help="""Whether to overwrite existing config files when copying"""
230
+ ).tag(config=True)
231
+
232
+ auto_create = Bool(False,
233
+ help="""Whether to create profile dir if it doesn't exist"""
234
+ ).tag(config=True)
235
+
236
+ config_files = List(Unicode())
237
+
238
+ @default('config_files')
239
+ def _config_files_default(self):
240
+ return [self.config_file_name]
241
+
242
+ copy_config_files = Bool(False,
243
+ help="""Whether to install the default config files into the profile dir.
244
+ If a new profile is being created, and IPython contains config files for that
245
+ profile, then they will be staged into the new directory. Otherwise,
246
+ default config files will be automatically generated.
247
+ """).tag(config=True)
248
+
249
+ verbose_crash = Bool(False,
250
+ help="""Create a massive crash report when IPython encounters what may be an
251
+ internal error. The default is to append a short message to the
252
+ usual traceback""").tag(config=True)
253
+
254
+ # The class to use as the crash handler.
255
+ crash_handler_class = Type(crashhandler.CrashHandler)
256
+
257
+ @catch_config_error
258
+ def __init__(self, **kwargs):
259
+ super(BaseIPythonApplication, self).__init__(**kwargs)
260
+ # ensure current working directory exists
261
+ try:
262
+ os.getcwd()
263
+ except:
264
+ # exit if cwd doesn't exist
265
+ self.log.error("Current working directory doesn't exist.")
266
+ self.exit(1)
267
+
268
+ #-------------------------------------------------------------------------
269
+ # Various stages of Application creation
270
+ #-------------------------------------------------------------------------
271
+
272
+ def init_crash_handler(self):
273
+ """Create a crash handler, typically setting sys.excepthook to it."""
274
+ self.crash_handler = self.crash_handler_class(self)
275
+ sys.excepthook = self.excepthook
276
+ def unset_crashhandler():
277
+ sys.excepthook = sys.__excepthook__
278
+ atexit.register(unset_crashhandler)
279
+
280
+ def excepthook(self, etype, evalue, tb):
281
+ """this is sys.excepthook after init_crashhandler
282
+
283
+ set self.verbose_crash=True to use our full crashhandler, instead of
284
+ a regular traceback with a short message (crash_handler_lite)
285
+ """
286
+
287
+ if self.verbose_crash:
288
+ return self.crash_handler(etype, evalue, tb)
289
+ else:
290
+ return crashhandler.crash_handler_lite(etype, evalue, tb)
291
+
292
+ @observe('ipython_dir')
293
+ def _ipython_dir_changed(self, change):
294
+ old = change['old']
295
+ new = change['new']
296
+ if old is not Undefined:
297
+ str_old = os.path.abspath(old)
298
+ if str_old in sys.path:
299
+ sys.path.remove(str_old)
300
+ if self.add_ipython_dir_to_sys_path:
301
+ str_path = os.path.abspath(new)
302
+ sys.path.append(str_path)
303
+ ensure_dir_exists(new)
304
+ readme = os.path.join(new, "README")
305
+ readme_src = os.path.join(
306
+ get_ipython_package_dir(), "config", "profile", "README"
307
+ )
308
+ if not os.path.exists(readme) and os.path.exists(readme_src):
309
+ shutil.copy(readme_src, readme)
310
+ for d in ("extensions", "nbextensions"):
311
+ path = os.path.join(new, d)
312
+ try:
313
+ ensure_dir_exists(path)
314
+ except OSError as e:
315
+ # this will not be EEXIST
316
+ self.log.error("couldn't create path %s: %s", path, e)
317
+ self.log.debug("IPYTHONDIR set to: %s", new)
318
+
319
+ def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS):
320
+ """Load the config file.
321
+
322
+ By default, errors in loading config are handled, and a warning
323
+ printed on screen. For testing, the suppress_errors option is set
324
+ to False, so errors will make tests fail.
325
+
326
+ `suppress_errors` default value is to be `None` in which case the
327
+ behavior default to the one of `traitlets.Application`.
328
+
329
+ The default value can be set :
330
+ - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive).
331
+ - to `True` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '1' or 'true' (case insensitive).
332
+ - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset.
333
+
334
+ Any other value are invalid, and will make IPython exit with a non-zero return code.
335
+ """
336
+
337
+
338
+ self.log.debug("Searching path %s for config files", self.config_file_paths)
339
+ base_config = 'ipython_config.py'
340
+ self.log.debug("Attempting to load config file: %s" %
341
+ base_config)
342
+ try:
343
+ if suppress_errors is not None:
344
+ old_value = Application.raise_config_file_errors
345
+ Application.raise_config_file_errors = not suppress_errors
346
+ Application.load_config_file(
347
+ self,
348
+ base_config,
349
+ path=self.config_file_paths
350
+ )
351
+ except ConfigFileNotFound:
352
+ # ignore errors loading parent
353
+ self.log.debug("Config file %s not found", base_config)
354
+ pass
355
+ if suppress_errors is not None:
356
+ Application.raise_config_file_errors = old_value
357
+
358
+ for config_file_name in self.config_files:
359
+ if not config_file_name or config_file_name == base_config:
360
+ continue
361
+ self.log.debug("Attempting to load config file: %s" %
362
+ self.config_file_name)
363
+ try:
364
+ Application.load_config_file(
365
+ self,
366
+ config_file_name,
367
+ path=self.config_file_paths
368
+ )
369
+ except ConfigFileNotFound:
370
+ # Only warn if the default config file was NOT being used.
371
+ if config_file_name in self.config_file_specified:
372
+ msg = self.log.warning
373
+ else:
374
+ msg = self.log.debug
375
+ msg("Config file not found, skipping: %s", config_file_name)
376
+ except Exception:
377
+ # For testing purposes.
378
+ if not suppress_errors:
379
+ raise
380
+ self.log.warning("Error loading config file: %s" %
381
+ self.config_file_name, exc_info=True)
382
+
383
+ def init_profile_dir(self):
384
+ """initialize the profile dir"""
385
+ self._in_init_profile_dir = True
386
+ if self.profile_dir is not None:
387
+ # already ran
388
+ return
389
+ if 'ProfileDir.location' not in self.config:
390
+ # location not specified, find by profile name
391
+ try:
392
+ p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
393
+ except ProfileDirError:
394
+ # not found, maybe create it (always create default profile)
395
+ if self.auto_create or self.profile == 'default':
396
+ try:
397
+ p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
398
+ except ProfileDirError:
399
+ self.log.fatal("Could not create profile: %r"%self.profile)
400
+ self.exit(1)
401
+ else:
402
+ self.log.info("Created profile dir: %r"%p.location)
403
+ else:
404
+ self.log.fatal("Profile %r not found."%self.profile)
405
+ self.exit(1)
406
+ else:
407
+ self.log.debug("Using existing profile dir: %r", p.location)
408
+ else:
409
+ location = self.config.ProfileDir.location
410
+ # location is fully specified
411
+ try:
412
+ p = ProfileDir.find_profile_dir(location, self.config)
413
+ except ProfileDirError:
414
+ # not found, maybe create it
415
+ if self.auto_create:
416
+ try:
417
+ p = ProfileDir.create_profile_dir(location, self.config)
418
+ except ProfileDirError:
419
+ self.log.fatal("Could not create profile directory: %r"%location)
420
+ self.exit(1)
421
+ else:
422
+ self.log.debug("Creating new profile dir: %r"%location)
423
+ else:
424
+ self.log.fatal("Profile directory %r not found."%location)
425
+ self.exit(1)
426
+ else:
427
+ self.log.debug("Using existing profile dir: %r", p.location)
428
+ # if profile_dir is specified explicitly, set profile name
429
+ dir_name = os.path.basename(p.location)
430
+ if dir_name.startswith('profile_'):
431
+ self.profile = dir_name[8:]
432
+
433
+ self.profile_dir = p
434
+ self.config_file_paths.append(p.location)
435
+ self._in_init_profile_dir = False
436
+
437
+ def init_config_files(self):
438
+ """[optionally] copy default config files into profile dir."""
439
+ self.config_file_paths.extend(ENV_CONFIG_DIRS)
440
+ self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
441
+ # copy config files
442
+ path = Path(self.builtin_profile_dir)
443
+ if self.copy_config_files:
444
+ src = self.profile
445
+
446
+ cfg = self.config_file_name
447
+ if path and (path / cfg).exists():
448
+ self.log.warning(
449
+ "Staging %r from %s into %r [overwrite=%s]"
450
+ % (cfg, src, self.profile_dir.location, self.overwrite)
451
+ )
452
+ self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
453
+ else:
454
+ self.stage_default_config_file()
455
+ else:
456
+ # Still stage *bundled* config files, but not generated ones
457
+ # This is necessary for `ipython profile=sympy` to load the profile
458
+ # on the first go
459
+ files = path.glob("*.py")
460
+ for fullpath in files:
461
+ cfg = fullpath.name
462
+ if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
463
+ # file was copied
464
+ self.log.warning("Staging bundled %s from %s into %r"%(
465
+ cfg, self.profile, self.profile_dir.location)
466
+ )
467
+
468
+
469
+ def stage_default_config_file(self):
470
+ """auto generate default config file, and stage it into the profile."""
471
+ s = self.generate_config_file()
472
+ config_file = Path(self.profile_dir.location) / self.config_file_name
473
+ if self.overwrite or not config_file.exists():
474
+ self.log.warning("Generating default config file: %r", (config_file))
475
+ config_file.write_text(s, encoding="utf-8")
476
+
477
+ @catch_config_error
478
+ def initialize(self, argv=None):
479
+ # don't hook up crash handler before parsing command-line
480
+ self.parse_command_line(argv)
481
+ self.init_crash_handler()
482
+ if self.subapp is not None:
483
+ # stop here if subapp is taking over
484
+ return
485
+ # save a copy of CLI config to re-load after config files
486
+ # so that it has highest priority
487
+ cl_config = deepcopy(self.config)
488
+ self.init_profile_dir()
489
+ self.init_config_files()
490
+ self.load_config_file()
491
+ # enforce cl-opts override configfile opts:
492
+ self.update_config(cl_config)
temp_venv/lib/python3.13/site-packages/IPython/core/autocall.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # encoding: utf-8
2
+ """
3
+ Autocall capabilities for IPython.core.
4
+
5
+ Authors:
6
+
7
+ * Brian Granger
8
+ * Fernando Perez
9
+ * Thomas Kluyver
10
+
11
+ Notes
12
+ -----
13
+ """
14
+
15
+ #-----------------------------------------------------------------------------
16
+ # Copyright (C) 2008-2011 The IPython Development Team
17
+ #
18
+ # Distributed under the terms of the BSD License. The full license is in
19
+ # the file COPYING, distributed as part of this software.
20
+ #-----------------------------------------------------------------------------
21
+
22
+ #-----------------------------------------------------------------------------
23
+ # Imports
24
+ #-----------------------------------------------------------------------------
25
+
26
+
27
+ #-----------------------------------------------------------------------------
28
+ # Code
29
+ #-----------------------------------------------------------------------------
30
+
31
+ class IPyAutocall:
32
+ """ Instances of this class are always autocalled
33
+
34
+ This happens regardless of 'autocall' variable state. Use this to
35
+ develop macro-like mechanisms.
36
+ """
37
+ _ip = None
38
+ rewrite = True
39
+ def __init__(self, ip=None):
40
+ self._ip = ip
41
+
42
+ def set_ip(self, ip):
43
+ """Will be used to set _ip point to current ipython instance b/f call
44
+
45
+ Override this method if you don't want this to happen.
46
+
47
+ """
48
+ self._ip = ip
49
+
50
+
51
+ class ExitAutocall(IPyAutocall):
52
+ """An autocallable object which will be added to the user namespace so that
53
+ exit, exit(), quit or quit() are all valid ways to close the shell."""
54
+ rewrite = False
55
+
56
+ def __call__(self):
57
+ self._ip.ask_exit()
58
+
59
+ class ZMQExitAutocall(ExitAutocall):
60
+ """Exit IPython. Autocallable, so it needn't be explicitly called.
61
+
62
+ Parameters
63
+ ----------
64
+ keep_kernel : bool
65
+ If True, leave the kernel alive. Otherwise, tell the kernel to exit too
66
+ (default).
67
+ """
68
+ def __call__(self, keep_kernel=False):
69
+ self._ip.keepkernel_on_exit = keep_kernel
70
+ self._ip.ask_exit()
temp_venv/lib/python3.13/site-packages/IPython/core/builtin_trap.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ A context manager for managing things injected into :mod:`builtins`.
3
+ """
4
+ # Copyright (c) IPython Development Team.
5
+ # Distributed under the terms of the Modified BSD License.
6
+ import builtins as builtin_mod
7
+
8
+ from traitlets.config.configurable import Configurable
9
+
10
+ from traitlets import Instance
11
+
12
+
13
+ class __BuiltinUndefined:
14
+ pass
15
+
16
+
17
+ BuiltinUndefined = __BuiltinUndefined()
18
+
19
+
20
+ class __HideBuiltin:
21
+ pass
22
+
23
+
24
+ HideBuiltin = __HideBuiltin()
25
+
26
+
27
+ class BuiltinTrap(Configurable):
28
+
29
+ shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
30
+ allow_none=True)
31
+
32
+ def __init__(self, shell=None):
33
+ super(BuiltinTrap, self).__init__(shell=shell, config=None)
34
+ self._orig_builtins = {}
35
+ # We define this to track if a single BuiltinTrap is nested.
36
+ # Only turn off the trap when the outermost call to __exit__ is made.
37
+ self._nested_level = 0
38
+ self.shell = shell
39
+ # builtins we always add - if set to HideBuiltin, they will just
40
+ # be removed instead of being replaced by something else
41
+ self.auto_builtins = {'exit': HideBuiltin,
42
+ 'quit': HideBuiltin,
43
+ 'get_ipython': self.shell.get_ipython,
44
+ }
45
+
46
+ def __enter__(self):
47
+ if self._nested_level == 0:
48
+ self.activate()
49
+ self._nested_level += 1
50
+ # I return self, so callers can use add_builtin in a with clause.
51
+ return self
52
+
53
+ def __exit__(self, type, value, traceback):
54
+ if self._nested_level == 1:
55
+ self.deactivate()
56
+ self._nested_level -= 1
57
+ # Returning False will cause exceptions to propagate
58
+ return False
59
+
60
+ def add_builtin(self, key, value):
61
+ """Add a builtin and save the original."""
62
+ bdict = builtin_mod.__dict__
63
+ orig = bdict.get(key, BuiltinUndefined)
64
+ if value is HideBuiltin:
65
+ if orig is not BuiltinUndefined: #same as 'key in bdict'
66
+ self._orig_builtins[key] = orig
67
+ del bdict[key]
68
+ else:
69
+ self._orig_builtins[key] = orig
70
+ bdict[key] = value
71
+
72
+ def remove_builtin(self, key, orig):
73
+ """Remove an added builtin and re-set the original."""
74
+ if orig is BuiltinUndefined:
75
+ del builtin_mod.__dict__[key]
76
+ else:
77
+ builtin_mod.__dict__[key] = orig
78
+
79
+ def activate(self):
80
+ """Store ipython references in the __builtin__ namespace."""
81
+
82
+ add_builtin = self.add_builtin
83
+ for name, func in self.auto_builtins.items():
84
+ add_builtin(name, func)
85
+
86
+ def deactivate(self):
87
+ """Remove any builtins which might have been added by add_builtins, or
88
+ restore overwritten ones to their previous values."""
89
+ remove_builtin = self.remove_builtin
90
+ for key, val in self._orig_builtins.items():
91
+ remove_builtin(key, val)
92
+ self._orig_builtins.clear()
93
+ self._builtins_added = False
temp_venv/lib/python3.13/site-packages/IPython/core/compilerop.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Compiler tools with improved interactive support.
2
+
3
+ Provides compilation machinery similar to codeop, but with caching support so
4
+ we can provide interactive tracebacks.
5
+
6
+ Authors
7
+ -------
8
+ * Robert Kern
9
+ * Fernando Perez
10
+ * Thomas Kluyver
11
+ """
12
+
13
+ # Note: though it might be more natural to name this module 'compiler', that
14
+ # name is in the stdlib and name collisions with the stdlib tend to produce
15
+ # weird problems (often with third-party tools).
16
+
17
+ #-----------------------------------------------------------------------------
18
+ # Copyright (C) 2010-2011 The IPython Development Team.
19
+ #
20
+ # Distributed under the terms of the BSD License.
21
+ #
22
+ # The full license is in the file COPYING.txt, distributed with this software.
23
+ #-----------------------------------------------------------------------------
24
+
25
+ #-----------------------------------------------------------------------------
26
+ # Imports
27
+ #-----------------------------------------------------------------------------
28
+
29
+ # Stdlib imports
30
+ import __future__
31
+ from ast import PyCF_ONLY_AST
32
+ import codeop
33
+ import functools
34
+ import hashlib
35
+ import linecache
36
+ import operator
37
+ import time
38
+ from contextlib import contextmanager
39
+
40
+ #-----------------------------------------------------------------------------
41
+ # Constants
42
+ #-----------------------------------------------------------------------------
43
+
44
+ # Roughly equal to PyCF_MASK | PyCF_MASK_OBSOLETE as defined in pythonrun.h,
45
+ # this is used as a bitmask to extract future-related code flags.
46
+ PyCF_MASK = functools.reduce(operator.or_,
47
+ (getattr(__future__, fname).compiler_flag
48
+ for fname in __future__.all_feature_names))
49
+
50
+ #-----------------------------------------------------------------------------
51
+ # Local utilities
52
+ #-----------------------------------------------------------------------------
53
+
54
+ def code_name(code, number=0):
55
+ """ Compute a (probably) unique name for code for caching.
56
+
57
+ This now expects code to be unicode.
58
+ """
59
+ hash_digest = hashlib.sha1(code.encode("utf-8")).hexdigest()
60
+ # Include the number and 12 characters of the hash in the name. It's
61
+ # pretty much impossible that in a single session we'll have collisions
62
+ # even with truncated hashes, and the full one makes tracebacks too long
63
+ return '<ipython-input-{0}-{1}>'.format(number, hash_digest[:12])
64
+
65
+ #-----------------------------------------------------------------------------
66
+ # Classes and functions
67
+ #-----------------------------------------------------------------------------
68
+
69
+ class CachingCompiler(codeop.Compile):
70
+ """A compiler that caches code compiled from interactive statements.
71
+ """
72
+
73
+ def __init__(self):
74
+ codeop.Compile.__init__(self)
75
+
76
+ # Caching a dictionary { filename: execution_count } for nicely
77
+ # rendered tracebacks. The filename corresponds to the filename
78
+ # argument used for the builtins.compile function.
79
+ self._filename_map = {}
80
+
81
+ def ast_parse(self, source, filename='<unknown>', symbol='exec'):
82
+ """Parse code to an AST with the current compiler flags active.
83
+
84
+ Arguments are exactly the same as ast.parse (in the standard library),
85
+ and are passed to the built-in compile function."""
86
+ return compile(source, filename, symbol, self.flags | PyCF_ONLY_AST, 1)
87
+
88
+ def reset_compiler_flags(self):
89
+ """Reset compiler flags to default state."""
90
+ # This value is copied from codeop.Compile.__init__, so if that ever
91
+ # changes, it will need to be updated.
92
+ self.flags = codeop.PyCF_DONT_IMPLY_DEDENT
93
+
94
+ @property
95
+ def compiler_flags(self):
96
+ """Flags currently active in the compilation process.
97
+ """
98
+ return self.flags
99
+
100
+ def get_code_name(self, raw_code, transformed_code, number):
101
+ """Compute filename given the code, and the cell number.
102
+
103
+ Parameters
104
+ ----------
105
+ raw_code : str
106
+ The raw cell code.
107
+ transformed_code : str
108
+ The executable Python source code to cache and compile.
109
+ number : int
110
+ A number which forms part of the code's name. Used for the execution
111
+ counter.
112
+
113
+ Returns
114
+ -------
115
+ The computed filename.
116
+ """
117
+ return code_name(transformed_code, number)
118
+
119
+ def format_code_name(self, name):
120
+ """Return a user-friendly label and name for a code block.
121
+
122
+ Parameters
123
+ ----------
124
+ name : str
125
+ The name for the code block returned from get_code_name
126
+
127
+ Returns
128
+ -------
129
+ A (label, name) pair that can be used in tracebacks, or None if the default formatting should be used.
130
+ """
131
+ if name in self._filename_map:
132
+ return "Cell", "In[%s]" % self._filename_map[name]
133
+
134
+ def cache(self, transformed_code, number=0, raw_code=None):
135
+ """Make a name for a block of code, and cache the code.
136
+
137
+ Parameters
138
+ ----------
139
+ transformed_code : str
140
+ The executable Python source code to cache and compile.
141
+ number : int
142
+ A number which forms part of the code's name. Used for the execution
143
+ counter.
144
+ raw_code : str
145
+ The raw code before transformation, if None, set to `transformed_code`.
146
+
147
+ Returns
148
+ -------
149
+ The name of the cached code (as a string). Pass this as the filename
150
+ argument to compilation, so that tracebacks are correctly hooked up.
151
+ """
152
+ if raw_code is None:
153
+ raw_code = transformed_code
154
+
155
+ name = self.get_code_name(raw_code, transformed_code, number)
156
+
157
+ # Save the execution count
158
+ self._filename_map[name] = number
159
+
160
+ # Since Python 2.5, setting mtime to `None` means the lines will
161
+ # never be removed by `linecache.checkcache`. This means all the
162
+ # monkeypatching has *never* been necessary, since this code was
163
+ # only added in 2010, at which point IPython had already stopped
164
+ # supporting Python 2.4.
165
+ #
166
+ # Note that `linecache.clearcache` and `linecache.updatecache` may
167
+ # still remove our code from the cache, but those show explicit
168
+ # intent, and we should not try to interfere. Normally the former
169
+ # is never called except when out of memory, and the latter is only
170
+ # called for lines *not* in the cache.
171
+ entry = (
172
+ len(transformed_code),
173
+ None,
174
+ [line + "\n" for line in transformed_code.splitlines()],
175
+ name,
176
+ )
177
+ linecache.cache[name] = entry
178
+ return name
179
+
180
+ @contextmanager
181
+ def extra_flags(self, flags):
182
+ ## bits that we'll set to 1
183
+ turn_on_bits = ~self.flags & flags
184
+
185
+
186
+ self.flags = self.flags | flags
187
+ try:
188
+ yield
189
+ finally:
190
+ # turn off only the bits we turned on so that something like
191
+ # __future__ that set flags stays.
192
+ self.flags &= ~turn_on_bits
temp_venv/lib/python3.13/site-packages/IPython/core/completer.py ADDED
The diff for this file is too large to render. See raw diff
 
temp_venv/lib/python3.13/site-packages/IPython/core/crashhandler.py ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """sys.excepthook for IPython itself, leaves a detailed report on disk.
2
+
3
+ Authors:
4
+
5
+ * Fernando Perez
6
+ * Brian E. Granger
7
+ """
8
+
9
+ #-----------------------------------------------------------------------------
10
+ # Copyright (C) 2001-2007 Fernando Perez. <[email protected]>
11
+ # Copyright (C) 2008-2011 The IPython Development Team
12
+ #
13
+ # Distributed under the terms of the BSD License. The full license is in
14
+ # the file COPYING, distributed as part of this software.
15
+ #-----------------------------------------------------------------------------
16
+
17
+ #-----------------------------------------------------------------------------
18
+ # Imports
19
+ #-----------------------------------------------------------------------------
20
+
21
+ import sys
22
+ import traceback
23
+ from pprint import pformat
24
+ from pathlib import Path
25
+
26
+ import builtins as builtin_mod
27
+
28
+ from IPython.core import ultratb
29
+ from IPython.core.application import Application
30
+ from IPython.core.release import author_email
31
+ from IPython.utils.sysinfo import sys_info
32
+
33
+ from IPython.core.release import __version__ as version
34
+
35
+ from typing import Optional, Dict
36
+ import types
37
+
38
+ #-----------------------------------------------------------------------------
39
+ # Code
40
+ #-----------------------------------------------------------------------------
41
+
42
+ # Template for the user message.
43
+ _default_message_template = """\
44
+ Oops, {app_name} crashed. We do our best to make it stable, but...
45
+
46
+ A crash report was automatically generated with the following information:
47
+ - A verbatim copy of the crash traceback.
48
+ - A copy of your input history during this session.
49
+ - Data on your current {app_name} configuration.
50
+
51
+ It was left in the file named:
52
+ \t'{crash_report_fname}'
53
+ If you can email this file to the developers, the information in it will help
54
+ them in understanding and correcting the problem.
55
+
56
+ You can mail it to: {contact_name} at {contact_email}
57
+ with the subject '{app_name} Crash Report'.
58
+
59
+ If you want to do it now, the following command will work (under Unix):
60
+ mail -s '{app_name} Crash Report' {contact_email} < {crash_report_fname}
61
+
62
+ In your email, please also include information about:
63
+ - The operating system under which the crash happened: Linux, macOS, Windows,
64
+ other, and which exact version (for example: Ubuntu 16.04.3, macOS 10.13.2,
65
+ Windows 10 Pro), and whether it is 32-bit or 64-bit;
66
+ - How {app_name} was installed: using pip or conda, from GitHub, as part of
67
+ a Docker container, or other, providing more detail if possible;
68
+ - How to reproduce the crash: what exact sequence of instructions can one
69
+ input to get the same crash? Ideally, find a minimal yet complete sequence
70
+ of instructions that yields the crash.
71
+
72
+ To ensure accurate tracking of this issue, please file a report about it at:
73
+ {bug_tracker}
74
+ """
75
+
76
+ _lite_message_template = """
77
+ If you suspect this is an IPython {version} bug, please report it at:
78
+ https://github.com/ipython/ipython/issues
79
+ or send an email to the mailing list at {email}
80
+
81
+ You can print a more detailed traceback right now with "%tb", or use "%debug"
82
+ to interactively debug it.
83
+
84
+ Extra-detailed tracebacks for bug-reporting purposes can be enabled via:
85
+ {config}Application.verbose_crash=True
86
+ """
87
+
88
+
89
+ class CrashHandler:
90
+ """Customizable crash handlers for IPython applications.
91
+
92
+ Instances of this class provide a :meth:`__call__` method which can be
93
+ used as a ``sys.excepthook``. The :meth:`__call__` signature is::
94
+
95
+ def __call__(self, etype, evalue, etb)
96
+ """
97
+
98
+ message_template = _default_message_template
99
+ section_sep = '\n\n'+'*'*75+'\n\n'
100
+ info: Dict[str, Optional[str]]
101
+
102
+ def __init__(
103
+ self,
104
+ app: Application,
105
+ contact_name: Optional[str] = None,
106
+ contact_email: Optional[str] = None,
107
+ bug_tracker: Optional[str] = None,
108
+ show_crash_traceback: bool = True,
109
+ call_pdb: bool = False,
110
+ ):
111
+ """Create a new crash handler
112
+
113
+ Parameters
114
+ ----------
115
+ app : Application
116
+ A running :class:`Application` instance, which will be queried at
117
+ crash time for internal information.
118
+ contact_name : str
119
+ A string with the name of the person to contact.
120
+ contact_email : str
121
+ A string with the email address of the contact.
122
+ bug_tracker : str
123
+ A string with the URL for your project's bug tracker.
124
+ show_crash_traceback : bool
125
+ If false, don't print the crash traceback on stderr, only generate
126
+ the on-disk report
127
+ call_pdb
128
+ Whether to call pdb on crash
129
+
130
+ Attributes
131
+ ----------
132
+ These instances contain some non-argument attributes which allow for
133
+ further customization of the crash handler's behavior. Please see the
134
+ source for further details.
135
+
136
+ """
137
+ self.crash_report_fname = "Crash_report_%s.txt" % app.name
138
+ self.app = app
139
+ self.call_pdb = call_pdb
140
+ #self.call_pdb = True # dbg
141
+ self.show_crash_traceback = show_crash_traceback
142
+ self.info = dict(app_name = app.name,
143
+ contact_name = contact_name,
144
+ contact_email = contact_email,
145
+ bug_tracker = bug_tracker,
146
+ crash_report_fname = self.crash_report_fname)
147
+
148
+ def __call__(
149
+ self,
150
+ etype: type[BaseException],
151
+ evalue: BaseException,
152
+ etb: types.TracebackType,
153
+ ) -> None:
154
+ """Handle an exception, call for compatible with sys.excepthook"""
155
+
156
+ # do not allow the crash handler to be called twice without reinstalling it
157
+ # this prevents unlikely errors in the crash handling from entering an
158
+ # infinite loop.
159
+ sys.excepthook = sys.__excepthook__
160
+
161
+
162
+ # Use this ONLY for developer debugging (keep commented out for release)
163
+ ipython_dir = getattr(self.app, "ipython_dir", None)
164
+ if ipython_dir is not None:
165
+ assert isinstance(ipython_dir, str)
166
+ rptdir = Path(ipython_dir)
167
+ else:
168
+ rptdir = Path.cwd()
169
+ if not rptdir.is_dir():
170
+ rptdir = Path.cwd()
171
+ report_name = rptdir / self.crash_report_fname
172
+ # write the report filename into the instance dict so it can get
173
+ # properly expanded out in the user message template
174
+ self.crash_report_fname = str(report_name)
175
+ self.info["crash_report_fname"] = str(report_name)
176
+ TBhandler = ultratb.VerboseTB(
177
+ theme_name="nocolor",
178
+ long_header=True,
179
+ call_pdb=self.call_pdb,
180
+ )
181
+ if self.call_pdb:
182
+ TBhandler(etype,evalue,etb)
183
+ return
184
+ else:
185
+ traceback = TBhandler.text(etype,evalue,etb,context=31)
186
+
187
+ # print traceback to screen
188
+ if self.show_crash_traceback:
189
+ print(traceback, file=sys.stderr)
190
+
191
+ # and generate a complete report on disk
192
+ try:
193
+ report = open(report_name, "w", encoding="utf-8")
194
+ except:
195
+ print('Could not create crash report on disk.', file=sys.stderr)
196
+ return
197
+
198
+ with report:
199
+ # Inform user on stderr of what happened
200
+ print('\n'+'*'*70+'\n', file=sys.stderr)
201
+ print(self.message_template.format(**self.info), file=sys.stderr)
202
+
203
+ # Construct report on disk
204
+ report.write(self.make_report(str(traceback)))
205
+
206
+ builtin_mod.input("Hit <Enter> to quit (your terminal may close):")
207
+
208
+ def make_report(self, traceback: str) -> str:
209
+ """Return a string containing a crash report."""
210
+
211
+ sec_sep = self.section_sep
212
+
213
+ report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
214
+ rpt_add = report.append
215
+ rpt_add(sys_info())
216
+
217
+ try:
218
+ config = pformat(self.app.config)
219
+ rpt_add(sec_sep)
220
+ rpt_add("Application name: %s\n\n" % self.app.name)
221
+ rpt_add("Current user configuration structure:\n\n")
222
+ rpt_add(config)
223
+ except:
224
+ pass
225
+ rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
226
+
227
+ return ''.join(report)
228
+
229
+
230
+ def crash_handler_lite(
231
+ etype: type[BaseException], evalue: BaseException, tb: types.TracebackType
232
+ ) -> None:
233
+ """a light excepthook, adding a small message to the usual traceback"""
234
+ traceback.print_exception(etype, evalue, tb)
235
+
236
+ from IPython.core.interactiveshell import InteractiveShell
237
+ if InteractiveShell.initialized():
238
+ # we are in a Shell environment, give %magic example
239
+ config = "%config "
240
+ else:
241
+ # we are not in a shell, show generic config
242
+ config = "c."
243
+ print(_lite_message_template.format(email=author_email, config=config, version=version), file=sys.stderr)
244
+
temp_venv/lib/python3.13/site-packages/IPython/core/debugger.py ADDED
@@ -0,0 +1,1240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Pdb debugger class.
3
+
4
+
5
+ This is an extension to PDB which adds a number of new features.
6
+ Note that there is also the `IPython.terminal.debugger` class which provides UI
7
+ improvements.
8
+
9
+ We also strongly recommend to use this via the `ipdb` package, which provides
10
+ extra configuration options.
11
+
12
+ Among other things, this subclass of PDB:
13
+ - supports many IPython magics like pdef/psource
14
+ - hide frames in tracebacks based on `__tracebackhide__`
15
+ - allows to skip frames based on `__debuggerskip__`
16
+
17
+
18
+ Global Configuration
19
+ --------------------
20
+
21
+ The IPython debugger will by read the global ``~/.pdbrc`` file.
22
+ That is to say you can list all commands supported by ipdb in your `~/.pdbrc`
23
+ configuration file, to globally configure pdb.
24
+
25
+ Example::
26
+
27
+ # ~/.pdbrc
28
+ skip_predicates debuggerskip false
29
+ skip_hidden false
30
+ context 25
31
+
32
+ Features
33
+ --------
34
+
35
+ The IPython debugger can hide and skip frames when printing or moving through
36
+ the stack. This can have a performance impact, so can be configures.
37
+
38
+ The skipping and hiding frames are configurable via the `skip_predicates`
39
+ command.
40
+
41
+ By default, frames from readonly files will be hidden, frames containing
42
+ ``__tracebackhide__ = True`` will be hidden.
43
+
44
+ Frames containing ``__debuggerskip__`` will be stepped over, frames whose parent
45
+ frames value of ``__debuggerskip__`` is ``True`` will also be skipped.
46
+
47
+ >>> def helpers_helper():
48
+ ... pass
49
+ ...
50
+ ... def helper_1():
51
+ ... print("don't step in me")
52
+ ... helpers_helpers() # will be stepped over unless breakpoint set.
53
+ ...
54
+ ...
55
+ ... def helper_2():
56
+ ... print("in me neither")
57
+ ...
58
+
59
+ One can define a decorator that wraps a function between the two helpers:
60
+
61
+ >>> def pdb_skipped_decorator(function):
62
+ ...
63
+ ...
64
+ ... def wrapped_fn(*args, **kwargs):
65
+ ... __debuggerskip__ = True
66
+ ... helper_1()
67
+ ... __debuggerskip__ = False
68
+ ... result = function(*args, **kwargs)
69
+ ... __debuggerskip__ = True
70
+ ... helper_2()
71
+ ... # setting __debuggerskip__ to False again is not necessary
72
+ ... return result
73
+ ...
74
+ ... return wrapped_fn
75
+
76
+ When decorating a function, ipdb will directly step into ``bar()`` by
77
+ default:
78
+
79
+ >>> @foo_decorator
80
+ ... def bar(x, y):
81
+ ... return x * y
82
+
83
+
84
+ You can toggle the behavior with
85
+
86
+ ipdb> skip_predicates debuggerskip false
87
+
88
+ or configure it in your ``.pdbrc``
89
+
90
+
91
+
92
+ License
93
+ -------
94
+
95
+ Modified from the standard pdb.Pdb class to avoid including readline, so that
96
+ the command line completion of other programs which include this isn't
97
+ damaged.
98
+
99
+ In the future, this class will be expanded with improvements over the standard
100
+ pdb.
101
+
102
+ The original code in this file is mainly lifted out of cmd.py in Python 2.2,
103
+ with minor changes. Licensing should therefore be under the standard Python
104
+ terms. For details on the PSF (Python Software Foundation) standard license,
105
+ see:
106
+
107
+ https://docs.python.org/2/license.html
108
+
109
+
110
+ All the changes since then are under the same license as IPython.
111
+
112
+ """
113
+
114
+ # *****************************************************************************
115
+ #
116
+ # This file is licensed under the PSF license.
117
+ #
118
+ # Copyright (C) 2001 Python Software Foundation, www.python.org
119
+ # Copyright (C) 2005-2006 Fernando Perez. <[email protected]>
120
+ #
121
+ #
122
+ # *****************************************************************************
123
+
124
+ from __future__ import annotations
125
+
126
+ import inspect
127
+ import linecache
128
+ import os
129
+ import re
130
+ import sys
131
+ import warnings
132
+ from contextlib import contextmanager
133
+ from functools import lru_cache
134
+
135
+ from IPython import get_ipython
136
+ from IPython.utils import PyColorize
137
+ from IPython.utils.PyColorize import TokenStream
138
+
139
+ from typing import TYPE_CHECKING
140
+ from types import FrameType
141
+
142
+ # We have to check this directly from sys.argv, config struct not yet available
143
+ from pdb import Pdb as OldPdb
144
+ from pygments.token import Token
145
+
146
+ if TYPE_CHECKING:
147
+ # otherwise circular import
148
+ from IPython.core.interactiveshell import InteractiveShell
149
+
150
+ # skip module docstests
151
+ __skip_doctest__ = True
152
+
153
+ prompt = "ipdb> "
154
+
155
+
156
+ # Allow the set_trace code to operate outside of an ipython instance, even if
157
+ # it does so with some limitations. The rest of this support is implemented in
158
+ # the Tracer constructor.
159
+
160
+ DEBUGGERSKIP = "__debuggerskip__"
161
+
162
+
163
+ # this has been implemented in Pdb in Python 3.13 (https://github.com/python/cpython/pull/106676
164
+ # on lower python versions, we backported the feature.
165
+ CHAIN_EXCEPTIONS = sys.version_info < (3, 13)
166
+
167
+
168
+ def BdbQuit_excepthook(et, ev, tb, excepthook=None):
169
+ """Exception hook which handles `BdbQuit` exceptions.
170
+
171
+ All other exceptions are processed using the `excepthook`
172
+ parameter.
173
+ """
174
+ raise ValueError(
175
+ "`BdbQuit_excepthook` is deprecated since version 5.1. It is still around only because it is still imported by ipdb.",
176
+ )
177
+
178
+
179
+ RGX_EXTRA_INDENT = re.compile(r"(?<=\n)\s+")
180
+
181
+
182
+ def strip_indentation(multiline_string):
183
+ return RGX_EXTRA_INDENT.sub("", multiline_string)
184
+
185
+
186
+ def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
187
+ """Make new_fn have old_fn's doc string. This is particularly useful
188
+ for the ``do_...`` commands that hook into the help system.
189
+ Adapted from from a comp.lang.python posting
190
+ by Duncan Booth."""
191
+
192
+ def wrapper(*args, **kw):
193
+ return new_fn(*args, **kw)
194
+
195
+ if old_fn.__doc__:
196
+ wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
197
+ return wrapper
198
+
199
+
200
+ class Pdb(OldPdb):
201
+ """Modified Pdb class, does not load readline.
202
+
203
+ for a standalone version that uses prompt_toolkit, see
204
+ `IPython.terminal.debugger.TerminalPdb` and
205
+ `IPython.terminal.debugger.set_trace()`
206
+
207
+
208
+ This debugger can hide and skip frames that are tagged according to some predicates.
209
+ See the `skip_predicates` commands.
210
+
211
+ """
212
+
213
+ shell: InteractiveShell
214
+ _theme_name: str
215
+ _context: int
216
+
217
+ _chained_exceptions: tuple[Exception, ...]
218
+ _chained_exception_index: int
219
+
220
+ if CHAIN_EXCEPTIONS:
221
+ MAX_CHAINED_EXCEPTION_DEPTH = 999
222
+
223
+ default_predicates = {
224
+ "tbhide": True,
225
+ "readonly": False,
226
+ "ipython_internal": True,
227
+ "debuggerskip": True,
228
+ }
229
+
230
+ def __init__(
231
+ self,
232
+ completekey=None,
233
+ stdin=None,
234
+ stdout=None,
235
+ context: int | None | str = 5,
236
+ **kwargs,
237
+ ):
238
+ """Create a new IPython debugger.
239
+
240
+ Parameters
241
+ ----------
242
+ completekey : default None
243
+ Passed to pdb.Pdb.
244
+ stdin : default None
245
+ Passed to pdb.Pdb.
246
+ stdout : default None
247
+ Passed to pdb.Pdb.
248
+ context : int
249
+ Number of lines of source code context to show when
250
+ displaying stacktrace information.
251
+ **kwargs
252
+ Passed to pdb.Pdb.
253
+
254
+ Notes
255
+ -----
256
+ The possibilities are python version dependent, see the python
257
+ docs for more info.
258
+ """
259
+ # ipdb issue, see https://github.com/ipython/ipython/issues/14811
260
+ if context is None:
261
+ context = 5
262
+ if isinstance(context, str):
263
+ context = int(context)
264
+ self.context = context
265
+
266
+ # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
267
+ OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
268
+
269
+ # IPython changes...
270
+ self.shell = get_ipython()
271
+
272
+ if self.shell is None:
273
+ save_main = sys.modules["__main__"]
274
+ # No IPython instance running, we must create one
275
+ from IPython.terminal.interactiveshell import TerminalInteractiveShell
276
+
277
+ self.shell = TerminalInteractiveShell.instance()
278
+ # needed by any code which calls __import__("__main__") after
279
+ # the debugger was entered. See also #9941.
280
+ sys.modules["__main__"] = save_main
281
+
282
+ self.aliases = {}
283
+
284
+ theme_name = self.shell.colors
285
+ assert isinstance(theme_name, str)
286
+ assert theme_name.lower() == theme_name
287
+
288
+ # Add a python parser so we can syntax highlight source while
289
+ # debugging.
290
+ self.parser = PyColorize.Parser(theme_name=theme_name)
291
+ self.set_theme_name(theme_name)
292
+
293
+ # Set the prompt - the default prompt is '(Pdb)'
294
+ self.prompt = prompt
295
+ self.skip_hidden = True
296
+ self.report_skipped = True
297
+
298
+ # list of predicates we use to skip frames
299
+ self._predicates = self.default_predicates
300
+
301
+ if CHAIN_EXCEPTIONS:
302
+ self._chained_exceptions = tuple()
303
+ self._chained_exception_index = 0
304
+
305
+ @property
306
+ def context(self) -> int:
307
+ return self._context
308
+
309
+ @context.setter
310
+ def context(self, value: int | str) -> None:
311
+ # ipdb issue see https://github.com/ipython/ipython/issues/14811
312
+ if not isinstance(value, int):
313
+ value = int(value)
314
+ assert isinstance(value, int)
315
+ assert value >= 0
316
+ self._context = value
317
+
318
+ def set_theme_name(self, name):
319
+ assert name.lower() == name
320
+ assert isinstance(name, str)
321
+ self._theme_name = name
322
+ self.parser.theme_name = name
323
+
324
+ @property
325
+ def theme(self):
326
+ return PyColorize.theme_table[self._theme_name]
327
+
328
+ #
329
+ def set_colors(self, scheme):
330
+ """Shorthand access to the color table scheme selector method."""
331
+ warnings.warn(
332
+ "set_colors is deprecated since IPython 9.0, use set_theme_name instead",
333
+ DeprecationWarning,
334
+ stacklevel=2,
335
+ )
336
+ assert scheme == scheme.lower()
337
+ self._theme_name = scheme.lower()
338
+ self.parser.theme_name = scheme.lower()
339
+
340
+ def set_trace(self, frame=None):
341
+ if frame is None:
342
+ frame = sys._getframe().f_back
343
+ self.initial_frame = frame
344
+ return super().set_trace(frame)
345
+
346
+ def _hidden_predicate(self, frame):
347
+ """
348
+ Given a frame return whether it it should be hidden or not by IPython.
349
+ """
350
+
351
+ if self._predicates["readonly"]:
352
+ fname = frame.f_code.co_filename
353
+ # we need to check for file existence and interactively define
354
+ # function would otherwise appear as RO.
355
+ if os.path.isfile(fname) and not os.access(fname, os.W_OK):
356
+ return True
357
+
358
+ if self._predicates["tbhide"]:
359
+ if frame in (self.curframe, getattr(self, "initial_frame", None)):
360
+ return False
361
+ frame_locals = self._get_frame_locals(frame)
362
+ if "__tracebackhide__" not in frame_locals:
363
+ return False
364
+ return frame_locals["__tracebackhide__"]
365
+ return False
366
+
367
+ def hidden_frames(self, stack):
368
+ """
369
+ Given an index in the stack return whether it should be skipped.
370
+
371
+ This is used in up/down and where to skip frames.
372
+ """
373
+ # The f_locals dictionary is updated from the actual frame
374
+ # locals whenever the .f_locals accessor is called, so we
375
+ # avoid calling it here to preserve self.curframe_locals.
376
+ # Furthermore, there is no good reason to hide the current frame.
377
+ ip_hide = [self._hidden_predicate(s[0]) for s in stack]
378
+ ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
379
+ if ip_start and self._predicates["ipython_internal"]:
380
+ ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
381
+ return ip_hide
382
+
383
+ if CHAIN_EXCEPTIONS:
384
+
385
+ def _get_tb_and_exceptions(self, tb_or_exc):
386
+ """
387
+ Given a tracecack or an exception, return a tuple of chained exceptions
388
+ and current traceback to inspect.
389
+ This will deal with selecting the right ``__cause__`` or ``__context__``
390
+ as well as handling cycles, and return a flattened list of exceptions we
391
+ can jump to with do_exceptions.
392
+ """
393
+ _exceptions = []
394
+ if isinstance(tb_or_exc, BaseException):
395
+ traceback, current = tb_or_exc.__traceback__, tb_or_exc
396
+
397
+ while current is not None:
398
+ if current in _exceptions:
399
+ break
400
+ _exceptions.append(current)
401
+ if current.__cause__ is not None:
402
+ current = current.__cause__
403
+ elif (
404
+ current.__context__ is not None
405
+ and not current.__suppress_context__
406
+ ):
407
+ current = current.__context__
408
+
409
+ if len(_exceptions) >= self.MAX_CHAINED_EXCEPTION_DEPTH:
410
+ self.message(
411
+ f"More than {self.MAX_CHAINED_EXCEPTION_DEPTH}"
412
+ " chained exceptions found, not all exceptions"
413
+ "will be browsable with `exceptions`."
414
+ )
415
+ break
416
+ else:
417
+ traceback = tb_or_exc
418
+ return tuple(reversed(_exceptions)), traceback
419
+
420
+ @contextmanager
421
+ def _hold_exceptions(self, exceptions):
422
+ """
423
+ Context manager to ensure proper cleaning of exceptions references
424
+ When given a chained exception instead of a traceback,
425
+ pdb may hold references to many objects which may leak memory.
426
+ We use this context manager to make sure everything is properly cleaned
427
+ """
428
+ try:
429
+ self._chained_exceptions = exceptions
430
+ self._chained_exception_index = len(exceptions) - 1
431
+ yield
432
+ finally:
433
+ # we can't put those in forget as otherwise they would
434
+ # be cleared on exception change
435
+ self._chained_exceptions = tuple()
436
+ self._chained_exception_index = 0
437
+
438
+ def do_exceptions(self, arg):
439
+ """exceptions [number]
440
+ List or change current exception in an exception chain.
441
+ Without arguments, list all the current exception in the exception
442
+ chain. Exceptions will be numbered, with the current exception indicated
443
+ with an arrow.
444
+ If given an integer as argument, switch to the exception at that index.
445
+ """
446
+ if not self._chained_exceptions:
447
+ self.message(
448
+ "Did not find chained exceptions. To move between"
449
+ " exceptions, pdb/post_mortem must be given an exception"
450
+ " object rather than a traceback."
451
+ )
452
+ return
453
+ if not arg:
454
+ for ix, exc in enumerate(self._chained_exceptions):
455
+ prompt = ">" if ix == self._chained_exception_index else " "
456
+ rep = repr(exc)
457
+ if len(rep) > 80:
458
+ rep = rep[:77] + "..."
459
+ indicator = (
460
+ " -"
461
+ if self._chained_exceptions[ix].__traceback__ is None
462
+ else f"{ix:>3}"
463
+ )
464
+ self.message(f"{prompt} {indicator} {rep}")
465
+ else:
466
+ try:
467
+ number = int(arg)
468
+ except ValueError:
469
+ self.error("Argument must be an integer")
470
+ return
471
+ if 0 <= number < len(self._chained_exceptions):
472
+ if self._chained_exceptions[number].__traceback__ is None:
473
+ self.error(
474
+ "This exception does not have a traceback, cannot jump to it"
475
+ )
476
+ return
477
+
478
+ self._chained_exception_index = number
479
+ self.setup(None, self._chained_exceptions[number].__traceback__)
480
+ self.print_stack_entry(self.stack[self.curindex])
481
+ else:
482
+ self.error("No exception with that number")
483
+
484
+ def interaction(self, frame, tb_or_exc):
485
+ try:
486
+ if CHAIN_EXCEPTIONS:
487
+ # this context manager is part of interaction in 3.13
488
+ _chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc)
489
+ if isinstance(tb_or_exc, BaseException):
490
+ assert tb is not None, "main exception must have a traceback"
491
+ with self._hold_exceptions(_chained_exceptions):
492
+ OldPdb.interaction(self, frame, tb)
493
+ else:
494
+ OldPdb.interaction(self, frame, tb_or_exc)
495
+
496
+ except KeyboardInterrupt:
497
+ self.stdout.write("\n" + self.shell.get_exception_only())
498
+
499
+ def precmd(self, line):
500
+ """Perform useful escapes on the command before it is executed."""
501
+
502
+ if line.endswith("??"):
503
+ line = "pinfo2 " + line[:-2]
504
+ elif line.endswith("?"):
505
+ line = "pinfo " + line[:-1]
506
+
507
+ line = super().precmd(line)
508
+
509
+ return line
510
+
511
+ def new_do_quit(self, arg):
512
+ return OldPdb.do_quit(self, arg)
513
+
514
+ do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
515
+
516
+ def print_stack_trace(self, context: int | None = None):
517
+ if context is None:
518
+ context = self.context
519
+ try:
520
+ skipped = 0
521
+ to_print = ""
522
+ for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
523
+ if hidden and self.skip_hidden:
524
+ skipped += 1
525
+ continue
526
+ if skipped:
527
+ to_print += self.theme.format(
528
+ [
529
+ (
530
+ Token.ExcName,
531
+ f" [... skipping {skipped} hidden frame(s)]",
532
+ ),
533
+ (Token, "\n"),
534
+ ]
535
+ )
536
+
537
+ skipped = 0
538
+ to_print += self.format_stack_entry(frame_lineno)
539
+ if skipped:
540
+ to_print += self.theme.format(
541
+ [
542
+ (
543
+ Token.ExcName,
544
+ f" [... skipping {skipped} hidden frame(s)]",
545
+ ),
546
+ (Token, "\n"),
547
+ ]
548
+ )
549
+ print(to_print, file=self.stdout)
550
+ except KeyboardInterrupt:
551
+ pass
552
+
553
+ def print_stack_entry(
554
+ self, frame_lineno: tuple[FrameType, int], prompt_prefix: str = "\n-> "
555
+ ) -> None:
556
+ """
557
+ Overwrite print_stack_entry from superclass (PDB)
558
+ """
559
+ print(self.format_stack_entry(frame_lineno, ""), file=self.stdout)
560
+
561
+ frame, lineno = frame_lineno
562
+ filename = frame.f_code.co_filename
563
+ self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
564
+
565
+ def _get_frame_locals(self, frame):
566
+ """ "
567
+ Accessing f_local of current frame reset the namespace, so we want to avoid
568
+ that or the following can happen
569
+
570
+ ipdb> foo
571
+ "old"
572
+ ipdb> foo = "new"
573
+ ipdb> foo
574
+ "new"
575
+ ipdb> where
576
+ ipdb> foo
577
+ "old"
578
+
579
+ So if frame is self.current_frame we instead return self.curframe_locals
580
+
581
+ """
582
+ if frame is getattr(self, "curframe", None):
583
+ return self.curframe_locals
584
+ else:
585
+ return frame.f_locals
586
+
587
+ def format_stack_entry(
588
+ self,
589
+ frame_lineno: tuple[FrameType, int], # type: ignore[override] # stubs are wrong
590
+ lprefix: str = ": ",
591
+ ) -> str:
592
+ """
593
+ overwrite from super class so must -> str
594
+ """
595
+ context = self.context
596
+ try:
597
+ context = int(context)
598
+ if context <= 0:
599
+ print("Context must be a positive integer", file=self.stdout)
600
+ except (TypeError, ValueError):
601
+ print("Context must be a positive integer", file=self.stdout)
602
+
603
+ import reprlib
604
+
605
+ ret_tok = []
606
+
607
+ frame, lineno = frame_lineno
608
+
609
+ return_value = ""
610
+ loc_frame = self._get_frame_locals(frame)
611
+ if "__return__" in loc_frame:
612
+ rv = loc_frame["__return__"]
613
+ # return_value += '->'
614
+ return_value += reprlib.repr(rv) + "\n"
615
+ ret_tok.extend([(Token, return_value)])
616
+
617
+ # s = filename + '(' + `lineno` + ')'
618
+ filename = self.canonic(frame.f_code.co_filename)
619
+ link_tok = (Token.FilenameEm, filename)
620
+
621
+ if frame.f_code.co_name:
622
+ func = frame.f_code.co_name
623
+ else:
624
+ func = "<lambda>"
625
+
626
+ call_toks = []
627
+ if func != "?":
628
+ if "__args__" in loc_frame:
629
+ args = reprlib.repr(loc_frame["__args__"])
630
+ else:
631
+ args = "()"
632
+ call_toks = [(Token.VName, func), (Token.ValEm, args)]
633
+
634
+ # The level info should be generated in the same format pdb uses, to
635
+ # avoid breaking the pdbtrack functionality of python-mode in *emacs.
636
+ if frame is self.curframe:
637
+ ret_tok.append((Token.CurrentFrame, self.theme.make_arrow(2)))
638
+ else:
639
+ ret_tok.append((Token, " "))
640
+
641
+ ret_tok.extend(
642
+ [
643
+ link_tok,
644
+ (Token, "("),
645
+ (Token.Lineno, str(lineno)),
646
+ (Token, ")"),
647
+ *call_toks,
648
+ (Token, "\n"),
649
+ ]
650
+ )
651
+
652
+ start = lineno - 1 - context // 2
653
+ lines = linecache.getlines(filename)
654
+ start = min(start, len(lines) - context)
655
+ start = max(start, 0)
656
+ lines = lines[start : start + context]
657
+
658
+ for i, line in enumerate(lines):
659
+ show_arrow = start + 1 + i == lineno
660
+
661
+ bp, num, colored_line = self.__line_content(
662
+ filename,
663
+ start + 1 + i,
664
+ line,
665
+ arrow=show_arrow,
666
+ )
667
+ if frame is self.curframe or show_arrow:
668
+ rlt = [
669
+ bp,
670
+ (Token.LinenoEm, num),
671
+ (Token, " "),
672
+ # TODO: investigate Toke.Line here, likely LineEm,
673
+ # Token is problematic here as line is already colored, a
674
+ # and this changes the full style of the colored line.
675
+ # ideally, __line_content returns the token and we modify the style.
676
+ (Token, colored_line),
677
+ ]
678
+ else:
679
+ rlt = [
680
+ bp,
681
+ (Token.Lineno, num),
682
+ (Token, " "),
683
+ # TODO: investigate Toke.Line here, likely Line
684
+ # Token is problematic here as line is already colored, a
685
+ # and this changes the full style of the colored line.
686
+ # ideally, __line_content returns the token and we modify the style.
687
+ (Token.Line, colored_line),
688
+ ]
689
+ ret_tok.extend(rlt)
690
+
691
+ return self.theme.format(ret_tok)
692
+
693
+ def __line_content(
694
+ self, filename: str, lineno: int, line: str, arrow: bool = False
695
+ ):
696
+ bp_mark = ""
697
+ BreakpointToken = Token.Breakpoint
698
+
699
+ new_line, err = self.parser.format2(line, "str")
700
+ if not err:
701
+ line = new_line
702
+
703
+ bp = None
704
+ if lineno in self.get_file_breaks(filename):
705
+ bps = self.get_breaks(filename, lineno)
706
+ bp = bps[-1]
707
+
708
+ if bp:
709
+ bp_mark = str(bp.number)
710
+ BreakpointToken = Token.Breakpoint.Enabled
711
+ if not bp.enabled:
712
+ BreakpointToken = Token.Breakpoint.Disabled
713
+ numbers_width = 7
714
+ if arrow:
715
+ # This is the line with the error
716
+ pad = numbers_width - len(str(lineno)) - len(bp_mark)
717
+ num = "%s%s" % (self.theme.make_arrow(pad), str(lineno))
718
+ else:
719
+ num = "%*s" % (numbers_width - len(bp_mark), str(lineno))
720
+ bp_str = (BreakpointToken, bp_mark)
721
+ return (bp_str, num, line)
722
+
723
+ def print_list_lines(self, filename: str, first: int, last: int) -> None:
724
+ """The printing (as opposed to the parsing part of a 'list'
725
+ command."""
726
+ toks: TokenStream = []
727
+ try:
728
+ if filename == "<string>" and hasattr(self, "_exec_filename"):
729
+ filename = self._exec_filename
730
+
731
+ for lineno in range(first, last + 1):
732
+ line = linecache.getline(filename, lineno)
733
+ if not line:
734
+ break
735
+
736
+ assert self.curframe is not None
737
+
738
+ if lineno == self.curframe.f_lineno:
739
+ bp, num, colored_line = self.__line_content(
740
+ filename, lineno, line, arrow=True
741
+ )
742
+ toks.extend(
743
+ [
744
+ bp,
745
+ (Token.LinenoEm, num),
746
+ (Token, " "),
747
+ # TODO: invsetigate Toke.Line here
748
+ (Token, colored_line),
749
+ ]
750
+ )
751
+ else:
752
+ bp, num, colored_line = self.__line_content(
753
+ filename, lineno, line, arrow=False
754
+ )
755
+ toks.extend(
756
+ [
757
+ bp,
758
+ (Token.Lineno, num),
759
+ (Token, " "),
760
+ (Token, colored_line),
761
+ ]
762
+ )
763
+
764
+ self.lineno = lineno
765
+
766
+ print(self.theme.format(toks), file=self.stdout)
767
+
768
+ except KeyboardInterrupt:
769
+ pass
770
+
771
+ def do_skip_predicates(self, args):
772
+ """
773
+ Turn on/off individual predicates as to whether a frame should be hidden/skip.
774
+
775
+ The global option to skip (or not) hidden frames is set with skip_hidden
776
+
777
+ To change the value of a predicate
778
+
779
+ skip_predicates key [true|false]
780
+
781
+ Call without arguments to see the current values.
782
+
783
+ To permanently change the value of an option add the corresponding
784
+ command to your ``~/.pdbrc`` file. If you are programmatically using the
785
+ Pdb instance you can also change the ``default_predicates`` class
786
+ attribute.
787
+ """
788
+ if not args.strip():
789
+ print("current predicates:")
790
+ for p, v in self._predicates.items():
791
+ print(" ", p, ":", v)
792
+ return
793
+ type_value = args.strip().split(" ")
794
+ if len(type_value) != 2:
795
+ print(
796
+ f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
797
+ )
798
+ return
799
+
800
+ type_, value = type_value
801
+ if type_ not in self._predicates:
802
+ print(f"{type_!r} not in {set(self._predicates.keys())}")
803
+ return
804
+ if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
805
+ print(
806
+ f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
807
+ )
808
+ return
809
+
810
+ self._predicates[type_] = value.lower() in ("true", "yes", "1")
811
+ if not any(self._predicates.values()):
812
+ print(
813
+ "Warning, all predicates set to False, skip_hidden may not have any effects."
814
+ )
815
+
816
+ def do_skip_hidden(self, arg):
817
+ """
818
+ Change whether or not we should skip frames with the
819
+ __tracebackhide__ attribute.
820
+ """
821
+ if not arg.strip():
822
+ print(
823
+ f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
824
+ )
825
+ elif arg.strip().lower() in ("true", "yes"):
826
+ self.skip_hidden = True
827
+ elif arg.strip().lower() in ("false", "no"):
828
+ self.skip_hidden = False
829
+ if not any(self._predicates.values()):
830
+ print(
831
+ "Warning, all predicates set to False, skip_hidden may not have any effects."
832
+ )
833
+
834
+ def do_list(self, arg):
835
+ """Print lines of code from the current stack frame"""
836
+ self.lastcmd = "list"
837
+ last = None
838
+ if arg and arg != ".":
839
+ try:
840
+ x = eval(arg, {}, {})
841
+ if type(x) == type(()):
842
+ first, last = x
843
+ first = int(first)
844
+ last = int(last)
845
+ if last < first:
846
+ # Assume it's a count
847
+ last = first + last
848
+ else:
849
+ first = max(1, int(x) - 5)
850
+ except:
851
+ print("*** Error in argument:", repr(arg), file=self.stdout)
852
+ return
853
+ elif self.lineno is None or arg == ".":
854
+ assert self.curframe is not None
855
+ first = max(1, self.curframe.f_lineno - 5)
856
+ else:
857
+ first = self.lineno + 1
858
+ if last is None:
859
+ last = first + 10
860
+ assert self.curframe is not None
861
+ self.print_list_lines(self.curframe.f_code.co_filename, first, last)
862
+
863
+ lineno = first
864
+ filename = self.curframe.f_code.co_filename
865
+ self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
866
+
867
+ do_l = do_list
868
+
869
+ def getsourcelines(self, obj):
870
+ lines, lineno = inspect.findsource(obj)
871
+ if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
872
+ # must be a module frame: do not try to cut a block out of it
873
+ return lines, 1
874
+ elif inspect.ismodule(obj):
875
+ return lines, 1
876
+ return inspect.getblock(lines[lineno:]), lineno + 1
877
+
878
+ def do_longlist(self, arg):
879
+ """Print lines of code from the current stack frame.
880
+
881
+ Shows more lines than 'list' does.
882
+ """
883
+ self.lastcmd = "longlist"
884
+ try:
885
+ lines, lineno = self.getsourcelines(self.curframe)
886
+ except OSError as err:
887
+ self.error(str(err))
888
+ return
889
+ last = lineno + len(lines)
890
+ assert self.curframe is not None
891
+ self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
892
+
893
+ do_ll = do_longlist
894
+
895
+ def do_debug(self, arg):
896
+ """debug code
897
+ Enter a recursive debugger that steps through the code
898
+ argument (which is an arbitrary expression or statement to be
899
+ executed in the current environment).
900
+ """
901
+ trace_function = sys.gettrace()
902
+ sys.settrace(None)
903
+ assert self.curframe is not None
904
+ globals = self.curframe.f_globals
905
+ locals = self.curframe_locals
906
+ p = self.__class__(
907
+ completekey=self.completekey, stdin=self.stdin, stdout=self.stdout
908
+ )
909
+ p.use_rawinput = self.use_rawinput
910
+ p.prompt = "(%s) " % self.prompt.strip()
911
+ self.message("ENTERING RECURSIVE DEBUGGER")
912
+ sys.call_tracing(p.run, (arg, globals, locals))
913
+ self.message("LEAVING RECURSIVE DEBUGGER")
914
+ sys.settrace(trace_function)
915
+ self.lastcmd = p.lastcmd
916
+
917
+ def do_pdef(self, arg):
918
+ """Print the call signature for any callable object.
919
+
920
+ The debugger interface to %pdef"""
921
+ assert self.curframe is not None
922
+ namespaces = [
923
+ ("Locals", self.curframe_locals),
924
+ ("Globals", self.curframe.f_globals),
925
+ ]
926
+ self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
927
+
928
+ def do_pdoc(self, arg):
929
+ """Print the docstring for an object.
930
+
931
+ The debugger interface to %pdoc."""
932
+ assert self.curframe is not None
933
+ namespaces = [
934
+ ("Locals", self.curframe_locals),
935
+ ("Globals", self.curframe.f_globals),
936
+ ]
937
+ self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
938
+
939
+ def do_pfile(self, arg):
940
+ """Print (or run through pager) the file where an object is defined.
941
+
942
+ The debugger interface to %pfile.
943
+ """
944
+ assert self.curframe is not None
945
+ namespaces = [
946
+ ("Locals", self.curframe_locals),
947
+ ("Globals", self.curframe.f_globals),
948
+ ]
949
+ self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
950
+
951
+ def do_pinfo(self, arg):
952
+ """Provide detailed information about an object.
953
+
954
+ The debugger interface to %pinfo, i.e., obj?."""
955
+ assert self.curframe is not None
956
+ namespaces = [
957
+ ("Locals", self.curframe_locals),
958
+ ("Globals", self.curframe.f_globals),
959
+ ]
960
+ self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
961
+
962
+ def do_pinfo2(self, arg):
963
+ """Provide extra detailed information about an object.
964
+
965
+ The debugger interface to %pinfo2, i.e., obj??."""
966
+ assert self.curframe is not None
967
+ namespaces = [
968
+ ("Locals", self.curframe_locals),
969
+ ("Globals", self.curframe.f_globals),
970
+ ]
971
+ self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
972
+
973
+ def do_psource(self, arg):
974
+ """Print (or run through pager) the source code for an object."""
975
+ assert self.curframe is not None
976
+ namespaces = [
977
+ ("Locals", self.curframe_locals),
978
+ ("Globals", self.curframe.f_globals),
979
+ ]
980
+ self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
981
+
982
+ def do_where(self, arg: str):
983
+ """w(here)
984
+ Print a stack trace, with the most recent frame at the bottom.
985
+ An arrow indicates the "current frame", which determines the
986
+ context of most commands. 'bt' is an alias for this command.
987
+
988
+ Take a number as argument as an (optional) number of context line to
989
+ print"""
990
+ if arg:
991
+ try:
992
+ context = int(arg)
993
+ except ValueError as err:
994
+ self.error(str(err))
995
+ return
996
+ self.print_stack_trace(context)
997
+ else:
998
+ self.print_stack_trace()
999
+
1000
+ do_w = do_where
1001
+
1002
+ def break_anywhere(self, frame):
1003
+ """
1004
+ _stop_in_decorator_internals is overly restrictive, as we may still want
1005
+ to trace function calls, so we need to also update break_anywhere so
1006
+ that is we don't `stop_here`, because of debugger skip, we may still
1007
+ stop at any point inside the function
1008
+
1009
+ """
1010
+
1011
+ sup = super().break_anywhere(frame)
1012
+ if sup:
1013
+ return sup
1014
+ if self._predicates["debuggerskip"]:
1015
+ if DEBUGGERSKIP in frame.f_code.co_varnames:
1016
+ return True
1017
+ if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
1018
+ return True
1019
+ return False
1020
+
1021
+ def _is_in_decorator_internal_and_should_skip(self, frame):
1022
+ """
1023
+ Utility to tell us whether we are in a decorator internal and should stop.
1024
+
1025
+ """
1026
+ # if we are disabled don't skip
1027
+ if not self._predicates["debuggerskip"]:
1028
+ return False
1029
+
1030
+ return self._cachable_skip(frame)
1031
+
1032
+ @lru_cache(1024)
1033
+ def _cached_one_parent_frame_debuggerskip(self, frame):
1034
+ """
1035
+ Cache looking up for DEBUGGERSKIP on parent frame.
1036
+
1037
+ This should speedup walking through deep frame when one of the highest
1038
+ one does have a debugger skip.
1039
+
1040
+ This is likely to introduce fake positive though.
1041
+ """
1042
+ while getattr(frame, "f_back", None):
1043
+ frame = frame.f_back
1044
+ if self._get_frame_locals(frame).get(DEBUGGERSKIP):
1045
+ return True
1046
+ return None
1047
+
1048
+ @lru_cache(1024)
1049
+ def _cachable_skip(self, frame):
1050
+ # if frame is tagged, skip by default.
1051
+ if DEBUGGERSKIP in frame.f_code.co_varnames:
1052
+ return True
1053
+
1054
+ # if one of the parent frame value set to True skip as well.
1055
+ if self._cached_one_parent_frame_debuggerskip(frame):
1056
+ return True
1057
+
1058
+ return False
1059
+
1060
+ def stop_here(self, frame):
1061
+ if self._is_in_decorator_internal_and_should_skip(frame) is True:
1062
+ return False
1063
+
1064
+ hidden = False
1065
+ if self.skip_hidden:
1066
+ hidden = self._hidden_predicate(frame)
1067
+ if hidden:
1068
+ if self.report_skipped:
1069
+ print(
1070
+ self.theme.format(
1071
+ [
1072
+ (
1073
+ Token.ExcName,
1074
+ " [... skipped 1 hidden frame(s)]",
1075
+ ),
1076
+ (Token, "\n"),
1077
+ ]
1078
+ )
1079
+ )
1080
+ return super().stop_here(frame)
1081
+
1082
+ def do_up(self, arg):
1083
+ """u(p) [count]
1084
+ Move the current frame count (default one) levels up in the
1085
+ stack trace (to an older frame).
1086
+
1087
+ Will skip hidden frames.
1088
+ """
1089
+ # modified version of upstream that skips
1090
+ # frames with __tracebackhide__
1091
+ if self.curindex == 0:
1092
+ self.error("Oldest frame")
1093
+ return
1094
+ try:
1095
+ count = int(arg or 1)
1096
+ except ValueError:
1097
+ self.error("Invalid frame count (%s)" % arg)
1098
+ return
1099
+ skipped = 0
1100
+ if count < 0:
1101
+ _newframe = 0
1102
+ else:
1103
+ counter = 0
1104
+ hidden_frames = self.hidden_frames(self.stack)
1105
+ for i in range(self.curindex - 1, -1, -1):
1106
+ if hidden_frames[i] and self.skip_hidden:
1107
+ skipped += 1
1108
+ continue
1109
+ counter += 1
1110
+ if counter >= count:
1111
+ break
1112
+ else:
1113
+ # if no break occurred.
1114
+ self.error(
1115
+ "all frames above hidden, use `skip_hidden False` to get get into those."
1116
+ )
1117
+ return
1118
+
1119
+ _newframe = i
1120
+ self._select_frame(_newframe)
1121
+ if skipped:
1122
+ print(
1123
+ self.theme.format(
1124
+ [
1125
+ (
1126
+ Token.ExcName,
1127
+ f" [... skipped {skipped} hidden frame(s)]",
1128
+ ),
1129
+ (Token, "\n"),
1130
+ ]
1131
+ )
1132
+ )
1133
+
1134
+ def do_down(self, arg):
1135
+ """d(own) [count]
1136
+ Move the current frame count (default one) levels down in the
1137
+ stack trace (to a newer frame).
1138
+
1139
+ Will skip hidden frames.
1140
+ """
1141
+ if self.curindex + 1 == len(self.stack):
1142
+ self.error("Newest frame")
1143
+ return
1144
+ try:
1145
+ count = int(arg or 1)
1146
+ except ValueError:
1147
+ self.error("Invalid frame count (%s)" % arg)
1148
+ return
1149
+ if count < 0:
1150
+ _newframe = len(self.stack) - 1
1151
+ else:
1152
+ counter = 0
1153
+ skipped = 0
1154
+ hidden_frames = self.hidden_frames(self.stack)
1155
+ for i in range(self.curindex + 1, len(self.stack)):
1156
+ if hidden_frames[i] and self.skip_hidden:
1157
+ skipped += 1
1158
+ continue
1159
+ counter += 1
1160
+ if counter >= count:
1161
+ break
1162
+ else:
1163
+ self.error(
1164
+ "all frames below hidden, use `skip_hidden False` to get get into those."
1165
+ )
1166
+ return
1167
+
1168
+ if skipped:
1169
+ print(
1170
+ self.theme.format(
1171
+ [
1172
+ (
1173
+ Token.ExcName,
1174
+ f" [... skipped {skipped} hidden frame(s)]",
1175
+ ),
1176
+ (Token, "\n"),
1177
+ ]
1178
+ )
1179
+ )
1180
+ _newframe = i
1181
+
1182
+ self._select_frame(_newframe)
1183
+
1184
+ do_d = do_down
1185
+ do_u = do_up
1186
+
1187
+ def do_context(self, context: str):
1188
+ """context number_of_lines
1189
+ Set the number of lines of source code to show when displaying
1190
+ stacktrace information.
1191
+ """
1192
+ try:
1193
+ new_context = int(context)
1194
+ if new_context <= 0:
1195
+ raise ValueError()
1196
+ self.context = new_context
1197
+ except ValueError:
1198
+ self.error(
1199
+ f"The 'context' command requires a positive integer argument (current value {self.context})."
1200
+ )
1201
+
1202
+
1203
+ class InterruptiblePdb(Pdb):
1204
+ """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
1205
+
1206
+ def cmdloop(self, intro=None):
1207
+ """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
1208
+ try:
1209
+ return OldPdb.cmdloop(self, intro=intro)
1210
+ except KeyboardInterrupt:
1211
+ self.stop_here = lambda frame: False # type: ignore[method-assign]
1212
+ self.do_quit("")
1213
+ sys.settrace(None)
1214
+ self.quitting = False
1215
+ raise
1216
+
1217
+ def _cmdloop(self):
1218
+ while True:
1219
+ try:
1220
+ # keyboard interrupts allow for an easy way to cancel
1221
+ # the current command, so allow them during interactive input
1222
+ self.allow_kbdint = True
1223
+ self.cmdloop()
1224
+ self.allow_kbdint = False
1225
+ break
1226
+ except KeyboardInterrupt:
1227
+ self.message("--KeyboardInterrupt--")
1228
+ raise
1229
+
1230
+
1231
+ def set_trace(frame=None, header=None):
1232
+ """
1233
+ Start debugging from `frame`.
1234
+
1235
+ If frame is not specified, debugging starts from caller's frame.
1236
+ """
1237
+ pdb = Pdb()
1238
+ if header is not None:
1239
+ pdb.message(header)
1240
+ pdb.set_trace(frame or sys._getframe().f_back)
temp_venv/lib/python3.13/site-packages/IPython/core/display.py ADDED
@@ -0,0 +1,1266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Top-level display functions for displaying object in different formats."""
2
+
3
+ # Copyright (c) IPython Development Team.
4
+ # Distributed under the terms of the Modified BSD License.
5
+
6
+
7
+ from binascii import b2a_base64, hexlify
8
+ import html
9
+ import json
10
+ import mimetypes
11
+ import os
12
+ import struct
13
+ import warnings
14
+ from copy import deepcopy
15
+ from os.path import splitext
16
+ from pathlib import Path, PurePath
17
+
18
+ from typing import Optional
19
+
20
+ from IPython.testing.skipdoctest import skip_doctest
21
+ from . import display_functions
22
+
23
+
24
+ __all__ = [
25
+ "display_pretty",
26
+ "display_html",
27
+ "display_markdown",
28
+ "display_svg",
29
+ "display_png",
30
+ "display_jpeg",
31
+ "display_webp",
32
+ "display_latex",
33
+ "display_json",
34
+ "display_javascript",
35
+ "display_pdf",
36
+ "DisplayObject",
37
+ "TextDisplayObject",
38
+ "Pretty",
39
+ "HTML",
40
+ "Markdown",
41
+ "Math",
42
+ "Latex",
43
+ "SVG",
44
+ "ProgressBar",
45
+ "JSON",
46
+ "GeoJSON",
47
+ "Javascript",
48
+ "Image",
49
+ "Video",
50
+ ]
51
+
52
+ #-----------------------------------------------------------------------------
53
+ # utility functions
54
+ #-----------------------------------------------------------------------------
55
+
56
+ def _safe_exists(path):
57
+ """Check path, but don't let exceptions raise"""
58
+ try:
59
+ return os.path.exists(path)
60
+ except Exception:
61
+ return False
62
+
63
+
64
+ def _display_mimetype(mimetype, objs, raw=False, metadata=None):
65
+ """internal implementation of all display_foo methods
66
+
67
+ Parameters
68
+ ----------
69
+ mimetype : str
70
+ The mimetype to be published (e.g. 'image/png')
71
+ *objs : object
72
+ The Python objects to display, or if raw=True raw text data to
73
+ display.
74
+ raw : bool
75
+ Are the data objects raw data or Python objects that need to be
76
+ formatted before display? [default: False]
77
+ metadata : dict (optional)
78
+ Metadata to be associated with the specific mimetype output.
79
+ """
80
+ if metadata:
81
+ metadata = {mimetype: metadata}
82
+ if raw:
83
+ # turn list of pngdata into list of { 'image/png': pngdata }
84
+ objs = [ {mimetype: obj} for obj in objs ]
85
+ display_functions.display(*objs, raw=raw, metadata=metadata, include=[mimetype])
86
+
87
+ #-----------------------------------------------------------------------------
88
+ # Main functions
89
+ #-----------------------------------------------------------------------------
90
+
91
+
92
+ def display_pretty(*objs, **kwargs):
93
+ """Display the pretty (default) representation of an object.
94
+
95
+ Parameters
96
+ ----------
97
+ *objs : object
98
+ The Python objects to display, or if raw=True raw text data to
99
+ display.
100
+ raw : bool
101
+ Are the data objects raw data or Python objects that need to be
102
+ formatted before display? [default: False]
103
+ metadata : dict (optional)
104
+ Metadata to be associated with the specific mimetype output.
105
+ """
106
+ _display_mimetype('text/plain', objs, **kwargs)
107
+
108
+
109
+ def display_html(*objs, **kwargs):
110
+ """Display the HTML representation of an object.
111
+
112
+ Note: If raw=False and the object does not have a HTML
113
+ representation, no HTML will be shown.
114
+
115
+ Parameters
116
+ ----------
117
+ *objs : object
118
+ The Python objects to display, or if raw=True raw HTML data to
119
+ display.
120
+ raw : bool
121
+ Are the data objects raw data or Python objects that need to be
122
+ formatted before display? [default: False]
123
+ metadata : dict (optional)
124
+ Metadata to be associated with the specific mimetype output.
125
+ """
126
+ _display_mimetype('text/html', objs, **kwargs)
127
+
128
+
129
+ def display_markdown(*objs, **kwargs):
130
+ """Displays the Markdown representation of an object.
131
+
132
+ Parameters
133
+ ----------
134
+ *objs : object
135
+ The Python objects to display, or if raw=True raw markdown data to
136
+ display.
137
+ raw : bool
138
+ Are the data objects raw data or Python objects that need to be
139
+ formatted before display? [default: False]
140
+ metadata : dict (optional)
141
+ Metadata to be associated with the specific mimetype output.
142
+ """
143
+
144
+ _display_mimetype('text/markdown', objs, **kwargs)
145
+
146
+
147
+ def display_svg(*objs, **kwargs):
148
+ """Display the SVG representation of an object.
149
+
150
+ Parameters
151
+ ----------
152
+ *objs : object
153
+ The Python objects to display, or if raw=True raw svg data to
154
+ display.
155
+ raw : bool
156
+ Are the data objects raw data or Python objects that need to be
157
+ formatted before display? [default: False]
158
+ metadata : dict (optional)
159
+ Metadata to be associated with the specific mimetype output.
160
+ """
161
+ _display_mimetype('image/svg+xml', objs, **kwargs)
162
+
163
+
164
+ def display_png(*objs, **kwargs):
165
+ """Display the PNG representation of an object.
166
+
167
+ Parameters
168
+ ----------
169
+ *objs : object
170
+ The Python objects to display, or if raw=True raw png data to
171
+ display.
172
+ raw : bool
173
+ Are the data objects raw data or Python objects that need to be
174
+ formatted before display? [default: False]
175
+ metadata : dict (optional)
176
+ Metadata to be associated with the specific mimetype output.
177
+ """
178
+ _display_mimetype('image/png', objs, **kwargs)
179
+
180
+
181
+ def display_jpeg(*objs, **kwargs):
182
+ """Display the JPEG representation of an object.
183
+
184
+ Parameters
185
+ ----------
186
+ *objs : object
187
+ The Python objects to display, or if raw=True raw JPEG data to
188
+ display.
189
+ raw : bool
190
+ Are the data objects raw data or Python objects that need to be
191
+ formatted before display? [default: False]
192
+ metadata : dict (optional)
193
+ Metadata to be associated with the specific mimetype output.
194
+ """
195
+ _display_mimetype('image/jpeg', objs, **kwargs)
196
+
197
+
198
+ def display_webp(*objs, **kwargs):
199
+ """Display the WEBP representation of an object.
200
+
201
+ Parameters
202
+ ----------
203
+ *objs : object
204
+ The Python objects to display, or if raw=True raw JPEG data to
205
+ display.
206
+ raw : bool
207
+ Are the data objects raw data or Python objects that need to be
208
+ formatted before display? [default: False]
209
+ metadata : dict (optional)
210
+ Metadata to be associated with the specific mimetype output.
211
+ """
212
+ _display_mimetype("image/webp", objs, **kwargs)
213
+
214
+
215
+ def display_latex(*objs, **kwargs):
216
+ """Display the LaTeX representation of an object.
217
+
218
+ Parameters
219
+ ----------
220
+ *objs : object
221
+ The Python objects to display, or if raw=True raw latex data to
222
+ display.
223
+ raw : bool
224
+ Are the data objects raw data or Python objects that need to be
225
+ formatted before display? [default: False]
226
+ metadata : dict (optional)
227
+ Metadata to be associated with the specific mimetype output.
228
+ """
229
+ _display_mimetype('text/latex', objs, **kwargs)
230
+
231
+
232
+ def display_json(*objs, **kwargs):
233
+ """Display the JSON representation of an object.
234
+
235
+ Note that not many frontends support displaying JSON.
236
+
237
+ Parameters
238
+ ----------
239
+ *objs : object
240
+ The Python objects to display, or if raw=True raw json data to
241
+ display.
242
+ raw : bool
243
+ Are the data objects raw data or Python objects that need to be
244
+ formatted before display? [default: False]
245
+ metadata : dict (optional)
246
+ Metadata to be associated with the specific mimetype output.
247
+ """
248
+ _display_mimetype('application/json', objs, **kwargs)
249
+
250
+
251
+ def display_javascript(*objs, **kwargs):
252
+ """Display the Javascript representation of an object.
253
+
254
+ Parameters
255
+ ----------
256
+ *objs : object
257
+ The Python objects to display, or if raw=True raw javascript data to
258
+ display.
259
+ raw : bool
260
+ Are the data objects raw data or Python objects that need to be
261
+ formatted before display? [default: False]
262
+ metadata : dict (optional)
263
+ Metadata to be associated with the specific mimetype output.
264
+ """
265
+ _display_mimetype('application/javascript', objs, **kwargs)
266
+
267
+
268
+ def display_pdf(*objs, **kwargs):
269
+ """Display the PDF representation of an object.
270
+
271
+ Parameters
272
+ ----------
273
+ *objs : object
274
+ The Python objects to display, or if raw=True raw javascript data to
275
+ display.
276
+ raw : bool
277
+ Are the data objects raw data or Python objects that need to be
278
+ formatted before display? [default: False]
279
+ metadata : dict (optional)
280
+ Metadata to be associated with the specific mimetype output.
281
+ """
282
+ _display_mimetype('application/pdf', objs, **kwargs)
283
+
284
+
285
+ #-----------------------------------------------------------------------------
286
+ # Smart classes
287
+ #-----------------------------------------------------------------------------
288
+
289
+
290
+ class DisplayObject:
291
+ """An object that wraps data to be displayed."""
292
+
293
+ _read_flags = 'r'
294
+ _show_mem_addr = False
295
+ metadata = None
296
+
297
+ def __init__(self, data=None, url=None, filename=None, metadata=None):
298
+ """Create a display object given raw data.
299
+
300
+ When this object is returned by an expression or passed to the
301
+ display function, it will result in the data being displayed
302
+ in the frontend. The MIME type of the data should match the
303
+ subclasses used, so the Png subclass should be used for 'image/png'
304
+ data. If the data is a URL, the data will first be downloaded
305
+ and then displayed.
306
+
307
+ Parameters
308
+ ----------
309
+ data : unicode, str or bytes
310
+ The raw data or a URL or file to load the data from
311
+ url : unicode
312
+ A URL to download the data from.
313
+ filename : unicode
314
+ Path to a local file to load the data from.
315
+ metadata : dict
316
+ Dict of metadata associated to be the object when displayed
317
+ """
318
+ if isinstance(data, (Path, PurePath)):
319
+ data = str(data)
320
+
321
+ if data is not None and isinstance(data, str):
322
+ if data.startswith('http') and url is None:
323
+ url = data
324
+ filename = None
325
+ data = None
326
+ elif _safe_exists(data) and filename is None:
327
+ url = None
328
+ filename = data
329
+ data = None
330
+
331
+ self.url = url
332
+ self.filename = filename
333
+ # because of @data.setter methods in
334
+ # subclasses ensure url and filename are set
335
+ # before assigning to self.data
336
+ self.data = data
337
+
338
+ if metadata is not None:
339
+ self.metadata = metadata
340
+ elif self.metadata is None:
341
+ self.metadata = {}
342
+
343
+ self.reload()
344
+ self._check_data()
345
+
346
+ def __repr__(self):
347
+ if not self._show_mem_addr:
348
+ cls = self.__class__
349
+ r = "<%s.%s object>" % (cls.__module__, cls.__name__)
350
+ else:
351
+ r = super(DisplayObject, self).__repr__()
352
+ return r
353
+
354
+ def _check_data(self):
355
+ """Override in subclasses if there's something to check."""
356
+ pass
357
+
358
+ def _data_and_metadata(self):
359
+ """shortcut for returning metadata with shape information, if defined"""
360
+ if self.metadata:
361
+ return self.data, deepcopy(self.metadata)
362
+ else:
363
+ return self.data
364
+
365
+ def reload(self):
366
+ """Reload the raw data from file or URL."""
367
+ if self.filename is not None:
368
+ encoding = None if "b" in self._read_flags else "utf-8"
369
+ with open(self.filename, self._read_flags, encoding=encoding) as f:
370
+ self.data = f.read()
371
+ elif self.url is not None:
372
+ # Deferred import
373
+ from urllib.request import urlopen
374
+ response = urlopen(self.url)
375
+ data = response.read()
376
+ # extract encoding from header, if there is one:
377
+ encoding = None
378
+ if 'content-type' in response.headers:
379
+ for sub in response.headers['content-type'].split(';'):
380
+ sub = sub.strip()
381
+ if sub.startswith('charset'):
382
+ encoding = sub.split('=')[-1].strip()
383
+ break
384
+ if 'content-encoding' in response.headers:
385
+ # TODO: do deflate?
386
+ if 'gzip' in response.headers['content-encoding']:
387
+ import gzip
388
+ from io import BytesIO
389
+
390
+ # assume utf-8 if encoding is not specified
391
+ with gzip.open(
392
+ BytesIO(data), "rt", encoding=encoding or "utf-8"
393
+ ) as fp:
394
+ encoding = None
395
+ data = fp.read()
396
+
397
+ # decode data, if an encoding was specified
398
+ # We only touch self.data once since
399
+ # subclasses such as SVG have @data.setter methods
400
+ # that transform self.data into ... well svg.
401
+ if encoding:
402
+ self.data = data.decode(encoding, 'replace')
403
+ else:
404
+ self.data = data
405
+
406
+
407
+ class TextDisplayObject(DisplayObject):
408
+ """Create a text display object given raw data.
409
+
410
+ Parameters
411
+ ----------
412
+ data : str or unicode
413
+ The raw data or a URL or file to load the data from.
414
+ url : unicode
415
+ A URL to download the data from.
416
+ filename : unicode
417
+ Path to a local file to load the data from.
418
+ metadata : dict
419
+ Dict of metadata associated to be the object when displayed
420
+ """
421
+ def _check_data(self):
422
+ if self.data is not None and not isinstance(self.data, str):
423
+ raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
424
+
425
+ class Pretty(TextDisplayObject):
426
+
427
+ def _repr_pretty_(self, pp, cycle):
428
+ return pp.text(self.data)
429
+
430
+
431
+ class HTML(TextDisplayObject):
432
+
433
+ def __init__(self, data=None, url=None, filename=None, metadata=None):
434
+ def warn():
435
+ if not data:
436
+ return False
437
+
438
+ #
439
+ # Avoid calling lower() on the entire data, because it could be a
440
+ # long string and we're only interested in its beginning and end.
441
+ #
442
+ prefix = data[:10].lower()
443
+ suffix = data[-10:].lower()
444
+ return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
445
+
446
+ if warn():
447
+ warnings.warn("Consider using IPython.display.IFrame instead")
448
+ super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
449
+
450
+ def _repr_html_(self):
451
+ return self._data_and_metadata()
452
+
453
+ def __html__(self):
454
+ """
455
+ This method exists to inform other HTML-using modules (e.g. Markupsafe,
456
+ htmltag, etc) that this object is HTML and does not need things like
457
+ special characters (<>&) escaped.
458
+ """
459
+ return self._repr_html_()
460
+
461
+
462
+ class Markdown(TextDisplayObject):
463
+
464
+ def _repr_markdown_(self):
465
+ return self._data_and_metadata()
466
+
467
+
468
+ class Math(TextDisplayObject):
469
+
470
+ def _repr_latex_(self):
471
+ s = r"$\displaystyle %s$" % self.data.strip('$')
472
+ if self.metadata:
473
+ return s, deepcopy(self.metadata)
474
+ else:
475
+ return s
476
+
477
+
478
+ class Latex(TextDisplayObject):
479
+
480
+ def _repr_latex_(self):
481
+ return self._data_and_metadata()
482
+
483
+
484
+ class SVG(DisplayObject):
485
+ """Embed an SVG into the display.
486
+
487
+ Note if you just want to view a svg image via a URL use `:class:Image` with
488
+ a url=URL keyword argument.
489
+ """
490
+
491
+ _read_flags = 'rb'
492
+ # wrap data in a property, which extracts the <svg> tag, discarding
493
+ # document headers
494
+ _data: Optional[str] = None
495
+
496
+ @property
497
+ def data(self):
498
+ return self._data
499
+
500
+ @data.setter
501
+ def data(self, svg):
502
+ if svg is None:
503
+ self._data = None
504
+ return
505
+ # parse into dom object
506
+ from xml.dom import minidom
507
+ x = minidom.parseString(svg)
508
+ # get svg tag (should be 1)
509
+ found_svg = x.getElementsByTagName('svg')
510
+ if found_svg:
511
+ svg = found_svg[0].toxml()
512
+ else:
513
+ # fallback on the input, trust the user
514
+ # but this is probably an error.
515
+ pass
516
+ if isinstance(svg, bytes):
517
+ self._data = svg.decode(errors="replace")
518
+ else:
519
+ self._data = svg
520
+
521
+ def _repr_svg_(self):
522
+ return self._data_and_metadata()
523
+
524
+ class ProgressBar(DisplayObject):
525
+ """Progressbar supports displaying a progressbar like element
526
+ """
527
+ def __init__(self, total):
528
+ """Creates a new progressbar
529
+
530
+ Parameters
531
+ ----------
532
+ total : int
533
+ maximum size of the progressbar
534
+ """
535
+ self.total = total
536
+ self._progress = 0
537
+ self.html_width = '60ex'
538
+ self.text_width = 60
539
+ self._display_id = hexlify(os.urandom(8)).decode('ascii')
540
+
541
+ def __repr__(self):
542
+ fraction = self.progress / self.total
543
+ filled = '=' * int(fraction * self.text_width)
544
+ rest = ' ' * (self.text_width - len(filled))
545
+ return '[{}{}] {}/{}'.format(
546
+ filled, rest,
547
+ self.progress, self.total,
548
+ )
549
+
550
+ def _repr_html_(self):
551
+ return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
552
+ self.html_width, self.total, self.progress)
553
+
554
+ def display(self):
555
+ display_functions.display(self, display_id=self._display_id)
556
+
557
+ def update(self):
558
+ display_functions.display(self, display_id=self._display_id, update=True)
559
+
560
+ @property
561
+ def progress(self):
562
+ return self._progress
563
+
564
+ @progress.setter
565
+ def progress(self, value):
566
+ self._progress = value
567
+ self.update()
568
+
569
+ def __iter__(self):
570
+ self.display()
571
+ self._progress = -1 # First iteration is 0
572
+ return self
573
+
574
+ def __next__(self):
575
+ """Returns current value and increments display by one."""
576
+ self.progress += 1
577
+ if self.progress < self.total:
578
+ return self.progress
579
+ else:
580
+ raise StopIteration()
581
+
582
+ class JSON(DisplayObject):
583
+ """JSON expects a JSON-able dict or list
584
+
585
+ not an already-serialized JSON string.
586
+
587
+ Scalar types (None, number, string) are not allowed, only dict or list containers.
588
+ """
589
+ # wrap data in a property, which warns about passing already-serialized JSON
590
+ _data = None
591
+ def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs):
592
+ """Create a JSON display object given raw data.
593
+
594
+ Parameters
595
+ ----------
596
+ data : dict or list
597
+ JSON data to display. Not an already-serialized JSON string.
598
+ Scalar types (None, number, string) are not allowed, only dict
599
+ or list containers.
600
+ url : unicode
601
+ A URL to download the data from.
602
+ filename : unicode
603
+ Path to a local file to load the data from.
604
+ expanded : boolean
605
+ Metadata to control whether a JSON display component is expanded.
606
+ metadata : dict
607
+ Specify extra metadata to attach to the json display object.
608
+ root : str
609
+ The name of the root element of the JSON tree
610
+ """
611
+ self.metadata = {
612
+ 'expanded': expanded,
613
+ 'root': root,
614
+ }
615
+ if metadata:
616
+ self.metadata.update(metadata)
617
+ if kwargs:
618
+ self.metadata.update(kwargs)
619
+ super(JSON, self).__init__(data=data, url=url, filename=filename)
620
+
621
+ def _check_data(self):
622
+ if self.data is not None and not isinstance(self.data, (dict, list)):
623
+ raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
624
+
625
+ @property
626
+ def data(self):
627
+ return self._data
628
+
629
+ @data.setter
630
+ def data(self, data):
631
+ if isinstance(data, (Path, PurePath)):
632
+ data = str(data)
633
+
634
+ if isinstance(data, str):
635
+ if self.filename is None and self.url is None:
636
+ warnings.warn("JSON expects JSONable dict or list, not JSON strings")
637
+ data = json.loads(data)
638
+ self._data = data
639
+
640
+ def _data_and_metadata(self):
641
+ return self.data, self.metadata
642
+
643
+ def _repr_json_(self):
644
+ return self._data_and_metadata()
645
+
646
+
647
+ _css_t = """var link = document.createElement("link");
648
+ link.rel = "stylesheet";
649
+ link.type = "text/css";
650
+ link.href = "%s";
651
+ document.head.appendChild(link);
652
+ """
653
+
654
+ _lib_t1 = """new Promise(function(resolve, reject) {
655
+ var script = document.createElement("script");
656
+ script.onload = resolve;
657
+ script.onerror = reject;
658
+ script.src = "%s";
659
+ document.head.appendChild(script);
660
+ }).then(() => {
661
+ """
662
+
663
+ _lib_t2 = """
664
+ });"""
665
+
666
+ class GeoJSON(JSON):
667
+ """GeoJSON expects JSON-able dict
668
+
669
+ not an already-serialized JSON string.
670
+
671
+ Scalar types (None, number, string) are not allowed, only dict containers.
672
+ """
673
+
674
+ def __init__(self, *args, **kwargs):
675
+ """Create a GeoJSON display object given raw data.
676
+
677
+ Parameters
678
+ ----------
679
+ data : dict or list
680
+ VegaLite data. Not an already-serialized JSON string.
681
+ Scalar types (None, number, string) are not allowed, only dict
682
+ or list containers.
683
+ url_template : string
684
+ Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
685
+ layer_options : dict
686
+ Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
687
+ url : unicode
688
+ A URL to download the data from.
689
+ filename : unicode
690
+ Path to a local file to load the data from.
691
+ metadata : dict
692
+ Specify extra metadata to attach to the json display object.
693
+
694
+ Examples
695
+ --------
696
+ The following will display an interactive map of Mars with a point of
697
+ interest on frontend that do support GeoJSON display.
698
+
699
+ >>> from IPython.display import GeoJSON
700
+
701
+ >>> GeoJSON(data={
702
+ ... "type": "Feature",
703
+ ... "geometry": {
704
+ ... "type": "Point",
705
+ ... "coordinates": [-81.327, 296.038]
706
+ ... }
707
+ ... },
708
+ ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
709
+ ... layer_options={
710
+ ... "basemap_id": "celestia_mars-shaded-16k_global",
711
+ ... "attribution" : "Celestia/praesepe",
712
+ ... "minZoom" : 0,
713
+ ... "maxZoom" : 18,
714
+ ... })
715
+ <IPython.core.display.GeoJSON object>
716
+
717
+ In the terminal IPython, you will only see the text representation of
718
+ the GeoJSON object.
719
+
720
+ """
721
+
722
+ super(GeoJSON, self).__init__(*args, **kwargs)
723
+
724
+
725
+ def _ipython_display_(self):
726
+ bundle = {
727
+ 'application/geo+json': self.data,
728
+ 'text/plain': '<IPython.display.GeoJSON object>'
729
+ }
730
+ metadata = {
731
+ 'application/geo+json': self.metadata
732
+ }
733
+ display_functions.display(bundle, metadata=metadata, raw=True)
734
+
735
+ class Javascript(TextDisplayObject):
736
+
737
+ def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
738
+ """Create a Javascript display object given raw data.
739
+
740
+ When this object is returned by an expression or passed to the
741
+ display function, it will result in the data being displayed
742
+ in the frontend. If the data is a URL, the data will first be
743
+ downloaded and then displayed.
744
+
745
+ In the Notebook, the containing element will be available as `element`,
746
+ and jQuery will be available. Content appended to `element` will be
747
+ visible in the output area.
748
+
749
+ Parameters
750
+ ----------
751
+ data : unicode, str or bytes
752
+ The Javascript source code or a URL to download it from.
753
+ url : unicode
754
+ A URL to download the data from.
755
+ filename : unicode
756
+ Path to a local file to load the data from.
757
+ lib : list or str
758
+ A sequence of Javascript library URLs to load asynchronously before
759
+ running the source code. The full URLs of the libraries should
760
+ be given. A single Javascript library URL can also be given as a
761
+ string.
762
+ css : list or str
763
+ A sequence of css files to load before running the source code.
764
+ The full URLs of the css files should be given. A single css URL
765
+ can also be given as a string.
766
+ """
767
+ if isinstance(lib, str):
768
+ lib = [lib]
769
+ elif lib is None:
770
+ lib = []
771
+ if isinstance(css, str):
772
+ css = [css]
773
+ elif css is None:
774
+ css = []
775
+ if not isinstance(lib, (list,tuple)):
776
+ raise TypeError('expected sequence, got: %r' % lib)
777
+ if not isinstance(css, (list,tuple)):
778
+ raise TypeError('expected sequence, got: %r' % css)
779
+ self.lib = lib
780
+ self.css = css
781
+ super(Javascript, self).__init__(data=data, url=url, filename=filename)
782
+
783
+ def _repr_javascript_(self):
784
+ r = ''
785
+ for c in self.css:
786
+ r += _css_t % c
787
+ for l in self.lib:
788
+ r += _lib_t1 % l
789
+ r += self.data
790
+ r += _lib_t2*len(self.lib)
791
+ return r
792
+
793
+
794
+ # constants for identifying png/jpeg/gif/webp data
795
+ _PNG = b"\x89PNG\r\n\x1a\n"
796
+ _JPEG = b"\xff\xd8"
797
+ _GIF1 = b"GIF87a"
798
+ _GIF2 = b"GIF89a"
799
+ _WEBP = b"WEBP"
800
+
801
+
802
+ def _pngxy(data):
803
+ """read the (width, height) from a PNG header"""
804
+ ihdr = data.index(b'IHDR')
805
+ # next 8 bytes are width/height
806
+ return struct.unpack('>ii', data[ihdr+4:ihdr+12])
807
+
808
+
809
+ def _jpegxy(data):
810
+ """read the (width, height) from a JPEG header"""
811
+ # adapted from http://www.64lines.com/jpeg-width-height
812
+
813
+ idx = 4
814
+ while True:
815
+ block_size = struct.unpack('>H', data[idx:idx+2])[0]
816
+ idx = idx + block_size
817
+ if data[idx:idx+2] == b'\xFF\xC0':
818
+ # found Start of Frame
819
+ iSOF = idx
820
+ break
821
+ else:
822
+ # read another block
823
+ idx += 2
824
+
825
+ h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
826
+ return w, h
827
+
828
+
829
+ def _gifxy(data):
830
+ """read the (width, height) from a GIF header"""
831
+ return struct.unpack('<HH', data[6:10])
832
+
833
+
834
+ def _webpxy(data):
835
+ """read the (width, height) from a WEBP header"""
836
+ if data[12:16] == b"VP8 ":
837
+ width, height = struct.unpack("<HH", data[24:30])
838
+ width = width & 0x3FFF
839
+ height = height & 0x3FFF
840
+ return (width, height)
841
+ elif data[12:16] == b"VP8L":
842
+ size_info = struct.unpack("<I", data[21:25])[0]
843
+ width = 1 + ((size_info & 0x3F) << 8) | (size_info >> 24)
844
+ height = 1 + (
845
+ (((size_info >> 8) & 0xF) << 10)
846
+ | (((size_info >> 14) & 0x3FC) << 2)
847
+ | ((size_info >> 22) & 0x3)
848
+ )
849
+ return (width, height)
850
+ else:
851
+ raise ValueError("Not a valid WEBP header")
852
+
853
+
854
+ class Image(DisplayObject):
855
+
856
+ _read_flags = "rb"
857
+ _FMT_JPEG = "jpeg"
858
+ _FMT_PNG = "png"
859
+ _FMT_GIF = "gif"
860
+ _FMT_WEBP = "webp"
861
+ _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF, _FMT_WEBP]
862
+ _MIMETYPES = {
863
+ _FMT_PNG: "image/png",
864
+ _FMT_JPEG: "image/jpeg",
865
+ _FMT_GIF: "image/gif",
866
+ _FMT_WEBP: "image/webp",
867
+ }
868
+
869
+ def __init__(
870
+ self,
871
+ data=None,
872
+ url=None,
873
+ filename=None,
874
+ format=None,
875
+ embed=None,
876
+ width=None,
877
+ height=None,
878
+ retina=False,
879
+ unconfined=False,
880
+ metadata=None,
881
+ alt=None,
882
+ ):
883
+ """Create a PNG/JPEG/GIF/WEBP image object given raw data.
884
+
885
+ When this object is returned by an input cell or passed to the
886
+ display function, it will result in the image being displayed
887
+ in the frontend.
888
+
889
+ Parameters
890
+ ----------
891
+ data : unicode, str or bytes
892
+ The raw image data or a URL or filename to load the data from.
893
+ This always results in embedded image data.
894
+
895
+ url : unicode
896
+ A URL to download the data from. If you specify `url=`,
897
+ the image data will not be embedded unless you also specify `embed=True`.
898
+
899
+ filename : unicode
900
+ Path to a local file to load the data from.
901
+ Images from a file are always embedded.
902
+
903
+ format : unicode
904
+ The format of the image data (png/jpeg/jpg/gif/webp). If a filename or URL is given
905
+ for format will be inferred from the filename extension.
906
+
907
+ embed : bool
908
+ Should the image data be embedded using a data URI (True) or be
909
+ loaded using an <img> tag. Set this to True if you want the image
910
+ to be viewable later with no internet connection in the notebook.
911
+
912
+ Default is `True`, unless the keyword argument `url` is set, then
913
+ default value is `False`.
914
+
915
+ Note that QtConsole is not able to display images if `embed` is set to `False`
916
+
917
+ width : int
918
+ Width in pixels to which to constrain the image in html
919
+
920
+ height : int
921
+ Height in pixels to which to constrain the image in html
922
+
923
+ retina : bool
924
+ Automatically set the width and height to half of the measured
925
+ width and height.
926
+ This only works for embedded images because it reads the width/height
927
+ from image data.
928
+ For non-embedded images, you can just set the desired display width
929
+ and height directly.
930
+
931
+ unconfined : bool
932
+ Set unconfined=True to disable max-width confinement of the image.
933
+
934
+ metadata : dict
935
+ Specify extra metadata to attach to the image.
936
+
937
+ alt : unicode
938
+ Alternative text for the image, for use by screen readers.
939
+
940
+ Examples
941
+ --------
942
+ embedded image data, works in qtconsole and notebook
943
+ when passed positionally, the first arg can be any of raw image data,
944
+ a URL, or a filename from which to load image data.
945
+ The result is always embedding image data for inline images.
946
+
947
+ >>> Image('https://www.google.fr/images/srpr/logo3w.png') # doctest: +SKIP
948
+ <IPython.core.display.Image object>
949
+
950
+ >>> Image('/path/to/image.jpg')
951
+ <IPython.core.display.Image object>
952
+
953
+ >>> Image(b'RAW_PNG_DATA...')
954
+ <IPython.core.display.Image object>
955
+
956
+ Specifying Image(url=...) does not embed the image data,
957
+ it only generates ``<img>`` tag with a link to the source.
958
+ This will not work in the qtconsole or offline.
959
+
960
+ >>> Image(url='https://www.google.fr/images/srpr/logo3w.png')
961
+ <IPython.core.display.Image object>
962
+
963
+ """
964
+ if isinstance(data, (Path, PurePath)):
965
+ data = str(data)
966
+
967
+ if filename is not None:
968
+ ext = self._find_ext(filename)
969
+ elif url is not None:
970
+ ext = self._find_ext(url)
971
+ elif data is None:
972
+ raise ValueError("No image data found. Expecting filename, url, or data.")
973
+ elif isinstance(data, str) and (
974
+ data.startswith('http') or _safe_exists(data)
975
+ ):
976
+ ext = self._find_ext(data)
977
+ else:
978
+ ext = None
979
+
980
+ if format is None:
981
+ if ext is not None:
982
+ if ext == u'jpg' or ext == u'jpeg':
983
+ format = self._FMT_JPEG
984
+ elif ext == u'png':
985
+ format = self._FMT_PNG
986
+ elif ext == u'gif':
987
+ format = self._FMT_GIF
988
+ elif ext == "webp":
989
+ format = self._FMT_WEBP
990
+ else:
991
+ format = ext.lower()
992
+ elif isinstance(data, bytes):
993
+ # infer image type from image data header,
994
+ # only if format has not been specified.
995
+ if data[:2] == _JPEG:
996
+ format = self._FMT_JPEG
997
+ elif data[:8] == _PNG:
998
+ format = self._FMT_PNG
999
+ elif data[8:12] == _WEBP:
1000
+ format = self._FMT_WEBP
1001
+ elif data[:6] == _GIF1 or data[:6] == _GIF2:
1002
+ format = self._FMT_GIF
1003
+
1004
+ # failed to detect format, default png
1005
+ if format is None:
1006
+ format = self._FMT_PNG
1007
+
1008
+ if format.lower() == 'jpg':
1009
+ # jpg->jpeg
1010
+ format = self._FMT_JPEG
1011
+
1012
+ self.format = format.lower()
1013
+ self.embed = embed if embed is not None else (url is None)
1014
+
1015
+ if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
1016
+ raise ValueError("Cannot embed the '%s' image format" % (self.format))
1017
+ if self.embed:
1018
+ self._mimetype = self._MIMETYPES.get(self.format)
1019
+
1020
+ self.width = width
1021
+ self.height = height
1022
+ self.retina = retina
1023
+ self.unconfined = unconfined
1024
+ self.alt = alt
1025
+ super(Image, self).__init__(data=data, url=url, filename=filename,
1026
+ metadata=metadata)
1027
+
1028
+ if self.width is None and self.metadata.get('width', {}):
1029
+ self.width = metadata['width']
1030
+
1031
+ if self.height is None and self.metadata.get('height', {}):
1032
+ self.height = metadata['height']
1033
+
1034
+ if self.alt is None and self.metadata.get("alt", {}):
1035
+ self.alt = metadata["alt"]
1036
+
1037
+ if retina:
1038
+ self._retina_shape()
1039
+
1040
+
1041
+ def _retina_shape(self):
1042
+ """load pixel-doubled width and height from image data"""
1043
+ if not self.embed:
1044
+ return
1045
+ if self.format == self._FMT_PNG:
1046
+ w, h = _pngxy(self.data)
1047
+ elif self.format == self._FMT_JPEG:
1048
+ w, h = _jpegxy(self.data)
1049
+ elif self.format == self._FMT_GIF:
1050
+ w, h = _gifxy(self.data)
1051
+ else:
1052
+ # retina only supports png
1053
+ return
1054
+ self.width = w // 2
1055
+ self.height = h // 2
1056
+
1057
+ def reload(self):
1058
+ """Reload the raw data from file or URL."""
1059
+ if self.embed:
1060
+ super(Image,self).reload()
1061
+ if self.retina:
1062
+ self._retina_shape()
1063
+
1064
+ def _repr_html_(self):
1065
+ if not self.embed:
1066
+ width = height = klass = alt = ""
1067
+ if self.width:
1068
+ width = ' width="%d"' % self.width
1069
+ if self.height:
1070
+ height = ' height="%d"' % self.height
1071
+ if self.unconfined:
1072
+ klass = ' class="unconfined"'
1073
+ if self.alt:
1074
+ alt = ' alt="%s"' % html.escape(self.alt)
1075
+ return '<img src="{url}"{width}{height}{klass}{alt}/>'.format(
1076
+ url=self.url,
1077
+ width=width,
1078
+ height=height,
1079
+ klass=klass,
1080
+ alt=alt,
1081
+ )
1082
+
1083
+ def _repr_mimebundle_(self, include=None, exclude=None):
1084
+ """Return the image as a mimebundle
1085
+
1086
+ Any new mimetype support should be implemented here.
1087
+ """
1088
+ if self.embed:
1089
+ mimetype = self._mimetype
1090
+ data, metadata = self._data_and_metadata(always_both=True)
1091
+ if metadata:
1092
+ metadata = {mimetype: metadata}
1093
+ return {mimetype: data}, metadata
1094
+ else:
1095
+ return {'text/html': self._repr_html_()}
1096
+
1097
+ def _data_and_metadata(self, always_both=False):
1098
+ """shortcut for returning metadata with shape information, if defined"""
1099
+ try:
1100
+ b64_data = b2a_base64(self.data, newline=False).decode("ascii")
1101
+ except TypeError as e:
1102
+ raise FileNotFoundError(
1103
+ "No such file or directory: '%s'" % (self.data)) from e
1104
+ md = {}
1105
+ if self.metadata:
1106
+ md.update(self.metadata)
1107
+ if self.width:
1108
+ md['width'] = self.width
1109
+ if self.height:
1110
+ md['height'] = self.height
1111
+ if self.unconfined:
1112
+ md['unconfined'] = self.unconfined
1113
+ if self.alt:
1114
+ md["alt"] = self.alt
1115
+ if md or always_both:
1116
+ return b64_data, md
1117
+ else:
1118
+ return b64_data
1119
+
1120
+ def _repr_png_(self):
1121
+ if self.embed and self.format == self._FMT_PNG:
1122
+ return self._data_and_metadata()
1123
+
1124
+ def _repr_jpeg_(self):
1125
+ if self.embed and self.format == self._FMT_JPEG:
1126
+ return self._data_and_metadata()
1127
+
1128
+ def _find_ext(self, s):
1129
+ base, ext = splitext(s)
1130
+
1131
+ if not ext:
1132
+ return base
1133
+
1134
+ # `splitext` includes leading period, so we skip it
1135
+ return ext[1:].lower()
1136
+
1137
+
1138
+ class Video(DisplayObject):
1139
+
1140
+ def __init__(self, data=None, url=None, filename=None, embed=False,
1141
+ mimetype=None, width=None, height=None, html_attributes="controls"):
1142
+ """Create a video object given raw data or an URL.
1143
+
1144
+ When this object is returned by an input cell or passed to the
1145
+ display function, it will result in the video being displayed
1146
+ in the frontend.
1147
+
1148
+ Parameters
1149
+ ----------
1150
+ data : unicode, str or bytes
1151
+ The raw video data or a URL or filename to load the data from.
1152
+ Raw data will require passing ``embed=True``.
1153
+
1154
+ url : unicode
1155
+ A URL for the video. If you specify ``url=``,
1156
+ the image data will not be embedded.
1157
+
1158
+ filename : unicode
1159
+ Path to a local file containing the video.
1160
+ Will be interpreted as a local URL unless ``embed=True``.
1161
+
1162
+ embed : bool
1163
+ Should the video be embedded using a data URI (True) or be
1164
+ loaded using a <video> tag (False).
1165
+
1166
+ Since videos are large, embedding them should be avoided, if possible.
1167
+ You must confirm embedding as your intention by passing ``embed=True``.
1168
+
1169
+ Local files can be displayed with URLs without embedding the content, via::
1170
+
1171
+ Video('./video.mp4')
1172
+
1173
+ mimetype : unicode
1174
+ Specify the mimetype for embedded videos.
1175
+ Default will be guessed from file extension, if available.
1176
+
1177
+ width : int
1178
+ Width in pixels to which to constrain the video in HTML.
1179
+ If not supplied, defaults to the width of the video.
1180
+
1181
+ height : int
1182
+ Height in pixels to which to constrain the video in html.
1183
+ If not supplied, defaults to the height of the video.
1184
+
1185
+ html_attributes : str
1186
+ Attributes for the HTML ``<video>`` block.
1187
+ Default: ``"controls"`` to get video controls.
1188
+ Other examples: ``"controls muted"`` for muted video with controls,
1189
+ ``"loop autoplay"`` for looping autoplaying video without controls.
1190
+
1191
+ Examples
1192
+ --------
1193
+ ::
1194
+
1195
+ Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1196
+ Video('path/to/video.mp4')
1197
+ Video('path/to/video.mp4', embed=True)
1198
+ Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1199
+ Video(b'raw-videodata', embed=True)
1200
+ """
1201
+ if isinstance(data, (Path, PurePath)):
1202
+ data = str(data)
1203
+
1204
+ if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1205
+ url = data
1206
+ data = None
1207
+ elif data is not None and os.path.exists(data):
1208
+ filename = data
1209
+ data = None
1210
+
1211
+ if data and not embed:
1212
+ msg = ''.join([
1213
+ "To embed videos, you must pass embed=True ",
1214
+ "(this may make your notebook files huge)\n",
1215
+ "Consider passing Video(url='...')",
1216
+ ])
1217
+ raise ValueError(msg)
1218
+
1219
+ self.mimetype = mimetype
1220
+ self.embed = embed
1221
+ self.width = width
1222
+ self.height = height
1223
+ self.html_attributes = html_attributes
1224
+ super(Video, self).__init__(data=data, url=url, filename=filename)
1225
+
1226
+ def _repr_html_(self):
1227
+ width = height = ''
1228
+ if self.width:
1229
+ width = ' width="%d"' % self.width
1230
+ if self.height:
1231
+ height = ' height="%d"' % self.height
1232
+
1233
+ # External URLs and potentially local files are not embedded into the
1234
+ # notebook output.
1235
+ if not self.embed:
1236
+ url = self.url if self.url is not None else self.filename
1237
+ output = """<video src="{0}" {1} {2} {3}>
1238
+ Your browser does not support the <code>video</code> element.
1239
+ </video>""".format(url, self.html_attributes, width, height)
1240
+ return output
1241
+
1242
+ # Embedded videos are base64-encoded.
1243
+ mimetype = self.mimetype
1244
+ if self.filename is not None:
1245
+ if not mimetype:
1246
+ mimetype, _ = mimetypes.guess_type(self.filename)
1247
+
1248
+ with open(self.filename, 'rb') as f:
1249
+ video = f.read()
1250
+ else:
1251
+ video = self.data
1252
+ if isinstance(video, str):
1253
+ # unicode input is already b64-encoded
1254
+ b64_video = video
1255
+ else:
1256
+ b64_video = b2a_base64(video, newline=False).decode("ascii").rstrip()
1257
+
1258
+ output = """<video {0} {1} {2}>
1259
+ <source src="data:{3};base64,{4}" type="{3}">
1260
+ Your browser does not support the video tag.
1261
+ </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1262
+ return output
1263
+
1264
+ def reload(self):
1265
+ # TODO
1266
+ pass
temp_venv/lib/python3.13/site-packages/IPython/core/display_functions.py ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Top-level display functions for displaying object in different formats."""
2
+
3
+ # Copyright (c) IPython Development Team.
4
+ # Distributed under the terms of the Modified BSD License.
5
+
6
+
7
+ from binascii import b2a_hex
8
+ import os
9
+ import sys
10
+ import warnings
11
+
12
+ __all__ = ['display', 'clear_output', 'publish_display_data', 'update_display', 'DisplayHandle']
13
+
14
+ #-----------------------------------------------------------------------------
15
+ # utility functions
16
+ #-----------------------------------------------------------------------------
17
+
18
+
19
+ def _merge(d1, d2):
20
+ """Like update, but merges sub-dicts instead of clobbering at the top level.
21
+
22
+ Updates d1 in-place
23
+ """
24
+
25
+ if not isinstance(d2, dict) or not isinstance(d1, dict):
26
+ return d2
27
+ for key, value in d2.items():
28
+ d1[key] = _merge(d1.get(key), value)
29
+ return d1
30
+
31
+
32
+ #-----------------------------------------------------------------------------
33
+ # Main functions
34
+ #-----------------------------------------------------------------------------
35
+
36
+ # use * to indicate transient is keyword-only
37
+ def publish_display_data(data, metadata=None, *, transient=None, **kwargs):
38
+ """Publish data and metadata to all frontends.
39
+
40
+ See the ``display_data`` message in the messaging documentation for
41
+ more details about this message type.
42
+
43
+ Keys of data and metadata can be any mime-type.
44
+
45
+ Parameters
46
+ ----------
47
+ data : dict
48
+ A dictionary having keys that are valid MIME types (like
49
+ 'text/plain' or 'image/svg+xml') and values that are the data for
50
+ that MIME type. The data itself must be a JSON'able data
51
+ structure. Minimally all data should have the 'text/plain' data,
52
+ which can be displayed by all frontends. If more than the plain
53
+ text is given, it is up to the frontend to decide which
54
+ representation to use.
55
+ metadata : dict
56
+ A dictionary for metadata related to the data. This can contain
57
+ arbitrary key, value pairs that frontends can use to interpret
58
+ the data. mime-type keys matching those in data can be used
59
+ to specify metadata about particular representations.
60
+ transient : dict, keyword-only
61
+ A dictionary of transient data, such as display_id.
62
+ """
63
+ from IPython.core.interactiveshell import InteractiveShell
64
+
65
+ display_pub = InteractiveShell.instance().display_pub
66
+
67
+ # only pass transient if supplied,
68
+ # to avoid errors with older ipykernel.
69
+ # TODO: We could check for ipykernel version and provide a detailed upgrade message.
70
+ if transient:
71
+ kwargs['transient'] = transient
72
+
73
+ display_pub.publish(
74
+ data=data,
75
+ metadata=metadata,
76
+ **kwargs
77
+ )
78
+
79
+
80
+ def _new_id():
81
+ """Generate a new random text id with urandom"""
82
+ return b2a_hex(os.urandom(16)).decode('ascii')
83
+
84
+
85
+ def display(
86
+ *objs,
87
+ include=None,
88
+ exclude=None,
89
+ metadata=None,
90
+ transient=None,
91
+ display_id=None,
92
+ raw=False,
93
+ clear=False,
94
+ **kwargs,
95
+ ):
96
+ """Display a Python object in all frontends.
97
+
98
+ By default all representations will be computed and sent to the frontends.
99
+ Frontends can decide which representation is used and how.
100
+
101
+ In terminal IPython this will be similar to using :func:`print`, for use in richer
102
+ frontends see Jupyter notebook examples with rich display logic.
103
+
104
+ Parameters
105
+ ----------
106
+ *objs : object
107
+ The Python objects to display.
108
+ raw : bool, optional
109
+ Are the objects to be displayed already mimetype-keyed dicts of raw display data,
110
+ or Python objects that need to be formatted before display? [default: False]
111
+ include : list, tuple or set, optional
112
+ A list of format type strings (MIME types) to include in the
113
+ format data dict. If this is set *only* the format types included
114
+ in this list will be computed.
115
+ exclude : list, tuple or set, optional
116
+ A list of format type strings (MIME types) to exclude in the format
117
+ data dict. If this is set all format types will be computed,
118
+ except for those included in this argument.
119
+ metadata : dict, optional
120
+ A dictionary of metadata to associate with the output.
121
+ mime-type keys in this dictionary will be associated with the individual
122
+ representation formats, if they exist.
123
+ transient : dict, optional
124
+ A dictionary of transient data to associate with the output.
125
+ Data in this dict should not be persisted to files (e.g. notebooks).
126
+ display_id : str, bool optional
127
+ Set an id for the display.
128
+ This id can be used for updating this display area later via update_display.
129
+ If given as `True`, generate a new `display_id`
130
+ clear : bool, optional
131
+ Should the output area be cleared before displaying anything? If True,
132
+ this will wait for additional output before clearing. [default: False]
133
+ **kwargs : additional keyword-args, optional
134
+ Additional keyword-arguments are passed through to the display publisher.
135
+
136
+ Returns
137
+ -------
138
+ handle: DisplayHandle
139
+ Returns a handle on updatable displays for use with :func:`update_display`,
140
+ if `display_id` is given. Returns :any:`None` if no `display_id` is given
141
+ (default).
142
+
143
+ Examples
144
+ --------
145
+ >>> class Json(object):
146
+ ... def __init__(self, json):
147
+ ... self.json = json
148
+ ... def _repr_pretty_(self, pp, cycle):
149
+ ... import json
150
+ ... pp.text(json.dumps(self.json, indent=2))
151
+ ... def __repr__(self):
152
+ ... return str(self.json)
153
+ ...
154
+
155
+ >>> d = Json({1:2, 3: {4:5}})
156
+
157
+ >>> print(d)
158
+ {1: 2, 3: {4: 5}}
159
+
160
+ >>> display(d)
161
+ {
162
+ "1": 2,
163
+ "3": {
164
+ "4": 5
165
+ }
166
+ }
167
+
168
+ >>> def int_formatter(integer, pp, cycle):
169
+ ... pp.text('I'*integer)
170
+
171
+ >>> plain = get_ipython().display_formatter.formatters['text/plain']
172
+ >>> plain.for_type(int, int_formatter)
173
+ <function _repr_pprint at 0x...>
174
+ >>> display(7-5)
175
+ II
176
+
177
+ >>> del plain.type_printers[int]
178
+ >>> display(7-5)
179
+ 2
180
+
181
+ See Also
182
+ --------
183
+ :func:`update_display`
184
+
185
+ Notes
186
+ -----
187
+ In Python, objects can declare their textual representation using the
188
+ `__repr__` method. IPython expands on this idea and allows objects to declare
189
+ other, rich representations including:
190
+
191
+ - HTML
192
+ - JSON
193
+ - PNG
194
+ - JPEG
195
+ - SVG
196
+ - LaTeX
197
+
198
+ A single object can declare some or all of these representations; all are
199
+ handled by IPython's display system.
200
+
201
+ The main idea of the first approach is that you have to implement special
202
+ display methods when you define your class, one for each representation you
203
+ want to use. Here is a list of the names of the special methods and the
204
+ values they must return:
205
+
206
+ - `_repr_html_`: return raw HTML as a string, or a tuple (see below).
207
+ - `_repr_json_`: return a JSONable dict, or a tuple (see below).
208
+ - `_repr_jpeg_`: return raw JPEG data, or a tuple (see below).
209
+ - `_repr_png_`: return raw PNG data, or a tuple (see below).
210
+ - `_repr_svg_`: return raw SVG data as a string, or a tuple (see below).
211
+ - `_repr_latex_`: return LaTeX commands in a string surrounded by "$",
212
+ or a tuple (see below).
213
+ - `_repr_mimebundle_`: return a full mimebundle containing the mapping
214
+ from all mimetypes to data.
215
+ Use this for any mime-type not listed above.
216
+
217
+ The above functions may also return the object's metadata alonside the
218
+ data. If the metadata is available, the functions will return a tuple
219
+ containing the data and metadata, in that order. If there is no metadata
220
+ available, then the functions will return the data only.
221
+
222
+ When you are directly writing your own classes, you can adapt them for
223
+ display in IPython by following the above approach. But in practice, you
224
+ often need to work with existing classes that you can't easily modify.
225
+
226
+ You can refer to the documentation on integrating with the display system in
227
+ order to register custom formatters for already existing types
228
+ (:ref:`integrating_rich_display`).
229
+
230
+ .. versionadded:: 5.4 display available without import
231
+ .. versionadded:: 6.1 display available without import
232
+
233
+ Since IPython 5.4 and 6.1 :func:`display` is automatically made available to
234
+ the user without import. If you are using display in a document that might
235
+ be used in a pure python context or with older version of IPython, use the
236
+ following import at the top of your file::
237
+
238
+ from IPython.display import display
239
+
240
+ """
241
+ from IPython.core.interactiveshell import InteractiveShell
242
+
243
+ if not InteractiveShell.initialized():
244
+ # Directly print objects.
245
+ print(*objs)
246
+ return
247
+
248
+ if transient is None:
249
+ transient = {}
250
+ if metadata is None:
251
+ metadata={}
252
+ if display_id:
253
+ if display_id is True:
254
+ display_id = _new_id()
255
+ transient['display_id'] = display_id
256
+ if kwargs.get('update') and 'display_id' not in transient:
257
+ raise TypeError('display_id required for update_display')
258
+ if transient:
259
+ kwargs['transient'] = transient
260
+
261
+ if not objs and display_id:
262
+ # if given no objects, but still a request for a display_id,
263
+ # we assume the user wants to insert an empty output that
264
+ # can be updated later
265
+ objs = [{}]
266
+ raw = True
267
+
268
+ if not raw:
269
+ format = InteractiveShell.instance().display_formatter.format
270
+
271
+ if clear:
272
+ clear_output(wait=True)
273
+
274
+ for obj in objs:
275
+ if raw:
276
+ publish_display_data(data=obj, metadata=metadata, **kwargs)
277
+ else:
278
+ format_dict, md_dict = format(obj, include=include, exclude=exclude)
279
+ if not format_dict:
280
+ # nothing to display (e.g. _ipython_display_ took over)
281
+ continue
282
+ if metadata:
283
+ # kwarg-specified metadata gets precedence
284
+ _merge(md_dict, metadata)
285
+ publish_display_data(data=format_dict, metadata=md_dict, **kwargs)
286
+ if display_id:
287
+ return DisplayHandle(display_id)
288
+
289
+
290
+ # use * for keyword-only display_id arg
291
+ def update_display(obj, *, display_id, **kwargs):
292
+ """Update an existing display by id
293
+
294
+ Parameters
295
+ ----------
296
+ obj
297
+ The object with which to update the display
298
+ display_id : keyword-only
299
+ The id of the display to update
300
+
301
+ See Also
302
+ --------
303
+ :func:`display`
304
+ """
305
+ kwargs['update'] = True
306
+ display(obj, display_id=display_id, **kwargs)
307
+
308
+
309
+ class DisplayHandle:
310
+ """A handle on an updatable display
311
+
312
+ Call `.update(obj)` to display a new object.
313
+
314
+ Call `.display(obj`) to add a new instance of this display,
315
+ and update existing instances.
316
+
317
+ See Also
318
+ --------
319
+
320
+ :func:`display`, :func:`update_display`
321
+
322
+ """
323
+
324
+ def __init__(self, display_id=None):
325
+ if display_id is None:
326
+ display_id = _new_id()
327
+ self.display_id = display_id
328
+
329
+ def __repr__(self):
330
+ return "<%s display_id=%s>" % (self.__class__.__name__, self.display_id)
331
+
332
+ def display(self, obj, **kwargs):
333
+ """Make a new display with my id, updating existing instances.
334
+
335
+ Parameters
336
+ ----------
337
+ obj
338
+ object to display
339
+ **kwargs
340
+ additional keyword arguments passed to display
341
+ """
342
+ display(obj, display_id=self.display_id, **kwargs)
343
+
344
+ def update(self, obj, **kwargs):
345
+ """Update existing displays with my id
346
+
347
+ Parameters
348
+ ----------
349
+ obj
350
+ object to display
351
+ **kwargs
352
+ additional keyword arguments passed to update_display
353
+ """
354
+ update_display(obj, display_id=self.display_id, **kwargs)
355
+
356
+
357
+ def clear_output(wait=False):
358
+ """Clear the output of the current cell receiving output.
359
+
360
+ Parameters
361
+ ----------
362
+ wait : bool [default: false]
363
+ Wait to clear the output until new output is available to replace it."""
364
+ from IPython.core.interactiveshell import InteractiveShell
365
+ if InteractiveShell.initialized():
366
+ InteractiveShell.instance().display_pub.clear_output(wait)
367
+ else:
368
+ print('\033[2K\r', end='')
369
+ sys.stdout.flush()
370
+ print('\033[2K\r', end='')
371
+ sys.stderr.flush()
temp_venv/lib/python3.13/site-packages/IPython/core/displayhook.py ADDED
@@ -0,0 +1,346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """Displayhook for IPython.
3
+
4
+ This defines a callable class that IPython uses for `sys.displayhook`.
5
+ """
6
+
7
+ # Copyright (c) IPython Development Team.
8
+ # Distributed under the terms of the Modified BSD License.
9
+
10
+ import builtins as builtin_mod
11
+ import sys
12
+ import io as _io
13
+ import tokenize
14
+
15
+ from traitlets.config.configurable import Configurable
16
+ from traitlets import Instance, Float
17
+ from warnings import warn
18
+
19
+ from .history import HistoryOutput
20
+
21
+ # TODO: Move the various attributes (cache_size, [others now moved]). Some
22
+ # of these are also attributes of InteractiveShell. They should be on ONE object
23
+ # only and the other objects should ask that one object for their values.
24
+
25
+ class DisplayHook(Configurable):
26
+ """The custom IPython displayhook to replace sys.displayhook.
27
+
28
+ This class does many things, but the basic idea is that it is a callable
29
+ that gets called anytime user code returns a value.
30
+ """
31
+
32
+ shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
33
+ allow_none=True)
34
+ exec_result = Instance('IPython.core.interactiveshell.ExecutionResult',
35
+ allow_none=True)
36
+ cull_fraction = Float(0.2)
37
+
38
+ def __init__(self, shell=None, cache_size=1000, **kwargs):
39
+ super(DisplayHook, self).__init__(shell=shell, **kwargs)
40
+ self._is_active = False
41
+ cache_size_min = 3
42
+ if cache_size <= 0:
43
+ self.do_full_cache = 0
44
+ cache_size = 0
45
+ elif cache_size < cache_size_min:
46
+ self.do_full_cache = 0
47
+ cache_size = 0
48
+ warn('caching was disabled (min value for cache size is %s).' %
49
+ cache_size_min,stacklevel=3)
50
+ else:
51
+ self.do_full_cache = 1
52
+
53
+ self.cache_size = cache_size
54
+
55
+ # we need a reference to the user-level namespace
56
+ self.shell = shell
57
+
58
+ self._,self.__,self.___ = '','',''
59
+
60
+ # these are deliberately global:
61
+ to_user_ns = {'_':self._,'__':self.__,'___':self.___}
62
+ self.shell.user_ns.update(to_user_ns)
63
+
64
+ @property
65
+ def prompt_count(self):
66
+ return self.shell.execution_count
67
+
68
+ #-------------------------------------------------------------------------
69
+ # Methods used in __call__. Override these methods to modify the behavior
70
+ # of the displayhook.
71
+ #-------------------------------------------------------------------------
72
+
73
+ def check_for_underscore(self):
74
+ """Check if the user has set the '_' variable by hand."""
75
+ # If something injected a '_' variable in __builtin__, delete
76
+ # ipython's automatic one so we don't clobber that. gettext() in
77
+ # particular uses _, so we need to stay away from it.
78
+ if '_' in builtin_mod.__dict__:
79
+ try:
80
+ user_value = self.shell.user_ns['_']
81
+ if user_value is not self._:
82
+ return
83
+ del self.shell.user_ns['_']
84
+ except KeyError:
85
+ pass
86
+
87
+ def quiet(self):
88
+ """Should we silence the display hook because of ';'?"""
89
+ # do not print output if input ends in ';'
90
+
91
+ try:
92
+ cell = self.shell.history_manager.input_hist_parsed[-1]
93
+ except IndexError:
94
+ # some uses of ipshellembed may fail here
95
+ return False
96
+
97
+ return self.semicolon_at_end_of_expression(cell)
98
+
99
+ @staticmethod
100
+ def semicolon_at_end_of_expression(expression):
101
+ """Parse Python expression and detects whether last token is ';'"""
102
+
103
+ sio = _io.StringIO(expression)
104
+ tokens = list(tokenize.generate_tokens(sio.readline))
105
+
106
+ for token in reversed(tokens):
107
+ if token[0] in (tokenize.ENDMARKER, tokenize.NL, tokenize.NEWLINE, tokenize.COMMENT):
108
+ continue
109
+ if (token[0] == tokenize.OP) and (token[1] == ';'):
110
+ return True
111
+ else:
112
+ return False
113
+
114
+ def start_displayhook(self):
115
+ """Start the displayhook, initializing resources."""
116
+ self._is_active = True
117
+
118
+ @property
119
+ def is_active(self):
120
+ return self._is_active
121
+
122
+ def write_output_prompt(self):
123
+ """Write the output prompt.
124
+
125
+ The default implementation simply writes the prompt to
126
+ ``sys.stdout``.
127
+ """
128
+ # Use write, not print which adds an extra space.
129
+ sys.stdout.write(self.shell.separate_out)
130
+ outprompt = 'Out[{}]: '.format(self.shell.execution_count)
131
+ if self.do_full_cache:
132
+ sys.stdout.write(outprompt)
133
+
134
+ def compute_format_data(self, result):
135
+ """Compute format data of the object to be displayed.
136
+
137
+ The format data is a generalization of the :func:`repr` of an object.
138
+ In the default implementation the format data is a :class:`dict` of
139
+ key value pair where the keys are valid MIME types and the values
140
+ are JSON'able data structure containing the raw data for that MIME
141
+ type. It is up to frontends to determine pick a MIME to to use and
142
+ display that data in an appropriate manner.
143
+
144
+ This method only computes the format data for the object and should
145
+ NOT actually print or write that to a stream.
146
+
147
+ Parameters
148
+ ----------
149
+ result : object
150
+ The Python object passed to the display hook, whose format will be
151
+ computed.
152
+
153
+ Returns
154
+ -------
155
+ (format_dict, md_dict) : dict
156
+ format_dict is a :class:`dict` whose keys are valid MIME types and values are
157
+ JSON'able raw data for that MIME type. It is recommended that
158
+ all return values of this should always include the "text/plain"
159
+ MIME type representation of the object.
160
+ md_dict is a :class:`dict` with the same MIME type keys
161
+ of metadata associated with each output.
162
+
163
+ """
164
+ return self.shell.display_formatter.format(result)
165
+
166
+ # This can be set to True by the write_output_prompt method in a subclass
167
+ prompt_end_newline = False
168
+
169
+ def write_format_data(self, format_dict, md_dict=None) -> None:
170
+ """Write the format data dict to the frontend.
171
+
172
+ This default version of this method simply writes the plain text
173
+ representation of the object to ``sys.stdout``. Subclasses should
174
+ override this method to send the entire `format_dict` to the
175
+ frontends.
176
+
177
+ Parameters
178
+ ----------
179
+ format_dict : dict
180
+ The format dict for the object passed to `sys.displayhook`.
181
+ md_dict : dict (optional)
182
+ The metadata dict to be associated with the display data.
183
+ """
184
+ if 'text/plain' not in format_dict:
185
+ # nothing to do
186
+ return
187
+ # We want to print because we want to always make sure we have a
188
+ # newline, even if all the prompt separators are ''. This is the
189
+ # standard IPython behavior.
190
+ result_repr = format_dict['text/plain']
191
+ if '\n' in result_repr:
192
+ # So that multi-line strings line up with the left column of
193
+ # the screen, instead of having the output prompt mess up
194
+ # their first line.
195
+ # We use the prompt template instead of the expanded prompt
196
+ # because the expansion may add ANSI escapes that will interfere
197
+ # with our ability to determine whether or not we should add
198
+ # a newline.
199
+ if not self.prompt_end_newline:
200
+ # But avoid extraneous empty lines.
201
+ result_repr = '\n' + result_repr
202
+
203
+ try:
204
+ print(result_repr)
205
+ except UnicodeEncodeError:
206
+ # If a character is not supported by the terminal encoding replace
207
+ # it with its \u or \x representation
208
+ print(result_repr.encode(sys.stdout.encoding,'backslashreplace').decode(sys.stdout.encoding))
209
+
210
+ def update_user_ns(self, result):
211
+ """Update user_ns with various things like _, __, _1, etc."""
212
+
213
+ # Avoid recursive reference when displaying _oh/Out
214
+ if self.cache_size and result is not self.shell.user_ns['_oh']:
215
+ if len(self.shell.user_ns['_oh']) >= self.cache_size and self.do_full_cache:
216
+ self.cull_cache()
217
+
218
+ # Don't overwrite '_' and friends if '_' is in __builtin__
219
+ # (otherwise we cause buggy behavior for things like gettext). and
220
+ # do not overwrite _, __ or ___ if one of these has been assigned
221
+ # by the user.
222
+ update_unders = True
223
+ for unders in ['_'*i for i in range(1,4)]:
224
+ if unders not in self.shell.user_ns:
225
+ continue
226
+ if getattr(self, unders) is not self.shell.user_ns.get(unders):
227
+ update_unders = False
228
+
229
+ self.___ = self.__
230
+ self.__ = self._
231
+ self._ = result
232
+
233
+ if ('_' not in builtin_mod.__dict__) and (update_unders):
234
+ self.shell.push({'_':self._,
235
+ '__':self.__,
236
+ '___':self.___}, interactive=False)
237
+
238
+ # hackish access to top-level namespace to create _1,_2... dynamically
239
+ to_main = {}
240
+ if self.do_full_cache:
241
+ new_result = '_%s' % self.prompt_count
242
+ to_main[new_result] = result
243
+ self.shell.push(to_main, interactive=False)
244
+ self.shell.user_ns['_oh'][self.prompt_count] = result
245
+
246
+ def fill_exec_result(self, result):
247
+ if self.exec_result is not None:
248
+ self.exec_result.result = result
249
+
250
+ def log_output(self, format_dict):
251
+ """Log the output."""
252
+ self.shell.history_manager.outputs[self.prompt_count].append(
253
+ HistoryOutput(output_type="execute_result", bundle=format_dict)
254
+ )
255
+ if "text/plain" not in format_dict:
256
+ # nothing to do
257
+ return
258
+ if self.shell.logger.log_output:
259
+ self.shell.logger.log_write(format_dict['text/plain'], 'output')
260
+ self.shell.history_manager.output_hist_reprs[self.prompt_count] = \
261
+ format_dict['text/plain']
262
+
263
+ def finish_displayhook(self):
264
+ """Finish up all displayhook activities."""
265
+ sys.stdout.write(self.shell.separate_out2)
266
+ sys.stdout.flush()
267
+ self._is_active = False
268
+
269
+ def __call__(self, result=None):
270
+ """Printing with history cache management.
271
+
272
+ This is invoked every time the interpreter needs to print, and is
273
+ activated by setting the variable sys.displayhook to it.
274
+ """
275
+ self.check_for_underscore()
276
+ if result is not None and not self.quiet():
277
+ self.start_displayhook()
278
+ self.write_output_prompt()
279
+ format_dict, md_dict = self.compute_format_data(result)
280
+ self.update_user_ns(result)
281
+ self.fill_exec_result(result)
282
+ if format_dict:
283
+ self.write_format_data(format_dict, md_dict)
284
+ self.log_output(format_dict)
285
+ self.finish_displayhook()
286
+
287
+ def cull_cache(self):
288
+ """Output cache is full, cull the oldest entries"""
289
+ oh = self.shell.user_ns.get('_oh', {})
290
+ sz = len(oh)
291
+ cull_count = max(int(sz * self.cull_fraction), 2)
292
+ warn('Output cache limit (currently {sz} entries) hit.\n'
293
+ 'Flushing oldest {cull_count} entries.'.format(sz=sz, cull_count=cull_count))
294
+
295
+ for i, n in enumerate(sorted(oh)):
296
+ if i >= cull_count:
297
+ break
298
+ self.shell.user_ns.pop('_%i' % n, None)
299
+ oh.pop(n, None)
300
+
301
+ def flush(self):
302
+ if not self.do_full_cache:
303
+ raise ValueError("You shouldn't have reached the cache flush "
304
+ "if full caching is not enabled!")
305
+ # delete auto-generated vars from global namespace
306
+
307
+ for n in range(1,self.prompt_count + 1):
308
+ key = '_'+repr(n)
309
+ try:
310
+ del self.shell.user_ns_hidden[key]
311
+ except KeyError:
312
+ pass
313
+ try:
314
+ del self.shell.user_ns[key]
315
+ except KeyError:
316
+ pass
317
+ # In some embedded circumstances, the user_ns doesn't have the
318
+ # '_oh' key set up.
319
+ oh = self.shell.user_ns.get('_oh', None)
320
+ if oh is not None:
321
+ oh.clear()
322
+
323
+ # Release our own references to objects:
324
+ self._, self.__, self.___ = '', '', ''
325
+
326
+ if '_' not in builtin_mod.__dict__:
327
+ self.shell.user_ns.update({'_':self._,'__':self.__,'___':self.___})
328
+ import gc
329
+ # TODO: Is this really needed?
330
+ # IronPython blocks here forever
331
+ if sys.platform != "cli":
332
+ gc.collect()
333
+
334
+
335
+ class CapturingDisplayHook:
336
+ def __init__(self, shell, outputs=None):
337
+ self.shell = shell
338
+ if outputs is None:
339
+ outputs = []
340
+ self.outputs = outputs
341
+
342
+ def __call__(self, result=None):
343
+ if result is None:
344
+ return
345
+ format_dict, md_dict = self.shell.display_formatter.format(result)
346
+ self.outputs.append({ 'data': format_dict, 'metadata': md_dict })
temp_venv/lib/python3.13/site-packages/IPython/core/displaypub.py ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """An interface for publishing rich data to frontends.
2
+
3
+ There are two components of the display system:
4
+
5
+ * Display formatters, which take a Python object and compute the
6
+ representation of the object in various formats (text, HTML, SVG, etc.).
7
+ * The display publisher that is used to send the representation data to the
8
+ various frontends.
9
+
10
+ This module defines the logic display publishing. The display publisher uses
11
+ the ``display_data`` message type that is defined in the IPython messaging
12
+ spec.
13
+ """
14
+
15
+ # Copyright (c) IPython Development Team.
16
+ # Distributed under the terms of the Modified BSD License.
17
+
18
+ import sys
19
+
20
+ from traitlets.config.configurable import Configurable
21
+ from traitlets import List
22
+
23
+ # This used to be defined here - it is imported for backwards compatibility
24
+ from .display_functions import publish_display_data
25
+ from .history import HistoryOutput
26
+
27
+ import typing as t
28
+
29
+ # -----------------------------------------------------------------------------
30
+ # Main payload class
31
+ # -----------------------------------------------------------------------------
32
+
33
+ _sentinel = object()
34
+
35
+
36
+ class DisplayPublisher(Configurable):
37
+ """A traited class that publishes display data to frontends.
38
+
39
+ Instances of this class are created by the main IPython object and should
40
+ be accessed there.
41
+ """
42
+
43
+ def __init__(self, shell=None, *args, **kwargs):
44
+ self.shell = shell
45
+ self._is_publishing = False
46
+ super().__init__(*args, **kwargs)
47
+
48
+ def _validate_data(self, data, metadata=None):
49
+ """Validate the display data.
50
+
51
+ Parameters
52
+ ----------
53
+ data : dict
54
+ The formata data dictionary.
55
+ metadata : dict
56
+ Any metadata for the data.
57
+ """
58
+
59
+ if not isinstance(data, dict):
60
+ raise TypeError("data must be a dict, got: %r" % data)
61
+ if metadata is not None:
62
+ if not isinstance(metadata, dict):
63
+ raise TypeError("metadata must be a dict, got: %r" % data)
64
+
65
+ # use * to indicate transient, update are keyword-only
66
+ def publish(
67
+ self,
68
+ data,
69
+ metadata=None,
70
+ source=_sentinel,
71
+ *,
72
+ transient=None,
73
+ update=False,
74
+ **kwargs,
75
+ ) -> None:
76
+ """Publish data and metadata to all frontends.
77
+
78
+ See the ``display_data`` message in the messaging documentation for
79
+ more details about this message type.
80
+
81
+ The following MIME types are currently implemented:
82
+
83
+ * text/plain
84
+ * text/html
85
+ * text/markdown
86
+ * text/latex
87
+ * application/json
88
+ * application/javascript
89
+ * image/png
90
+ * image/jpeg
91
+ * image/svg+xml
92
+
93
+ Parameters
94
+ ----------
95
+ data : dict
96
+ A dictionary having keys that are valid MIME types (like
97
+ 'text/plain' or 'image/svg+xml') and values that are the data for
98
+ that MIME type. The data itself must be a JSON'able data
99
+ structure. Minimally all data should have the 'text/plain' data,
100
+ which can be displayed by all frontends. If more than the plain
101
+ text is given, it is up to the frontend to decide which
102
+ representation to use.
103
+ metadata : dict
104
+ A dictionary for metadata related to the data. This can contain
105
+ arbitrary key, value pairs that frontends can use to interpret
106
+ the data. Metadata specific to each mime-type can be specified
107
+ in the metadata dict with the same mime-type keys as
108
+ the data itself.
109
+ source : str, deprecated
110
+ Unused.
111
+ transient : dict, keyword-only
112
+ A dictionary for transient data.
113
+ Data in this dictionary should not be persisted as part of saving this output.
114
+ Examples include 'display_id'.
115
+ update : bool, keyword-only, default: False
116
+ If True, only update existing outputs with the same display_id,
117
+ rather than creating a new output.
118
+ """
119
+
120
+ if source is not _sentinel:
121
+ import warnings
122
+
123
+ warnings.warn(
124
+ "The 'source' parameter is deprecated since IPython 3.0 and will be ignored "
125
+ "(this warning is present since 9.0). `source` parameter will be removed in the future.",
126
+ DeprecationWarning,
127
+ stacklevel=2,
128
+ )
129
+
130
+ handlers: t.Dict = {}
131
+ if self.shell is not None:
132
+ handlers = getattr(self.shell, "mime_renderers", {})
133
+
134
+ outputs = self.shell.history_manager.outputs
135
+
136
+ outputs[self.shell.execution_count].append(
137
+ HistoryOutput(output_type="display_data", bundle=data)
138
+ )
139
+
140
+ for mime, handler in handlers.items():
141
+ if mime in data:
142
+ handler(data[mime], metadata.get(mime, None))
143
+ return
144
+
145
+ self._is_publishing = True
146
+ if "text/plain" in data:
147
+ print(data["text/plain"])
148
+ self._is_publishing = False
149
+
150
+ @property
151
+ def is_publishing(self):
152
+ return self._is_publishing
153
+
154
+ def clear_output(self, wait=False):
155
+ """Clear the output of the cell receiving output."""
156
+ print("\033[2K\r", end="")
157
+ sys.stdout.flush()
158
+ print("\033[2K\r", end="")
159
+ sys.stderr.flush()
160
+
161
+
162
+ class CapturingDisplayPublisher(DisplayPublisher):
163
+ """A DisplayPublisher that stores"""
164
+
165
+ outputs: List = List()
166
+
167
+ def publish(
168
+ self, data, metadata=None, source=None, *, transient=None, update=False
169
+ ):
170
+ self.outputs.append(
171
+ {
172
+ "data": data,
173
+ "metadata": metadata,
174
+ "transient": transient,
175
+ "update": update,
176
+ }
177
+ )
178
+
179
+ def clear_output(self, wait=False):
180
+ super(CapturingDisplayPublisher, self).clear_output(wait)
181
+
182
+ # empty the list, *do not* reassign a new list
183
+ self.outputs.clear()
temp_venv/lib/python3.13/site-packages/IPython/core/doctb.py ADDED
@@ -0,0 +1,444 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+ import linecache
3
+ import sys
4
+ from collections.abc import Sequence
5
+ from types import TracebackType
6
+ from typing import Any, Callable, Optional
7
+
8
+ import stack_data
9
+ from pygments.formatters.terminal256 import Terminal256Formatter
10
+ from pygments.token import Token
11
+
12
+ from IPython.utils.PyColorize import Theme, TokenStream, theme_table
13
+ from IPython.utils.terminal import get_terminal_size
14
+
15
+ from .tbtools import (
16
+ FrameInfo,
17
+ TBTools,
18
+ _safe_string,
19
+ _tokens_filename,
20
+ eqrepr,
21
+ get_line_number_of_frame,
22
+ nullrepr,
23
+ )
24
+
25
+ INDENT_SIZE = 8
26
+
27
+
28
+ def _format_traceback_lines(
29
+ lines: list[stack_data.Line],
30
+ theme: Theme,
31
+ has_colors: bool,
32
+ lvals_toks: list[TokenStream],
33
+ ) -> TokenStream:
34
+ """
35
+ Format tracebacks lines with pointing arrow, leading numbers,
36
+ this assumes the stack have been extracted using stackdata.
37
+
38
+
39
+ Parameters
40
+ ----------
41
+ lines : list[Line]
42
+ """
43
+ numbers_width = INDENT_SIZE - 1
44
+ tokens: TokenStream = [(Token, "\n")]
45
+
46
+ for stack_line in lines:
47
+ if stack_line is stack_data.LINE_GAP:
48
+ toks = [(Token.LinenoEm, " (...)")]
49
+ tokens.extend(toks)
50
+ continue
51
+
52
+ lineno = stack_line.lineno
53
+ line = stack_line.render(pygmented=has_colors).rstrip("\n") + "\n"
54
+ if stack_line.is_current:
55
+ # This is the line with the error
56
+ pad = numbers_width - len(str(lineno))
57
+ toks = [
58
+ (Token.Prompt, theme.make_arrow(3)),
59
+ (Token, " "),
60
+ (Token, line),
61
+ ]
62
+ else:
63
+ # num = "%*s" % (numbers_width, lineno)
64
+ toks = [
65
+ # (Token.LinenoEm, str(num)),
66
+ (Token, "..."),
67
+ (Token, " "),
68
+ (Token, line),
69
+ ]
70
+
71
+ tokens.extend(toks)
72
+ if lvals_toks and stack_line.is_current:
73
+ for lv in lvals_toks:
74
+ tokens.append((Token, " " * INDENT_SIZE))
75
+ tokens.extend(lv)
76
+ tokens.append((Token, "\n"))
77
+ # strip the last newline
78
+ tokens = tokens[:-1]
79
+
80
+ return tokens
81
+
82
+
83
+ class DocTB(TBTools):
84
+ """
85
+
86
+ A stripped down version of Verbose TB, simplified to not have too much information when
87
+ running doctests
88
+
89
+ """
90
+
91
+ tb_highlight = ""
92
+ tb_highlight_style = "default"
93
+ tb_offset: int
94
+ long_header: bool
95
+ include_vars: bool
96
+
97
+ _mode: str
98
+
99
+ def __init__(
100
+ self,
101
+ # TODO: no default ?
102
+ theme_name: str = "linux",
103
+ call_pdb: bool = False,
104
+ ostream: Any = None,
105
+ tb_offset: int = 0,
106
+ long_header: bool = False,
107
+ include_vars: bool = True,
108
+ check_cache: Callable[[], None] | None = None,
109
+ debugger_cls: type | None = None,
110
+ ):
111
+ """Specify traceback offset, headers and color scheme.
112
+
113
+ Define how many frames to drop from the tracebacks. Calling it with
114
+ tb_offset=1 allows use of this handler in interpreters which will have
115
+ their own code at the top of the traceback (VerboseTB will first
116
+ remove that frame before printing the traceback info)."""
117
+ assert isinstance(theme_name, str)
118
+ super().__init__(
119
+ theme_name=theme_name,
120
+ call_pdb=call_pdb,
121
+ ostream=ostream,
122
+ debugger_cls=debugger_cls,
123
+ )
124
+ self.tb_offset = tb_offset
125
+ self.long_header = long_header
126
+ self.include_vars = include_vars
127
+ # By default we use linecache.checkcache, but the user can provide a
128
+ # different check_cache implementation. This was formerly used by the
129
+ # IPython kernel for interactive code, but is no longer necessary.
130
+ if check_cache is None:
131
+ check_cache = linecache.checkcache
132
+ self.check_cache = check_cache
133
+
134
+ self.skip_hidden = True
135
+
136
+ def format_record(self, frame_info: FrameInfo) -> str:
137
+ """Format a single stack frame"""
138
+ assert isinstance(frame_info, FrameInfo)
139
+
140
+ if isinstance(frame_info._sd, stack_data.RepeatedFrames):
141
+ return theme_table[self._theme_name].format(
142
+ [
143
+ (Token, " "),
144
+ (
145
+ Token.ExcName,
146
+ "[... skipping similar frames: %s]" % frame_info.description,
147
+ ),
148
+ (Token, "\n"),
149
+ ]
150
+ )
151
+
152
+ indent: str = " " * INDENT_SIZE
153
+
154
+ assert isinstance(frame_info.lineno, int)
155
+ args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
156
+ if frame_info.executing is not None:
157
+ func = frame_info.executing.code_qualname()
158
+ else:
159
+ func = "?"
160
+ if func == "<module>":
161
+ call = ""
162
+ else:
163
+ # Decide whether to include variable details or not
164
+ var_repr = eqrepr if self.include_vars else nullrepr
165
+ try:
166
+ scope = inspect.formatargvalues(
167
+ args, varargs, varkw, locals_, formatvalue=var_repr
168
+ )
169
+ assert isinstance(scope, str)
170
+ call = theme_table[self._theme_name].format(
171
+ [(Token, "in "), (Token.VName, func), (Token.ValEm, scope)]
172
+ )
173
+ except KeyError:
174
+ # This happens in situations like errors inside generator
175
+ # expressions, where local variables are listed in the
176
+ # line, but can't be extracted from the frame. I'm not
177
+ # 100% sure this isn't actually a bug in inspect itself,
178
+ # but since there's no info for us to compute with, the
179
+ # best we can do is report the failure and move on. Here
180
+ # we must *not* call any traceback construction again,
181
+ # because that would mess up use of %debug later on. So we
182
+ # simply report the failure and move on. The only
183
+ # limitation will be that this frame won't have locals
184
+ # listed in the call signature. Quite subtle problem...
185
+ # I can't think of a good way to validate this in a unit
186
+ # test, but running a script consisting of:
187
+ # dict( (k,v.strip()) for (k,v) in range(10) )
188
+ # will illustrate the error, if this exception catch is
189
+ # disabled.
190
+ call = theme_table[self._theme_name].format(
191
+ [
192
+ (Token, "in "),
193
+ (Token.VName, func),
194
+ (Token.ValEm, "(***failed resolving arguments***)"),
195
+ ]
196
+ )
197
+
198
+ lvals_toks: list[TokenStream] = []
199
+ if self.include_vars:
200
+ try:
201
+ # we likely want to fix stackdata at some point, but
202
+ # still need a workaround.
203
+ fibp = frame_info.variables_in_executing_piece
204
+ for var in fibp:
205
+ lvals_toks.append(
206
+ [
207
+ (Token, var.name),
208
+ (Token, " "),
209
+ (Token.ValEm, "= "),
210
+ (Token.ValEm, repr(var.value)),
211
+ ]
212
+ )
213
+ except Exception:
214
+ lvals_toks.append(
215
+ [
216
+ (
217
+ Token,
218
+ "Exception trying to inspect frame. No more locals available.",
219
+ ),
220
+ ]
221
+ )
222
+
223
+ assert frame_info._sd is not None
224
+ result = theme_table[self._theme_name].format(
225
+ _tokens_filename(True, frame_info.filename, lineno=frame_info.lineno)
226
+ )
227
+ result += ", " if call else ""
228
+ result += f"{call}\n"
229
+ result += theme_table[self._theme_name].format(
230
+ _format_traceback_lines(
231
+ frame_info.lines,
232
+ theme_table[self._theme_name],
233
+ self.has_colors,
234
+ lvals_toks,
235
+ )
236
+ )
237
+ return result
238
+
239
+ def prepare_header(self, etype: str) -> str:
240
+ width = min(75, get_terminal_size()[0])
241
+ head = theme_table[self._theme_name].format(
242
+ [
243
+ (
244
+ Token,
245
+ "Traceback (most recent call last):",
246
+ ),
247
+ (Token, " "),
248
+ ]
249
+ )
250
+
251
+ return head
252
+
253
+ def format_exception(self, etype: Any, evalue: Any) -> Any:
254
+ # Get (safely) a string form of the exception info
255
+ try:
256
+ etype_str, evalue_str = map(str, (etype, evalue))
257
+ except:
258
+ # User exception is improperly defined.
259
+ etype, evalue = str, sys.exc_info()[:2]
260
+ etype_str, evalue_str = map(str, (etype, evalue))
261
+
262
+ # PEP-678 notes
263
+ notes = getattr(evalue, "__notes__", [])
264
+ if not isinstance(notes, Sequence) or isinstance(notes, (str, bytes)):
265
+ notes = [_safe_string(notes, "__notes__", func=repr)]
266
+
267
+ # ... and format it
268
+ return [
269
+ theme_table[self._theme_name].format(
270
+ [(Token.ExcName, etype_str), (Token, ": "), (Token, evalue_str)]
271
+ ),
272
+ *(
273
+ theme_table[self._theme_name].format([(Token, _safe_string(n, "note"))])
274
+ for n in notes
275
+ ),
276
+ ]
277
+
278
+ def format_exception_as_a_whole(
279
+ self,
280
+ etype: type,
281
+ evalue: Optional[BaseException],
282
+ etb: Optional[TracebackType],
283
+ context: int,
284
+ tb_offset: Optional[int],
285
+ ) -> list[list[str]]:
286
+ """Formats the header, traceback and exception message for a single exception.
287
+
288
+ This may be called multiple times by Python 3 exception chaining
289
+ (PEP 3134).
290
+ """
291
+ # some locals
292
+ orig_etype = etype
293
+ try:
294
+ etype = etype.__name__ # type: ignore[assignment]
295
+ except AttributeError:
296
+ pass
297
+
298
+ tb_offset = self.tb_offset if tb_offset is None else tb_offset
299
+ assert isinstance(tb_offset, int)
300
+ head = self.prepare_header(str(etype))
301
+ assert context == 1, context
302
+ records = self.get_records(etb, context, tb_offset) if etb else []
303
+
304
+ frames = []
305
+ skipped = 0
306
+ nskipped = len(records) - 1
307
+ frames.append(self.format_record(records[0]))
308
+ if nskipped:
309
+ frames.append(
310
+ theme_table[self._theme_name].format(
311
+ [
312
+ (Token, "\n"),
313
+ (Token, " "),
314
+ (Token, "[... %s skipped frames]" % nskipped),
315
+ (Token, "\n"),
316
+ (Token, "\n"),
317
+ ]
318
+ )
319
+ )
320
+
321
+ formatted_exception = self.format_exception(etype, evalue)
322
+ return [[head] + frames + formatted_exception]
323
+
324
+ def get_records(self, etb: TracebackType, context: int, tb_offset: int) -> Any:
325
+ assert context == 1, context
326
+ assert etb is not None
327
+ context = context - 1
328
+ after = context // 2
329
+ before = context - after
330
+ if self.has_colors:
331
+ base_style = theme_table[self._theme_name].as_pygments_style()
332
+ style = stack_data.style_with_executing_node(base_style, self.tb_highlight)
333
+ formatter = Terminal256Formatter(style=style)
334
+ else:
335
+ formatter = None
336
+ options = stack_data.Options(
337
+ before=before,
338
+ after=after,
339
+ pygments_formatter=formatter,
340
+ )
341
+
342
+ # Let's estimate the amount of code we will have to parse/highlight.
343
+ cf: Optional[TracebackType] = etb
344
+ max_len = 0
345
+ tbs = []
346
+ while cf is not None:
347
+ try:
348
+ mod = inspect.getmodule(cf.tb_frame)
349
+ if mod is not None:
350
+ mod_name = mod.__name__
351
+ root_name, *_ = mod_name.split(".")
352
+ if root_name == "IPython":
353
+ cf = cf.tb_next
354
+ continue
355
+ max_len = get_line_number_of_frame(cf.tb_frame)
356
+
357
+ except OSError:
358
+ max_len = 0
359
+ max_len = max(max_len, max_len)
360
+ tbs.append(cf)
361
+ cf = getattr(cf, "tb_next", None)
362
+
363
+ res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
364
+ res2 = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
365
+ return res2
366
+
367
+ def structured_traceback(
368
+ self,
369
+ etype: type,
370
+ evalue: Optional[BaseException],
371
+ etb: Optional[TracebackType] = None,
372
+ tb_offset: Optional[int] = None,
373
+ context: int = 1,
374
+ ) -> list[str]:
375
+ """Return a nice text document describing the traceback."""
376
+ assert context > 0
377
+ assert context == 1, context
378
+ formatted_exceptions: list[list[str]] = self.format_exception_as_a_whole(
379
+ etype, evalue, etb, context, tb_offset
380
+ )
381
+
382
+ termsize = min(75, get_terminal_size()[0])
383
+ theme = theme_table[self._theme_name]
384
+ structured_traceback_parts: list[str] = []
385
+ chained_exceptions_tb_offset = 0
386
+ lines_of_context = 3
387
+ exception = self.get_parts_of_chained_exception(evalue)
388
+ if exception:
389
+ assert evalue is not None
390
+ formatted_exceptions += self.prepare_chained_exception_message(
391
+ evalue.__cause__
392
+ )
393
+ etype, evalue, etb = exception
394
+ else:
395
+ evalue = None
396
+ chained_exc_ids = set()
397
+ while evalue:
398
+ formatted_exceptions += self.format_exception_as_a_whole(
399
+ etype, evalue, etb, lines_of_context, chained_exceptions_tb_offset
400
+ )
401
+ exception = self.get_parts_of_chained_exception(evalue)
402
+
403
+ if exception and id(exception[1]) not in chained_exc_ids:
404
+ chained_exc_ids.add(
405
+ id(exception[1])
406
+ ) # trace exception to avoid infinite 'cause' loop
407
+ formatted_exceptions += self.prepare_chained_exception_message(
408
+ evalue.__cause__
409
+ )
410
+ etype, evalue, etb = exception
411
+ else:
412
+ evalue = None
413
+
414
+ # we want to see exceptions in a reversed order:
415
+ # the first exception should be on top
416
+ for fx in reversed(formatted_exceptions):
417
+ structured_traceback_parts += fx
418
+
419
+ return structured_traceback_parts
420
+
421
+ def debugger(self, force: bool = False) -> None:
422
+ raise RuntimeError("canot rundebugger in Docs mode")
423
+
424
+ def handler(self, info: tuple[Any, Any, Any] | None = None) -> None:
425
+ (etype, evalue, etb) = info or sys.exc_info()
426
+ self.tb = etb
427
+ ostream = self.ostream
428
+ ostream.flush()
429
+ ostream.write(self.text(etype, evalue, etb)) # type:ignore[arg-type]
430
+ ostream.write("\n")
431
+ ostream.flush()
432
+
433
+ # Changed so an instance can just be called as VerboseTB_inst() and print
434
+ # out the right info on its own.
435
+ def __call__(self, etype: Any = None, evalue: Any = None, etb: Any = None) -> None:
436
+ """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
437
+ if etb is None:
438
+ self.handler()
439
+ else:
440
+ self.handler((etype, evalue, etb))
441
+ try:
442
+ self.debugger()
443
+ except KeyboardInterrupt:
444
+ print("\nKeyboardInterrupt")
temp_venv/lib/python3.13/site-packages/IPython/core/events.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Infrastructure for registering and firing callbacks on application events.
2
+
3
+ Unlike :mod:`IPython.core.hooks`, which lets end users set single functions to
4
+ be called at specific times, or a collection of alternative methods to try,
5
+ callbacks are designed to be used by extension authors. A number of callbacks
6
+ can be registered for the same event without needing to be aware of one another.
7
+
8
+ The functions defined in this module are no-ops indicating the names of available
9
+ events and the arguments which will be passed to them.
10
+
11
+ .. note::
12
+
13
+ This API is experimental in IPython 2.0, and may be revised in future versions.
14
+ """
15
+
16
+
17
+ class EventManager:
18
+ """Manage a collection of events and a sequence of callbacks for each.
19
+
20
+ This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`
21
+ instances as an ``events`` attribute.
22
+
23
+ .. note::
24
+
25
+ This API is experimental in IPython 2.0, and may be revised in future versions.
26
+ """
27
+
28
+ def __init__(self, shell, available_events, print_on_error=True):
29
+ """Initialise the :class:`CallbackManager`.
30
+
31
+ Parameters
32
+ ----------
33
+ shell
34
+ The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
35
+ available_events
36
+ An iterable of names for callback events.
37
+ print_on_error:
38
+ A boolean flag to set whether the EventManager will print a warning which a event errors.
39
+ """
40
+ self.shell = shell
41
+ self.callbacks = {n:[] for n in available_events}
42
+ self.print_on_error = print_on_error
43
+
44
+ def register(self, event, function):
45
+ """Register a new event callback.
46
+
47
+ Parameters
48
+ ----------
49
+ event : str
50
+ The event for which to register this callback.
51
+ function : callable
52
+ A function to be called on the given event. It should take the same
53
+ parameters as the appropriate callback prototype.
54
+
55
+ Raises
56
+ ------
57
+ TypeError
58
+ If ``function`` is not callable.
59
+ KeyError
60
+ If ``event`` is not one of the known events.
61
+ """
62
+ if not callable(function):
63
+ raise TypeError('Need a callable, got %r' % function)
64
+ if function not in self.callbacks[event]:
65
+ self.callbacks[event].append(function)
66
+
67
+ def unregister(self, event, function):
68
+ """Remove a callback from the given event."""
69
+ if function in self.callbacks[event]:
70
+ return self.callbacks[event].remove(function)
71
+
72
+ raise ValueError('Function {!r} is not registered as a {} callback'.format(function, event))
73
+
74
+ def trigger(self, event, *args, **kwargs):
75
+ """Call callbacks for ``event``.
76
+
77
+ Any additional arguments are passed to all callbacks registered for this
78
+ event. Exceptions raised by callbacks are caught, and a message printed.
79
+ """
80
+ for func in self.callbacks[event][:]:
81
+ try:
82
+ func(*args, **kwargs)
83
+ except (Exception, KeyboardInterrupt):
84
+ if self.print_on_error:
85
+ print(
86
+ "Error in callback {} (for {}), with arguments args {},kwargs {}:".format(
87
+ func, event, args, kwargs
88
+ )
89
+ )
90
+ self.shell.showtraceback()
91
+
92
+ # event_name -> prototype mapping
93
+ available_events = {}
94
+
95
+ def _define_event(callback_function):
96
+ available_events[callback_function.__name__] = callback_function
97
+ return callback_function
98
+
99
+ # ------------------------------------------------------------------------------
100
+ # Callback prototypes
101
+ #
102
+ # No-op functions which describe the names of available events and the
103
+ # signatures of callbacks for those events.
104
+ # ------------------------------------------------------------------------------
105
+
106
+ @_define_event
107
+ def pre_execute():
108
+ """Fires before code is executed in response to user/frontend action.
109
+
110
+ This includes comm and widget messages and silent execution, as well as user
111
+ code cells.
112
+ """
113
+ pass
114
+
115
+ @_define_event
116
+ def pre_run_cell(info):
117
+ """Fires before user-entered code runs.
118
+
119
+ Parameters
120
+ ----------
121
+ info : :class:`~IPython.core.interactiveshell.ExecutionInfo`
122
+ An object containing information used for the code execution.
123
+ """
124
+ pass
125
+
126
+ @_define_event
127
+ def post_execute():
128
+ """Fires after code is executed in response to user/frontend action.
129
+
130
+ This includes comm and widget messages and silent execution, as well as user
131
+ code cells.
132
+ """
133
+ pass
134
+
135
+ @_define_event
136
+ def post_run_cell(result):
137
+ """Fires after user-entered code runs.
138
+
139
+ Parameters
140
+ ----------
141
+ result : :class:`~IPython.core.interactiveshell.ExecutionResult`
142
+ The object which will be returned as the execution result.
143
+ """
144
+ pass
145
+
146
+ @_define_event
147
+ def shell_initialized(ip):
148
+ """Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`.
149
+
150
+ This is before extensions and startup scripts are loaded, so it can only be
151
+ set by subclassing.
152
+
153
+ Parameters
154
+ ----------
155
+ ip : :class:`~IPython.core.interactiveshell.InteractiveShell`
156
+ The newly initialised shell.
157
+ """
158
+ pass
temp_venv/lib/python3.13/site-packages/IPython/core/extensions.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # encoding: utf-8
2
+ """A class for managing IPython extensions."""
3
+
4
+ # Copyright (c) IPython Development Team.
5
+ # Distributed under the terms of the Modified BSD License.
6
+
7
+ import os
8
+ import os.path
9
+ import sys
10
+ from importlib import import_module, reload
11
+
12
+ from traitlets.config.configurable import Configurable
13
+ from IPython.utils.path import ensure_dir_exists
14
+ from traitlets import Instance
15
+
16
+
17
+ #-----------------------------------------------------------------------------
18
+ # Main class
19
+ #-----------------------------------------------------------------------------
20
+
21
+ BUILTINS_EXTS = {"storemagic": False, "autoreload": False}
22
+
23
+
24
+ class ExtensionManager(Configurable):
25
+ """A class to manage IPython extensions.
26
+
27
+ An IPython extension is an importable Python module that has
28
+ a function with the signature::
29
+
30
+ def load_ipython_extension(ipython):
31
+ # Do things with ipython
32
+
33
+ This function is called after your extension is imported and the
34
+ currently active :class:`InteractiveShell` instance is passed as
35
+ the only argument. You can do anything you want with IPython at
36
+ that point, including defining new magic and aliases, adding new
37
+ components, etc.
38
+
39
+ You can also optionally define an :func:`unload_ipython_extension(ipython)`
40
+ function, which will be called if the user unloads or reloads the extension.
41
+ The extension manager will only call :func:`load_ipython_extension` again
42
+ if the extension is reloaded.
43
+
44
+ You can put your extension modules anywhere you want, as long as
45
+ they can be imported by Python's standard import mechanism.
46
+ """
47
+
48
+ shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
49
+
50
+ def __init__(self, shell=None, **kwargs):
51
+ super(ExtensionManager, self).__init__(shell=shell, **kwargs)
52
+ self.loaded = set()
53
+
54
+ def load_extension(self, module_str: str):
55
+ """Load an IPython extension by its module name.
56
+
57
+ Returns the string "already loaded" if the extension is already loaded,
58
+ "no load function" if the module doesn't have a load_ipython_extension
59
+ function, or None if it succeeded.
60
+ """
61
+ try:
62
+ return self._load_extension(module_str)
63
+ except ModuleNotFoundError:
64
+ if module_str in BUILTINS_EXTS:
65
+ BUILTINS_EXTS[module_str] = True
66
+ return self._load_extension("IPython.extensions." + module_str)
67
+ raise
68
+
69
+ def _load_extension(self, module_str: str):
70
+ if module_str in self.loaded:
71
+ return "already loaded"
72
+
73
+ assert self.shell is not None
74
+
75
+ with self.shell.builtin_trap:
76
+ if module_str not in sys.modules:
77
+ mod = import_module(module_str)
78
+ mod = sys.modules[module_str]
79
+ if self._call_load_ipython_extension(mod):
80
+ self.loaded.add(module_str)
81
+ else:
82
+ return "no load function"
83
+
84
+ def unload_extension(self, module_str: str):
85
+ """Unload an IPython extension by its module name.
86
+
87
+ This function looks up the extension's name in ``sys.modules`` and
88
+ simply calls ``mod.unload_ipython_extension(self)``.
89
+
90
+ Returns the string "no unload function" if the extension doesn't define
91
+ a function to unload itself, "not loaded" if the extension isn't loaded,
92
+ otherwise None.
93
+ """
94
+ if BUILTINS_EXTS.get(module_str, False) is True:
95
+ module_str = "IPython.extensions." + module_str
96
+ if module_str not in self.loaded:
97
+ return "not loaded"
98
+
99
+ if module_str in sys.modules:
100
+ mod = sys.modules[module_str]
101
+ if self._call_unload_ipython_extension(mod):
102
+ self.loaded.discard(module_str)
103
+ else:
104
+ return "no unload function"
105
+
106
+ def reload_extension(self, module_str: str):
107
+ """Reload an IPython extension by calling reload.
108
+
109
+ If the module has not been loaded before,
110
+ :meth:`InteractiveShell.load_extension` is called. Otherwise
111
+ :func:`reload` is called and then the :func:`load_ipython_extension`
112
+ function of the module, if it exists is called.
113
+ """
114
+
115
+ if BUILTINS_EXTS.get(module_str, False) is True:
116
+ module_str = "IPython.extensions." + module_str
117
+
118
+ if (module_str in self.loaded) and (module_str in sys.modules):
119
+ self.unload_extension(module_str)
120
+ mod = sys.modules[module_str]
121
+ reload(mod)
122
+ if self._call_load_ipython_extension(mod):
123
+ self.loaded.add(module_str)
124
+ else:
125
+ self.load_extension(module_str)
126
+
127
+ def _call_load_ipython_extension(self, mod):
128
+ if hasattr(mod, 'load_ipython_extension'):
129
+ mod.load_ipython_extension(self.shell)
130
+ return True
131
+
132
+ def _call_unload_ipython_extension(self, mod):
133
+ if hasattr(mod, 'unload_ipython_extension'):
134
+ mod.unload_ipython_extension(self.shell)
135
+ return True
temp_venv/lib/python3.13/site-packages/IPython/core/formatters.py ADDED
@@ -0,0 +1,1090 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """Display formatters.
3
+
4
+ This module defines the base instances in order to implement custom
5
+ formatters/mimetypes
6
+ got objects:
7
+
8
+ As we want to see internal IPython working we are going to use the following
9
+ function to diaply objects instead of the normal print or display method:
10
+
11
+ >>> ip = get_ipython()
12
+ >>> ip.display_formatter.format(...)
13
+ ({'text/plain': 'Ellipsis'}, {})
14
+
15
+ This return a tuple with the mimebumdle for the current object, and the
16
+ associated metadata.
17
+
18
+
19
+ We can now define our own formatter and register it:
20
+
21
+
22
+ >>> from IPython.core.formatters import BaseFormatter, FormatterABC
23
+
24
+
25
+ >>> class LLMFormatter(BaseFormatter):
26
+ ...
27
+ ... format_type = 'x-vendor/llm'
28
+ ... print_method = '_repr_llm_'
29
+ ... _return_type = (dict, str)
30
+
31
+ >>> llm_formatter = LLMFormatter(parent=ip.display_formatter)
32
+
33
+ >>> ip.display_formatter.formatters[LLMFormatter.format_type] = llm_formatter
34
+
35
+ Now any class that define `_repr_llm_` will return a x-vendor/llm as part of
36
+ it's display data:
37
+
38
+ >>> class A:
39
+ ...
40
+ ... def _repr_llm_(self, *kwargs):
41
+ ... return 'This a A'
42
+ ...
43
+
44
+ >>> ip.display_formatter.format(A())
45
+ ({'text/plain': '<IPython.core.formatters.A at ...>', 'x-vendor/llm': 'This a A'}, {})
46
+
47
+ As usual, you can register methods for third party types (see
48
+ :ref:`third_party_formatting`)
49
+
50
+ >>> def llm_int(obj):
51
+ ... return 'This is the integer %s, in between %s and %s'%(obj, obj-1, obj+1)
52
+
53
+ >>> llm_formatter.for_type(int, llm_int)
54
+
55
+ >>> ip.display_formatter.format(42)
56
+ ({'text/plain': '42', 'x-vendor/llm': 'This is the integer 42, in between 41 and 43'}, {})
57
+
58
+
59
+ Inheritance diagram:
60
+
61
+ .. inheritance-diagram:: IPython.core.formatters
62
+ :parts: 3
63
+ """
64
+
65
+ # Copyright (c) IPython Development Team.
66
+ # Distributed under the terms of the Modified BSD License.
67
+
68
+ import abc
69
+ import sys
70
+ import traceback
71
+ import warnings
72
+ from io import StringIO
73
+
74
+ from decorator import decorator
75
+
76
+ from traitlets.config.configurable import Configurable
77
+ from .getipython import get_ipython
78
+ from ..utils.sentinel import Sentinel
79
+ from ..utils.dir2 import get_real_method
80
+ from ..lib import pretty
81
+ from traitlets import (
82
+ Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List,
83
+ ForwardDeclaredInstance,
84
+ default, observe,
85
+ )
86
+
87
+ from typing import Any
88
+
89
+
90
+ class DisplayFormatter(Configurable):
91
+
92
+ active_types = List(Unicode(),
93
+ help="""List of currently active mime-types to display.
94
+ You can use this to set a white-list for formats to display.
95
+
96
+ Most users will not need to change this value.
97
+ """,
98
+ ).tag(config=True)
99
+
100
+ @default('active_types')
101
+ def _active_types_default(self):
102
+ return self.format_types
103
+
104
+ @observe('active_types')
105
+ def _active_types_changed(self, change):
106
+ for key, formatter in self.formatters.items():
107
+ if key in change['new']:
108
+ formatter.enabled = True
109
+ else:
110
+ formatter.enabled = False
111
+
112
+ ipython_display_formatter = ForwardDeclaredInstance("FormatterABC") # type: ignore
113
+
114
+ @default("ipython_display_formatter")
115
+ def _default_formatter(self):
116
+ return IPythonDisplayFormatter(parent=self)
117
+
118
+ mimebundle_formatter = ForwardDeclaredInstance("FormatterABC") # type: ignore
119
+
120
+ @default("mimebundle_formatter")
121
+ def _default_mime_formatter(self):
122
+ return MimeBundleFormatter(parent=self)
123
+
124
+ # A dict of formatter whose keys are format types (MIME types) and whose
125
+ # values are subclasses of BaseFormatter.
126
+ formatters = Dict()
127
+
128
+ @default("formatters")
129
+ def _formatters_default(self):
130
+ """Activate the default formatters."""
131
+ formatter_classes = [
132
+ PlainTextFormatter,
133
+ HTMLFormatter,
134
+ MarkdownFormatter,
135
+ SVGFormatter,
136
+ PNGFormatter,
137
+ PDFFormatter,
138
+ JPEGFormatter,
139
+ LatexFormatter,
140
+ JSONFormatter,
141
+ JavascriptFormatter
142
+ ]
143
+ d = {}
144
+ for cls in formatter_classes:
145
+ f = cls(parent=self)
146
+ d[f.format_type] = f
147
+ return d
148
+
149
+ def format(self, obj, include=None, exclude=None):
150
+ """Return a format data dict for an object.
151
+
152
+ By default all format types will be computed.
153
+
154
+ The following MIME types are usually implemented:
155
+
156
+ * text/plain
157
+ * text/html
158
+ * text/markdown
159
+ * text/latex
160
+ * application/json
161
+ * application/javascript
162
+ * application/pdf
163
+ * image/png
164
+ * image/jpeg
165
+ * image/svg+xml
166
+
167
+ Parameters
168
+ ----------
169
+ obj : object
170
+ The Python object whose format data will be computed.
171
+ include : list, tuple or set; optional
172
+ A list of format type strings (MIME types) to include in the
173
+ format data dict. If this is set *only* the format types included
174
+ in this list will be computed.
175
+ exclude : list, tuple or set; optional
176
+ A list of format type string (MIME types) to exclude in the format
177
+ data dict. If this is set all format types will be computed,
178
+ except for those included in this argument.
179
+ Mimetypes present in exclude will take precedence over the ones in include
180
+
181
+ Returns
182
+ -------
183
+ (format_dict, metadata_dict) : tuple of two dicts
184
+ format_dict is a dictionary of key/value pairs, one of each format that was
185
+ generated for the object. The keys are the format types, which
186
+ will usually be MIME type strings and the values and JSON'able
187
+ data structure containing the raw data for the representation in
188
+ that format.
189
+
190
+ metadata_dict is a dictionary of metadata about each mime-type output.
191
+ Its keys will be a strict subset of the keys in format_dict.
192
+
193
+ Notes
194
+ -----
195
+ If an object implement `_repr_mimebundle_` as well as various
196
+ `_repr_*_`, the data returned by `_repr_mimebundle_` will take
197
+ precedence and the corresponding `_repr_*_` for this mimetype will
198
+ not be called.
199
+
200
+ """
201
+ format_dict = {}
202
+ md_dict = {}
203
+
204
+ if self.ipython_display_formatter(obj):
205
+ # object handled itself, don't proceed
206
+ return {}, {}
207
+
208
+ format_dict, md_dict = self.mimebundle_formatter(obj, include=include, exclude=exclude)
209
+
210
+ if format_dict or md_dict:
211
+ if include:
212
+ format_dict = {k:v for k,v in format_dict.items() if k in include}
213
+ md_dict = {k:v for k,v in md_dict.items() if k in include}
214
+ if exclude:
215
+ format_dict = {k:v for k,v in format_dict.items() if k not in exclude}
216
+ md_dict = {k:v for k,v in md_dict.items() if k not in exclude}
217
+
218
+ for format_type, formatter in self.formatters.items():
219
+ if format_type in format_dict:
220
+ # already got it from mimebundle, maybe don't render again.
221
+ # exception: manually registered per-mime renderer
222
+ # check priority:
223
+ # 1. user-registered per-mime formatter
224
+ # 2. mime-bundle (user-registered or repr method)
225
+ # 3. default per-mime formatter (e.g. repr method)
226
+ try:
227
+ formatter.lookup(obj)
228
+ except KeyError:
229
+ # no special formatter, use mime-bundle-provided value
230
+ continue
231
+ if include and format_type not in include:
232
+ continue
233
+ if exclude and format_type in exclude:
234
+ continue
235
+
236
+ md = None
237
+ try:
238
+ data = formatter(obj)
239
+ except:
240
+ # FIXME: log the exception
241
+ raise
242
+
243
+ # formatters can return raw data or (data, metadata)
244
+ if isinstance(data, tuple) and len(data) == 2:
245
+ data, md = data
246
+
247
+ if data is not None:
248
+ format_dict[format_type] = data
249
+ if md is not None:
250
+ md_dict[format_type] = md
251
+ return format_dict, md_dict
252
+
253
+ @property
254
+ def format_types(self):
255
+ """Return the format types (MIME types) of the active formatters."""
256
+ return list(self.formatters.keys())
257
+
258
+
259
+ #-----------------------------------------------------------------------------
260
+ # Formatters for specific format types (text, html, svg, etc.)
261
+ #-----------------------------------------------------------------------------
262
+
263
+
264
+ def _safe_repr(obj):
265
+ """Try to return a repr of an object
266
+
267
+ always returns a string, at least.
268
+ """
269
+ try:
270
+ return repr(obj)
271
+ except Exception as e:
272
+ return "un-repr-able object (%r)" % e
273
+
274
+
275
+ class FormatterWarning(UserWarning):
276
+ """Warning class for errors in formatters"""
277
+
278
+ @decorator
279
+ def catch_format_error(method, self, *args, **kwargs):
280
+ """show traceback on failed format call"""
281
+ try:
282
+ r = method(self, *args, **kwargs)
283
+ except NotImplementedError:
284
+ # don't warn on NotImplementedErrors
285
+ return self._check_return(None, args[0])
286
+ except Exception:
287
+ exc_info = sys.exc_info()
288
+ ip = get_ipython()
289
+ if ip is not None:
290
+ ip.showtraceback(exc_info)
291
+ else:
292
+ traceback.print_exception(*exc_info)
293
+ return self._check_return(None, args[0])
294
+ return self._check_return(r, args[0])
295
+
296
+
297
+ class FormatterABC(metaclass=abc.ABCMeta):
298
+ """ Abstract base class for Formatters.
299
+
300
+ A formatter is a callable class that is responsible for computing the
301
+ raw format data for a particular format type (MIME type). For example,
302
+ an HTML formatter would have a format type of `text/html` and would return
303
+ the HTML representation of the object when called.
304
+ """
305
+
306
+ # The format type of the data returned, usually a MIME type.
307
+ format_type = 'text/plain'
308
+
309
+ # Is the formatter enabled...
310
+ enabled = True
311
+
312
+ @abc.abstractmethod
313
+ def __call__(self, obj):
314
+ """Return a JSON'able representation of the object.
315
+
316
+ If the object cannot be formatted by this formatter,
317
+ warn and return None.
318
+ """
319
+ return repr(obj)
320
+
321
+
322
+ def _mod_name_key(typ):
323
+ """Return a (__module__, __name__) tuple for a type.
324
+
325
+ Used as key in Formatter.deferred_printers.
326
+ """
327
+ module = getattr(typ, '__module__', None)
328
+ name = getattr(typ, '__name__', None)
329
+ return (module, name)
330
+
331
+
332
+ def _get_type(obj):
333
+ """Return the type of an instance (old and new-style)"""
334
+ return getattr(obj, '__class__', None) or type(obj)
335
+
336
+
337
+ _raise_key_error = Sentinel(
338
+ "_raise_key_error",
339
+ __name__,
340
+ """
341
+ Special value to raise a KeyError
342
+
343
+ Raise KeyError in `BaseFormatter.pop` if passed as the default value to `pop`
344
+ """,
345
+ )
346
+
347
+
348
+ class BaseFormatter(Configurable):
349
+ """A base formatter class that is configurable.
350
+
351
+ This formatter should usually be used as the base class of all formatters.
352
+ It is a traited :class:`Configurable` class and includes an extensible
353
+ API for users to determine how their objects are formatted. The following
354
+ logic is used to find a function to format an given object.
355
+
356
+ 1. The object is introspected to see if it has a method with the name
357
+ :attr:`print_method`. If is does, that object is passed to that method
358
+ for formatting.
359
+ 2. If no print method is found, three internal dictionaries are consulted
360
+ to find print method: :attr:`singleton_printers`, :attr:`type_printers`
361
+ and :attr:`deferred_printers`.
362
+
363
+ Users should use these dictionaries to register functions that will be
364
+ used to compute the format data for their objects (if those objects don't
365
+ have the special print methods). The easiest way of using these
366
+ dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
367
+ methods.
368
+
369
+ If no function/callable is found to compute the format data, ``None`` is
370
+ returned and this format type is not used.
371
+ """
372
+
373
+ format_type = Unicode("text/plain")
374
+ _return_type: Any = str
375
+
376
+ enabled = Bool(True).tag(config=True)
377
+
378
+ print_method = ObjectName('__repr__')
379
+
380
+ # The singleton printers.
381
+ # Maps the IDs of the builtin singleton objects to the format functions.
382
+ singleton_printers = Dict().tag(config=True)
383
+
384
+ # The type-specific printers.
385
+ # Map type objects to the format functions.
386
+ type_printers = Dict().tag(config=True)
387
+
388
+ # The deferred-import type-specific printers.
389
+ # Map (modulename, classname) pairs to the format functions.
390
+ deferred_printers = Dict().tag(config=True)
391
+
392
+ @catch_format_error
393
+ def __call__(self, obj):
394
+ """Compute the format for an object."""
395
+ if self.enabled:
396
+ # lookup registered printer
397
+ try:
398
+ printer = self.lookup(obj)
399
+ except KeyError:
400
+ pass
401
+ else:
402
+ return printer(obj)
403
+ # Finally look for special method names
404
+ method = get_real_method(obj, self.print_method)
405
+ if method is not None:
406
+ return method()
407
+ return None
408
+ else:
409
+ return None
410
+
411
+ def __contains__(self, typ):
412
+ """map in to lookup_by_type"""
413
+ try:
414
+ self.lookup_by_type(typ)
415
+ except KeyError:
416
+ return False
417
+ else:
418
+ return True
419
+
420
+ def _check_return(self, r, obj):
421
+ """Check that a return value is appropriate
422
+
423
+ Return the value if so, None otherwise, warning if invalid.
424
+ """
425
+ if r is None or isinstance(r, self._return_type) or \
426
+ (isinstance(r, tuple) and r and isinstance(r[0], self._return_type)):
427
+ return r
428
+ else:
429
+ warnings.warn(
430
+ "%s formatter returned invalid type %s (expected %s) for object: %s" % \
431
+ (self.format_type, type(r), self._return_type, _safe_repr(obj)),
432
+ FormatterWarning
433
+ )
434
+
435
+ def lookup(self, obj):
436
+ """Look up the formatter for a given instance.
437
+
438
+ Parameters
439
+ ----------
440
+ obj : object instance
441
+
442
+ Returns
443
+ -------
444
+ f : callable
445
+ The registered formatting callable for the type.
446
+
447
+ Raises
448
+ ------
449
+ KeyError if the type has not been registered.
450
+ """
451
+ # look for singleton first
452
+ obj_id = id(obj)
453
+ if obj_id in self.singleton_printers:
454
+ return self.singleton_printers[obj_id]
455
+ # then lookup by type
456
+ return self.lookup_by_type(_get_type(obj))
457
+
458
+ def lookup_by_type(self, typ):
459
+ """Look up the registered formatter for a type.
460
+
461
+ Parameters
462
+ ----------
463
+ typ : type or '__module__.__name__' string for a type
464
+
465
+ Returns
466
+ -------
467
+ f : callable
468
+ The registered formatting callable for the type.
469
+
470
+ Raises
471
+ ------
472
+ KeyError if the type has not been registered.
473
+ """
474
+ if isinstance(typ, str):
475
+ typ_key = tuple(typ.rsplit('.',1))
476
+ if typ_key not in self.deferred_printers:
477
+ # We may have it cached in the type map. We will have to
478
+ # iterate over all of the types to check.
479
+ for cls in self.type_printers:
480
+ if _mod_name_key(cls) == typ_key:
481
+ return self.type_printers[cls]
482
+ else:
483
+ return self.deferred_printers[typ_key]
484
+ else:
485
+ for cls in pretty._get_mro(typ):
486
+ if cls in self.type_printers or self._in_deferred_types(cls):
487
+ return self.type_printers[cls]
488
+
489
+ # If we have reached here, the lookup failed.
490
+ raise KeyError("No registered printer for {0!r}".format(typ))
491
+
492
+ def for_type(self, typ, func=None):
493
+ """Add a format function for a given type.
494
+
495
+ Parameters
496
+ ----------
497
+ typ : type or '__module__.__name__' string for a type
498
+ The class of the object that will be formatted using `func`.
499
+
500
+ func : callable
501
+ A callable for computing the format data.
502
+ `func` will be called with the object to be formatted,
503
+ and will return the raw data in this formatter's format.
504
+ Subclasses may use a different call signature for the
505
+ `func` argument.
506
+
507
+ If `func` is None or not specified, there will be no change,
508
+ only returning the current value.
509
+
510
+ Returns
511
+ -------
512
+ oldfunc : callable
513
+ The currently registered callable.
514
+ If you are registering a new formatter,
515
+ this will be the previous value (to enable restoring later).
516
+ """
517
+ # if string given, interpret as 'pkg.module.class_name'
518
+ if isinstance(typ, str):
519
+ type_module, type_name = typ.rsplit('.', 1)
520
+ return self.for_type_by_name(type_module, type_name, func)
521
+
522
+ try:
523
+ oldfunc = self.lookup_by_type(typ)
524
+ except KeyError:
525
+ oldfunc = None
526
+
527
+ if func is not None:
528
+ self.type_printers[typ] = func
529
+
530
+ return oldfunc
531
+
532
+ def for_type_by_name(self, type_module, type_name, func=None):
533
+ """Add a format function for a type specified by the full dotted
534
+ module and name of the type, rather than the type of the object.
535
+
536
+ Parameters
537
+ ----------
538
+ type_module : str
539
+ The full dotted name of the module the type is defined in, like
540
+ ``numpy``.
541
+
542
+ type_name : str
543
+ The name of the type (the class name), like ``dtype``
544
+
545
+ func : callable
546
+ A callable for computing the format data.
547
+ `func` will be called with the object to be formatted,
548
+ and will return the raw data in this formatter's format.
549
+ Subclasses may use a different call signature for the
550
+ `func` argument.
551
+
552
+ If `func` is None or unspecified, there will be no change,
553
+ only returning the current value.
554
+
555
+ Returns
556
+ -------
557
+ oldfunc : callable
558
+ The currently registered callable.
559
+ If you are registering a new formatter,
560
+ this will be the previous value (to enable restoring later).
561
+ """
562
+ key = (type_module, type_name)
563
+
564
+ try:
565
+ oldfunc = self.lookup_by_type("%s.%s" % key)
566
+ except KeyError:
567
+ oldfunc = None
568
+
569
+ if func is not None:
570
+ self.deferred_printers[key] = func
571
+ return oldfunc
572
+
573
+ def pop(self, typ, default=_raise_key_error):
574
+ """Pop a formatter for the given type.
575
+
576
+ Parameters
577
+ ----------
578
+ typ : type or '__module__.__name__' string for a type
579
+ default : object
580
+ value to be returned if no formatter is registered for typ.
581
+
582
+ Returns
583
+ -------
584
+ obj : object
585
+ The last registered object for the type.
586
+
587
+ Raises
588
+ ------
589
+ KeyError if the type is not registered and default is not specified.
590
+ """
591
+
592
+ if isinstance(typ, str):
593
+ typ_key = tuple(typ.rsplit('.',1))
594
+ if typ_key not in self.deferred_printers:
595
+ # We may have it cached in the type map. We will have to
596
+ # iterate over all of the types to check.
597
+ for cls in self.type_printers:
598
+ if _mod_name_key(cls) == typ_key:
599
+ old = self.type_printers.pop(cls)
600
+ break
601
+ else:
602
+ old = default
603
+ else:
604
+ old = self.deferred_printers.pop(typ_key)
605
+ else:
606
+ if typ in self.type_printers:
607
+ old = self.type_printers.pop(typ)
608
+ else:
609
+ old = self.deferred_printers.pop(_mod_name_key(typ), default)
610
+ if old is _raise_key_error:
611
+ raise KeyError("No registered value for {0!r}".format(typ))
612
+ return old
613
+
614
+ def _in_deferred_types(self, cls):
615
+ """
616
+ Check if the given class is specified in the deferred type registry.
617
+
618
+ Successful matches will be moved to the regular type registry for future use.
619
+ """
620
+ mod = getattr(cls, '__module__', None)
621
+ name = getattr(cls, '__name__', None)
622
+ key = (mod, name)
623
+ if key in self.deferred_printers:
624
+ # Move the printer over to the regular registry.
625
+ printer = self.deferred_printers.pop(key)
626
+ self.type_printers[cls] = printer
627
+ return True
628
+ return False
629
+
630
+
631
+ class PlainTextFormatter(BaseFormatter):
632
+ """The default pretty-printer.
633
+
634
+ This uses :mod:`IPython.lib.pretty` to compute the format data of
635
+ the object. If the object cannot be pretty printed, :func:`repr` is used.
636
+ See the documentation of :mod:`IPython.lib.pretty` for details on
637
+ how to write pretty printers. Here is a simple example::
638
+
639
+ def dtype_pprinter(obj, p, cycle):
640
+ if cycle:
641
+ return p.text('dtype(...)')
642
+ if hasattr(obj, 'fields'):
643
+ if obj.fields is None:
644
+ p.text(repr(obj))
645
+ else:
646
+ p.begin_group(7, 'dtype([')
647
+ for i, field in enumerate(obj.descr):
648
+ if i > 0:
649
+ p.text(',')
650
+ p.breakable()
651
+ p.pretty(field)
652
+ p.end_group(7, '])')
653
+ """
654
+
655
+ # The format type of data returned.
656
+ format_type = Unicode('text/plain')
657
+
658
+ # This subclass ignores this attribute as it always need to return
659
+ # something.
660
+ enabled = Bool(True).tag(config=False)
661
+
662
+ max_seq_length = Integer(pretty.MAX_SEQ_LENGTH,
663
+ help="""Truncate large collections (lists, dicts, tuples, sets) to this size.
664
+
665
+ Set to 0 to disable truncation.
666
+ """,
667
+ ).tag(config=True)
668
+
669
+ # Look for a _repr_pretty_ methods to use for pretty printing.
670
+ print_method = ObjectName('_repr_pretty_')
671
+
672
+ # Whether to pretty-print or not.
673
+ pprint = Bool(True).tag(config=True)
674
+
675
+ # Whether to be verbose or not.
676
+ verbose = Bool(False).tag(config=True)
677
+
678
+ # The maximum width.
679
+ max_width = Integer(79).tag(config=True)
680
+
681
+ # The newline character.
682
+ newline = Unicode('\n').tag(config=True)
683
+
684
+ # format-string for pprinting floats
685
+ float_format = Unicode('%r')
686
+ # setter for float precision, either int or direct format-string
687
+ float_precision = CUnicode('').tag(config=True)
688
+
689
+ @observe('float_precision')
690
+ def _float_precision_changed(self, change):
691
+ """float_precision changed, set float_format accordingly.
692
+
693
+ float_precision can be set by int or str.
694
+ This will set float_format, after interpreting input.
695
+ If numpy has been imported, numpy print precision will also be set.
696
+
697
+ integer `n` sets format to '%.nf', otherwise, format set directly.
698
+
699
+ An empty string returns to defaults (repr for float, 8 for numpy).
700
+
701
+ This parameter can be set via the '%precision' magic.
702
+ """
703
+ new = change['new']
704
+ if '%' in new:
705
+ # got explicit format string
706
+ fmt = new
707
+ try:
708
+ fmt%3.14159
709
+ except Exception as e:
710
+ raise ValueError("Precision must be int or format string, not %r"%new) from e
711
+ elif new:
712
+ # otherwise, should be an int
713
+ try:
714
+ i = int(new)
715
+ assert i >= 0
716
+ except ValueError as e:
717
+ raise ValueError("Precision must be int or format string, not %r"%new) from e
718
+ except AssertionError as e:
719
+ raise ValueError("int precision must be non-negative, not %r"%i) from e
720
+
721
+ fmt = '%%.%if'%i
722
+ if 'numpy' in sys.modules:
723
+ # set numpy precision if it has been imported
724
+ import numpy
725
+ numpy.set_printoptions(precision=i)
726
+ else:
727
+ # default back to repr
728
+ fmt = '%r'
729
+ if 'numpy' in sys.modules:
730
+ import numpy
731
+ # numpy default is 8
732
+ numpy.set_printoptions(precision=8)
733
+ self.float_format = fmt
734
+
735
+ # Use the default pretty printers from IPython.lib.pretty.
736
+ @default('singleton_printers')
737
+ def _singleton_printers_default(self):
738
+ return pretty._singleton_pprinters.copy()
739
+
740
+ @default('type_printers')
741
+ def _type_printers_default(self):
742
+ d = pretty._type_pprinters.copy()
743
+ d[float] = lambda obj,p,cycle: p.text(self.float_format%obj)
744
+ # if NumPy is used, set precision for its float64 type
745
+ if "numpy" in sys.modules:
746
+ import numpy
747
+
748
+ d[numpy.float64] = lambda obj, p, cycle: p.text(self.float_format % obj)
749
+ return d
750
+
751
+ @default('deferred_printers')
752
+ def _deferred_printers_default(self):
753
+ return pretty._deferred_type_pprinters.copy()
754
+
755
+ #### FormatterABC interface ####
756
+
757
+ @catch_format_error
758
+ def __call__(self, obj):
759
+ """Compute the pretty representation of the object."""
760
+ if not self.pprint:
761
+ return repr(obj)
762
+ else:
763
+ stream = StringIO()
764
+ printer = pretty.RepresentationPrinter(stream, self.verbose,
765
+ self.max_width, self.newline,
766
+ max_seq_length=self.max_seq_length,
767
+ singleton_pprinters=self.singleton_printers,
768
+ type_pprinters=self.type_printers,
769
+ deferred_pprinters=self.deferred_printers)
770
+ printer.pretty(obj)
771
+ printer.flush()
772
+ return stream.getvalue()
773
+
774
+
775
+ class HTMLFormatter(BaseFormatter):
776
+ """An HTML formatter.
777
+
778
+ To define the callables that compute the HTML representation of your
779
+ objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
780
+ or :meth:`for_type_by_name` methods to register functions that handle
781
+ this.
782
+
783
+ The return value of this formatter should be a valid HTML snippet that
784
+ could be injected into an existing DOM. It should *not* include the
785
+ ```<html>`` or ```<body>`` tags.
786
+ """
787
+ format_type = Unicode('text/html')
788
+
789
+ print_method = ObjectName('_repr_html_')
790
+
791
+
792
+ class MarkdownFormatter(BaseFormatter):
793
+ """A Markdown formatter.
794
+
795
+ To define the callables that compute the Markdown representation of your
796
+ objects, define a :meth:`_repr_markdown_` method or use the :meth:`for_type`
797
+ or :meth:`for_type_by_name` methods to register functions that handle
798
+ this.
799
+
800
+ The return value of this formatter should be a valid Markdown.
801
+ """
802
+ format_type = Unicode('text/markdown')
803
+
804
+ print_method = ObjectName('_repr_markdown_')
805
+
806
+ class SVGFormatter(BaseFormatter):
807
+ """An SVG formatter.
808
+
809
+ To define the callables that compute the SVG representation of your
810
+ objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
811
+ or :meth:`for_type_by_name` methods to register functions that handle
812
+ this.
813
+
814
+ The return value of this formatter should be valid SVG enclosed in
815
+ ```<svg>``` tags, that could be injected into an existing DOM. It should
816
+ *not* include the ```<html>`` or ```<body>`` tags.
817
+ """
818
+ format_type = Unicode('image/svg+xml')
819
+
820
+ print_method = ObjectName('_repr_svg_')
821
+
822
+
823
+ class PNGFormatter(BaseFormatter):
824
+ """A PNG formatter.
825
+
826
+ To define the callables that compute the PNG representation of your
827
+ objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
828
+ or :meth:`for_type_by_name` methods to register functions that handle
829
+ this.
830
+
831
+ The return value of this formatter should be raw PNG data, *not*
832
+ base64 encoded.
833
+ """
834
+ format_type = Unicode('image/png')
835
+
836
+ print_method = ObjectName('_repr_png_')
837
+
838
+ _return_type = (bytes, str)
839
+
840
+
841
+ class JPEGFormatter(BaseFormatter):
842
+ """A JPEG formatter.
843
+
844
+ To define the callables that compute the JPEG representation of your
845
+ objects, define a :meth:`_repr_jpeg_` method or use the :meth:`for_type`
846
+ or :meth:`for_type_by_name` methods to register functions that handle
847
+ this.
848
+
849
+ The return value of this formatter should be raw JPEG data, *not*
850
+ base64 encoded.
851
+ """
852
+ format_type = Unicode('image/jpeg')
853
+
854
+ print_method = ObjectName('_repr_jpeg_')
855
+
856
+ _return_type = (bytes, str)
857
+
858
+
859
+ class LatexFormatter(BaseFormatter):
860
+ """A LaTeX formatter.
861
+
862
+ To define the callables that compute the LaTeX representation of your
863
+ objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
864
+ or :meth:`for_type_by_name` methods to register functions that handle
865
+ this.
866
+
867
+ The return value of this formatter should be a valid LaTeX equation,
868
+ enclosed in either ```$```, ```$$``` or another LaTeX equation
869
+ environment.
870
+ """
871
+ format_type = Unicode('text/latex')
872
+
873
+ print_method = ObjectName('_repr_latex_')
874
+
875
+
876
+ class JSONFormatter(BaseFormatter):
877
+ """A JSON string formatter.
878
+
879
+ To define the callables that compute the JSONable representation of
880
+ your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
881
+ or :meth:`for_type_by_name` methods to register functions that handle
882
+ this.
883
+
884
+ The return value of this formatter should be a JSONable list or dict.
885
+ JSON scalars (None, number, string) are not allowed, only dict or list containers.
886
+ """
887
+ format_type = Unicode('application/json')
888
+ _return_type = (list, dict)
889
+
890
+ print_method = ObjectName('_repr_json_')
891
+
892
+ def _check_return(self, r, obj):
893
+ """Check that a return value is appropriate
894
+
895
+ Return the value if so, None otherwise, warning if invalid.
896
+ """
897
+ if r is None:
898
+ return
899
+ md = None
900
+ if isinstance(r, tuple):
901
+ # unpack data, metadata tuple for type checking on first element
902
+ r, md = r
903
+
904
+ assert not isinstance(
905
+ r, str
906
+ ), "JSON-as-string has been deprecated since IPython < 3"
907
+
908
+ if md is not None:
909
+ # put the tuple back together
910
+ r = (r, md)
911
+ return super(JSONFormatter, self)._check_return(r, obj)
912
+
913
+
914
+ class JavascriptFormatter(BaseFormatter):
915
+ """A Javascript formatter.
916
+
917
+ To define the callables that compute the Javascript representation of
918
+ your objects, define a :meth:`_repr_javascript_` method or use the
919
+ :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
920
+ that handle this.
921
+
922
+ The return value of this formatter should be valid Javascript code and
923
+ should *not* be enclosed in ```<script>``` tags.
924
+ """
925
+ format_type = Unicode('application/javascript')
926
+
927
+ print_method = ObjectName('_repr_javascript_')
928
+
929
+
930
+ class PDFFormatter(BaseFormatter):
931
+ """A PDF formatter.
932
+
933
+ To define the callables that compute the PDF representation of your
934
+ objects, define a :meth:`_repr_pdf_` method or use the :meth:`for_type`
935
+ or :meth:`for_type_by_name` methods to register functions that handle
936
+ this.
937
+
938
+ The return value of this formatter should be raw PDF data, *not*
939
+ base64 encoded.
940
+ """
941
+ format_type = Unicode('application/pdf')
942
+
943
+ print_method = ObjectName('_repr_pdf_')
944
+
945
+ _return_type = (bytes, str)
946
+
947
+ class IPythonDisplayFormatter(BaseFormatter):
948
+ """An escape-hatch Formatter for objects that know how to display themselves.
949
+
950
+ To define the callables that compute the representation of your
951
+ objects, define a :meth:`_ipython_display_` method or use the :meth:`for_type`
952
+ or :meth:`for_type_by_name` methods to register functions that handle
953
+ this. Unlike mime-type displays, this method should not return anything,
954
+ instead calling any appropriate display methods itself.
955
+
956
+ This display formatter has highest priority.
957
+ If it fires, no other display formatter will be called.
958
+
959
+ Prior to IPython 6.1, `_ipython_display_` was the only way to display custom mime-types
960
+ without registering a new Formatter.
961
+
962
+ IPython 6.1 introduces `_repr_mimebundle_` for displaying custom mime-types,
963
+ so `_ipython_display_` should only be used for objects that require unusual
964
+ display patterns, such as multiple display calls.
965
+ """
966
+ print_method = ObjectName('_ipython_display_')
967
+ _return_type = (type(None), bool)
968
+
969
+ @catch_format_error
970
+ def __call__(self, obj):
971
+ """Compute the format for an object."""
972
+ if self.enabled:
973
+ # lookup registered printer
974
+ try:
975
+ printer = self.lookup(obj)
976
+ except KeyError:
977
+ pass
978
+ else:
979
+ printer(obj)
980
+ return True
981
+ # Finally look for special method names
982
+ method = get_real_method(obj, self.print_method)
983
+ if method is not None:
984
+ method()
985
+ return True
986
+
987
+
988
+ class MimeBundleFormatter(BaseFormatter):
989
+ """A Formatter for arbitrary mime-types.
990
+
991
+ Unlike other `_repr_<mimetype>_` methods,
992
+ `_repr_mimebundle_` should return mime-bundle data,
993
+ either the mime-keyed `data` dictionary or the tuple `(data, metadata)`.
994
+ Any mime-type is valid.
995
+
996
+ To define the callables that compute the mime-bundle representation of your
997
+ objects, define a :meth:`_repr_mimebundle_` method or use the :meth:`for_type`
998
+ or :meth:`for_type_by_name` methods to register functions that handle
999
+ this.
1000
+
1001
+ .. versionadded:: 6.1
1002
+ """
1003
+ print_method = ObjectName('_repr_mimebundle_')
1004
+ _return_type = dict
1005
+
1006
+ def _check_return(self, r, obj):
1007
+ r = super(MimeBundleFormatter, self)._check_return(r, obj)
1008
+ # always return (data, metadata):
1009
+ if r is None:
1010
+ return {}, {}
1011
+ if not isinstance(r, tuple):
1012
+ return r, {}
1013
+ return r
1014
+
1015
+ @catch_format_error
1016
+ def __call__(self, obj, include=None, exclude=None):
1017
+ """Compute the format for an object.
1018
+
1019
+ Identical to parent's method but we pass extra parameters to the method.
1020
+
1021
+ Unlike other _repr_*_ `_repr_mimebundle_` should allow extra kwargs, in
1022
+ particular `include` and `exclude`.
1023
+ """
1024
+ if self.enabled:
1025
+ # lookup registered printer
1026
+ try:
1027
+ printer = self.lookup(obj)
1028
+ except KeyError:
1029
+ pass
1030
+ else:
1031
+ return printer(obj)
1032
+ # Finally look for special method names
1033
+ method = get_real_method(obj, self.print_method)
1034
+
1035
+ if method is not None:
1036
+ return method(include=include, exclude=exclude)
1037
+ return None
1038
+ else:
1039
+ return None
1040
+
1041
+
1042
+ FormatterABC.register(BaseFormatter)
1043
+ FormatterABC.register(PlainTextFormatter)
1044
+ FormatterABC.register(HTMLFormatter)
1045
+ FormatterABC.register(MarkdownFormatter)
1046
+ FormatterABC.register(SVGFormatter)
1047
+ FormatterABC.register(PNGFormatter)
1048
+ FormatterABC.register(PDFFormatter)
1049
+ FormatterABC.register(JPEGFormatter)
1050
+ FormatterABC.register(LatexFormatter)
1051
+ FormatterABC.register(JSONFormatter)
1052
+ FormatterABC.register(JavascriptFormatter)
1053
+ FormatterABC.register(IPythonDisplayFormatter)
1054
+ FormatterABC.register(MimeBundleFormatter)
1055
+
1056
+
1057
+ def format_display_data(obj, include=None, exclude=None):
1058
+ """Return a format data dict for an object.
1059
+
1060
+ By default all format types will be computed.
1061
+
1062
+ Parameters
1063
+ ----------
1064
+ obj : object
1065
+ The Python object whose format data will be computed.
1066
+
1067
+ Returns
1068
+ -------
1069
+ format_dict : dict
1070
+ A dictionary of key/value pairs, one or each format that was
1071
+ generated for the object. The keys are the format types, which
1072
+ will usually be MIME type strings and the values and JSON'able
1073
+ data structure containing the raw data for the representation in
1074
+ that format.
1075
+ include : list or tuple, optional
1076
+ A list of format type strings (MIME types) to include in the
1077
+ format data dict. If this is set *only* the format types included
1078
+ in this list will be computed.
1079
+ exclude : list or tuple, optional
1080
+ A list of format type string (MIME types) to exclude in the format
1081
+ data dict. If this is set all format types will be computed,
1082
+ except for those included in this argument.
1083
+ """
1084
+ from .interactiveshell import InteractiveShell
1085
+
1086
+ return InteractiveShell.instance().display_formatter.format(
1087
+ obj,
1088
+ include,
1089
+ exclude
1090
+ )
temp_venv/lib/python3.13/site-packages/IPython/core/getipython.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Simple function to call to get the current InteractiveShell instance
2
+ """
3
+
4
+ #-----------------------------------------------------------------------------
5
+ # Copyright (C) 2013 The IPython Development Team
6
+ #
7
+ # Distributed under the terms of the BSD License. The full license is in
8
+ # the file COPYING, distributed as part of this software.
9
+ #-----------------------------------------------------------------------------
10
+
11
+ #-----------------------------------------------------------------------------
12
+ # Classes and functions
13
+ #-----------------------------------------------------------------------------
14
+
15
+
16
+ def get_ipython():
17
+ """Get the global InteractiveShell instance.
18
+
19
+ Returns None if no InteractiveShell instance is registered.
20
+ """
21
+ from IPython.core.interactiveshell import InteractiveShell
22
+ if InteractiveShell.initialized():
23
+ return InteractiveShell.instance()
temp_venv/lib/python3.13/site-packages/IPython/core/guarded_eval.py ADDED
@@ -0,0 +1,893 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from inspect import isclass, signature, Signature
2
+ from pygments.formatters.terminal256 import Terminal256Formatter
3
+ from typing import (
4
+ Annotated,
5
+ AnyStr,
6
+ Callable,
7
+ Dict,
8
+ Literal,
9
+ NamedTuple,
10
+ NewType,
11
+ Optional,
12
+ Protocol,
13
+ Set,
14
+ Sequence,
15
+ Tuple,
16
+ Type,
17
+ TypeGuard,
18
+ Union,
19
+ get_args,
20
+ get_origin,
21
+ is_typeddict,
22
+ )
23
+ import ast
24
+ import builtins
25
+ import collections
26
+ import operator
27
+ import sys
28
+ from functools import cached_property
29
+ from dataclasses import dataclass, field
30
+ from types import MethodDescriptorType, ModuleType
31
+
32
+ from IPython.utils.decorators import undoc
33
+
34
+
35
+ from typing import Self, LiteralString
36
+
37
+ if sys.version_info < (3, 12):
38
+ from typing_extensions import TypeAliasType
39
+ else:
40
+ from typing import TypeAliasType
41
+
42
+
43
+ @undoc
44
+ class HasGetItem(Protocol):
45
+ def __getitem__(self, key) -> None: ...
46
+
47
+
48
+ @undoc
49
+ class InstancesHaveGetItem(Protocol):
50
+ def __call__(self, *args, **kwargs) -> HasGetItem: ...
51
+
52
+
53
+ @undoc
54
+ class HasGetAttr(Protocol):
55
+ def __getattr__(self, key) -> None: ...
56
+
57
+
58
+ @undoc
59
+ class DoesNotHaveGetAttr(Protocol):
60
+ pass
61
+
62
+
63
+ # By default `__getattr__` is not explicitly implemented on most objects
64
+ MayHaveGetattr = Union[HasGetAttr, DoesNotHaveGetAttr]
65
+
66
+
67
+ def _unbind_method(func: Callable) -> Union[Callable, None]:
68
+ """Get unbound method for given bound method.
69
+
70
+ Returns None if cannot get unbound method, or method is already unbound.
71
+ """
72
+ owner = getattr(func, "__self__", None)
73
+ owner_class = type(owner)
74
+ name = getattr(func, "__name__", None)
75
+ instance_dict_overrides = getattr(owner, "__dict__", None)
76
+ if (
77
+ owner is not None
78
+ and name
79
+ and (
80
+ not instance_dict_overrides
81
+ or (instance_dict_overrides and name not in instance_dict_overrides)
82
+ )
83
+ ):
84
+ return getattr(owner_class, name)
85
+ return None
86
+
87
+
88
+ @undoc
89
+ @dataclass
90
+ class EvaluationPolicy:
91
+ """Definition of evaluation policy."""
92
+
93
+ allow_locals_access: bool = False
94
+ allow_globals_access: bool = False
95
+ allow_item_access: bool = False
96
+ allow_attr_access: bool = False
97
+ allow_builtins_access: bool = False
98
+ allow_all_operations: bool = False
99
+ allow_any_calls: bool = False
100
+ allowed_calls: set[Callable] = field(default_factory=set)
101
+
102
+ def can_get_item(self, value, item):
103
+ return self.allow_item_access
104
+
105
+ def can_get_attr(self, value, attr):
106
+ return self.allow_attr_access
107
+
108
+ def can_operate(self, dunders: tuple[str, ...], a, b=None):
109
+ if self.allow_all_operations:
110
+ return True
111
+
112
+ def can_call(self, func):
113
+ if self.allow_any_calls:
114
+ return True
115
+
116
+ if func in self.allowed_calls:
117
+ return True
118
+
119
+ owner_method = _unbind_method(func)
120
+
121
+ if owner_method and owner_method in self.allowed_calls:
122
+ return True
123
+
124
+
125
+ def _get_external(module_name: str, access_path: Sequence[str]):
126
+ """Get value from external module given a dotted access path.
127
+
128
+ Raises:
129
+ * `KeyError` if module is removed not found, and
130
+ * `AttributeError` if access path does not match an exported object
131
+ """
132
+ member_type = sys.modules[module_name]
133
+ for attr in access_path:
134
+ member_type = getattr(member_type, attr)
135
+ return member_type
136
+
137
+
138
+ def _has_original_dunder_external(
139
+ value,
140
+ module_name: str,
141
+ access_path: Sequence[str],
142
+ method_name: str,
143
+ ):
144
+ if module_name not in sys.modules:
145
+ # LBYLB as it is faster
146
+ return False
147
+ try:
148
+ member_type = _get_external(module_name, access_path)
149
+ value_type = type(value)
150
+ if type(value) == member_type:
151
+ return True
152
+ if method_name == "__getattribute__":
153
+ # we have to short-circuit here due to an unresolved issue in
154
+ # `isinstance` implementation: https://bugs.python.org/issue32683
155
+ return False
156
+ if isinstance(value, member_type):
157
+ method = getattr(value_type, method_name, None)
158
+ member_method = getattr(member_type, method_name, None)
159
+ if member_method == method:
160
+ return True
161
+ except (AttributeError, KeyError):
162
+ return False
163
+
164
+
165
+ def _has_original_dunder(
166
+ value, allowed_types, allowed_methods, allowed_external, method_name
167
+ ):
168
+ # note: Python ignores `__getattr__`/`__getitem__` on instances,
169
+ # we only need to check at class level
170
+ value_type = type(value)
171
+
172
+ # strict type check passes → no need to check method
173
+ if value_type in allowed_types:
174
+ return True
175
+
176
+ method = getattr(value_type, method_name, None)
177
+
178
+ if method is None:
179
+ return None
180
+
181
+ if method in allowed_methods:
182
+ return True
183
+
184
+ for module_name, *access_path in allowed_external:
185
+ if _has_original_dunder_external(value, module_name, access_path, method_name):
186
+ return True
187
+
188
+ return False
189
+
190
+
191
+ @undoc
192
+ @dataclass
193
+ class SelectivePolicy(EvaluationPolicy):
194
+ allowed_getitem: set[InstancesHaveGetItem] = field(default_factory=set)
195
+ allowed_getitem_external: set[tuple[str, ...]] = field(default_factory=set)
196
+
197
+ allowed_getattr: set[MayHaveGetattr] = field(default_factory=set)
198
+ allowed_getattr_external: set[tuple[str, ...]] = field(default_factory=set)
199
+
200
+ allowed_operations: set = field(default_factory=set)
201
+ allowed_operations_external: set[tuple[str, ...]] = field(default_factory=set)
202
+
203
+ _operation_methods_cache: dict[str, set[Callable]] = field(
204
+ default_factory=dict, init=False
205
+ )
206
+
207
+ def can_get_attr(self, value, attr):
208
+ has_original_attribute = _has_original_dunder(
209
+ value,
210
+ allowed_types=self.allowed_getattr,
211
+ allowed_methods=self._getattribute_methods,
212
+ allowed_external=self.allowed_getattr_external,
213
+ method_name="__getattribute__",
214
+ )
215
+ has_original_attr = _has_original_dunder(
216
+ value,
217
+ allowed_types=self.allowed_getattr,
218
+ allowed_methods=self._getattr_methods,
219
+ allowed_external=self.allowed_getattr_external,
220
+ method_name="__getattr__",
221
+ )
222
+
223
+ accept = False
224
+
225
+ # Many objects do not have `__getattr__`, this is fine.
226
+ if has_original_attr is None and has_original_attribute:
227
+ accept = True
228
+ else:
229
+ # Accept objects without modifications to `__getattr__` and `__getattribute__`
230
+ accept = has_original_attr and has_original_attribute
231
+
232
+ if accept:
233
+ # We still need to check for overridden properties.
234
+
235
+ value_class = type(value)
236
+ if not hasattr(value_class, attr):
237
+ return True
238
+
239
+ class_attr_val = getattr(value_class, attr)
240
+ is_property = isinstance(class_attr_val, property)
241
+
242
+ if not is_property:
243
+ return True
244
+
245
+ # Properties in allowed types are ok (although we do not include any
246
+ # properties in our default allow list currently).
247
+ if type(value) in self.allowed_getattr:
248
+ return True # pragma: no cover
249
+
250
+ # Properties in subclasses of allowed types may be ok if not changed
251
+ for module_name, *access_path in self.allowed_getattr_external:
252
+ try:
253
+ external_class = _get_external(module_name, access_path)
254
+ external_class_attr_val = getattr(external_class, attr)
255
+ except (KeyError, AttributeError):
256
+ return False # pragma: no cover
257
+ return class_attr_val == external_class_attr_val
258
+
259
+ return False
260
+
261
+ def can_get_item(self, value, item):
262
+ """Allow accessing `__getiitem__` of allow-listed instances unless it was not modified."""
263
+ return _has_original_dunder(
264
+ value,
265
+ allowed_types=self.allowed_getitem,
266
+ allowed_methods=self._getitem_methods,
267
+ allowed_external=self.allowed_getitem_external,
268
+ method_name="__getitem__",
269
+ )
270
+
271
+ def can_operate(self, dunders: tuple[str, ...], a, b=None):
272
+ objects = [a]
273
+ if b is not None:
274
+ objects.append(b)
275
+ return all(
276
+ [
277
+ _has_original_dunder(
278
+ obj,
279
+ allowed_types=self.allowed_operations,
280
+ allowed_methods=self._operator_dunder_methods(dunder),
281
+ allowed_external=self.allowed_operations_external,
282
+ method_name=dunder,
283
+ )
284
+ for dunder in dunders
285
+ for obj in objects
286
+ ]
287
+ )
288
+
289
+ def _operator_dunder_methods(self, dunder: str) -> set[Callable]:
290
+ if dunder not in self._operation_methods_cache:
291
+ self._operation_methods_cache[dunder] = self._safe_get_methods(
292
+ self.allowed_operations, dunder
293
+ )
294
+ return self._operation_methods_cache[dunder]
295
+
296
+ @cached_property
297
+ def _getitem_methods(self) -> set[Callable]:
298
+ return self._safe_get_methods(self.allowed_getitem, "__getitem__")
299
+
300
+ @cached_property
301
+ def _getattr_methods(self) -> set[Callable]:
302
+ return self._safe_get_methods(self.allowed_getattr, "__getattr__")
303
+
304
+ @cached_property
305
+ def _getattribute_methods(self) -> set[Callable]:
306
+ return self._safe_get_methods(self.allowed_getattr, "__getattribute__")
307
+
308
+ def _safe_get_methods(self, classes, name) -> set[Callable]:
309
+ return {
310
+ method
311
+ for class_ in classes
312
+ for method in [getattr(class_, name, None)]
313
+ if method
314
+ }
315
+
316
+
317
+ class _DummyNamedTuple(NamedTuple):
318
+ """Used internally to retrieve methods of named tuple instance."""
319
+
320
+
321
+ class EvaluationContext(NamedTuple):
322
+ #: Local namespace
323
+ locals: dict
324
+ #: Global namespace
325
+ globals: dict
326
+ #: Evaluation policy identifier
327
+ evaluation: Literal["forbidden", "minimal", "limited", "unsafe", "dangerous"] = (
328
+ "forbidden"
329
+ )
330
+ #: Whether the evaluation of code takes place inside of a subscript.
331
+ #: Useful for evaluating ``:-1, 'col'`` in ``df[:-1, 'col']``.
332
+ in_subscript: bool = False
333
+
334
+
335
+ class _IdentitySubscript:
336
+ """Returns the key itself when item is requested via subscript."""
337
+
338
+ def __getitem__(self, key):
339
+ return key
340
+
341
+
342
+ IDENTITY_SUBSCRIPT = _IdentitySubscript()
343
+ SUBSCRIPT_MARKER = "__SUBSCRIPT_SENTINEL__"
344
+ UNKNOWN_SIGNATURE = Signature()
345
+ NOT_EVALUATED = object()
346
+
347
+
348
+ class GuardRejection(Exception):
349
+ """Exception raised when guard rejects evaluation attempt."""
350
+
351
+ pass
352
+
353
+
354
+ def guarded_eval(code: str, context: EvaluationContext):
355
+ """Evaluate provided code in the evaluation context.
356
+
357
+ If evaluation policy given by context is set to ``forbidden``
358
+ no evaluation will be performed; if it is set to ``dangerous``
359
+ standard :func:`eval` will be used; finally, for any other,
360
+ policy :func:`eval_node` will be called on parsed AST.
361
+ """
362
+ locals_ = context.locals
363
+
364
+ if context.evaluation == "forbidden":
365
+ raise GuardRejection("Forbidden mode")
366
+
367
+ # note: not using `ast.literal_eval` as it does not implement
368
+ # getitem at all, for example it fails on simple `[0][1]`
369
+
370
+ if context.in_subscript:
371
+ # syntactic sugar for ellipsis (:) is only available in subscripts
372
+ # so we need to trick the ast parser into thinking that we have
373
+ # a subscript, but we need to be able to later recognise that we did
374
+ # it so we can ignore the actual __getitem__ operation
375
+ if not code:
376
+ return tuple()
377
+ locals_ = locals_.copy()
378
+ locals_[SUBSCRIPT_MARKER] = IDENTITY_SUBSCRIPT
379
+ code = SUBSCRIPT_MARKER + "[" + code + "]"
380
+ context = EvaluationContext(**{**context._asdict(), **{"locals": locals_}})
381
+
382
+ if context.evaluation == "dangerous":
383
+ return eval(code, context.globals, context.locals)
384
+
385
+ expression = ast.parse(code, mode="eval")
386
+
387
+ return eval_node(expression, context)
388
+
389
+
390
+ BINARY_OP_DUNDERS: dict[type[ast.operator], tuple[str]] = {
391
+ ast.Add: ("__add__",),
392
+ ast.Sub: ("__sub__",),
393
+ ast.Mult: ("__mul__",),
394
+ ast.Div: ("__truediv__",),
395
+ ast.FloorDiv: ("__floordiv__",),
396
+ ast.Mod: ("__mod__",),
397
+ ast.Pow: ("__pow__",),
398
+ ast.LShift: ("__lshift__",),
399
+ ast.RShift: ("__rshift__",),
400
+ ast.BitOr: ("__or__",),
401
+ ast.BitXor: ("__xor__",),
402
+ ast.BitAnd: ("__and__",),
403
+ ast.MatMult: ("__matmul__",),
404
+ }
405
+
406
+ COMP_OP_DUNDERS: dict[type[ast.cmpop], tuple[str, ...]] = {
407
+ ast.Eq: ("__eq__",),
408
+ ast.NotEq: ("__ne__", "__eq__"),
409
+ ast.Lt: ("__lt__", "__gt__"),
410
+ ast.LtE: ("__le__", "__ge__"),
411
+ ast.Gt: ("__gt__", "__lt__"),
412
+ ast.GtE: ("__ge__", "__le__"),
413
+ ast.In: ("__contains__",),
414
+ # Note: ast.Is, ast.IsNot, ast.NotIn are handled specially
415
+ }
416
+
417
+ UNARY_OP_DUNDERS: dict[type[ast.unaryop], tuple[str, ...]] = {
418
+ ast.USub: ("__neg__",),
419
+ ast.UAdd: ("__pos__",),
420
+ # we have to check both __inv__ and __invert__!
421
+ ast.Invert: ("__invert__", "__inv__"),
422
+ ast.Not: ("__not__",),
423
+ }
424
+
425
+
426
+ class ImpersonatingDuck:
427
+ """A dummy class used to create objects of other classes without calling their ``__init__``"""
428
+
429
+ # no-op: override __class__ to impersonate
430
+
431
+
432
+ class _Duck:
433
+ """A dummy class used to create objects pretending to have given attributes"""
434
+
435
+ def __init__(self, attributes: Optional[dict] = None, items: Optional[dict] = None):
436
+ self.attributes = attributes or {}
437
+ self.items = items or {}
438
+
439
+ def __getattr__(self, attr: str):
440
+ return self.attributes[attr]
441
+
442
+ def __hasattr__(self, attr: str):
443
+ return attr in self.attributes
444
+
445
+ def __dir__(self):
446
+ return [*dir(super), *self.attributes]
447
+
448
+ def __getitem__(self, key: str):
449
+ return self.items[key]
450
+
451
+ def __hasitem__(self, key: str):
452
+ return self.items[key]
453
+
454
+ def _ipython_key_completions_(self):
455
+ return self.items.keys()
456
+
457
+
458
+ def _find_dunder(node_op, dunders) -> Union[tuple[str, ...], None]:
459
+ dunder = None
460
+ for op, candidate_dunder in dunders.items():
461
+ if isinstance(node_op, op):
462
+ dunder = candidate_dunder
463
+ return dunder
464
+
465
+
466
+ def eval_node(node: Union[ast.AST, None], context: EvaluationContext):
467
+ """Evaluate AST node in provided context.
468
+
469
+ Applies evaluation restrictions defined in the context. Currently does not support evaluation of functions with keyword arguments.
470
+
471
+ Does not evaluate actions that always have side effects:
472
+
473
+ - class definitions (``class sth: ...``)
474
+ - function definitions (``def sth: ...``)
475
+ - variable assignments (``x = 1``)
476
+ - augmented assignments (``x += 1``)
477
+ - deletions (``del x``)
478
+
479
+ Does not evaluate operations which do not return values:
480
+
481
+ - assertions (``assert x``)
482
+ - pass (``pass``)
483
+ - imports (``import x``)
484
+ - control flow:
485
+
486
+ - conditionals (``if x:``) except for ternary IfExp (``a if x else b``)
487
+ - loops (``for`` and ``while``)
488
+ - exception handling
489
+
490
+ The purpose of this function is to guard against unwanted side-effects;
491
+ it does not give guarantees on protection from malicious code execution.
492
+ """
493
+ policy = EVALUATION_POLICIES[context.evaluation]
494
+ if node is None:
495
+ return None
496
+ if isinstance(node, ast.Expression):
497
+ return eval_node(node.body, context)
498
+ if isinstance(node, ast.BinOp):
499
+ left = eval_node(node.left, context)
500
+ right = eval_node(node.right, context)
501
+ dunders = _find_dunder(node.op, BINARY_OP_DUNDERS)
502
+ if dunders:
503
+ if policy.can_operate(dunders, left, right):
504
+ return getattr(left, dunders[0])(right)
505
+ else:
506
+ raise GuardRejection(
507
+ f"Operation (`{dunders}`) for",
508
+ type(left),
509
+ f"not allowed in {context.evaluation} mode",
510
+ )
511
+ if isinstance(node, ast.Compare):
512
+ left = eval_node(node.left, context)
513
+ all_true = True
514
+ negate = False
515
+ for op, right in zip(node.ops, node.comparators):
516
+ right = eval_node(right, context)
517
+ dunder = None
518
+ dunders = _find_dunder(op, COMP_OP_DUNDERS)
519
+ if not dunders:
520
+ if isinstance(op, ast.NotIn):
521
+ dunders = COMP_OP_DUNDERS[ast.In]
522
+ negate = True
523
+ if isinstance(op, ast.Is):
524
+ dunder = "is_"
525
+ if isinstance(op, ast.IsNot):
526
+ dunder = "is_"
527
+ negate = True
528
+ if not dunder and dunders:
529
+ dunder = dunders[0]
530
+ if dunder:
531
+ a, b = (right, left) if dunder == "__contains__" else (left, right)
532
+ if dunder == "is_" or dunders and policy.can_operate(dunders, a, b):
533
+ result = getattr(operator, dunder)(a, b)
534
+ if negate:
535
+ result = not result
536
+ if not result:
537
+ all_true = False
538
+ left = right
539
+ else:
540
+ raise GuardRejection(
541
+ f"Comparison (`{dunder}`) for",
542
+ type(left),
543
+ f"not allowed in {context.evaluation} mode",
544
+ )
545
+ else:
546
+ raise ValueError(
547
+ f"Comparison `{dunder}` not supported"
548
+ ) # pragma: no cover
549
+ return all_true
550
+ if isinstance(node, ast.Constant):
551
+ return node.value
552
+ if isinstance(node, ast.Tuple):
553
+ return tuple(eval_node(e, context) for e in node.elts)
554
+ if isinstance(node, ast.List):
555
+ return [eval_node(e, context) for e in node.elts]
556
+ if isinstance(node, ast.Set):
557
+ return {eval_node(e, context) for e in node.elts}
558
+ if isinstance(node, ast.Dict):
559
+ return dict(
560
+ zip(
561
+ [eval_node(k, context) for k in node.keys],
562
+ [eval_node(v, context) for v in node.values],
563
+ )
564
+ )
565
+ if isinstance(node, ast.Slice):
566
+ return slice(
567
+ eval_node(node.lower, context),
568
+ eval_node(node.upper, context),
569
+ eval_node(node.step, context),
570
+ )
571
+ if isinstance(node, ast.UnaryOp):
572
+ value = eval_node(node.operand, context)
573
+ dunders = _find_dunder(node.op, UNARY_OP_DUNDERS)
574
+ if dunders:
575
+ if policy.can_operate(dunders, value):
576
+ return getattr(value, dunders[0])()
577
+ else:
578
+ raise GuardRejection(
579
+ f"Operation (`{dunders}`) for",
580
+ type(value),
581
+ f"not allowed in {context.evaluation} mode",
582
+ )
583
+ if isinstance(node, ast.Subscript):
584
+ value = eval_node(node.value, context)
585
+ slice_ = eval_node(node.slice, context)
586
+ if policy.can_get_item(value, slice_):
587
+ return value[slice_]
588
+ raise GuardRejection(
589
+ "Subscript access (`__getitem__`) for",
590
+ type(value), # not joined to avoid calling `repr`
591
+ f" not allowed in {context.evaluation} mode",
592
+ )
593
+ if isinstance(node, ast.Name):
594
+ return _eval_node_name(node.id, context)
595
+ if isinstance(node, ast.Attribute):
596
+ value = eval_node(node.value, context)
597
+ if policy.can_get_attr(value, node.attr):
598
+ return getattr(value, node.attr)
599
+ raise GuardRejection(
600
+ "Attribute access (`__getattr__`) for",
601
+ type(value), # not joined to avoid calling `repr`
602
+ f"not allowed in {context.evaluation} mode",
603
+ )
604
+ if isinstance(node, ast.IfExp):
605
+ test = eval_node(node.test, context)
606
+ if test:
607
+ return eval_node(node.body, context)
608
+ else:
609
+ return eval_node(node.orelse, context)
610
+ if isinstance(node, ast.Call):
611
+ func = eval_node(node.func, context)
612
+ if policy.can_call(func) and not node.keywords:
613
+ args = [eval_node(arg, context) for arg in node.args]
614
+ return func(*args)
615
+ if isclass(func):
616
+ # this code path gets entered when calling class e.g. `MyClass()`
617
+ # or `my_instance.__class__()` - in both cases `func` is `MyClass`.
618
+ # Should return `MyClass` if `__new__` is not overridden,
619
+ # otherwise whatever `__new__` return type is.
620
+ overridden_return_type = _eval_return_type(func.__new__, node, context)
621
+ if overridden_return_type is not NOT_EVALUATED:
622
+ return overridden_return_type
623
+ return _create_duck_for_heap_type(func)
624
+ else:
625
+ return_type = _eval_return_type(func, node, context)
626
+ if return_type is not NOT_EVALUATED:
627
+ return return_type
628
+ raise GuardRejection(
629
+ "Call for",
630
+ func, # not joined to avoid calling `repr`
631
+ f"not allowed in {context.evaluation} mode",
632
+ )
633
+ raise ValueError("Unhandled node", ast.dump(node))
634
+
635
+
636
+ def _eval_return_type(func: Callable, node: ast.Call, context: EvaluationContext):
637
+ """Evaluate return type of a given callable function.
638
+
639
+ Returns the built-in type, a duck or NOT_EVALUATED sentinel.
640
+ """
641
+ try:
642
+ sig = signature(func)
643
+ except ValueError:
644
+ sig = UNKNOWN_SIGNATURE
645
+ # if annotation was not stringized, or it was stringized
646
+ # but resolved by signature call we know the return type
647
+ not_empty = sig.return_annotation is not Signature.empty
648
+ if not_empty:
649
+ return _resolve_annotation(sig.return_annotation, sig, func, node, context)
650
+ return NOT_EVALUATED
651
+
652
+
653
+ def _resolve_annotation(
654
+ annotation,
655
+ sig: Signature,
656
+ func: Callable,
657
+ node: ast.Call,
658
+ context: EvaluationContext,
659
+ ):
660
+ """Resolve annotation created by user with `typing` module and custom objects."""
661
+ annotation = (
662
+ _eval_node_name(annotation, context)
663
+ if isinstance(annotation, str)
664
+ else annotation
665
+ )
666
+ origin = get_origin(annotation)
667
+ if annotation is Self and hasattr(func, "__self__"):
668
+ return func.__self__
669
+ elif origin is Literal:
670
+ type_args = get_args(annotation)
671
+ if len(type_args) == 1:
672
+ return type_args[0]
673
+ elif annotation is LiteralString:
674
+ return ""
675
+ elif annotation is AnyStr:
676
+ index = None
677
+ for i, (key, value) in enumerate(sig.parameters.items()):
678
+ if value.annotation is AnyStr:
679
+ index = i
680
+ break
681
+ if index is not None and index < len(node.args):
682
+ return eval_node(node.args[index], context)
683
+ elif origin is TypeGuard:
684
+ return False
685
+ elif origin is Union:
686
+ attributes = [
687
+ attr
688
+ for type_arg in get_args(annotation)
689
+ for attr in dir(_resolve_annotation(type_arg, sig, func, node, context))
690
+ ]
691
+ return _Duck(attributes=dict.fromkeys(attributes))
692
+ elif is_typeddict(annotation):
693
+ return _Duck(
694
+ attributes=dict.fromkeys(dir(dict())),
695
+ items={
696
+ k: _resolve_annotation(v, sig, func, node, context)
697
+ for k, v in annotation.__annotations__.items()
698
+ },
699
+ )
700
+ elif hasattr(annotation, "_is_protocol"):
701
+ return _Duck(attributes=dict.fromkeys(dir(annotation)))
702
+ elif origin is Annotated:
703
+ type_arg = get_args(annotation)[0]
704
+ return _resolve_annotation(type_arg, sig, func, node, context)
705
+ elif isinstance(annotation, NewType):
706
+ return _eval_or_create_duck(annotation.__supertype__, node, context)
707
+ elif isinstance(annotation, TypeAliasType):
708
+ return _eval_or_create_duck(annotation.__value__, node, context)
709
+ else:
710
+ return _eval_or_create_duck(annotation, node, context)
711
+
712
+
713
+ def _eval_node_name(node_id: str, context: EvaluationContext):
714
+ policy = EVALUATION_POLICIES[context.evaluation]
715
+ if policy.allow_locals_access and node_id in context.locals:
716
+ return context.locals[node_id]
717
+ if policy.allow_globals_access and node_id in context.globals:
718
+ return context.globals[node_id]
719
+ if policy.allow_builtins_access and hasattr(builtins, node_id):
720
+ # note: do not use __builtins__, it is implementation detail of cPython
721
+ return getattr(builtins, node_id)
722
+ if not policy.allow_globals_access and not policy.allow_locals_access:
723
+ raise GuardRejection(
724
+ f"Namespace access not allowed in {context.evaluation} mode"
725
+ )
726
+ else:
727
+ raise NameError(f"{node_id} not found in locals, globals, nor builtins")
728
+
729
+
730
+ def _eval_or_create_duck(duck_type, node: ast.Call, context: EvaluationContext):
731
+ policy = EVALUATION_POLICIES[context.evaluation]
732
+ # if allow-listed builtin is on type annotation, instantiate it
733
+ if policy.can_call(duck_type) and not node.keywords:
734
+ args = [eval_node(arg, context) for arg in node.args]
735
+ return duck_type(*args)
736
+ # if custom class is in type annotation, mock it
737
+ return _create_duck_for_heap_type(duck_type)
738
+
739
+
740
+ def _create_duck_for_heap_type(duck_type):
741
+ """Create an imitation of an object of a given type (a duck).
742
+
743
+ Returns the duck or NOT_EVALUATED sentinel if duck could not be created.
744
+ """
745
+ duck = ImpersonatingDuck()
746
+ try:
747
+ # this only works for heap types, not builtins
748
+ duck.__class__ = duck_type
749
+ return duck
750
+ except TypeError:
751
+ pass
752
+ return NOT_EVALUATED
753
+
754
+
755
+ SUPPORTED_EXTERNAL_GETITEM = {
756
+ ("pandas", "core", "indexing", "_iLocIndexer"),
757
+ ("pandas", "core", "indexing", "_LocIndexer"),
758
+ ("pandas", "DataFrame"),
759
+ ("pandas", "Series"),
760
+ ("numpy", "ndarray"),
761
+ ("numpy", "void"),
762
+ }
763
+
764
+
765
+ BUILTIN_GETITEM: set[InstancesHaveGetItem] = {
766
+ dict,
767
+ str, # type: ignore[arg-type]
768
+ bytes, # type: ignore[arg-type]
769
+ list,
770
+ tuple,
771
+ collections.defaultdict,
772
+ collections.deque,
773
+ collections.OrderedDict,
774
+ collections.ChainMap,
775
+ collections.UserDict,
776
+ collections.UserList,
777
+ collections.UserString, # type: ignore[arg-type]
778
+ _DummyNamedTuple,
779
+ _IdentitySubscript,
780
+ }
781
+
782
+
783
+ def _list_methods(cls, source=None):
784
+ """For use on immutable objects or with methods returning a copy"""
785
+ return [getattr(cls, k) for k in (source if source else dir(cls))]
786
+
787
+
788
+ dict_non_mutating_methods = ("copy", "keys", "values", "items")
789
+ list_non_mutating_methods = ("copy", "index", "count")
790
+ set_non_mutating_methods = set(dir(set)) & set(dir(frozenset))
791
+
792
+
793
+ dict_keys: type[collections.abc.KeysView] = type({}.keys())
794
+
795
+ NUMERICS = {int, float, complex}
796
+
797
+ ALLOWED_CALLS = {
798
+ bytes,
799
+ *_list_methods(bytes),
800
+ dict,
801
+ *_list_methods(dict, dict_non_mutating_methods),
802
+ dict_keys.isdisjoint,
803
+ list,
804
+ *_list_methods(list, list_non_mutating_methods),
805
+ set,
806
+ *_list_methods(set, set_non_mutating_methods),
807
+ frozenset,
808
+ *_list_methods(frozenset),
809
+ range,
810
+ str,
811
+ *_list_methods(str),
812
+ tuple,
813
+ *_list_methods(tuple),
814
+ *NUMERICS,
815
+ *[method for numeric_cls in NUMERICS for method in _list_methods(numeric_cls)],
816
+ collections.deque,
817
+ *_list_methods(collections.deque, list_non_mutating_methods),
818
+ collections.defaultdict,
819
+ *_list_methods(collections.defaultdict, dict_non_mutating_methods),
820
+ collections.OrderedDict,
821
+ *_list_methods(collections.OrderedDict, dict_non_mutating_methods),
822
+ collections.UserDict,
823
+ *_list_methods(collections.UserDict, dict_non_mutating_methods),
824
+ collections.UserList,
825
+ *_list_methods(collections.UserList, list_non_mutating_methods),
826
+ collections.UserString,
827
+ *_list_methods(collections.UserString, dir(str)),
828
+ collections.Counter,
829
+ *_list_methods(collections.Counter, dict_non_mutating_methods),
830
+ collections.Counter.elements,
831
+ collections.Counter.most_common,
832
+ }
833
+
834
+ BUILTIN_GETATTR: set[MayHaveGetattr] = {
835
+ *BUILTIN_GETITEM,
836
+ set,
837
+ frozenset,
838
+ object,
839
+ type, # `type` handles a lot of generic cases, e.g. numbers as in `int.real`.
840
+ *NUMERICS,
841
+ dict_keys,
842
+ MethodDescriptorType,
843
+ ModuleType,
844
+ }
845
+
846
+
847
+ BUILTIN_OPERATIONS = {*BUILTIN_GETATTR}
848
+
849
+ EVALUATION_POLICIES = {
850
+ "minimal": EvaluationPolicy(
851
+ allow_builtins_access=True,
852
+ allow_locals_access=False,
853
+ allow_globals_access=False,
854
+ allow_item_access=False,
855
+ allow_attr_access=False,
856
+ allowed_calls=set(),
857
+ allow_any_calls=False,
858
+ allow_all_operations=False,
859
+ ),
860
+ "limited": SelectivePolicy(
861
+ allowed_getitem=BUILTIN_GETITEM,
862
+ allowed_getitem_external=SUPPORTED_EXTERNAL_GETITEM,
863
+ allowed_getattr=BUILTIN_GETATTR,
864
+ allowed_getattr_external={
865
+ # pandas Series/Frame implements custom `__getattr__`
866
+ ("pandas", "DataFrame"),
867
+ ("pandas", "Series"),
868
+ },
869
+ allowed_operations=BUILTIN_OPERATIONS,
870
+ allow_builtins_access=True,
871
+ allow_locals_access=True,
872
+ allow_globals_access=True,
873
+ allowed_calls=ALLOWED_CALLS,
874
+ ),
875
+ "unsafe": EvaluationPolicy(
876
+ allow_builtins_access=True,
877
+ allow_locals_access=True,
878
+ allow_globals_access=True,
879
+ allow_attr_access=True,
880
+ allow_item_access=True,
881
+ allow_any_calls=True,
882
+ allow_all_operations=True,
883
+ ),
884
+ }
885
+
886
+
887
+ __all__ = [
888
+ "guarded_eval",
889
+ "eval_node",
890
+ "GuardRejection",
891
+ "EvaluationContext",
892
+ "_unbind_method",
893
+ ]
temp_venv/lib/python3.13/site-packages/IPython/core/history.py ADDED
@@ -0,0 +1,1218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """History related magics and functionality"""
2
+
3
+ from __future__ import annotations
4
+
5
+ # Copyright (c) IPython Development Team.
6
+ # Distributed under the terms of the Modified BSD License.
7
+
8
+
9
+ import atexit
10
+ import datetime
11
+ import re
12
+
13
+
14
+ import threading
15
+ from pathlib import Path
16
+
17
+ from collections import defaultdict
18
+ from contextlib import contextmanager
19
+ from dataclasses import dataclass
20
+ from decorator import decorator
21
+ from traitlets import (
22
+ Any,
23
+ Bool,
24
+ Dict,
25
+ Instance,
26
+ Integer,
27
+ List,
28
+ TraitError,
29
+ Unicode,
30
+ Union,
31
+ default,
32
+ observe,
33
+ )
34
+ from traitlets.config.configurable import LoggingConfigurable
35
+
36
+ from IPython.paths import locate_profile
37
+ from IPython.utils.decorators import undoc
38
+ from typing import Iterable, Tuple, Optional, TYPE_CHECKING
39
+ import typing
40
+ from warnings import warn
41
+ from weakref import ref, WeakSet
42
+
43
+ if TYPE_CHECKING:
44
+ from IPython.core.interactiveshell import InteractiveShell
45
+ from IPython.config.Configuration import Configuration
46
+
47
+ try:
48
+ from sqlite3 import DatabaseError, OperationalError
49
+ import sqlite3
50
+
51
+ sqlite3.register_converter(
52
+ "timestamp", lambda val: datetime.datetime.fromisoformat(val.decode())
53
+ )
54
+
55
+ sqlite3_found = True
56
+ except ModuleNotFoundError:
57
+ sqlite3_found = False
58
+
59
+ class DatabaseError(Exception): # type: ignore [no-redef]
60
+ pass
61
+
62
+ class OperationalError(Exception): # type: ignore [no-redef]
63
+ pass
64
+
65
+
66
+ InOrInOut = typing.Union[str, tuple[str, Optional[str]]]
67
+
68
+ # -----------------------------------------------------------------------------
69
+ # Classes and functions
70
+ # -----------------------------------------------------------------------------
71
+
72
+
73
+ @undoc
74
+ class DummyDB:
75
+ """Dummy DB that will act as a black hole for history.
76
+
77
+ Only used in the absence of sqlite"""
78
+
79
+ def execute(*args: typing.Any, **kwargs: typing.Any) -> list:
80
+ return []
81
+
82
+ def commit(self, *args, **kwargs): # type: ignore [no-untyped-def]
83
+ pass
84
+
85
+ def __enter__(self, *args, **kwargs): # type: ignore [no-untyped-def]
86
+ pass
87
+
88
+ def __exit__(self, *args, **kwargs): # type: ignore [no-untyped-def]
89
+ pass
90
+
91
+
92
+ @decorator
93
+ def only_when_enabled(f, self, *a, **kw): # type: ignore [no-untyped-def]
94
+ """Decorator: return an empty list in the absence of sqlite."""
95
+ if not self.enabled:
96
+ return []
97
+ else:
98
+ return f(self, *a, **kw)
99
+
100
+
101
+ # use 16kB as threshold for whether a corrupt history db should be saved
102
+ # that should be at least 100 entries or so
103
+ _SAVE_DB_SIZE = 16384
104
+
105
+
106
+ @decorator
107
+ def catch_corrupt_db(f, self, *a, **kw): # type: ignore [no-untyped-def]
108
+ """A decorator which wraps HistoryAccessor method calls to catch errors from
109
+ a corrupt SQLite database, move the old database out of the way, and create
110
+ a new one.
111
+
112
+ We avoid clobbering larger databases because this may be triggered due to filesystem issues,
113
+ not just a corrupt file.
114
+ """
115
+ try:
116
+ return f(self, *a, **kw)
117
+ except (DatabaseError, OperationalError) as e:
118
+ self._corrupt_db_counter += 1
119
+ self.log.error("Failed to open SQLite history %s (%s).", self.hist_file, e)
120
+ if self.hist_file != ":memory:":
121
+ if self._corrupt_db_counter > self._corrupt_db_limit:
122
+ self.hist_file = ":memory:"
123
+ self.log.error(
124
+ "Failed to load history too many times, history will not be saved."
125
+ )
126
+ elif self.hist_file.is_file():
127
+ # move the file out of the way
128
+ base = str(self.hist_file.parent / self.hist_file.stem)
129
+ ext = self.hist_file.suffix
130
+ size = self.hist_file.stat().st_size
131
+ if size >= _SAVE_DB_SIZE:
132
+ # if there's significant content, avoid clobbering
133
+ now = (
134
+ datetime.datetime.now(datetime.timezone.utc)
135
+ .isoformat()
136
+ .replace(":", ".")
137
+ )
138
+ newpath = base + "-corrupt-" + now + ext
139
+ # don't clobber previous corrupt backups
140
+ for i in range(100):
141
+ if not Path(newpath).exists():
142
+ break
143
+ else:
144
+ newpath = base + "-corrupt-" + now + ("-%i" % i) + ext
145
+ else:
146
+ # not much content, possibly empty; don't worry about clobbering
147
+ # maybe we should just delete it?
148
+ newpath = base + "-corrupt" + ext
149
+ self.hist_file.rename(newpath)
150
+ self.log.error(
151
+ "History file was moved to %s and a new file created.", newpath
152
+ )
153
+ self.init_db()
154
+ return []
155
+ else:
156
+ # Failed with :memory:, something serious is wrong
157
+ raise
158
+
159
+
160
+ class HistoryAccessorBase(LoggingConfigurable):
161
+ """An abstract class for History Accessors"""
162
+
163
+ def get_tail(
164
+ self,
165
+ n: int = 10,
166
+ raw: bool = True,
167
+ output: bool = False,
168
+ include_latest: bool = False,
169
+ ) -> Iterable[tuple[int, int, InOrInOut]]:
170
+ raise NotImplementedError
171
+
172
+ def search(
173
+ self,
174
+ pattern: str = "*",
175
+ raw: bool = True,
176
+ search_raw: bool = True,
177
+ output: bool = False,
178
+ n: Optional[int] = None,
179
+ unique: bool = False,
180
+ ) -> Iterable[tuple[int, int, InOrInOut]]:
181
+ raise NotImplementedError
182
+
183
+ def get_range(
184
+ self,
185
+ session: int,
186
+ start: int = 1,
187
+ stop: Optional[int] = None,
188
+ raw: bool = True,
189
+ output: bool = False,
190
+ ) -> Iterable[tuple[int, int, InOrInOut]]:
191
+ raise NotImplementedError
192
+
193
+ def get_range_by_str(
194
+ self, rangestr: str, raw: bool = True, output: bool = False
195
+ ) -> Iterable[tuple[int, int, InOrInOut]]:
196
+ raise NotImplementedError
197
+
198
+
199
+ class HistoryAccessor(HistoryAccessorBase):
200
+ """Access the history database without adding to it.
201
+
202
+ This is intended for use by standalone history tools. IPython shells use
203
+ HistoryManager, below, which is a subclass of this."""
204
+
205
+ # counter for init_db retries, so we don't keep trying over and over
206
+ _corrupt_db_counter = 0
207
+ # after two failures, fallback on :memory:
208
+ _corrupt_db_limit = 2
209
+
210
+ # String holding the path to the history file
211
+ hist_file = Union(
212
+ [Instance(Path), Unicode()],
213
+ help="""Path to file to use for SQLite history database.
214
+
215
+ By default, IPython will put the history database in the IPython
216
+ profile directory. If you would rather share one history among
217
+ profiles, you can set this value in each, so that they are consistent.
218
+
219
+ Due to an issue with fcntl, SQLite is known to misbehave on some NFS
220
+ mounts. If you see IPython hanging, try setting this to something on a
221
+ local disk, e.g::
222
+
223
+ ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite
224
+
225
+ you can also use the specific value `:memory:` (including the colon
226
+ at both end but not the back ticks), to avoid creating an history file.
227
+
228
+ """,
229
+ ).tag(config=True)
230
+
231
+ enabled = Bool(
232
+ sqlite3_found,
233
+ help="""enable the SQLite history
234
+
235
+ set enabled=False to disable the SQLite history,
236
+ in which case there will be no stored history, no SQLite connection,
237
+ and no background saving thread. This may be necessary in some
238
+ threaded environments where IPython is embedded.
239
+ """,
240
+ ).tag(config=True)
241
+
242
+ connection_options = Dict(
243
+ help="""Options for configuring the SQLite connection
244
+
245
+ These options are passed as keyword args to sqlite3.connect
246
+ when establishing database connections.
247
+ """
248
+ ).tag(config=True)
249
+
250
+ @default("connection_options")
251
+ def _default_connection_options(self) -> dict[str, bool]:
252
+ return dict(check_same_thread=False)
253
+
254
+ # The SQLite database
255
+ db = Any()
256
+
257
+ @observe("db")
258
+ @only_when_enabled
259
+ def _db_changed(self, change): # type: ignore [no-untyped-def]
260
+ """validate the db, since it can be an Instance of two different types"""
261
+ new = change["new"]
262
+ connection_types = (DummyDB, sqlite3.Connection)
263
+ if not isinstance(new, connection_types):
264
+ msg = "%s.db must be sqlite3 Connection or DummyDB, not %r" % (
265
+ self.__class__.__name__,
266
+ new,
267
+ )
268
+ raise TraitError(msg)
269
+
270
+ def __init__(
271
+ self, profile: str = "default", hist_file: str = "", **traits: typing.Any
272
+ ) -> None:
273
+ """Create a new history accessor.
274
+
275
+ Parameters
276
+ ----------
277
+ profile : str
278
+ The name of the profile from which to open history.
279
+ hist_file : str
280
+ Path to an SQLite history database stored by IPython. If specified,
281
+ hist_file overrides profile.
282
+ config : :class:`~traitlets.config.loader.Config`
283
+ Config object. hist_file can also be set through this.
284
+ """
285
+ super(HistoryAccessor, self).__init__(**traits)
286
+ # defer setting hist_file from kwarg until after init,
287
+ # otherwise the default kwarg value would clobber any value
288
+ # set by config
289
+ if hist_file:
290
+ self.hist_file = hist_file
291
+
292
+ try:
293
+ self.hist_file
294
+ except TraitError:
295
+ # No one has set the hist_file, yet.
296
+ self.hist_file = self._get_hist_file_name(profile)
297
+
298
+ self.init_db()
299
+
300
+ def _get_hist_file_name(self, profile: str = "default") -> Path:
301
+ """Find the history file for the given profile name.
302
+
303
+ This is overridden by the HistoryManager subclass, to use the shell's
304
+ active profile.
305
+
306
+ Parameters
307
+ ----------
308
+ profile : str
309
+ The name of a profile which has a history file.
310
+ """
311
+ return Path(locate_profile(profile)) / "history.sqlite"
312
+
313
+ @catch_corrupt_db
314
+ def init_db(self) -> None:
315
+ """Connect to the database, and create tables if necessary."""
316
+ if not self.enabled:
317
+ self.db = DummyDB()
318
+ return
319
+
320
+ # use detect_types so that timestamps return datetime objects
321
+ kwargs = dict(detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES)
322
+ kwargs.update(self.connection_options)
323
+ self.db = sqlite3.connect(str(self.hist_file), **kwargs) # type: ignore [call-overload]
324
+ with self.db:
325
+ self.db.execute(
326
+ """CREATE TABLE IF NOT EXISTS sessions (session integer
327
+ primary key autoincrement, start timestamp,
328
+ end timestamp, num_cmds integer, remark text)"""
329
+ )
330
+ self.db.execute(
331
+ """CREATE TABLE IF NOT EXISTS history
332
+ (session integer, line integer, source text, source_raw text,
333
+ PRIMARY KEY (session, line))"""
334
+ )
335
+ # Output history is optional, but ensure the table's there so it can be
336
+ # enabled later.
337
+ self.db.execute(
338
+ """CREATE TABLE IF NOT EXISTS output_history
339
+ (session integer, line integer, output text,
340
+ PRIMARY KEY (session, line))"""
341
+ )
342
+ # success! reset corrupt db count
343
+ self._corrupt_db_counter = 0
344
+
345
+ def writeout_cache(self) -> None:
346
+ """Overridden by HistoryManager to dump the cache before certain
347
+ database lookups."""
348
+ pass
349
+
350
+ ## -------------------------------
351
+ ## Methods for retrieving history:
352
+ ## -------------------------------
353
+ def _run_sql(
354
+ self,
355
+ sql: str,
356
+ params: tuple,
357
+ raw: bool = True,
358
+ output: bool = False,
359
+ latest: bool = False,
360
+ ) -> Iterable[tuple[int, int, InOrInOut]]:
361
+ """Prepares and runs an SQL query for the history database.
362
+
363
+ Parameters
364
+ ----------
365
+ sql : str
366
+ Any filtering expressions to go after SELECT ... FROM ...
367
+ params : tuple
368
+ Parameters passed to the SQL query (to replace "?")
369
+ raw, output : bool
370
+ See :meth:`get_range`
371
+ latest : bool
372
+ Select rows with max (session, line)
373
+
374
+ Returns
375
+ -------
376
+ Tuples as :meth:`get_range`
377
+ """
378
+ toget = "source_raw" if raw else "source"
379
+ sqlfrom = "history"
380
+ if output:
381
+ sqlfrom = "history LEFT JOIN output_history USING (session, line)"
382
+ toget = "history.%s, output_history.output" % toget
383
+ if latest:
384
+ toget += ", MAX(session * 128 * 1024 + line)"
385
+ this_querry = "SELECT session, line, %s FROM %s " % (toget, sqlfrom) + sql
386
+ cur = self.db.execute(this_querry, params)
387
+ if latest:
388
+ cur = (row[:-1] for row in cur)
389
+ if output: # Regroup into 3-tuples, and parse JSON
390
+ return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
391
+ return cur
392
+
393
+ @only_when_enabled
394
+ @catch_corrupt_db
395
+ def get_session_info(
396
+ self, session: int
397
+ ) -> tuple[int, datetime.datetime, Optional[datetime.datetime], Optional[int], str]:
398
+ """Get info about a session.
399
+
400
+ Parameters
401
+ ----------
402
+ session : int
403
+ Session number to retrieve.
404
+
405
+ Returns
406
+ -------
407
+ session_id : int
408
+ Session ID number
409
+ start : datetime
410
+ Timestamp for the start of the session.
411
+ end : datetime
412
+ Timestamp for the end of the session, or None if IPython crashed.
413
+ num_cmds : int
414
+ Number of commands run, or None if IPython crashed.
415
+ remark : str
416
+ A manually set description.
417
+ """
418
+ query = "SELECT * from sessions where session == ?"
419
+ return self.db.execute(query, (session,)).fetchone()
420
+
421
+ @catch_corrupt_db
422
+ def get_last_session_id(self) -> Optional[int]:
423
+ """Get the last session ID currently in the database.
424
+
425
+ Within IPython, this should be the same as the value stored in
426
+ :attr:`HistoryManager.session_number`.
427
+ """
428
+ for record in self.get_tail(n=1, include_latest=True):
429
+ return record[0]
430
+ return None
431
+
432
+ @catch_corrupt_db
433
+ def get_tail(
434
+ self,
435
+ n: int = 10,
436
+ raw: bool = True,
437
+ output: bool = False,
438
+ include_latest: bool = False,
439
+ ) -> Iterable[tuple[int, int, InOrInOut]]:
440
+ """Get the last n lines from the history database.
441
+
442
+ Parameters
443
+ ----------
444
+ n : int
445
+ The number of lines to get
446
+ raw, output : bool
447
+ See :meth:`get_range`
448
+ include_latest : bool
449
+ If False (default), n+1 lines are fetched, and the latest one
450
+ is discarded. This is intended to be used where the function
451
+ is called by a user command, which it should not return.
452
+
453
+ Returns
454
+ -------
455
+ Tuples as :meth:`get_range`
456
+ """
457
+ self.writeout_cache()
458
+ if not include_latest:
459
+ n += 1
460
+ cur = self._run_sql(
461
+ "ORDER BY session DESC, line DESC LIMIT ?", (n,), raw=raw, output=output
462
+ )
463
+ if not include_latest:
464
+ return reversed(list(cur)[1:])
465
+ return reversed(list(cur))
466
+
467
+ @catch_corrupt_db
468
+ def search(
469
+ self,
470
+ pattern: str = "*",
471
+ raw: bool = True,
472
+ search_raw: bool = True,
473
+ output: bool = False,
474
+ n: Optional[int] = None,
475
+ unique: bool = False,
476
+ ) -> Iterable[tuple[int, int, InOrInOut]]:
477
+ """Search the database using unix glob-style matching (wildcards
478
+ * and ?).
479
+
480
+ Parameters
481
+ ----------
482
+ pattern : str
483
+ The wildcarded pattern to match when searching
484
+ search_raw : bool
485
+ If True, search the raw input, otherwise, the parsed input
486
+ raw, output : bool
487
+ See :meth:`get_range`
488
+ n : None or int
489
+ If an integer is given, it defines the limit of
490
+ returned entries.
491
+ unique : bool
492
+ When it is true, return only unique entries.
493
+
494
+ Returns
495
+ -------
496
+ Tuples as :meth:`get_range`
497
+ """
498
+ tosearch = "source_raw" if search_raw else "source"
499
+ if output:
500
+ tosearch = "history." + tosearch
501
+ self.writeout_cache()
502
+ sqlform = "WHERE %s GLOB ?" % tosearch
503
+ params: tuple[typing.Any, ...] = (pattern,)
504
+ if unique:
505
+ sqlform += " GROUP BY {0}".format(tosearch)
506
+ if n is not None:
507
+ sqlform += " ORDER BY session DESC, line DESC LIMIT ?"
508
+ params += (n,)
509
+ elif unique:
510
+ sqlform += " ORDER BY session, line"
511
+ cur = self._run_sql(sqlform, params, raw=raw, output=output, latest=unique)
512
+ if n is not None:
513
+ return reversed(list(cur))
514
+ return cur
515
+
516
+ @catch_corrupt_db
517
+ def get_range(
518
+ self,
519
+ session: int,
520
+ start: int = 1,
521
+ stop: Optional[int] = None,
522
+ raw: bool = True,
523
+ output: bool = False,
524
+ ) -> Iterable[tuple[int, int, InOrInOut]]:
525
+ """Retrieve input by session.
526
+
527
+ Parameters
528
+ ----------
529
+ session : int
530
+ Session number to retrieve.
531
+ start : int
532
+ First line to retrieve.
533
+ stop : int
534
+ End of line range (excluded from output itself). If None, retrieve
535
+ to the end of the session.
536
+ raw : bool
537
+ If True, return untranslated input
538
+ output : bool
539
+ If True, attempt to include output. This will be 'real' Python
540
+ objects for the current session, or text reprs from previous
541
+ sessions if db_log_output was enabled at the time. Where no output
542
+ is found, None is used.
543
+
544
+ Returns
545
+ -------
546
+ entries
547
+ An iterator over the desired lines. Each line is a 3-tuple, either
548
+ (session, line, input) if output is False, or
549
+ (session, line, (input, output)) if output is True.
550
+ """
551
+ params: tuple[typing.Any, ...]
552
+ if stop:
553
+ lineclause = "line >= ? AND line < ?"
554
+ params = (session, start, stop)
555
+ else:
556
+ lineclause = "line>=?"
557
+ params = (session, start)
558
+
559
+ return self._run_sql(
560
+ "WHERE session==? AND %s" % lineclause, params, raw=raw, output=output
561
+ )
562
+
563
+ def get_range_by_str(
564
+ self, rangestr: str, raw: bool = True, output: bool = False
565
+ ) -> Iterable[tuple[int, int, InOrInOut]]:
566
+ """Get lines of history from a string of ranges, as used by magic
567
+ commands %hist, %save, %macro, etc.
568
+
569
+ Parameters
570
+ ----------
571
+ rangestr : str
572
+ A string specifying ranges, e.g. "5 ~2/1-4". If empty string is used,
573
+ this will return everything from current session's history.
574
+
575
+ See the documentation of :func:`%history` for the full details.
576
+
577
+ raw, output : bool
578
+ As :meth:`get_range`
579
+
580
+ Returns
581
+ -------
582
+ Tuples as :meth:`get_range`
583
+ """
584
+ for sess, s, e in extract_hist_ranges(rangestr):
585
+ yield from self.get_range(sess, s, e, raw=raw, output=output)
586
+
587
+
588
+ @dataclass
589
+ class HistoryOutput:
590
+ output_type: typing.Literal[
591
+ "out_stream", "err_stream", "display_data", "execute_result"
592
+ ]
593
+ bundle: typing.Dict[str, str]
594
+
595
+
596
+ class HistoryManager(HistoryAccessor):
597
+ """A class to organize all history-related functionality in one place."""
598
+
599
+ # Public interface
600
+
601
+ # An instance of the IPython shell we are attached to
602
+ shell = Instance(
603
+ "IPython.core.interactiveshell.InteractiveShellABC", allow_none=False
604
+ )
605
+ # Lists to hold processed and raw history. These start with a blank entry
606
+ # so that we can index them starting from 1
607
+ input_hist_parsed = List([""])
608
+ input_hist_raw = List([""])
609
+ # A list of directories visited during session
610
+ dir_hist: List = List()
611
+
612
+ @default("dir_hist")
613
+ def _dir_hist_default(self) -> list[Path]:
614
+ try:
615
+ return [Path.cwd()]
616
+ except OSError:
617
+ return []
618
+
619
+ # A dict of output history, keyed with ints from the shell's
620
+ # execution count.
621
+ output_hist = Dict()
622
+ # The text/plain repr of outputs.
623
+ output_hist_reprs: typing.Dict[int, str] = Dict() # type: ignore [assignment]
624
+ # Maps execution_count to MIME bundles
625
+ outputs: typing.Dict[int, typing.List[HistoryOutput]] = defaultdict(list)
626
+ # Maps execution_count to exception tracebacks
627
+ exceptions: typing.Dict[int, typing.Dict[str, Any]] = Dict() # type: ignore [assignment]
628
+
629
+ # The number of the current session in the history database
630
+ session_number: int = Integer() # type: ignore [assignment]
631
+
632
+ db_log_output = Bool(
633
+ False, help="Should the history database include output? (default: no)"
634
+ ).tag(config=True)
635
+ db_cache_size = Integer(
636
+ 0,
637
+ help="Write to database every x commands (higher values save disk access & power).\n"
638
+ "Values of 1 or less effectively disable caching.",
639
+ ).tag(config=True)
640
+ # The input and output caches
641
+ db_input_cache: List[tuple[int, str, str]] = List()
642
+ db_output_cache: List[tuple[int, str]] = List()
643
+
644
+ # History saving in separate thread
645
+ save_thread = Instance("IPython.core.history.HistorySavingThread", allow_none=True)
646
+
647
+ @property
648
+ def save_flag(self) -> threading.Event | None:
649
+ if self.save_thread is not None:
650
+ return self.save_thread.save_flag
651
+ return None
652
+
653
+ # Private interface
654
+ # Variables used to store the three last inputs from the user. On each new
655
+ # history update, we populate the user's namespace with these, shifted as
656
+ # necessary.
657
+ _i00 = Unicode("")
658
+ _i = Unicode("")
659
+ _ii = Unicode("")
660
+ _iii = Unicode("")
661
+
662
+ # A regex matching all forms of the exit command, so that we don't store
663
+ # them in the history (it's annoying to rewind the first entry and land on
664
+ # an exit call).
665
+ _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$")
666
+
667
+ _instances: WeakSet[HistoryManager] = WeakSet()
668
+ _max_inst: int | float = float("inf")
669
+
670
+ def __init__(
671
+ self,
672
+ shell: InteractiveShell,
673
+ config: Optional[Configuration] = None,
674
+ **traits: typing.Any,
675
+ ):
676
+ """Create a new history manager associated with a shell instance."""
677
+ super().__init__(shell=shell, config=config, **traits)
678
+ self.db_input_cache_lock = threading.Lock()
679
+ self.db_output_cache_lock = threading.Lock()
680
+
681
+ try:
682
+ self.new_session()
683
+ except OperationalError:
684
+ self.log.error(
685
+ "Failed to create history session in %s. History will not be saved.",
686
+ self.hist_file,
687
+ exc_info=True,
688
+ )
689
+ self.hist_file = ":memory:"
690
+
691
+ if self.enabled and self.hist_file != ":memory:":
692
+ self.save_thread = HistorySavingThread(self)
693
+ try:
694
+ self.save_thread.start()
695
+ except RuntimeError:
696
+ self.log.error(
697
+ "Failed to start history saving thread. History will not be saved.",
698
+ exc_info=True,
699
+ )
700
+ self.hist_file = ":memory:"
701
+ self._instances.add(self)
702
+ assert len(HistoryManager._instances) <= HistoryManager._max_inst, (
703
+ len(HistoryManager._instances),
704
+ HistoryManager._max_inst,
705
+ )
706
+
707
+ def __del__(self) -> None:
708
+ if self.save_thread is not None:
709
+ self.save_thread.stop()
710
+
711
+ def _get_hist_file_name(self, profile: Optional[str] = None) -> Path:
712
+ """Get default history file name based on the Shell's profile.
713
+
714
+ The profile parameter is ignored, but must exist for compatibility with
715
+ the parent class."""
716
+ profile_dir = self.shell.profile_dir.location
717
+ return Path(profile_dir) / "history.sqlite"
718
+
719
+ @only_when_enabled
720
+ def new_session(self, conn: Optional[sqlite3.Connection] = None) -> None:
721
+ """Get a new session number."""
722
+ if conn is None:
723
+ conn = self.db
724
+
725
+ with conn:
726
+ cur = conn.execute(
727
+ """INSERT INTO sessions VALUES (NULL, ?, NULL,
728
+ NULL, '') """,
729
+ (datetime.datetime.now().isoformat(" "),),
730
+ )
731
+ assert isinstance(cur.lastrowid, int)
732
+ self.session_number = cur.lastrowid
733
+
734
+ def end_session(self) -> None:
735
+ """Close the database session, filling in the end time and line count."""
736
+ self.writeout_cache()
737
+ with self.db:
738
+ self.db.execute(
739
+ """UPDATE sessions SET end=?, num_cmds=? WHERE
740
+ session==?""",
741
+ (
742
+ datetime.datetime.now(datetime.timezone.utc).isoformat(" "),
743
+ len(self.input_hist_parsed) - 1,
744
+ self.session_number,
745
+ ),
746
+ )
747
+ self.session_number = 0
748
+
749
+ def name_session(self, name: str) -> None:
750
+ """Give the current session a name in the history database."""
751
+ warn(
752
+ "name_session is deprecated in IPython 9.0 and will be removed in future versions",
753
+ DeprecationWarning,
754
+ stacklevel=2,
755
+ )
756
+ with self.db:
757
+ self.db.execute(
758
+ "UPDATE sessions SET remark=? WHERE session==?",
759
+ (name, self.session_number),
760
+ )
761
+
762
+ def reset(self, new_session: bool = True) -> None:
763
+ """Clear the session history, releasing all object references, and
764
+ optionally open a new session."""
765
+ self.output_hist.clear()
766
+ self.outputs.clear()
767
+ self.exceptions.clear()
768
+
769
+ # The directory history can't be completely empty
770
+ self.dir_hist[:] = [Path.cwd()]
771
+
772
+ if new_session:
773
+ if self.session_number:
774
+ self.end_session()
775
+ self.input_hist_parsed[:] = [""]
776
+ self.input_hist_raw[:] = [""]
777
+ self.new_session()
778
+
779
+ # ------------------------------
780
+ # Methods for retrieving history
781
+ # ------------------------------
782
+ def get_session_info(
783
+ self, session: int = 0
784
+ ) -> tuple[int, datetime.datetime, Optional[datetime.datetime], Optional[int], str]:
785
+ """Get info about a session.
786
+
787
+ Parameters
788
+ ----------
789
+ session : int
790
+ Session number to retrieve. The current session is 0, and negative
791
+ numbers count back from current session, so -1 is the previous session.
792
+
793
+ Returns
794
+ -------
795
+ session_id : int
796
+ Session ID number
797
+ start : datetime
798
+ Timestamp for the start of the session.
799
+ end : datetime
800
+ Timestamp for the end of the session, or None if IPython crashed.
801
+ num_cmds : int
802
+ Number of commands run, or None if IPython crashed.
803
+ remark : str
804
+ A manually set description.
805
+ """
806
+ if session <= 0:
807
+ session += self.session_number
808
+
809
+ return super(HistoryManager, self).get_session_info(session=session)
810
+
811
+ @catch_corrupt_db
812
+ def get_tail(
813
+ self,
814
+ n: int = 10,
815
+ raw: bool = True,
816
+ output: bool = False,
817
+ include_latest: bool = False,
818
+ ) -> Iterable[tuple[int, int, InOrInOut]]:
819
+ """Get the last n lines from the history database.
820
+
821
+ Most recent entry last.
822
+
823
+ Completion will be reordered so that that the last ones are when
824
+ possible from current session.
825
+
826
+ Parameters
827
+ ----------
828
+ n : int
829
+ The number of lines to get
830
+ raw, output : bool
831
+ See :meth:`get_range`
832
+ include_latest : bool
833
+ If False (default), n+1 lines are fetched, and the latest one
834
+ is discarded. This is intended to be used where the function
835
+ is called by a user command, which it should not return.
836
+
837
+ Returns
838
+ -------
839
+ Tuples as :meth:`get_range`
840
+ """
841
+ self.writeout_cache()
842
+ if not include_latest:
843
+ n += 1
844
+ # cursor/line/entry
845
+ this_cur = list(
846
+ self._run_sql(
847
+ "WHERE session == ? ORDER BY line DESC LIMIT ? ",
848
+ (self.session_number, n),
849
+ raw=raw,
850
+ output=output,
851
+ )
852
+ )
853
+ other_cur = list(
854
+ self._run_sql(
855
+ "WHERE session != ? ORDER BY session DESC, line DESC LIMIT ?",
856
+ (self.session_number, n),
857
+ raw=raw,
858
+ output=output,
859
+ )
860
+ )
861
+
862
+ everything: list[tuple[int, int, InOrInOut]] = this_cur + other_cur
863
+
864
+ everything = everything[:n]
865
+
866
+ if not include_latest:
867
+ return list(everything)[:0:-1]
868
+ return list(everything)[::-1]
869
+
870
+ def _get_range_session(
871
+ self,
872
+ start: int = 1,
873
+ stop: Optional[int] = None,
874
+ raw: bool = True,
875
+ output: bool = False,
876
+ ) -> Iterable[tuple[int, int, InOrInOut]]:
877
+ """Get input and output history from the current session. Called by
878
+ get_range, and takes similar parameters."""
879
+ input_hist = self.input_hist_raw if raw else self.input_hist_parsed
880
+
881
+ n = len(input_hist)
882
+ if start < 0:
883
+ start += n
884
+ if not stop or (stop > n):
885
+ stop = n
886
+ elif stop < 0:
887
+ stop += n
888
+ line: InOrInOut
889
+ for i in range(start, stop):
890
+ if output:
891
+ line = (input_hist[i], self.output_hist_reprs.get(i))
892
+ else:
893
+ line = input_hist[i]
894
+ yield (0, i, line)
895
+
896
+ def get_range(
897
+ self,
898
+ session: int = 0,
899
+ start: int = 1,
900
+ stop: Optional[int] = None,
901
+ raw: bool = True,
902
+ output: bool = False,
903
+ ) -> Iterable[tuple[int, int, InOrInOut]]:
904
+ """Retrieve input by session.
905
+
906
+ Parameters
907
+ ----------
908
+ session : int
909
+ Session number to retrieve. The current session is 0, and negative
910
+ numbers count back from current session, so -1 is previous session.
911
+ start : int
912
+ First line to retrieve.
913
+ stop : int
914
+ End of line range (excluded from output itself). If None, retrieve
915
+ to the end of the session.
916
+ raw : bool
917
+ If True, return untranslated input
918
+ output : bool
919
+ If True, attempt to include output. This will be 'real' Python
920
+ objects for the current session, or text reprs from previous
921
+ sessions if db_log_output was enabled at the time. Where no output
922
+ is found, None is used.
923
+
924
+ Returns
925
+ -------
926
+ entries
927
+ An iterator over the desired lines. Each line is a 3-tuple, either
928
+ (session, line, input) if output is False, or
929
+ (session, line, (input, output)) if output is True.
930
+ """
931
+ if session <= 0:
932
+ session += self.session_number
933
+ if session == self.session_number: # Current session
934
+ return self._get_range_session(start, stop, raw, output)
935
+ return super(HistoryManager, self).get_range(session, start, stop, raw, output)
936
+
937
+ ## ----------------------------
938
+ ## Methods for storing history:
939
+ ## ----------------------------
940
+ def store_inputs(
941
+ self, line_num: int, source: str, source_raw: Optional[str] = None
942
+ ) -> None:
943
+ """Store source and raw input in history and create input cache
944
+ variables ``_i*``.
945
+
946
+ Parameters
947
+ ----------
948
+ line_num : int
949
+ The prompt number of this input.
950
+ source : str
951
+ Python input.
952
+ source_raw : str, optional
953
+ If given, this is the raw input without any IPython transformations
954
+ applied to it. If not given, ``source`` is used.
955
+ """
956
+ if source_raw is None:
957
+ source_raw = source
958
+ source = source.rstrip("\n")
959
+ source_raw = source_raw.rstrip("\n")
960
+
961
+ # do not store exit/quit commands
962
+ if self._exit_re.match(source_raw.strip()):
963
+ return
964
+
965
+ self.input_hist_parsed.append(source)
966
+ self.input_hist_raw.append(source_raw)
967
+
968
+ with self.db_input_cache_lock:
969
+ self.db_input_cache.append((line_num, source, source_raw))
970
+ # Trigger to flush cache and write to DB.
971
+ if len(self.db_input_cache) >= self.db_cache_size:
972
+ if self.save_flag:
973
+ self.save_flag.set()
974
+
975
+ # update the auto _i variables
976
+ self._iii = self._ii
977
+ self._ii = self._i
978
+ self._i = self._i00
979
+ self._i00 = source_raw
980
+
981
+ # hackish access to user namespace to create _i1,_i2... dynamically
982
+ new_i = "_i%s" % line_num
983
+ to_main = {"_i": self._i, "_ii": self._ii, "_iii": self._iii, new_i: self._i00}
984
+
985
+ if self.shell is not None:
986
+ self.shell.push(to_main, interactive=False)
987
+
988
+ def store_output(self, line_num: int) -> None:
989
+ """If database output logging is enabled, this saves all the
990
+ outputs from the indicated prompt number to the database. It's
991
+ called by run_cell after code has been executed.
992
+
993
+ Parameters
994
+ ----------
995
+ line_num : int
996
+ The line number from which to save outputs
997
+ """
998
+ if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
999
+ return
1000
+ lnum: int = line_num
1001
+ output = self.output_hist_reprs[line_num]
1002
+
1003
+ with self.db_output_cache_lock:
1004
+ self.db_output_cache.append((line_num, output))
1005
+ if self.db_cache_size <= 1 and self.save_flag is not None:
1006
+ self.save_flag.set()
1007
+
1008
+ def _writeout_input_cache(self, conn: sqlite3.Connection) -> None:
1009
+ with conn:
1010
+ for line in self.db_input_cache:
1011
+ conn.execute(
1012
+ "INSERT INTO history VALUES (?, ?, ?, ?)",
1013
+ (self.session_number,) + line,
1014
+ )
1015
+
1016
+ def _writeout_output_cache(self, conn: sqlite3.Connection) -> None:
1017
+ with conn:
1018
+ for line in self.db_output_cache:
1019
+ conn.execute(
1020
+ "INSERT INTO output_history VALUES (?, ?, ?)",
1021
+ (self.session_number,) + line,
1022
+ )
1023
+
1024
+ @only_when_enabled
1025
+ def writeout_cache(self, conn: Optional[sqlite3.Connection] = None) -> None:
1026
+ """Write any entries in the cache to the database."""
1027
+ if conn is None:
1028
+ conn = self.db
1029
+
1030
+ with self.db_input_cache_lock:
1031
+ try:
1032
+ self._writeout_input_cache(conn)
1033
+ except sqlite3.IntegrityError:
1034
+ self.new_session(conn)
1035
+ print(
1036
+ "ERROR! Session/line number was not unique in",
1037
+ "database. History logging moved to new session",
1038
+ self.session_number,
1039
+ )
1040
+ try:
1041
+ # Try writing to the new session. If this fails, don't
1042
+ # recurse
1043
+ self._writeout_input_cache(conn)
1044
+ except sqlite3.IntegrityError:
1045
+ pass
1046
+ finally:
1047
+ self.db_input_cache = []
1048
+
1049
+ with self.db_output_cache_lock:
1050
+ try:
1051
+ self._writeout_output_cache(conn)
1052
+ except sqlite3.IntegrityError:
1053
+ print(
1054
+ "!! Session/line number for output was not unique",
1055
+ "in database. Output will not be stored.",
1056
+ )
1057
+ finally:
1058
+ self.db_output_cache = []
1059
+
1060
+
1061
+ from typing import Callable, Iterator
1062
+ from weakref import ReferenceType
1063
+
1064
+
1065
+ @contextmanager
1066
+ def hold(ref: ReferenceType[HistoryManager]) -> Iterator[ReferenceType[HistoryManager]]:
1067
+ """
1068
+ Context manger that hold a reference to a weak ref to make sure it
1069
+ is not GC'd during it's context.
1070
+ """
1071
+ r = ref()
1072
+ yield ref
1073
+ del r
1074
+
1075
+
1076
+ class HistorySavingThread(threading.Thread):
1077
+ """This thread takes care of writing history to the database, so that
1078
+ the UI isn't held up while that happens.
1079
+
1080
+ It waits for the HistoryManager's save_flag to be set, then writes out
1081
+ the history cache. The main thread is responsible for setting the flag when
1082
+ the cache size reaches a defined threshold."""
1083
+
1084
+ save_flag: threading.Event
1085
+ daemon: bool = True
1086
+ _stop_now: bool = False
1087
+ enabled: bool = True
1088
+ history_manager: ref[HistoryManager]
1089
+ _stopped = False
1090
+
1091
+ def __init__(self, history_manager: HistoryManager) -> None:
1092
+ super(HistorySavingThread, self).__init__(name="IPythonHistorySavingThread")
1093
+ self.history_manager = ref(history_manager)
1094
+ self.enabled = history_manager.enabled
1095
+ self.save_flag = threading.Event()
1096
+
1097
+ @only_when_enabled
1098
+ def run(self) -> None:
1099
+ atexit.register(self.stop)
1100
+ # We need a separate db connection per thread:
1101
+ try:
1102
+ hm: ReferenceType[HistoryManager]
1103
+ with hold(self.history_manager) as hm:
1104
+ if hm() is not None:
1105
+ self.db = sqlite3.connect(
1106
+ str(hm().hist_file), # type: ignore [union-attr]
1107
+ **hm().connection_options, # type: ignore [union-attr]
1108
+ )
1109
+ while True:
1110
+ self.save_flag.wait()
1111
+ with hold(self.history_manager) as hm:
1112
+ if hm() is None:
1113
+ self._stop_now = True
1114
+ if self._stop_now:
1115
+ self.db.close()
1116
+ return
1117
+ self.save_flag.clear()
1118
+ if hm() is not None:
1119
+ hm().writeout_cache(self.db) # type: ignore [union-attr]
1120
+
1121
+ except Exception as e:
1122
+ print(
1123
+ (
1124
+ "The history saving thread hit an unexpected error (%s)."
1125
+ "History will not be written to the database."
1126
+ )
1127
+ % repr(e)
1128
+ )
1129
+ finally:
1130
+ atexit.unregister(self.stop)
1131
+
1132
+ def stop(self) -> None:
1133
+ """This can be called from the main thread to safely stop this thread.
1134
+
1135
+ Note that it does not attempt to write out remaining history before
1136
+ exiting. That should be done by calling the HistoryManager's
1137
+ end_session method."""
1138
+ if self._stopped:
1139
+ return
1140
+ self._stop_now = True
1141
+
1142
+ self.save_flag.set()
1143
+ self._stopped = True
1144
+ if self != threading.current_thread():
1145
+ self.join()
1146
+
1147
+ def __del__(self) -> None:
1148
+ self.stop()
1149
+
1150
+
1151
+ # To match, e.g. ~5/8-~2/3
1152
+ range_re = re.compile(
1153
+ r"""
1154
+ ((?P<startsess>~?\d+)/)?
1155
+ (?P<start>\d+)?
1156
+ ((?P<sep>[\-:])
1157
+ ((?P<endsess>~?\d+)/)?
1158
+ (?P<end>\d+))?
1159
+ $""",
1160
+ re.VERBOSE,
1161
+ )
1162
+
1163
+
1164
+ def extract_hist_ranges(ranges_str: str) -> Iterable[tuple[int, int, Optional[int]]]:
1165
+ """Turn a string of history ranges into 3-tuples of (session, start, stop).
1166
+
1167
+ Empty string results in a `[(0, 1, None)]`, i.e. "everything from current
1168
+ session".
1169
+
1170
+ Examples
1171
+ --------
1172
+ >>> list(extract_hist_ranges("~8/5-~7/4 2"))
1173
+ [(-8, 5, None), (-7, 1, 5), (0, 2, 3)]
1174
+ """
1175
+ if ranges_str == "":
1176
+ yield (0, 1, None) # Everything from current session
1177
+ return
1178
+
1179
+ for range_str in ranges_str.split():
1180
+ rmatch = range_re.match(range_str)
1181
+ if not rmatch:
1182
+ continue
1183
+ start = rmatch.group("start")
1184
+ if start:
1185
+ start = int(start)
1186
+ end = rmatch.group("end")
1187
+ # If no end specified, get (a, a + 1)
1188
+ end = int(end) if end else start + 1
1189
+ else: # start not specified
1190
+ if not rmatch.group("startsess"): # no startsess
1191
+ continue
1192
+ start = 1
1193
+ end = None # provide the entire session hist
1194
+
1195
+ if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
1196
+ assert end is not None
1197
+ end += 1
1198
+ startsess = rmatch.group("startsess") or "0"
1199
+ endsess = rmatch.group("endsess") or startsess
1200
+ startsess = int(startsess.replace("~", "-"))
1201
+ endsess = int(endsess.replace("~", "-"))
1202
+ assert endsess >= startsess, "start session must be earlier than end session"
1203
+
1204
+ if endsess == startsess:
1205
+ yield (startsess, start, end)
1206
+ continue
1207
+ # Multiple sessions in one range:
1208
+ yield (startsess, start, None)
1209
+ for sess in range(startsess + 1, endsess):
1210
+ yield (sess, 1, None)
1211
+ yield (endsess, 1, end)
1212
+
1213
+
1214
+ def _format_lineno(session: int, line: int) -> str:
1215
+ """Helper function to format line numbers properly."""
1216
+ if session == 0:
1217
+ return str(line)
1218
+ return "%s#%s" % (session, line)
temp_venv/lib/python3.13/site-packages/IPython/core/inputtransformer2.py ADDED
@@ -0,0 +1,808 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Input transformer machinery to support IPython special syntax.
2
+
3
+ This includes the machinery to recognise and transform ``%magic`` commands,
4
+ ``!system`` commands, ``help?`` querying, prompt stripping, and so forth.
5
+
6
+ Added: IPython 7.0. Replaces inputsplitter and inputtransformer which were
7
+ deprecated in 7.0 and removed in 9.0
8
+ """
9
+
10
+ # Copyright (c) IPython Development Team.
11
+ # Distributed under the terms of the Modified BSD License.
12
+
13
+ import ast
14
+ from codeop import CommandCompiler, Compile
15
+ import re
16
+ import sys
17
+ import tokenize
18
+ from typing import List, Tuple, Optional, Any
19
+ import warnings
20
+ from textwrap import dedent
21
+
22
+ from IPython.utils import tokenutil
23
+
24
+ _indent_re = re.compile(r'^[ \t]+')
25
+
26
+ def leading_empty_lines(lines):
27
+ """Remove leading empty lines
28
+
29
+ If the leading lines are empty or contain only whitespace, they will be
30
+ removed.
31
+ """
32
+ if not lines:
33
+ return lines
34
+ for i, line in enumerate(lines):
35
+ if line and not line.isspace():
36
+ return lines[i:]
37
+ return lines
38
+
39
+ def leading_indent(lines):
40
+ """Remove leading indentation.
41
+
42
+ Removes the minimum common leading indentation from all lines.
43
+ """
44
+ if not lines:
45
+ return lines
46
+ return dedent("".join(lines)).splitlines(keepends=True)
47
+
48
+ class PromptStripper:
49
+ """Remove matching input prompts from a block of input.
50
+
51
+ Parameters
52
+ ----------
53
+ prompt_re : regular expression
54
+ A regular expression matching any input prompt (including continuation,
55
+ e.g. ``...``)
56
+ initial_re : regular expression, optional
57
+ A regular expression matching only the initial prompt, but not continuation.
58
+ If no initial expression is given, prompt_re will be used everywhere.
59
+ Used mainly for plain Python prompts (``>>>``), where the continuation prompt
60
+ ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
61
+
62
+ Notes
63
+ -----
64
+
65
+ If initial_re and prompt_re differ,
66
+ only initial_re will be tested against the first line.
67
+ If any prompt is found on the first two lines,
68
+ prompts will be stripped from the rest of the block.
69
+ """
70
+ def __init__(self, prompt_re, initial_re=None):
71
+ self.prompt_re = prompt_re
72
+ self.initial_re = initial_re or prompt_re
73
+
74
+ def _strip(self, lines):
75
+ return [self.prompt_re.sub('', l, count=1) for l in lines]
76
+
77
+ def __call__(self, lines):
78
+ if not lines:
79
+ return lines
80
+ if self.initial_re.match(lines[0]) or \
81
+ (len(lines) > 1 and self.prompt_re.match(lines[1])):
82
+ return self._strip(lines)
83
+ return lines
84
+
85
+ classic_prompt = PromptStripper(
86
+ prompt_re=re.compile(r'^(>>>|\.\.\.)( |$)'),
87
+ initial_re=re.compile(r'^>>>( |$)')
88
+ )
89
+
90
+ ipython_prompt = PromptStripper(
91
+ re.compile(
92
+ r"""
93
+ ^( # Match from the beginning of a line, either:
94
+
95
+ # 1. First-line prompt:
96
+ ((\[nav\]|\[ins\])?\ )? # Vi editing mode prompt, if it's there
97
+ In\ # The 'In' of the prompt, with a space
98
+ \[\d+\]: # Command index, as displayed in the prompt
99
+ \ # With a mandatory trailing space
100
+
101
+ | # ... or ...
102
+
103
+ # 2. The three dots of the multiline prompt
104
+ \s* # All leading whitespace characters
105
+ \.{3,}: # The three (or more) dots
106
+ \ ? # With an optional trailing space
107
+
108
+ )
109
+ """,
110
+ re.VERBOSE,
111
+ )
112
+ )
113
+
114
+
115
+ def cell_magic(lines):
116
+ if not lines or not lines[0].startswith('%%'):
117
+ return lines
118
+ if re.match(r'%%\w+\?', lines[0]):
119
+ # This case will be handled by help_end
120
+ return lines
121
+ magic_name, _, first_line = lines[0][2:].rstrip().partition(' ')
122
+ body = ''.join(lines[1:])
123
+ return ['get_ipython().run_cell_magic(%r, %r, %r)\n'
124
+ % (magic_name, first_line, body)]
125
+
126
+
127
+ def _find_assign_op(token_line) -> Optional[int]:
128
+ """Get the index of the first assignment in the line ('=' not inside brackets)
129
+
130
+ Note: We don't try to support multiple special assignment (a = b = %foo)
131
+ """
132
+ paren_level = 0
133
+ for i, ti in enumerate(token_line):
134
+ s = ti.string
135
+ if s == '=' and paren_level == 0:
136
+ return i
137
+ if s in {'(','[','{'}:
138
+ paren_level += 1
139
+ elif s in {')', ']', '}'}:
140
+ if paren_level > 0:
141
+ paren_level -= 1
142
+ return None
143
+
144
+ def find_end_of_continued_line(lines, start_line: int):
145
+ """Find the last line of a line explicitly extended using backslashes.
146
+
147
+ Uses 0-indexed line numbers.
148
+ """
149
+ end_line = start_line
150
+ while lines[end_line].endswith('\\\n'):
151
+ end_line += 1
152
+ if end_line >= len(lines):
153
+ break
154
+ return end_line
155
+
156
+ def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
157
+ r"""Assemble a single line from multiple continued line pieces
158
+
159
+ Continued lines are lines ending in ``\``, and the line following the last
160
+ ``\`` in the block.
161
+
162
+ For example, this code continues over multiple lines::
163
+
164
+ if (assign_ix is not None) \
165
+ and (len(line) >= assign_ix + 2) \
166
+ and (line[assign_ix+1].string == '%') \
167
+ and (line[assign_ix+2].type == tokenize.NAME):
168
+
169
+ This statement contains four continued line pieces.
170
+ Assembling these pieces into a single line would give::
171
+
172
+ if (assign_ix is not None) and (len(line) >= assign_ix + 2) and (line[...
173
+
174
+ This uses 0-indexed line numbers. *start* is (lineno, colno).
175
+
176
+ Used to allow ``%magic`` and ``!system`` commands to be continued over
177
+ multiple lines.
178
+ """
179
+ parts = [lines[start[0]][start[1]:]] + lines[start[0]+1:end_line+1]
180
+ return ' '.join([p.rstrip()[:-1] for p in parts[:-1]] # Strip backslash+newline
181
+ + [parts[-1].rstrip()]) # Strip newline from last line
182
+
183
+ class TokenTransformBase:
184
+ """Base class for transformations which examine tokens.
185
+
186
+ Special syntax should not be transformed when it occurs inside strings or
187
+ comments. This is hard to reliably avoid with regexes. The solution is to
188
+ tokenise the code as Python, and recognise the special syntax in the tokens.
189
+
190
+ IPython's special syntax is not valid Python syntax, so tokenising may go
191
+ wrong after the special syntax starts. These classes therefore find and
192
+ transform *one* instance of special syntax at a time into regular Python
193
+ syntax. After each transformation, tokens are regenerated to find the next
194
+ piece of special syntax.
195
+
196
+ Subclasses need to implement one class method (find)
197
+ and one regular method (transform).
198
+
199
+ The priority attribute can select which transformation to apply if multiple
200
+ transformers match in the same place. Lower numbers have higher priority.
201
+ This allows "%magic?" to be turned into a help call rather than a magic call.
202
+ """
203
+ # Lower numbers -> higher priority (for matches in the same location)
204
+ priority = 10
205
+
206
+ def sortby(self):
207
+ return self.start_line, self.start_col, self.priority
208
+
209
+ def __init__(self, start):
210
+ self.start_line = start[0] - 1 # Shift from 1-index to 0-index
211
+ self.start_col = start[1]
212
+
213
+ @classmethod
214
+ def find(cls, tokens_by_line):
215
+ """Find one instance of special syntax in the provided tokens.
216
+
217
+ Tokens are grouped into logical lines for convenience,
218
+ so it is easy to e.g. look at the first token of each line.
219
+ *tokens_by_line* is a list of lists of tokenize.TokenInfo objects.
220
+
221
+ This should return an instance of its class, pointing to the start
222
+ position it has found, or None if it found no match.
223
+ """
224
+ raise NotImplementedError
225
+
226
+ def transform(self, lines: List[str]):
227
+ """Transform one instance of special syntax found by ``find()``
228
+
229
+ Takes a list of strings representing physical lines,
230
+ returns a similar list of transformed lines.
231
+ """
232
+ raise NotImplementedError
233
+
234
+ class MagicAssign(TokenTransformBase):
235
+ """Transformer for assignments from magics (a = %foo)"""
236
+ @classmethod
237
+ def find(cls, tokens_by_line):
238
+ """Find the first magic assignment (a = %foo) in the cell.
239
+ """
240
+ for line in tokens_by_line:
241
+ assign_ix = _find_assign_op(line)
242
+ if (assign_ix is not None) \
243
+ and (len(line) >= assign_ix + 2) \
244
+ and (line[assign_ix+1].string == '%') \
245
+ and (line[assign_ix+2].type == tokenize.NAME):
246
+ return cls(line[assign_ix+1].start)
247
+
248
+ def transform(self, lines: List[str]):
249
+ """Transform a magic assignment found by the ``find()`` classmethod.
250
+ """
251
+ start_line, start_col = self.start_line, self.start_col
252
+ lhs = lines[start_line][:start_col]
253
+ end_line = find_end_of_continued_line(lines, start_line)
254
+ rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
255
+ assert rhs.startswith('%'), rhs
256
+ magic_name, _, args = rhs[1:].partition(' ')
257
+
258
+ lines_before = lines[:start_line]
259
+ call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
260
+ new_line = lhs + call + '\n'
261
+ lines_after = lines[end_line+1:]
262
+
263
+ return lines_before + [new_line] + lines_after
264
+
265
+
266
+ class SystemAssign(TokenTransformBase):
267
+ """Transformer for assignments from system commands (a = !foo)"""
268
+ @classmethod
269
+ def find_pre_312(cls, tokens_by_line):
270
+ for line in tokens_by_line:
271
+ assign_ix = _find_assign_op(line)
272
+ if (assign_ix is not None) \
273
+ and not line[assign_ix].line.strip().startswith('=') \
274
+ and (len(line) >= assign_ix + 2) \
275
+ and (line[assign_ix + 1].type == tokenize.ERRORTOKEN):
276
+ ix = assign_ix + 1
277
+
278
+ while ix < len(line) and line[ix].type == tokenize.ERRORTOKEN:
279
+ if line[ix].string == '!':
280
+ return cls(line[ix].start)
281
+ elif not line[ix].string.isspace():
282
+ break
283
+ ix += 1
284
+
285
+ @classmethod
286
+ def find_post_312(cls, tokens_by_line):
287
+ for line in tokens_by_line:
288
+ assign_ix = _find_assign_op(line)
289
+ if (
290
+ (assign_ix is not None)
291
+ and not line[assign_ix].line.strip().startswith("=")
292
+ and (len(line) >= assign_ix + 2)
293
+ and (line[assign_ix + 1].type == tokenize.OP)
294
+ and (line[assign_ix + 1].string == "!")
295
+ ):
296
+ return cls(line[assign_ix + 1].start)
297
+
298
+ @classmethod
299
+ def find(cls, tokens_by_line):
300
+ """Find the first system assignment (a = !foo) in the cell."""
301
+ if sys.version_info < (3, 12):
302
+ return cls.find_pre_312(tokens_by_line)
303
+ return cls.find_post_312(tokens_by_line)
304
+
305
+ def transform(self, lines: List[str]):
306
+ """Transform a system assignment found by the ``find()`` classmethod.
307
+ """
308
+ start_line, start_col = self.start_line, self.start_col
309
+
310
+ lhs = lines[start_line][:start_col]
311
+ end_line = find_end_of_continued_line(lines, start_line)
312
+ rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
313
+ assert rhs.startswith('!'), rhs
314
+ cmd = rhs[1:]
315
+
316
+ lines_before = lines[:start_line]
317
+ call = "get_ipython().getoutput({!r})".format(cmd)
318
+ new_line = lhs + call + '\n'
319
+ lines_after = lines[end_line + 1:]
320
+
321
+ return lines_before + [new_line] + lines_after
322
+
323
+ # The escape sequences that define the syntax transformations IPython will
324
+ # apply to user input. These can NOT be just changed here: many regular
325
+ # expressions and other parts of the code may use their hardcoded values, and
326
+ # for all intents and purposes they constitute the 'IPython syntax', so they
327
+ # should be considered fixed.
328
+
329
+ ESC_SHELL = '!' # Send line to underlying system shell
330
+ ESC_SH_CAP = '!!' # Send line to system shell and capture output
331
+ ESC_HELP = '?' # Find information about object
332
+ ESC_HELP2 = '??' # Find extra-detailed information about object
333
+ ESC_MAGIC = '%' # Call magic function
334
+ ESC_MAGIC2 = '%%' # Call cell-magic function
335
+ ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
336
+ ESC_QUOTE2 = ';' # Quote all args as a single string, call
337
+ ESC_PAREN = '/' # Call first argument with rest of line as arguments
338
+
339
+ ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'}
340
+ ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately
341
+
342
+ def _make_help_call(target, esc):
343
+ """Prepares a pinfo(2)/psearch call from a target name and the escape
344
+ (i.e. ? or ??)"""
345
+ method = 'pinfo2' if esc == '??' \
346
+ else 'psearch' if '*' in target \
347
+ else 'pinfo'
348
+ arg = " ".join([method, target])
349
+ #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
350
+ t_magic_name, _, t_magic_arg_s = arg.partition(' ')
351
+ t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
352
+ return "get_ipython().run_line_magic(%r, %r)" % (t_magic_name, t_magic_arg_s)
353
+
354
+
355
+ def _tr_help(content):
356
+ """Translate lines escaped with: ?
357
+
358
+ A naked help line should fire the intro help screen (shell.show_usage())
359
+ """
360
+ if not content:
361
+ return 'get_ipython().show_usage()'
362
+
363
+ return _make_help_call(content, '?')
364
+
365
+ def _tr_help2(content):
366
+ """Translate lines escaped with: ??
367
+
368
+ A naked help line should fire the intro help screen (shell.show_usage())
369
+ """
370
+ if not content:
371
+ return 'get_ipython().show_usage()'
372
+
373
+ return _make_help_call(content, '??')
374
+
375
+ def _tr_magic(content):
376
+ "Translate lines escaped with a percent sign: %"
377
+ name, _, args = content.partition(' ')
378
+ return 'get_ipython().run_line_magic(%r, %r)' % (name, args)
379
+
380
+ def _tr_quote(content):
381
+ "Translate lines escaped with a comma: ,"
382
+ name, _, args = content.partition(' ')
383
+ return '%s("%s")' % (name, '", "'.join(args.split()) )
384
+
385
+ def _tr_quote2(content):
386
+ "Translate lines escaped with a semicolon: ;"
387
+ name, _, args = content.partition(' ')
388
+ return '%s("%s")' % (name, args)
389
+
390
+ def _tr_paren(content):
391
+ "Translate lines escaped with a slash: /"
392
+ name, _, args = content.partition(" ")
393
+ if name == "":
394
+ raise SyntaxError(f'"{ESC_SHELL}" must be followed by a callable name')
395
+
396
+ return '%s(%s)' % (name, ", ".join(args.split()))
397
+
398
+ tr = { ESC_SHELL : 'get_ipython().system({!r})'.format,
399
+ ESC_SH_CAP : 'get_ipython().getoutput({!r})'.format,
400
+ ESC_HELP : _tr_help,
401
+ ESC_HELP2 : _tr_help2,
402
+ ESC_MAGIC : _tr_magic,
403
+ ESC_QUOTE : _tr_quote,
404
+ ESC_QUOTE2 : _tr_quote2,
405
+ ESC_PAREN : _tr_paren }
406
+
407
+ class EscapedCommand(TokenTransformBase):
408
+ """Transformer for escaped commands like %foo, !foo, or /foo"""
409
+ @classmethod
410
+ def find(cls, tokens_by_line):
411
+ """Find the first escaped command (%foo, !foo, etc.) in the cell.
412
+ """
413
+ for line in tokens_by_line:
414
+ if not line:
415
+ continue
416
+ ix = 0
417
+ ll = len(line)
418
+ while ll > ix and line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
419
+ ix += 1
420
+ if ix >= ll:
421
+ continue
422
+ if line[ix].string in ESCAPE_SINGLES:
423
+ return cls(line[ix].start)
424
+
425
+ def transform(self, lines):
426
+ """Transform an escaped line found by the ``find()`` classmethod.
427
+ """
428
+ start_line, start_col = self.start_line, self.start_col
429
+
430
+ indent = lines[start_line][:start_col]
431
+ end_line = find_end_of_continued_line(lines, start_line)
432
+ line = assemble_continued_line(lines, (start_line, start_col), end_line)
433
+
434
+ if len(line) > 1 and line[:2] in ESCAPE_DOUBLES:
435
+ escape, content = line[:2], line[2:]
436
+ else:
437
+ escape, content = line[:1], line[1:]
438
+
439
+ if escape in tr:
440
+ call = tr[escape](content)
441
+ else:
442
+ call = ''
443
+
444
+ lines_before = lines[:start_line]
445
+ new_line = indent + call + '\n'
446
+ lines_after = lines[end_line + 1:]
447
+
448
+ return lines_before + [new_line] + lines_after
449
+
450
+
451
+ _help_end_re = re.compile(
452
+ r"""(%{0,2}
453
+ (?!\d)[\w*]+ # Variable name
454
+ (\.(?!\d)[\w*]+|\[-?[0-9]+\])* # .etc.etc or [0], we only support literal integers.
455
+ )
456
+ (\?\??)$ # ? or ??
457
+ """,
458
+ re.VERBOSE,
459
+ )
460
+
461
+
462
+ class HelpEnd(TokenTransformBase):
463
+ """Transformer for help syntax: obj? and obj??"""
464
+ # This needs to be higher priority (lower number) than EscapedCommand so
465
+ # that inspecting magics (%foo?) works.
466
+ priority = 5
467
+
468
+ def __init__(self, start, q_locn):
469
+ super().__init__(start)
470
+ self.q_line = q_locn[0] - 1 # Shift from 1-indexed to 0-indexed
471
+ self.q_col = q_locn[1]
472
+
473
+ @classmethod
474
+ def find(cls, tokens_by_line):
475
+ """Find the first help command (foo?) in the cell.
476
+ """
477
+ for line in tokens_by_line:
478
+ # Last token is NEWLINE; look at last but one
479
+ if len(line) > 2 and line[-2].string == '?':
480
+ # Find the first token that's not INDENT/DEDENT
481
+ ix = 0
482
+ while line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
483
+ ix += 1
484
+ return cls(line[ix].start, line[-2].start)
485
+
486
+ def transform(self, lines):
487
+ """Transform a help command found by the ``find()`` classmethod.
488
+ """
489
+
490
+ piece = "".join(lines[self.start_line : self.q_line + 1])
491
+ indent, content = piece[: self.start_col], piece[self.start_col :]
492
+ lines_before = lines[: self.start_line]
493
+ lines_after = lines[self.q_line + 1 :]
494
+
495
+ m = _help_end_re.search(content)
496
+ if not m:
497
+ raise SyntaxError(content)
498
+ assert m is not None, content
499
+ target = m.group(1)
500
+ esc = m.group(3)
501
+
502
+
503
+ call = _make_help_call(target, esc)
504
+ new_line = indent + call + '\n'
505
+
506
+ return lines_before + [new_line] + lines_after
507
+
508
+ def make_tokens_by_line(lines:List[str]):
509
+ """Tokenize a series of lines and group tokens by line.
510
+
511
+ The tokens for a multiline Python string or expression are grouped as one
512
+ line. All lines except the last lines should keep their line ending ('\\n',
513
+ '\\r\\n') for this to properly work. Use `.splitlines(keeplineending=True)`
514
+ for example when passing block of text to this function.
515
+
516
+ """
517
+ # NL tokens are used inside multiline expressions, but also after blank
518
+ # lines or comments. This is intentional - see https://bugs.python.org/issue17061
519
+ # We want to group the former case together but split the latter, so we
520
+ # track parentheses level, similar to the internals of tokenize.
521
+
522
+ # reexported from token on 3.7+
523
+ NEWLINE, NL = tokenize.NEWLINE, tokenize.NL # type: ignore
524
+ tokens_by_line: List[List[Any]] = [[]]
525
+ if len(lines) > 1 and not lines[0].endswith(("\n", "\r", "\r\n", "\x0b", "\x0c")):
526
+ warnings.warn(
527
+ "`make_tokens_by_line` received a list of lines which do not have lineending markers ('\\n', '\\r', '\\r\\n', '\\x0b', '\\x0c'), behavior will be unspecified",
528
+ stacklevel=2,
529
+ )
530
+ parenlev = 0
531
+ try:
532
+ for token in tokenutil.generate_tokens_catch_errors(
533
+ iter(lines).__next__, extra_errors_to_catch=["expected EOF"]
534
+ ):
535
+ tokens_by_line[-1].append(token)
536
+ if (token.type == NEWLINE) \
537
+ or ((token.type == NL) and (parenlev <= 0)):
538
+ tokens_by_line.append([])
539
+ elif token.string in {'(', '[', '{'}:
540
+ parenlev += 1
541
+ elif token.string in {')', ']', '}'}:
542
+ if parenlev > 0:
543
+ parenlev -= 1
544
+ except tokenize.TokenError:
545
+ # Input ended in a multiline string or expression. That's OK for us.
546
+ pass
547
+
548
+
549
+ if not tokens_by_line[-1]:
550
+ tokens_by_line.pop()
551
+
552
+
553
+ return tokens_by_line
554
+
555
+
556
+ def has_sunken_brackets(tokens: List[tokenize.TokenInfo]):
557
+ """Check if the depth of brackets in the list of tokens drops below 0"""
558
+ parenlev = 0
559
+ for token in tokens:
560
+ if token.string in {"(", "[", "{"}:
561
+ parenlev += 1
562
+ elif token.string in {")", "]", "}"}:
563
+ parenlev -= 1
564
+ if parenlev < 0:
565
+ return True
566
+ return False
567
+
568
+ # Arbitrary limit to prevent getting stuck in infinite loops
569
+ TRANSFORM_LOOP_LIMIT = 500
570
+
571
+ class TransformerManager:
572
+ """Applies various transformations to a cell or code block.
573
+
574
+ The key methods for external use are ``transform_cell()``
575
+ and ``check_complete()``.
576
+ """
577
+ def __init__(self):
578
+ self.cleanup_transforms = [
579
+ leading_empty_lines,
580
+ leading_indent,
581
+ classic_prompt,
582
+ ipython_prompt,
583
+ ]
584
+ self.line_transforms = [
585
+ cell_magic,
586
+ ]
587
+ self.token_transformers = [
588
+ MagicAssign,
589
+ SystemAssign,
590
+ EscapedCommand,
591
+ HelpEnd,
592
+ ]
593
+
594
+ def do_one_token_transform(self, lines):
595
+ """Find and run the transform earliest in the code.
596
+
597
+ Returns (changed, lines).
598
+
599
+ This method is called repeatedly until changed is False, indicating
600
+ that all available transformations are complete.
601
+
602
+ The tokens following IPython special syntax might not be valid, so
603
+ the transformed code is retokenised every time to identify the next
604
+ piece of special syntax. Hopefully long code cells are mostly valid
605
+ Python, not using lots of IPython special syntax, so this shouldn't be
606
+ a performance issue.
607
+ """
608
+ tokens_by_line = make_tokens_by_line(lines)
609
+ candidates = []
610
+ for transformer_cls in self.token_transformers:
611
+ transformer = transformer_cls.find(tokens_by_line)
612
+ if transformer:
613
+ candidates.append(transformer)
614
+
615
+ if not candidates:
616
+ # Nothing to transform
617
+ return False, lines
618
+ ordered_transformers = sorted(candidates, key=TokenTransformBase.sortby)
619
+ for transformer in ordered_transformers:
620
+ try:
621
+ return True, transformer.transform(lines)
622
+ except SyntaxError:
623
+ pass
624
+ return False, lines
625
+
626
+ def do_token_transforms(self, lines):
627
+ for _ in range(TRANSFORM_LOOP_LIMIT):
628
+ changed, lines = self.do_one_token_transform(lines)
629
+ if not changed:
630
+ return lines
631
+
632
+ raise RuntimeError("Input transformation still changing after "
633
+ "%d iterations. Aborting." % TRANSFORM_LOOP_LIMIT)
634
+
635
+ def transform_cell(self, cell: str) -> str:
636
+ """Transforms a cell of input code"""
637
+ if not cell.endswith('\n'):
638
+ cell += '\n' # Ensure the cell has a trailing newline
639
+ lines = cell.splitlines(keepends=True)
640
+ for transform in self.cleanup_transforms + self.line_transforms:
641
+ lines = transform(lines)
642
+
643
+ lines = self.do_token_transforms(lines)
644
+ return ''.join(lines)
645
+
646
+ def check_complete(self, cell: str):
647
+ """Return whether a block of code is ready to execute, or should be continued
648
+
649
+ Parameters
650
+ ----------
651
+ cell : string
652
+ Python input code, which can be multiline.
653
+
654
+ Returns
655
+ -------
656
+ status : str
657
+ One of 'complete', 'incomplete', or 'invalid' if source is not a
658
+ prefix of valid code.
659
+ indent_spaces : int or None
660
+ The number of spaces by which to indent the next line of code. If
661
+ status is not 'incomplete', this is None.
662
+ """
663
+ # Remember if the lines ends in a new line.
664
+ ends_with_newline = False
665
+ for character in reversed(cell):
666
+ if character == '\n':
667
+ ends_with_newline = True
668
+ break
669
+ elif character.strip():
670
+ break
671
+ else:
672
+ continue
673
+
674
+ if not ends_with_newline:
675
+ # Append an newline for consistent tokenization
676
+ # See https://bugs.python.org/issue33899
677
+ cell += '\n'
678
+
679
+ lines = cell.splitlines(keepends=True)
680
+
681
+ if not lines:
682
+ return 'complete', None
683
+
684
+ for line in reversed(lines):
685
+ if not line.strip():
686
+ continue
687
+ elif line.strip("\n").endswith("\\"):
688
+ return "incomplete", find_last_indent(lines)
689
+ else:
690
+ break
691
+
692
+ try:
693
+ for transform in self.cleanup_transforms:
694
+ if not getattr(transform, 'has_side_effects', False):
695
+ lines = transform(lines)
696
+ except SyntaxError:
697
+ return 'invalid', None
698
+
699
+ if lines[0].startswith('%%'):
700
+ # Special case for cell magics - completion marked by blank line
701
+ if lines[-1].strip():
702
+ return 'incomplete', find_last_indent(lines)
703
+ else:
704
+ return 'complete', None
705
+
706
+ try:
707
+ for transform in self.line_transforms:
708
+ if not getattr(transform, 'has_side_effects', False):
709
+ lines = transform(lines)
710
+ lines = self.do_token_transforms(lines)
711
+ except SyntaxError:
712
+ return 'invalid', None
713
+
714
+ tokens_by_line = make_tokens_by_line(lines)
715
+
716
+ # Bail if we got one line and there are more closing parentheses than
717
+ # the opening ones
718
+ if (
719
+ len(lines) == 1
720
+ and tokens_by_line
721
+ and has_sunken_brackets(tokens_by_line[0])
722
+ ):
723
+ return "invalid", None
724
+
725
+ if not tokens_by_line:
726
+ return 'incomplete', find_last_indent(lines)
727
+
728
+ if (
729
+ tokens_by_line[-1][-1].type != tokenize.ENDMARKER
730
+ and tokens_by_line[-1][-1].type != tokenize.ERRORTOKEN
731
+ ):
732
+ # We're in a multiline string or expression
733
+ return 'incomplete', find_last_indent(lines)
734
+
735
+ newline_types = {tokenize.NEWLINE, tokenize.COMMENT, tokenize.ENDMARKER} # type: ignore
736
+
737
+ # Pop the last line which only contains DEDENTs and ENDMARKER
738
+ last_token_line = None
739
+ if {t.type for t in tokens_by_line[-1]} in [
740
+ {tokenize.DEDENT, tokenize.ENDMARKER},
741
+ {tokenize.ENDMARKER}
742
+ ] and len(tokens_by_line) > 1:
743
+ last_token_line = tokens_by_line.pop()
744
+
745
+ while tokens_by_line[-1] and tokens_by_line[-1][-1].type in newline_types:
746
+ tokens_by_line[-1].pop()
747
+
748
+ if not tokens_by_line[-1]:
749
+ return 'incomplete', find_last_indent(lines)
750
+
751
+ if tokens_by_line[-1][-1].string == ':':
752
+ # The last line starts a block (e.g. 'if foo:')
753
+ ix = 0
754
+ while tokens_by_line[-1][ix].type in {tokenize.INDENT, tokenize.DEDENT}:
755
+ ix += 1
756
+
757
+ indent = tokens_by_line[-1][ix].start[1]
758
+ return 'incomplete', indent + 4
759
+
760
+ if tokens_by_line[-1][0].line.endswith('\\'):
761
+ return 'incomplete', None
762
+
763
+ # At this point, our checks think the code is complete (or invalid).
764
+ # We'll use codeop.compile_command to check this with the real parser
765
+ try:
766
+ with warnings.catch_warnings():
767
+ warnings.simplefilter('error', SyntaxWarning)
768
+ res = compile_command(''.join(lines), symbol='exec')
769
+ except (SyntaxError, OverflowError, ValueError, TypeError,
770
+ MemoryError, SyntaxWarning):
771
+ return 'invalid', None
772
+ else:
773
+ if res is None:
774
+ return 'incomplete', find_last_indent(lines)
775
+
776
+ if last_token_line and last_token_line[0].type == tokenize.DEDENT:
777
+ if ends_with_newline:
778
+ return 'complete', None
779
+ return 'incomplete', find_last_indent(lines)
780
+
781
+ # If there's a blank line at the end, assume we're ready to execute
782
+ if not lines[-1].strip():
783
+ return 'complete', None
784
+
785
+ return 'complete', None
786
+
787
+
788
+ def find_last_indent(lines):
789
+ m = _indent_re.match(lines[-1])
790
+ if not m:
791
+ return 0
792
+ return len(m.group(0).replace('\t', ' '*4))
793
+
794
+
795
+ class MaybeAsyncCompile(Compile):
796
+ def __init__(self, extra_flags=0):
797
+ super().__init__()
798
+ self.flags |= extra_flags
799
+
800
+
801
+ class MaybeAsyncCommandCompiler(CommandCompiler):
802
+ def __init__(self, extra_flags=0):
803
+ self.compiler = MaybeAsyncCompile(extra_flags=extra_flags)
804
+
805
+
806
+ _extra_flags = ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
807
+
808
+ compile_command = MaybeAsyncCommandCompiler(extra_flags=_extra_flags)
temp_venv/lib/python3.13/site-packages/IPython/core/interactiveshell.py ADDED
The diff for this file is too large to render. See raw diff
 
temp_venv/lib/python3.13/site-packages/IPython/core/latex_symbols.py ADDED
@@ -0,0 +1,1306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # encoding: utf-8
2
+
3
+ # DO NOT EDIT THIS FILE BY HAND.
4
+
5
+ # To update this file, run the script /tools/gen_latex_symbols.py using Python 3
6
+
7
+ # This file is autogenerated from the file:
8
+ # https://raw.githubusercontent.com/JuliaLang/julia/master/stdlib/REPL/src/latex_symbols.jl
9
+ # This original list is filtered to remove any unicode characters that are not valid
10
+ # Python identifiers.
11
+
12
+ latex_symbols = {
13
+ "\\euler": "ℯ",
14
+ "\\ohm": "Ω",
15
+ "\\^a": "ᵃ",
16
+ "\\^b": "ᵇ",
17
+ "\\^c": "ᶜ",
18
+ "\\^d": "ᵈ",
19
+ "\\^e": "ᵉ",
20
+ "\\^f": "ᶠ",
21
+ "\\^g": "ᵍ",
22
+ "\\^h": "ʰ",
23
+ "\\^i": "ⁱ",
24
+ "\\^j": "ʲ",
25
+ "\\^k": "ᵏ",
26
+ "\\^l": "ˡ",
27
+ "\\^m": "ᵐ",
28
+ "\\^n": "ⁿ",
29
+ "\\^o": "ᵒ",
30
+ "\\^p": "ᵖ",
31
+ "\\^r": "ʳ",
32
+ "\\^s": "ˢ",
33
+ "\\^t": "ᵗ",
34
+ "\\^u": "ᵘ",
35
+ "\\^v": "ᵛ",
36
+ "\\^w": "ʷ",
37
+ "\\^x": "ˣ",
38
+ "\\^y": "ʸ",
39
+ "\\^z": "ᶻ",
40
+ "\\^A": "ᴬ",
41
+ "\\^B": "ᴮ",
42
+ "\\^D": "ᴰ",
43
+ "\\^E": "ᴱ",
44
+ "\\^G": "ᴳ",
45
+ "\\^H": "ᴴ",
46
+ "\\^I": "ᴵ",
47
+ "\\^J": "ᴶ",
48
+ "\\^K": "ᴷ",
49
+ "\\^L": "ᴸ",
50
+ "\\^M": "ᴹ",
51
+ "\\^N": "ᴺ",
52
+ "\\^O": "ᴼ",
53
+ "\\^P": "ᴾ",
54
+ "\\^R": "ᴿ",
55
+ "\\^T": "ᵀ",
56
+ "\\^U": "ᵁ",
57
+ "\\^V": "ⱽ",
58
+ "\\^W": "ᵂ",
59
+ "\\^alpha": "ᵅ",
60
+ "\\^beta": "ᵝ",
61
+ "\\^gamma": "ᵞ",
62
+ "\\^delta": "ᵟ",
63
+ "\\^epsilon": "ᵋ",
64
+ "\\^theta": "ᶿ",
65
+ "\\^iota": "ᶥ",
66
+ "\\^phi": "ᵠ",
67
+ "\\^chi": "ᵡ",
68
+ "\\^ltphi": "ᶲ",
69
+ "\\^uparrow": "ꜛ",
70
+ "\\^downarrow": "ꜜ",
71
+ "\\^!": "ꜝ",
72
+ "\\_a": "ₐ",
73
+ "\\_e": "ₑ",
74
+ "\\_h": "ₕ",
75
+ "\\_i": "ᵢ",
76
+ "\\_j": "ⱼ",
77
+ "\\_k": "ₖ",
78
+ "\\_l": "ₗ",
79
+ "\\_m": "ₘ",
80
+ "\\_n": "ₙ",
81
+ "\\_o": "ₒ",
82
+ "\\_p": "ₚ",
83
+ "\\_r": "ᵣ",
84
+ "\\_s": "ₛ",
85
+ "\\_t": "ₜ",
86
+ "\\_u": "ᵤ",
87
+ "\\_v": "ᵥ",
88
+ "\\_x": "ₓ",
89
+ "\\_schwa": "ₔ",
90
+ "\\_beta": "ᵦ",
91
+ "\\_gamma": "ᵧ",
92
+ "\\_rho": "ᵨ",
93
+ "\\_phi": "ᵩ",
94
+ "\\_chi": "ᵪ",
95
+ "\\hbar": "ħ",
96
+ "\\sout": "̶",
97
+ "\\ordfeminine": "ª",
98
+ "\\cdotp": "·",
99
+ "\\ordmasculine": "º",
100
+ "\\AA": "Å",
101
+ "\\AE": "Æ",
102
+ "\\DH": "Ð",
103
+ "\\O": "Ø",
104
+ "\\TH": "Þ",
105
+ "\\ss": "ß",
106
+ "\\aa": "å",
107
+ "\\ae": "æ",
108
+ "\\eth": "ð",
109
+ "\\dh": "ð",
110
+ "\\o": "ø",
111
+ "\\th": "þ",
112
+ "\\DJ": "Đ",
113
+ "\\dj": "đ",
114
+ "\\imath": "ı",
115
+ "\\jmath": "ȷ",
116
+ "\\L": "Ł",
117
+ "\\l": "ł",
118
+ "\\NG": "Ŋ",
119
+ "\\ng": "ŋ",
120
+ "\\OE": "Œ",
121
+ "\\oe": "œ",
122
+ "\\hvlig": "ƕ",
123
+ "\\nrleg": "ƞ",
124
+ "\\doublepipe": "ǂ",
125
+ "\\trna": "ɐ",
126
+ "\\trnsa": "ɒ",
127
+ "\\openo": "ɔ",
128
+ "\\rtld": "ɖ",
129
+ "\\schwa": "ə",
130
+ "\\varepsilon": "ε",
131
+ "\\pgamma": "ɣ",
132
+ "\\pbgam": "ɤ",
133
+ "\\trnh": "ɥ",
134
+ "\\btdl": "ɬ",
135
+ "\\rtll": "ɭ",
136
+ "\\trnm": "ɯ",
137
+ "\\trnmlr": "ɰ",
138
+ "\\ltlmr": "ɱ",
139
+ "\\ltln": "ɲ",
140
+ "\\rtln": "ɳ",
141
+ "\\clomeg": "ɷ",
142
+ "\\ltphi": "ɸ",
143
+ "\\trnr": "ɹ",
144
+ "\\trnrl": "ɺ",
145
+ "\\rttrnr": "ɻ",
146
+ "\\rl": "ɼ",
147
+ "\\rtlr": "ɽ",
148
+ "\\fhr": "ɾ",
149
+ "\\rtls": "ʂ",
150
+ "\\esh": "ʃ",
151
+ "\\trnt": "ʇ",
152
+ "\\rtlt": "ʈ",
153
+ "\\pupsil": "ʊ",
154
+ "\\pscrv": "ʋ",
155
+ "\\invv": "ʌ",
156
+ "\\invw": "ʍ",
157
+ "\\trny": "ʎ",
158
+ "\\rtlz": "ʐ",
159
+ "\\yogh": "ʒ",
160
+ "\\glst": "ʔ",
161
+ "\\reglst": "ʕ",
162
+ "\\inglst": "ʖ",
163
+ "\\turnk": "ʞ",
164
+ "\\dyogh": "ʤ",
165
+ "\\tesh": "ʧ",
166
+ "\\rasp": "ʼ",
167
+ "\\verts": "ˈ",
168
+ "\\verti": "ˌ",
169
+ "\\lmrk": "ː",
170
+ "\\hlmrk": "ˑ",
171
+ "\\grave": "̀",
172
+ "\\acute": "́",
173
+ "\\hat": "̂",
174
+ "\\tilde": "̃",
175
+ "\\bar": "̄",
176
+ "\\breve": "̆",
177
+ "\\dot": "̇",
178
+ "\\ddot": "̈",
179
+ "\\ocirc": "̊",
180
+ "\\H": "̋",
181
+ "\\check": "̌",
182
+ "\\palh": "̡",
183
+ "\\rh": "̢",
184
+ "\\c": "̧",
185
+ "\\k": "̨",
186
+ "\\sbbrg": "̪",
187
+ "\\strike": "̶",
188
+ "\\Alpha": "Α",
189
+ "\\Beta": "Β",
190
+ "\\Gamma": "Γ",
191
+ "\\Delta": "Δ",
192
+ "\\Epsilon": "Ε",
193
+ "\\Zeta": "Ζ",
194
+ "\\Eta": "Η",
195
+ "\\Theta": "Θ",
196
+ "\\Iota": "Ι",
197
+ "\\Kappa": "Κ",
198
+ "\\Lambda": "Λ",
199
+ "\\Xi": "Ξ",
200
+ "\\Pi": "Π",
201
+ "\\Rho": "Ρ",
202
+ "\\Sigma": "Σ",
203
+ "\\Tau": "Τ",
204
+ "\\Upsilon": "Υ",
205
+ "\\Phi": "Φ",
206
+ "\\Chi": "Χ",
207
+ "\\Psi": "Ψ",
208
+ "\\Omega": "Ω",
209
+ "\\alpha": "α",
210
+ "\\beta": "β",
211
+ "\\gamma": "γ",
212
+ "\\delta": "δ",
213
+ "\\zeta": "ζ",
214
+ "\\eta": "η",
215
+ "\\theta": "θ",
216
+ "\\iota": "ι",
217
+ "\\kappa": "κ",
218
+ "\\lambda": "λ",
219
+ "\\mu": "μ",
220
+ "\\nu": "ν",
221
+ "\\xi": "ξ",
222
+ "\\pi": "π",
223
+ "\\rho": "ρ",
224
+ "\\varsigma": "ς",
225
+ "\\sigma": "σ",
226
+ "\\tau": "τ",
227
+ "\\upsilon": "υ",
228
+ "\\varphi": "φ",
229
+ "\\chi": "χ",
230
+ "\\psi": "ψ",
231
+ "\\omega": "ω",
232
+ "\\vartheta": "ϑ",
233
+ "\\phi": "ϕ",
234
+ "\\varpi": "ϖ",
235
+ "\\Stigma": "Ϛ",
236
+ "\\Digamma": "Ϝ",
237
+ "\\digamma": "ϝ",
238
+ "\\Koppa": "Ϟ",
239
+ "\\Sampi": "Ϡ",
240
+ "\\varkappa": "ϰ",
241
+ "\\varrho": "ϱ",
242
+ "\\varTheta": "ϴ",
243
+ "\\epsilon": "ϵ",
244
+ "\\dddot": "⃛",
245
+ "\\ddddot": "⃜",
246
+ "\\hslash": "ℏ",
247
+ "\\Im": "ℑ",
248
+ "\\ell": "ℓ",
249
+ "\\wp": "℘",
250
+ "\\Re": "ℜ",
251
+ "\\aleph": "ℵ",
252
+ "\\beth": "ℶ",
253
+ "\\gimel": "ℷ",
254
+ "\\daleth": "ℸ",
255
+ "\\bbPi": "ℿ",
256
+ "\\Zbar": "Ƶ",
257
+ "\\overbar": "̅",
258
+ "\\ovhook": "̉",
259
+ "\\candra": "̐",
260
+ "\\oturnedcomma": "̒",
261
+ "\\ocommatopright": "̕",
262
+ "\\droang": "̚",
263
+ "\\wideutilde": "̰",
264
+ "\\not": "̸",
265
+ "\\Mu": "Μ",
266
+ "\\Nu": "Ν",
267
+ "\\Omicron": "Ο",
268
+ "\\omicron": "ο",
269
+ "\\varbeta": "ϐ",
270
+ "\\oldKoppa": "Ϙ",
271
+ "\\oldkoppa": "ϙ",
272
+ "\\stigma": "ϛ",
273
+ "\\koppa": "ϟ",
274
+ "\\sampi": "ϡ",
275
+ "\\tieconcat": "⁀",
276
+ "\\leftharpoonaccent": "⃐",
277
+ "\\rightharpoonaccent": "⃑",
278
+ "\\vertoverlay": "⃒",
279
+ "\\overleftarrow": "⃖",
280
+ "\\vec": "⃗",
281
+ "\\overleftrightarrow": "⃡",
282
+ "\\annuity": "⃧",
283
+ "\\threeunderdot": "⃨",
284
+ "\\widebridgeabove": "⃩",
285
+ "\\bbC": "ℂ",
286
+ "\\eulermascheroni": "ℇ",
287
+ "\\scrg": "ℊ",
288
+ "\\scrH": "ℋ",
289
+ "\\frakH": "ℌ",
290
+ "\\bbH": "ℍ",
291
+ "\\planck": "ℎ",
292
+ "\\scrI": "ℐ",
293
+ "\\scrL": "ℒ",
294
+ "\\bbN": "ℕ",
295
+ "\\bbP": "ℙ",
296
+ "\\bbQ": "ℚ",
297
+ "\\scrR": "ℛ",
298
+ "\\bbR": "ℝ",
299
+ "\\bbZ": "ℤ",
300
+ "\\frakZ": "ℨ",
301
+ "\\Angstrom": "Å",
302
+ "\\scrB": "ℬ",
303
+ "\\frakC": "ℭ",
304
+ "\\scre": "ℯ",
305
+ "\\scrE": "ℰ",
306
+ "\\scrF": "ℱ",
307
+ "\\Finv": "Ⅎ",
308
+ "\\scrM": "ℳ",
309
+ "\\scro": "ℴ",
310
+ "\\bbgamma": "ℽ",
311
+ "\\bbGamma": "ℾ",
312
+ "\\bbiD": "ⅅ",
313
+ "\\bbid": "ⅆ",
314
+ "\\bbie": "ⅇ",
315
+ "\\bbii": "ⅈ",
316
+ "\\bbij": "ⅉ",
317
+ "\\bfA": "𝐀",
318
+ "\\bfB": "𝐁",
319
+ "\\bfC": "𝐂",
320
+ "\\bfD": "𝐃",
321
+ "\\bfE": "𝐄",
322
+ "\\bfF": "𝐅",
323
+ "\\bfG": "𝐆",
324
+ "\\bfH": "𝐇",
325
+ "\\bfI": "𝐈",
326
+ "\\bfJ": "𝐉",
327
+ "\\bfK": "𝐊",
328
+ "\\bfL": "𝐋",
329
+ "\\bfM": "𝐌",
330
+ "\\bfN": "𝐍",
331
+ "\\bfO": "𝐎",
332
+ "\\bfP": "𝐏",
333
+ "\\bfQ": "𝐐",
334
+ "\\bfR": "𝐑",
335
+ "\\bfS": "𝐒",
336
+ "\\bfT": "𝐓",
337
+ "\\bfU": "𝐔",
338
+ "\\bfV": "𝐕",
339
+ "\\bfW": "𝐖",
340
+ "\\bfX": "𝐗",
341
+ "\\bfY": "𝐘",
342
+ "\\bfZ": "𝐙",
343
+ "\\bfa": "𝐚",
344
+ "\\bfb": "𝐛",
345
+ "\\bfc": "𝐜",
346
+ "\\bfd": "𝐝",
347
+ "\\bfe": "𝐞",
348
+ "\\bff": "𝐟",
349
+ "\\bfg": "𝐠",
350
+ "\\bfh": "𝐡",
351
+ "\\bfi": "𝐢",
352
+ "\\bfj": "𝐣",
353
+ "\\bfk": "𝐤",
354
+ "\\bfl": "𝐥",
355
+ "\\bfm": "𝐦",
356
+ "\\bfn": "𝐧",
357
+ "\\bfo": "𝐨",
358
+ "\\bfp": "𝐩",
359
+ "\\bfq": "𝐪",
360
+ "\\bfr": "𝐫",
361
+ "\\bfs": "𝐬",
362
+ "\\bft": "𝐭",
363
+ "\\bfu": "𝐮",
364
+ "\\bfv": "𝐯",
365
+ "\\bfw": "𝐰",
366
+ "\\bfx": "𝐱",
367
+ "\\bfy": "𝐲",
368
+ "\\bfz": "𝐳",
369
+ "\\itA": "𝐴",
370
+ "\\itB": "𝐵",
371
+ "\\itC": "𝐶",
372
+ "\\itD": "𝐷",
373
+ "\\itE": "𝐸",
374
+ "\\itF": "𝐹",
375
+ "\\itG": "𝐺",
376
+ "\\itH": "𝐻",
377
+ "\\itI": "𝐼",
378
+ "\\itJ": "𝐽",
379
+ "\\itK": "𝐾",
380
+ "\\itL": "𝐿",
381
+ "\\itM": "𝑀",
382
+ "\\itN": "𝑁",
383
+ "\\itO": "𝑂",
384
+ "\\itP": "𝑃",
385
+ "\\itQ": "𝑄",
386
+ "\\itR": "𝑅",
387
+ "\\itS": "𝑆",
388
+ "\\itT": "𝑇",
389
+ "\\itU": "𝑈",
390
+ "\\itV": "𝑉",
391
+ "\\itW": "𝑊",
392
+ "\\itX": "𝑋",
393
+ "\\itY": "𝑌",
394
+ "\\itZ": "𝑍",
395
+ "\\ita": "𝑎",
396
+ "\\itb": "𝑏",
397
+ "\\itc": "𝑐",
398
+ "\\itd": "𝑑",
399
+ "\\ite": "𝑒",
400
+ "\\itf": "𝑓",
401
+ "\\itg": "𝑔",
402
+ "\\ith": "ℎ",
403
+ "\\iti": "𝑖",
404
+ "\\itj": "𝑗",
405
+ "\\itk": "𝑘",
406
+ "\\itl": "𝑙",
407
+ "\\itm": "𝑚",
408
+ "\\itn": "𝑛",
409
+ "\\ito": "𝑜",
410
+ "\\itp": "𝑝",
411
+ "\\itq": "𝑞",
412
+ "\\itr": "𝑟",
413
+ "\\its": "𝑠",
414
+ "\\itt": "𝑡",
415
+ "\\itu": "𝑢",
416
+ "\\itv": "𝑣",
417
+ "\\itw": "𝑤",
418
+ "\\itx": "𝑥",
419
+ "\\ity": "𝑦",
420
+ "\\itz": "𝑧",
421
+ "\\biA": "𝑨",
422
+ "\\biB": "𝑩",
423
+ "\\biC": "𝑪",
424
+ "\\biD": "𝑫",
425
+ "\\biE": "𝑬",
426
+ "\\biF": "𝑭",
427
+ "\\biG": "𝑮",
428
+ "\\biH": "𝑯",
429
+ "\\biI": "𝑰",
430
+ "\\biJ": "𝑱",
431
+ "\\biK": "𝑲",
432
+ "\\biL": "𝑳",
433
+ "\\biM": "𝑴",
434
+ "\\biN": "𝑵",
435
+ "\\biO": "𝑶",
436
+ "\\biP": "𝑷",
437
+ "\\biQ": "𝑸",
438
+ "\\biR": "𝑹",
439
+ "\\biS": "𝑺",
440
+ "\\biT": "𝑻",
441
+ "\\biU": "𝑼",
442
+ "\\biV": "𝑽",
443
+ "\\biW": "𝑾",
444
+ "\\biX": "𝑿",
445
+ "\\biY": "𝒀",
446
+ "\\biZ": "𝒁",
447
+ "\\bia": "𝒂",
448
+ "\\bib": "𝒃",
449
+ "\\bic": "𝒄",
450
+ "\\bid": "𝒅",
451
+ "\\bie": "𝒆",
452
+ "\\bif": "𝒇",
453
+ "\\big": "𝒈",
454
+ "\\bih": "𝒉",
455
+ "\\bii": "𝒊",
456
+ "\\bij": "𝒋",
457
+ "\\bik": "𝒌",
458
+ "\\bil": "𝒍",
459
+ "\\bim": "𝒎",
460
+ "\\bin": "𝒏",
461
+ "\\bio": "𝒐",
462
+ "\\bip": "𝒑",
463
+ "\\biq": "𝒒",
464
+ "\\bir": "𝒓",
465
+ "\\bis": "𝒔",
466
+ "\\bit": "𝒕",
467
+ "\\biu": "𝒖",
468
+ "\\biv": "𝒗",
469
+ "\\biw": "𝒘",
470
+ "\\bix": "𝒙",
471
+ "\\biy": "𝒚",
472
+ "\\biz": "𝒛",
473
+ "\\scrA": "𝒜",
474
+ "\\scrC": "𝒞",
475
+ "\\scrD": "𝒟",
476
+ "\\scrG": "𝒢",
477
+ "\\scrJ": "𝒥",
478
+ "\\scrK": "𝒦",
479
+ "\\scrN": "𝒩",
480
+ "\\scrO": "𝒪",
481
+ "\\scrP": "𝒫",
482
+ "\\scrQ": "𝒬",
483
+ "\\scrS": "𝒮",
484
+ "\\scrT": "𝒯",
485
+ "\\scrU": "𝒰",
486
+ "\\scrV": "𝒱",
487
+ "\\scrW": "𝒲",
488
+ "\\scrX": "𝒳",
489
+ "\\scrY": "𝒴",
490
+ "\\scrZ": "𝒵",
491
+ "\\scra": "𝒶",
492
+ "\\scrb": "𝒷",
493
+ "\\scrc": "𝒸",
494
+ "\\scrd": "𝒹",
495
+ "\\scrf": "𝒻",
496
+ "\\scrh": "𝒽",
497
+ "\\scri": "𝒾",
498
+ "\\scrj": "𝒿",
499
+ "\\scrk": "𝓀",
500
+ "\\scrm": "𝓂",
501
+ "\\scrn": "𝓃",
502
+ "\\scrp": "𝓅",
503
+ "\\scrq": "𝓆",
504
+ "\\scrr": "𝓇",
505
+ "\\scrs": "𝓈",
506
+ "\\scrt": "𝓉",
507
+ "\\scru": "𝓊",
508
+ "\\scrv": "𝓋",
509
+ "\\scrw": "𝓌",
510
+ "\\scrx": "𝓍",
511
+ "\\scry": "𝓎",
512
+ "\\scrz": "𝓏",
513
+ "\\bscrA": "𝓐",
514
+ "\\bscrB": "𝓑",
515
+ "\\bscrC": "𝓒",
516
+ "\\bscrD": "𝓓",
517
+ "\\bscrE": "𝓔",
518
+ "\\bscrF": "𝓕",
519
+ "\\bscrG": "𝓖",
520
+ "\\bscrH": "𝓗",
521
+ "\\bscrI": "𝓘",
522
+ "\\bscrJ": "𝓙",
523
+ "\\bscrK": "𝓚",
524
+ "\\bscrL": "𝓛",
525
+ "\\bscrM": "𝓜",
526
+ "\\bscrN": "𝓝",
527
+ "\\bscrO": "𝓞",
528
+ "\\bscrP": "𝓟",
529
+ "\\bscrQ": "𝓠",
530
+ "\\bscrR": "𝓡",
531
+ "\\bscrS": "𝓢",
532
+ "\\bscrT": "𝓣",
533
+ "\\bscrU": "𝓤",
534
+ "\\bscrV": "𝓥",
535
+ "\\bscrW": "𝓦",
536
+ "\\bscrX": "𝓧",
537
+ "\\bscrY": "𝓨",
538
+ "\\bscrZ": "𝓩",
539
+ "\\bscra": "𝓪",
540
+ "\\bscrb": "𝓫",
541
+ "\\bscrc": "𝓬",
542
+ "\\bscrd": "𝓭",
543
+ "\\bscre": "𝓮",
544
+ "\\bscrf": "𝓯",
545
+ "\\bscrg": "𝓰",
546
+ "\\bscrh": "𝓱",
547
+ "\\bscri": "𝓲",
548
+ "\\bscrj": "𝓳",
549
+ "\\bscrk": "𝓴",
550
+ "\\bscrl": "𝓵",
551
+ "\\bscrm": "𝓶",
552
+ "\\bscrn": "𝓷",
553
+ "\\bscro": "𝓸",
554
+ "\\bscrp": "𝓹",
555
+ "\\bscrq": "𝓺",
556
+ "\\bscrr": "𝓻",
557
+ "\\bscrs": "𝓼",
558
+ "\\bscrt": "𝓽",
559
+ "\\bscru": "𝓾",
560
+ "\\bscrv": "𝓿",
561
+ "\\bscrw": "𝔀",
562
+ "\\bscrx": "𝔁",
563
+ "\\bscry": "𝔂",
564
+ "\\bscrz": "𝔃",
565
+ "\\frakA": "𝔄",
566
+ "\\frakB": "𝔅",
567
+ "\\frakD": "𝔇",
568
+ "\\frakE": "𝔈",
569
+ "\\frakF": "𝔉",
570
+ "\\frakG": "𝔊",
571
+ "\\frakI": "ℑ",
572
+ "\\frakJ": "𝔍",
573
+ "\\frakK": "𝔎",
574
+ "\\frakL": "𝔏",
575
+ "\\frakM": "𝔐",
576
+ "\\frakN": "𝔑",
577
+ "\\frakO": "𝔒",
578
+ "\\frakP": "𝔓",
579
+ "\\frakQ": "𝔔",
580
+ "\\frakR": "ℜ",
581
+ "\\frakS": "𝔖",
582
+ "\\frakT": "𝔗",
583
+ "\\frakU": "𝔘",
584
+ "\\frakV": "𝔙",
585
+ "\\frakW": "𝔚",
586
+ "\\frakX": "𝔛",
587
+ "\\frakY": "𝔜",
588
+ "\\fraka": "𝔞",
589
+ "\\frakb": "𝔟",
590
+ "\\frakc": "𝔠",
591
+ "\\frakd": "𝔡",
592
+ "\\frake": "𝔢",
593
+ "\\frakf": "𝔣",
594
+ "\\frakg": "𝔤",
595
+ "\\frakh": "𝔥",
596
+ "\\fraki": "𝔦",
597
+ "\\frakj": "𝔧",
598
+ "\\frakk": "𝔨",
599
+ "\\frakl": "𝔩",
600
+ "\\frakm": "𝔪",
601
+ "\\frakn": "𝔫",
602
+ "\\frako": "𝔬",
603
+ "\\frakp": "𝔭",
604
+ "\\frakq": "𝔮",
605
+ "\\frakr": "𝔯",
606
+ "\\fraks": "𝔰",
607
+ "\\frakt": "𝔱",
608
+ "\\fraku": "𝔲",
609
+ "\\frakv": "𝔳",
610
+ "\\frakw": "𝔴",
611
+ "\\frakx": "𝔵",
612
+ "\\fraky": "𝔶",
613
+ "\\frakz": "𝔷",
614
+ "\\bbA": "𝔸",
615
+ "\\bbB": "𝔹",
616
+ "\\bbD": "𝔻",
617
+ "\\bbE": "𝔼",
618
+ "\\bbF": "𝔽",
619
+ "\\bbG": "𝔾",
620
+ "\\bbI": "𝕀",
621
+ "\\bbJ": "𝕁",
622
+ "\\bbK": "𝕂",
623
+ "\\bbL": "𝕃",
624
+ "\\bbM": "𝕄",
625
+ "\\bbO": "𝕆",
626
+ "\\bbS": "𝕊",
627
+ "\\bbT": "𝕋",
628
+ "\\bbU": "𝕌",
629
+ "\\bbV": "𝕍",
630
+ "\\bbW": "𝕎",
631
+ "\\bbX": "𝕏",
632
+ "\\bbY": "𝕐",
633
+ "\\bba": "𝕒",
634
+ "\\bbb": "𝕓",
635
+ "\\bbc": "𝕔",
636
+ "\\bbd": "𝕕",
637
+ "\\bbe": "𝕖",
638
+ "\\bbf": "𝕗",
639
+ "\\bbg": "𝕘",
640
+ "\\bbh": "𝕙",
641
+ "\\bbi": "𝕚",
642
+ "\\bbj": "𝕛",
643
+ "\\bbk": "𝕜",
644
+ "\\bbl": "𝕝",
645
+ "\\bbm": "𝕞",
646
+ "\\bbn": "𝕟",
647
+ "\\bbo": "𝕠",
648
+ "\\bbp": "𝕡",
649
+ "\\bbq": "𝕢",
650
+ "\\bbr": "𝕣",
651
+ "\\bbs": "𝕤",
652
+ "\\bbt": "𝕥",
653
+ "\\bbu": "𝕦",
654
+ "\\bbv": "𝕧",
655
+ "\\bbw": "𝕨",
656
+ "\\bbx": "𝕩",
657
+ "\\bby": "𝕪",
658
+ "\\bbz": "𝕫",
659
+ "\\bfrakA": "𝕬",
660
+ "\\bfrakB": "𝕭",
661
+ "\\bfrakC": "𝕮",
662
+ "\\bfrakD": "𝕯",
663
+ "\\bfrakE": "𝕰",
664
+ "\\bfrakF": "𝕱",
665
+ "\\bfrakG": "𝕲",
666
+ "\\bfrakH": "𝕳",
667
+ "\\bfrakI": "𝕴",
668
+ "\\bfrakJ": "𝕵",
669
+ "\\bfrakK": "𝕶",
670
+ "\\bfrakL": "𝕷",
671
+ "\\bfrakM": "𝕸",
672
+ "\\bfrakN": "𝕹",
673
+ "\\bfrakO": "𝕺",
674
+ "\\bfrakP": "𝕻",
675
+ "\\bfrakQ": "𝕼",
676
+ "\\bfrakR": "𝕽",
677
+ "\\bfrakS": "𝕾",
678
+ "\\bfrakT": "𝕿",
679
+ "\\bfrakU": "𝖀",
680
+ "\\bfrakV": "𝖁",
681
+ "\\bfrakW": "𝖂",
682
+ "\\bfrakX": "𝖃",
683
+ "\\bfrakY": "𝖄",
684
+ "\\bfrakZ": "𝖅",
685
+ "\\bfraka": "𝖆",
686
+ "\\bfrakb": "𝖇",
687
+ "\\bfrakc": "𝖈",
688
+ "\\bfrakd": "𝖉",
689
+ "\\bfrake": "𝖊",
690
+ "\\bfrakf": "𝖋",
691
+ "\\bfrakg": "𝖌",
692
+ "\\bfrakh": "𝖍",
693
+ "\\bfraki": "𝖎",
694
+ "\\bfrakj": "𝖏",
695
+ "\\bfrakk": "𝖐",
696
+ "\\bfrakl": "𝖑",
697
+ "\\bfrakm": "𝖒",
698
+ "\\bfrakn": "𝖓",
699
+ "\\bfrako": "𝖔",
700
+ "\\bfrakp": "𝖕",
701
+ "\\bfrakq": "𝖖",
702
+ "\\bfrakr": "𝖗",
703
+ "\\bfraks": "𝖘",
704
+ "\\bfrakt": "𝖙",
705
+ "\\bfraku": "𝖚",
706
+ "\\bfrakv": "𝖛",
707
+ "\\bfrakw": "𝖜",
708
+ "\\bfrakx": "𝖝",
709
+ "\\bfraky": "𝖞",
710
+ "\\bfrakz": "𝖟",
711
+ "\\sansA": "𝖠",
712
+ "\\sansB": "𝖡",
713
+ "\\sansC": "𝖢",
714
+ "\\sansD": "𝖣",
715
+ "\\sansE": "𝖤",
716
+ "\\sansF": "𝖥",
717
+ "\\sansG": "𝖦",
718
+ "\\sansH": "𝖧",
719
+ "\\sansI": "𝖨",
720
+ "\\sansJ": "𝖩",
721
+ "\\sansK": "𝖪",
722
+ "\\sansL": "𝖫",
723
+ "\\sansM": "𝖬",
724
+ "\\sansN": "𝖭",
725
+ "\\sansO": "𝖮",
726
+ "\\sansP": "𝖯",
727
+ "\\sansQ": "𝖰",
728
+ "\\sansR": "𝖱",
729
+ "\\sansS": "𝖲",
730
+ "\\sansT": "𝖳",
731
+ "\\sansU": "𝖴",
732
+ "\\sansV": "𝖵",
733
+ "\\sansW": "𝖶",
734
+ "\\sansX": "𝖷",
735
+ "\\sansY": "𝖸",
736
+ "\\sansZ": "𝖹",
737
+ "\\sansa": "𝖺",
738
+ "\\sansb": "𝖻",
739
+ "\\sansc": "𝖼",
740
+ "\\sansd": "𝖽",
741
+ "\\sanse": "𝖾",
742
+ "\\sansf": "𝖿",
743
+ "\\sansg": "𝗀",
744
+ "\\sansh": "𝗁",
745
+ "\\sansi": "𝗂",
746
+ "\\sansj": "𝗃",
747
+ "\\sansk": "𝗄",
748
+ "\\sansl": "𝗅",
749
+ "\\sansm": "𝗆",
750
+ "\\sansn": "𝗇",
751
+ "\\sanso": "𝗈",
752
+ "\\sansp": "𝗉",
753
+ "\\sansq": "𝗊",
754
+ "\\sansr": "𝗋",
755
+ "\\sanss": "𝗌",
756
+ "\\sanst": "𝗍",
757
+ "\\sansu": "𝗎",
758
+ "\\sansv": "𝗏",
759
+ "\\sansw": "𝗐",
760
+ "\\sansx": "𝗑",
761
+ "\\sansy": "𝗒",
762
+ "\\sansz": "𝗓",
763
+ "\\bsansA": "𝗔",
764
+ "\\bsansB": "𝗕",
765
+ "\\bsansC": "𝗖",
766
+ "\\bsansD": "𝗗",
767
+ "\\bsansE": "𝗘",
768
+ "\\bsansF": "𝗙",
769
+ "\\bsansG": "𝗚",
770
+ "\\bsansH": "𝗛",
771
+ "\\bsansI": "𝗜",
772
+ "\\bsansJ": "𝗝",
773
+ "\\bsansK": "𝗞",
774
+ "\\bsansL": "𝗟",
775
+ "\\bsansM": "𝗠",
776
+ "\\bsansN": "𝗡",
777
+ "\\bsansO": "𝗢",
778
+ "\\bsansP": "𝗣",
779
+ "\\bsansQ": "𝗤",
780
+ "\\bsansR": "𝗥",
781
+ "\\bsansS": "𝗦",
782
+ "\\bsansT": "𝗧",
783
+ "\\bsansU": "𝗨",
784
+ "\\bsansV": "𝗩",
785
+ "\\bsansW": "𝗪",
786
+ "\\bsansX": "𝗫",
787
+ "\\bsansY": "𝗬",
788
+ "\\bsansZ": "𝗭",
789
+ "\\bsansa": "𝗮",
790
+ "\\bsansb": "𝗯",
791
+ "\\bsansc": "𝗰",
792
+ "\\bsansd": "𝗱",
793
+ "\\bsanse": "𝗲",
794
+ "\\bsansf": "𝗳",
795
+ "\\bsansg": "𝗴",
796
+ "\\bsansh": "𝗵",
797
+ "\\bsansi": "𝗶",
798
+ "\\bsansj": "𝗷",
799
+ "\\bsansk": "𝗸",
800
+ "\\bsansl": "𝗹",
801
+ "\\bsansm": "𝗺",
802
+ "\\bsansn": "𝗻",
803
+ "\\bsanso": "𝗼",
804
+ "\\bsansp": "𝗽",
805
+ "\\bsansq": "𝗾",
806
+ "\\bsansr": "𝗿",
807
+ "\\bsanss": "𝘀",
808
+ "\\bsanst": "𝘁",
809
+ "\\bsansu": "𝘂",
810
+ "\\bsansv": "𝘃",
811
+ "\\bsansw": "𝘄",
812
+ "\\bsansx": "𝘅",
813
+ "\\bsansy": "𝘆",
814
+ "\\bsansz": "𝘇",
815
+ "\\isansA": "𝘈",
816
+ "\\isansB": "𝘉",
817
+ "\\isansC": "𝘊",
818
+ "\\isansD": "𝘋",
819
+ "\\isansE": "𝘌",
820
+ "\\isansF": "𝘍",
821
+ "\\isansG": "𝘎",
822
+ "\\isansH": "𝘏",
823
+ "\\isansI": "𝘐",
824
+ "\\isansJ": "𝘑",
825
+ "\\isansK": "𝘒",
826
+ "\\isansL": "𝘓",
827
+ "\\isansM": "𝘔",
828
+ "\\isansN": "𝘕",
829
+ "\\isansO": "𝘖",
830
+ "\\isansP": "𝘗",
831
+ "\\isansQ": "𝘘",
832
+ "\\isansR": "𝘙",
833
+ "\\isansS": "𝘚",
834
+ "\\isansT": "𝘛",
835
+ "\\isansU": "𝘜",
836
+ "\\isansV": "𝘝",
837
+ "\\isansW": "𝘞",
838
+ "\\isansX": "𝘟",
839
+ "\\isansY": "𝘠",
840
+ "\\isansZ": "𝘡",
841
+ "\\isansa": "𝘢",
842
+ "\\isansb": "𝘣",
843
+ "\\isansc": "𝘤",
844
+ "\\isansd": "𝘥",
845
+ "\\isanse": "𝘦",
846
+ "\\isansf": "𝘧",
847
+ "\\isansg": "𝘨",
848
+ "\\isansh": "𝘩",
849
+ "\\isansi": "𝘪",
850
+ "\\isansj": "𝘫",
851
+ "\\isansk": "𝘬",
852
+ "\\isansl": "𝘭",
853
+ "\\isansm": "𝘮",
854
+ "\\isansn": "𝘯",
855
+ "\\isanso": "𝘰",
856
+ "\\isansp": "𝘱",
857
+ "\\isansq": "𝘲",
858
+ "\\isansr": "𝘳",
859
+ "\\isanss": "𝘴",
860
+ "\\isanst": "𝘵",
861
+ "\\isansu": "𝘶",
862
+ "\\isansv": "𝘷",
863
+ "\\isansw": "𝘸",
864
+ "\\isansx": "𝘹",
865
+ "\\isansy": "𝘺",
866
+ "\\isansz": "𝘻",
867
+ "\\bisansA": "𝘼",
868
+ "\\bisansB": "𝘽",
869
+ "\\bisansC": "𝘾",
870
+ "\\bisansD": "𝘿",
871
+ "\\bisansE": "𝙀",
872
+ "\\bisansF": "𝙁",
873
+ "\\bisansG": "𝙂",
874
+ "\\bisansH": "𝙃",
875
+ "\\bisansI": "𝙄",
876
+ "\\bisansJ": "𝙅",
877
+ "\\bisansK": "𝙆",
878
+ "\\bisansL": "𝙇",
879
+ "\\bisansM": "𝙈",
880
+ "\\bisansN": "𝙉",
881
+ "\\bisansO": "𝙊",
882
+ "\\bisansP": "𝙋",
883
+ "\\bisansQ": "𝙌",
884
+ "\\bisansR": "𝙍",
885
+ "\\bisansS": "𝙎",
886
+ "\\bisansT": "𝙏",
887
+ "\\bisansU": "𝙐",
888
+ "\\bisansV": "𝙑",
889
+ "\\bisansW": "𝙒",
890
+ "\\bisansX": "𝙓",
891
+ "\\bisansY": "𝙔",
892
+ "\\bisansZ": "𝙕",
893
+ "\\bisansa": "𝙖",
894
+ "\\bisansb": "𝙗",
895
+ "\\bisansc": "𝙘",
896
+ "\\bisansd": "𝙙",
897
+ "\\bisanse": "𝙚",
898
+ "\\bisansf": "𝙛",
899
+ "\\bisansg": "𝙜",
900
+ "\\bisansh": "𝙝",
901
+ "\\bisansi": "𝙞",
902
+ "\\bisansj": "𝙟",
903
+ "\\bisansk": "𝙠",
904
+ "\\bisansl": "𝙡",
905
+ "\\bisansm": "𝙢",
906
+ "\\bisansn": "𝙣",
907
+ "\\bisanso": "𝙤",
908
+ "\\bisansp": "𝙥",
909
+ "\\bisansq": "𝙦",
910
+ "\\bisansr": "𝙧",
911
+ "\\bisanss": "𝙨",
912
+ "\\bisanst": "𝙩",
913
+ "\\bisansu": "𝙪",
914
+ "\\bisansv": "𝙫",
915
+ "\\bisansw": "𝙬",
916
+ "\\bisansx": "𝙭",
917
+ "\\bisansy": "𝙮",
918
+ "\\bisansz": "𝙯",
919
+ "\\ttA": "𝙰",
920
+ "\\ttB": "𝙱",
921
+ "\\ttC": "𝙲",
922
+ "\\ttD": "𝙳",
923
+ "\\ttE": "𝙴",
924
+ "\\ttF": "𝙵",
925
+ "\\ttG": "𝙶",
926
+ "\\ttH": "𝙷",
927
+ "\\ttI": "𝙸",
928
+ "\\ttJ": "𝙹",
929
+ "\\ttK": "𝙺",
930
+ "\\ttL": "𝙻",
931
+ "\\ttM": "𝙼",
932
+ "\\ttN": "𝙽",
933
+ "\\ttO": "𝙾",
934
+ "\\ttP": "𝙿",
935
+ "\\ttQ": "𝚀",
936
+ "\\ttR": "𝚁",
937
+ "\\ttS": "𝚂",
938
+ "\\ttT": "𝚃",
939
+ "\\ttU": "𝚄",
940
+ "\\ttV": "𝚅",
941
+ "\\ttW": "𝚆",
942
+ "\\ttX": "𝚇",
943
+ "\\ttY": "𝚈",
944
+ "\\ttZ": "𝚉",
945
+ "\\tta": "𝚊",
946
+ "\\ttb": "𝚋",
947
+ "\\ttc": "𝚌",
948
+ "\\ttd": "𝚍",
949
+ "\\tte": "𝚎",
950
+ "\\ttf": "𝚏",
951
+ "\\ttg": "𝚐",
952
+ "\\tth": "𝚑",
953
+ "\\tti": "𝚒",
954
+ "\\ttj": "𝚓",
955
+ "\\ttk": "𝚔",
956
+ "\\ttl": "𝚕",
957
+ "\\ttm": "𝚖",
958
+ "\\ttn": "𝚗",
959
+ "\\tto": "𝚘",
960
+ "\\ttp": "𝚙",
961
+ "\\ttq": "𝚚",
962
+ "\\ttr": "𝚛",
963
+ "\\tts": "𝚜",
964
+ "\\ttt": "𝚝",
965
+ "\\ttu": "𝚞",
966
+ "\\ttv": "𝚟",
967
+ "\\ttw": "𝚠",
968
+ "\\ttx": "𝚡",
969
+ "\\tty": "𝚢",
970
+ "\\ttz": "𝚣",
971
+ "\\bfAlpha": "𝚨",
972
+ "\\bfBeta": "𝚩",
973
+ "\\bfGamma": "𝚪",
974
+ "\\bfDelta": "𝚫",
975
+ "\\bfEpsilon": "𝚬",
976
+ "\\bfZeta": "𝚭",
977
+ "\\bfEta": "𝚮",
978
+ "\\bfTheta": "𝚯",
979
+ "\\bfIota": "𝚰",
980
+ "\\bfKappa": "𝚱",
981
+ "\\bfLambda": "𝚲",
982
+ "\\bfMu": "𝚳",
983
+ "\\bfNu": "𝚴",
984
+ "\\bfXi": "𝚵",
985
+ "\\bfOmicron": "𝚶",
986
+ "\\bfPi": "𝚷",
987
+ "\\bfRho": "𝚸",
988
+ "\\bfvarTheta": "𝚹",
989
+ "\\bfSigma": "𝚺",
990
+ "\\bfTau": "𝚻",
991
+ "\\bfUpsilon": "𝚼",
992
+ "\\bfPhi": "𝚽",
993
+ "\\bfChi": "𝚾",
994
+ "\\bfPsi": "𝚿",
995
+ "\\bfOmega": "𝛀",
996
+ "\\bfalpha": "𝛂",
997
+ "\\bfbeta": "𝛃",
998
+ "\\bfgamma": "𝛄",
999
+ "\\bfdelta": "𝛅",
1000
+ "\\bfvarepsilon": "𝛆",
1001
+ "\\bfzeta": "𝛇",
1002
+ "\\bfeta": "𝛈",
1003
+ "\\bftheta": "𝛉",
1004
+ "\\bfiota": "𝛊",
1005
+ "\\bfkappa": "𝛋",
1006
+ "\\bflambda": "𝛌",
1007
+ "\\bfmu": "𝛍",
1008
+ "\\bfnu": "𝛎",
1009
+ "\\bfxi": "𝛏",
1010
+ "\\bfomicron": "𝛐",
1011
+ "\\bfpi": "𝛑",
1012
+ "\\bfrho": "𝛒",
1013
+ "\\bfvarsigma": "𝛓",
1014
+ "\\bfsigma": "𝛔",
1015
+ "\\bftau": "𝛕",
1016
+ "\\bfupsilon": "𝛖",
1017
+ "\\bfvarphi": "𝛗",
1018
+ "\\bfchi": "𝛘",
1019
+ "\\bfpsi": "𝛙",
1020
+ "\\bfomega": "𝛚",
1021
+ "\\bfepsilon": "𝛜",
1022
+ "\\bfvartheta": "𝛝",
1023
+ "\\bfvarkappa": "𝛞",
1024
+ "\\bfphi": "𝛟",
1025
+ "\\bfvarrho": "𝛠",
1026
+ "\\bfvarpi": "𝛡",
1027
+ "\\itAlpha": "𝛢",
1028
+ "\\itBeta": "𝛣",
1029
+ "\\itGamma": "𝛤",
1030
+ "\\itDelta": "𝛥",
1031
+ "\\itEpsilon": "𝛦",
1032
+ "\\itZeta": "𝛧",
1033
+ "\\itEta": "𝛨",
1034
+ "\\itTheta": "𝛩",
1035
+ "\\itIota": "𝛪",
1036
+ "\\itKappa": "𝛫",
1037
+ "\\itLambda": "𝛬",
1038
+ "\\itMu": "𝛭",
1039
+ "\\itNu": "𝛮",
1040
+ "\\itXi": "𝛯",
1041
+ "\\itOmicron": "𝛰",
1042
+ "\\itPi": "𝛱",
1043
+ "\\itRho": "𝛲",
1044
+ "\\itvarTheta": "𝛳",
1045
+ "\\itSigma": "𝛴",
1046
+ "\\itTau": "𝛵",
1047
+ "\\itUpsilon": "𝛶",
1048
+ "\\itPhi": "𝛷",
1049
+ "\\itChi": "𝛸",
1050
+ "\\itPsi": "𝛹",
1051
+ "\\itOmega": "𝛺",
1052
+ "\\italpha": "𝛼",
1053
+ "\\itbeta": "𝛽",
1054
+ "\\itgamma": "𝛾",
1055
+ "\\itdelta": "𝛿",
1056
+ "\\itvarepsilon": "𝜀",
1057
+ "\\itzeta": "𝜁",
1058
+ "\\iteta": "𝜂",
1059
+ "\\ittheta": "𝜃",
1060
+ "\\itiota": "𝜄",
1061
+ "\\itkappa": "𝜅",
1062
+ "\\itlambda": "𝜆",
1063
+ "\\itmu": "𝜇",
1064
+ "\\itnu": "𝜈",
1065
+ "\\itxi": "𝜉",
1066
+ "\\itomicron": "𝜊",
1067
+ "\\itpi": "𝜋",
1068
+ "\\itrho": "𝜌",
1069
+ "\\itvarsigma": "𝜍",
1070
+ "\\itsigma": "𝜎",
1071
+ "\\ittau": "𝜏",
1072
+ "\\itupsilon": "𝜐",
1073
+ "\\itvarphi": "𝜑",
1074
+ "\\itchi": "𝜒",
1075
+ "\\itpsi": "𝜓",
1076
+ "\\itomega": "𝜔",
1077
+ "\\itepsilon": "𝜖",
1078
+ "\\itvartheta": "𝜗",
1079
+ "\\itvarkappa": "𝜘",
1080
+ "\\itphi": "𝜙",
1081
+ "\\itvarrho": "𝜚",
1082
+ "\\itvarpi": "𝜛",
1083
+ "\\biAlpha": "𝜜",
1084
+ "\\biBeta": "𝜝",
1085
+ "\\biGamma": "𝜞",
1086
+ "\\biDelta": "𝜟",
1087
+ "\\biEpsilon": "𝜠",
1088
+ "\\biZeta": "𝜡",
1089
+ "\\biEta": "𝜢",
1090
+ "\\biTheta": "𝜣",
1091
+ "\\biIota": "𝜤",
1092
+ "\\biKappa": "𝜥",
1093
+ "\\biLambda": "����",
1094
+ "\\biMu": "𝜧",
1095
+ "\\biNu": "𝜨",
1096
+ "\\biXi": "𝜩",
1097
+ "\\biOmicron": "𝜪",
1098
+ "\\biPi": "𝜫",
1099
+ "\\biRho": "𝜬",
1100
+ "\\bivarTheta": "𝜭",
1101
+ "\\biSigma": "𝜮",
1102
+ "\\biTau": "𝜯",
1103
+ "\\biUpsilon": "𝜰",
1104
+ "\\biPhi": "𝜱",
1105
+ "\\biChi": "𝜲",
1106
+ "\\biPsi": "𝜳",
1107
+ "\\biOmega": "𝜴",
1108
+ "\\bialpha": "𝜶",
1109
+ "\\bibeta": "𝜷",
1110
+ "\\bigamma": "𝜸",
1111
+ "\\bidelta": "𝜹",
1112
+ "\\bivarepsilon": "𝜺",
1113
+ "\\bizeta": "𝜻",
1114
+ "\\bieta": "𝜼",
1115
+ "\\bitheta": "𝜽",
1116
+ "\\biiota": "𝜾",
1117
+ "\\bikappa": "𝜿",
1118
+ "\\bilambda": "𝝀",
1119
+ "\\bimu": "𝝁",
1120
+ "\\binu": "𝝂",
1121
+ "\\bixi": "𝝃",
1122
+ "\\biomicron": "𝝄",
1123
+ "\\bipi": "𝝅",
1124
+ "\\birho": "𝝆",
1125
+ "\\bivarsigma": "𝝇",
1126
+ "\\bisigma": "𝝈",
1127
+ "\\bitau": "𝝉",
1128
+ "\\biupsilon": "𝝊",
1129
+ "\\bivarphi": "𝝋",
1130
+ "\\bichi": "𝝌",
1131
+ "\\bipsi": "𝝍",
1132
+ "\\biomega": "𝝎",
1133
+ "\\biepsilon": "𝝐",
1134
+ "\\bivartheta": "𝝑",
1135
+ "\\bivarkappa": "𝝒",
1136
+ "\\biphi": "𝝓",
1137
+ "\\bivarrho": "𝝔",
1138
+ "\\bivarpi": "𝝕",
1139
+ "\\bsansAlpha": "𝝖",
1140
+ "\\bsansBeta": "𝝗",
1141
+ "\\bsansGamma": "𝝘",
1142
+ "\\bsansDelta": "𝝙",
1143
+ "\\bsansEpsilon": "𝝚",
1144
+ "\\bsansZeta": "𝝛",
1145
+ "\\bsansEta": "𝝜",
1146
+ "\\bsansTheta": "𝝝",
1147
+ "\\bsansIota": "𝝞",
1148
+ "\\bsansKappa": "𝝟",
1149
+ "\\bsansLambda": "𝝠",
1150
+ "\\bsansMu": "𝝡",
1151
+ "\\bsansNu": "𝝢",
1152
+ "\\bsansXi": "𝝣",
1153
+ "\\bsansOmicron": "𝝤",
1154
+ "\\bsansPi": "𝝥",
1155
+ "\\bsansRho": "𝝦",
1156
+ "\\bsansvarTheta": "𝝧",
1157
+ "\\bsansSigma": "𝝨",
1158
+ "\\bsansTau": "𝝩",
1159
+ "\\bsansUpsilon": "𝝪",
1160
+ "\\bsansPhi": "𝝫",
1161
+ "\\bsansChi": "𝝬",
1162
+ "\\bsansPsi": "𝝭",
1163
+ "\\bsansOmega": "𝝮",
1164
+ "\\bsansalpha": "𝝰",
1165
+ "\\bsansbeta": "𝝱",
1166
+ "\\bsansgamma": "𝝲",
1167
+ "\\bsansdelta": "𝝳",
1168
+ "\\bsansvarepsilon": "𝝴",
1169
+ "\\bsanszeta": "𝝵",
1170
+ "\\bsanseta": "𝝶",
1171
+ "\\bsanstheta": "𝝷",
1172
+ "\\bsansiota": "𝝸",
1173
+ "\\bsanskappa": "𝝹",
1174
+ "\\bsanslambda": "𝝺",
1175
+ "\\bsansmu": "𝝻",
1176
+ "\\bsansnu": "𝝼",
1177
+ "\\bsansxi": "𝝽",
1178
+ "\\bsansomicron": "𝝾",
1179
+ "\\bsanspi": "𝝿",
1180
+ "\\bsansrho": "𝞀",
1181
+ "\\bsansvarsigma": "𝞁",
1182
+ "\\bsanssigma": "𝞂",
1183
+ "\\bsanstau": "𝞃",
1184
+ "\\bsansupsilon": "𝞄",
1185
+ "\\bsansvarphi": "𝞅",
1186
+ "\\bsanschi": "𝞆",
1187
+ "\\bsanspsi": "𝞇",
1188
+ "\\bsansomega": "𝞈",
1189
+ "\\bsansepsilon": "𝞊",
1190
+ "\\bsansvartheta": "𝞋",
1191
+ "\\bsansvarkappa": "𝞌",
1192
+ "\\bsansphi": "𝞍",
1193
+ "\\bsansvarrho": "𝞎",
1194
+ "\\bsansvarpi": "𝞏",
1195
+ "\\bisansAlpha": "𝞐",
1196
+ "\\bisansBeta": "𝞑",
1197
+ "\\bisansGamma": "𝞒",
1198
+ "\\bisansDelta": "𝞓",
1199
+ "\\bisansEpsilon": "𝞔",
1200
+ "\\bisansZeta": "𝞕",
1201
+ "\\bisansEta": "𝞖",
1202
+ "\\bisansTheta": "𝞗",
1203
+ "\\bisansIota": "𝞘",
1204
+ "\\bisansKappa": "𝞙",
1205
+ "\\bisansLambda": "𝞚",
1206
+ "\\bisansMu": "𝞛",
1207
+ "\\bisansNu": "𝞜",
1208
+ "\\bisansXi": "𝞝",
1209
+ "\\bisansOmicron": "𝞞",
1210
+ "\\bisansPi": "𝞟",
1211
+ "\\bisansRho": "𝞠",
1212
+ "\\bisansvarTheta": "𝞡",
1213
+ "\\bisansSigma": "𝞢",
1214
+ "\\bisansTau": "𝞣",
1215
+ "\\bisansUpsilon": "𝞤",
1216
+ "\\bisansPhi": "𝞥",
1217
+ "\\bisansChi": "𝞦",
1218
+ "\\bisansPsi": "𝞧",
1219
+ "\\bisansOmega": "𝞨",
1220
+ "\\bisansalpha": "𝞪",
1221
+ "\\bisansbeta": "𝞫",
1222
+ "\\bisansgamma": "𝞬",
1223
+ "\\bisansdelta": "𝞭",
1224
+ "\\bisansvarepsilon": "𝞮",
1225
+ "\\bisanszeta": "𝞯",
1226
+ "\\bisanseta": "𝞰",
1227
+ "\\bisanstheta": "𝞱",
1228
+ "\\bisansiota": "𝞲",
1229
+ "\\bisanskappa": "𝞳",
1230
+ "\\bisanslambda": "𝞴",
1231
+ "\\bisansmu": "𝞵",
1232
+ "\\bisansnu": "𝞶",
1233
+ "\\bisansxi": "𝞷",
1234
+ "\\bisansomicron": "𝞸",
1235
+ "\\bisanspi": "𝞹",
1236
+ "\\bisansrho": "𝞺",
1237
+ "\\bisansvarsigma": "𝞻",
1238
+ "\\bisanssigma": "𝞼",
1239
+ "\\bisanstau": "𝞽",
1240
+ "\\bisansupsilon": "𝞾",
1241
+ "\\bisansvarphi": "𝞿",
1242
+ "\\bisanschi": "𝟀",
1243
+ "\\bisanspsi": "𝟁",
1244
+ "\\bisansomega": "𝟂",
1245
+ "\\bisansepsilon": "𝟄",
1246
+ "\\bisansvartheta": "𝟅",
1247
+ "\\bisansvarkappa": "𝟆",
1248
+ "\\bisansphi": "𝟇",
1249
+ "\\bisansvarrho": "𝟈",
1250
+ "\\bisansvarpi": "𝟉",
1251
+ "\\bfzero": "𝟎",
1252
+ "\\bfone": "𝟏",
1253
+ "\\bftwo": "𝟐",
1254
+ "\\bfthree": "𝟑",
1255
+ "\\bffour": "𝟒",
1256
+ "\\bffive": "𝟓",
1257
+ "\\bfsix": "𝟔",
1258
+ "\\bfseven": "𝟕",
1259
+ "\\bfeight": "𝟖",
1260
+ "\\bfnine": "𝟗",
1261
+ "\\bbzero": "𝟘",
1262
+ "\\bbone": "𝟙",
1263
+ "\\bbtwo": "𝟚",
1264
+ "\\bbthree": "𝟛",
1265
+ "\\bbfour": "𝟜",
1266
+ "\\bbfive": "𝟝",
1267
+ "\\bbsix": "𝟞",
1268
+ "\\bbseven": "𝟟",
1269
+ "\\bbeight": "𝟠",
1270
+ "\\bbnine": "𝟡",
1271
+ "\\sanszero": "𝟢",
1272
+ "\\sansone": "𝟣",
1273
+ "\\sanstwo": "𝟤",
1274
+ "\\sansthree": "𝟥",
1275
+ "\\sansfour": "𝟦",
1276
+ "\\sansfive": "𝟧",
1277
+ "\\sanssix": "𝟨",
1278
+ "\\sansseven": "𝟩",
1279
+ "\\sanseight": "𝟪",
1280
+ "\\sansnine": "𝟫",
1281
+ "\\bsanszero": "𝟬",
1282
+ "\\bsansone": "𝟭",
1283
+ "\\bsanstwo": "𝟮",
1284
+ "\\bsansthree": "𝟯",
1285
+ "\\bsansfour": "𝟰",
1286
+ "\\bsansfive": "𝟱",
1287
+ "\\bsanssix": "𝟲",
1288
+ "\\bsansseven": "𝟳",
1289
+ "\\bsanseight": "𝟴",
1290
+ "\\bsansnine": "𝟵",
1291
+ "\\ttzero": "𝟶",
1292
+ "\\ttone": "𝟷",
1293
+ "\\tttwo": "𝟸",
1294
+ "\\ttthree": "𝟹",
1295
+ "\\ttfour": "𝟺",
1296
+ "\\ttfive": "𝟻",
1297
+ "\\ttsix": "𝟼",
1298
+ "\\ttseven": "𝟽",
1299
+ "\\tteight": "𝟾",
1300
+ "\\ttnine": "𝟿",
1301
+ "\\underbar": "̲",
1302
+ "\\underleftrightarrow": "͍",
1303
+ }
1304
+
1305
+
1306
+ reverse_latex_symbol = {v: k for k, v in latex_symbols.items()}
temp_venv/lib/python3.13/site-packages/IPython/core/logger.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Logger class for IPython's logging facilities.
2
+ """
3
+
4
+ #*****************************************************************************
5
+ # Copyright (C) 2001 Janko Hauser <[email protected]> and
6
+ # Copyright (C) 2001-2006 Fernando Perez <[email protected]>
7
+ #
8
+ # Distributed under the terms of the BSD License. The full license is in
9
+ # the file COPYING, distributed as part of this software.
10
+ #*****************************************************************************
11
+
12
+ #****************************************************************************
13
+ # Modules and globals
14
+
15
+ # Python standard modules
16
+ import glob
17
+ import io
18
+ import logging
19
+ import os
20
+ import time
21
+
22
+
23
+ # prevent jedi/parso's debug messages pipe into interactiveshell
24
+ logging.getLogger("parso").setLevel(logging.WARNING)
25
+
26
+ #****************************************************************************
27
+ # FIXME: This class isn't a mixin anymore, but it still needs attributes from
28
+ # ipython and does input cache management. Finish cleanup later...
29
+
30
+ class Logger:
31
+ """A Logfile class with different policies for file creation"""
32
+
33
+ def __init__(self, home_dir, logfname='Logger.log', loghead=u'',
34
+ logmode='over'):
35
+
36
+ # this is the full ipython instance, we need some attributes from it
37
+ # which won't exist until later. What a mess, clean up later...
38
+ self.home_dir = home_dir
39
+
40
+ self.logfname = logfname
41
+ self.loghead = loghead
42
+ self.logmode = logmode
43
+ self.logfile = None
44
+
45
+ # Whether to log raw or processed input
46
+ self.log_raw_input = False
47
+
48
+ # whether to also log output
49
+ self.log_output = False
50
+
51
+ # whether to put timestamps before each log entry
52
+ self.timestamp = False
53
+
54
+ # activity control flags
55
+ self.log_active = False
56
+
57
+ # logmode is a validated property
58
+ def _set_mode(self,mode):
59
+ if mode not in ['append','backup','global','over','rotate']:
60
+ raise ValueError('invalid log mode %s given' % mode)
61
+ self._logmode = mode
62
+
63
+ def _get_mode(self):
64
+ return self._logmode
65
+
66
+ logmode = property(_get_mode,_set_mode)
67
+
68
+ def logstart(self, logfname=None, loghead=None, logmode=None,
69
+ log_output=False, timestamp=False, log_raw_input=False):
70
+ """Generate a new log-file with a default header.
71
+
72
+ Raises RuntimeError if the log has already been started"""
73
+
74
+ if self.logfile is not None:
75
+ raise RuntimeError('Log file is already active: %s' %
76
+ self.logfname)
77
+
78
+ # The parameters can override constructor defaults
79
+ if logfname is not None: self.logfname = logfname
80
+ if loghead is not None: self.loghead = loghead
81
+ if logmode is not None: self.logmode = logmode
82
+
83
+ # Parameters not part of the constructor
84
+ self.timestamp = timestamp
85
+ self.log_output = log_output
86
+ self.log_raw_input = log_raw_input
87
+
88
+ # init depending on the log mode requested
89
+ isfile = os.path.isfile
90
+ logmode = self.logmode
91
+
92
+ if logmode == 'append':
93
+ self.logfile = io.open(self.logfname, 'a', encoding='utf-8')
94
+
95
+ elif logmode == 'backup':
96
+ if isfile(self.logfname):
97
+ backup_logname = self.logfname+'~'
98
+ # Manually remove any old backup, since os.rename may fail
99
+ # under Windows.
100
+ if isfile(backup_logname):
101
+ os.remove(backup_logname)
102
+ os.rename(self.logfname,backup_logname)
103
+ self.logfile = io.open(self.logfname, 'w', encoding='utf-8')
104
+
105
+ elif logmode == 'global':
106
+ self.logfname = os.path.join(self.home_dir,self.logfname)
107
+ self.logfile = io.open(self.logfname, 'a', encoding='utf-8')
108
+
109
+ elif logmode == 'over':
110
+ if isfile(self.logfname):
111
+ os.remove(self.logfname)
112
+ self.logfile = io.open(self.logfname,'w', encoding='utf-8')
113
+
114
+ elif logmode == 'rotate':
115
+ if isfile(self.logfname):
116
+ if isfile(self.logfname+'.001~'):
117
+ old = glob.glob(self.logfname+'.*~')
118
+ old.sort()
119
+ old.reverse()
120
+ for f in old:
121
+ root, ext = os.path.splitext(f)
122
+ num = int(ext[1:-1])+1
123
+ os.rename(f, root+'.'+repr(num).zfill(3)+'~')
124
+ os.rename(self.logfname, self.logfname+'.001~')
125
+ self.logfile = io.open(self.logfname, 'w', encoding='utf-8')
126
+
127
+ if logmode != 'append':
128
+ self.logfile.write(self.loghead)
129
+
130
+ self.logfile.flush()
131
+ self.log_active = True
132
+
133
+ def switch_log(self,val):
134
+ """Switch logging on/off. val should be ONLY a boolean."""
135
+
136
+ if val not in [False,True,0,1]:
137
+ raise ValueError('Call switch_log ONLY with a boolean argument, '
138
+ 'not with: %s' % val)
139
+
140
+ label = {0:'OFF',1:'ON',False:'OFF',True:'ON'}
141
+
142
+ if self.logfile is None:
143
+ print("""
144
+ Logging hasn't been started yet (use logstart for that).
145
+
146
+ %logon/%logoff are for temporarily starting and stopping logging for a logfile
147
+ which already exists. But you must first start the logging process with
148
+ %logstart (optionally giving a logfile name).""")
149
+
150
+ else:
151
+ if self.log_active == val:
152
+ print('Logging is already',label[val])
153
+ else:
154
+ print('Switching logging',label[val])
155
+ self.log_active = not self.log_active
156
+ self.log_active_out = self.log_active
157
+
158
+ def logstate(self):
159
+ """Print a status message about the logger."""
160
+ if self.logfile is None:
161
+ print('Logging has not been activated.')
162
+ else:
163
+ state = self.log_active and 'active' or 'temporarily suspended'
164
+ print('Filename :', self.logfname)
165
+ print('Mode :', self.logmode)
166
+ print('Output logging :', self.log_output)
167
+ print('Raw input log :', self.log_raw_input)
168
+ print('Timestamping :', self.timestamp)
169
+ print('State :', state)
170
+
171
+ def log(self, line_mod, line_ori):
172
+ """Write the sources to a log.
173
+
174
+ Inputs:
175
+
176
+ - line_mod: possibly modified input, such as the transformations made
177
+ by input prefilters or input handlers of various kinds. This should
178
+ always be valid Python.
179
+
180
+ - line_ori: unmodified input line from the user. This is not
181
+ necessarily valid Python.
182
+ """
183
+
184
+ # Write the log line, but decide which one according to the
185
+ # log_raw_input flag, set when the log is started.
186
+ if self.log_raw_input:
187
+ self.log_write(line_ori)
188
+ else:
189
+ self.log_write(line_mod)
190
+
191
+ def log_write(self, data, kind='input'):
192
+ """Write data to the log file, if active"""
193
+
194
+ # print('data: %r' % data) # dbg
195
+ if self.log_active and data:
196
+ write = self.logfile.write
197
+ if kind=='input':
198
+ if self.timestamp:
199
+ write(time.strftime('# %a, %d %b %Y %H:%M:%S\n', time.localtime()))
200
+ write(data)
201
+ elif kind=='output' and self.log_output:
202
+ odata = u'\n'.join([u'#[Out]# %s' % s
203
+ for s in data.splitlines()])
204
+ write(u'%s\n' % odata)
205
+ try:
206
+ self.logfile.flush()
207
+ except OSError:
208
+ print("Failed to flush the log file.")
209
+ print(
210
+ f"Please check that {self.logfname} exists and have the right permissions."
211
+ )
212
+ print(
213
+ "Also consider turning off the log with `%logstop` to avoid this warning."
214
+ )
215
+
216
+ def logstop(self):
217
+ """Fully stop logging and close log file.
218
+
219
+ In order to start logging again, a new logstart() call needs to be
220
+ made, possibly (though not necessarily) with a new filename, mode and
221
+ other options."""
222
+
223
+ if self.logfile is not None:
224
+ self.logfile.close()
225
+ self.logfile = None
226
+ else:
227
+ print("Logging hadn't been started.")
228
+ self.log_active = False
229
+
230
+ # For backwards compatibility, in case anyone was using this.
231
+ close_log = logstop
temp_venv/lib/python3.13/site-packages/IPython/core/macro.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Support for interactive macros in IPython"""
2
+
3
+ #*****************************************************************************
4
+ # Copyright (C) 2001-2005 Fernando Perez <[email protected]>
5
+ #
6
+ # Distributed under the terms of the BSD License. The full license is in
7
+ # the file COPYING, distributed as part of this software.
8
+ #*****************************************************************************
9
+
10
+ import re
11
+
12
+ from IPython.utils.encoding import DEFAULT_ENCODING
13
+
14
+ coding_declaration = re.compile(r"#\s*coding[:=]\s*([-\w.]+)")
15
+
16
+ class Macro:
17
+ """Simple class to store the value of macros as strings.
18
+
19
+ Macro is just a callable that executes a string of IPython
20
+ input when called.
21
+ """
22
+
23
+ def __init__(self,code):
24
+ """store the macro value, as a single string which can be executed"""
25
+ lines = []
26
+ enc = None
27
+ for line in code.splitlines():
28
+ coding_match = coding_declaration.match(line)
29
+ if coding_match:
30
+ enc = coding_match.group(1)
31
+ else:
32
+ lines.append(line)
33
+ code = "\n".join(lines)
34
+ if isinstance(code, bytes):
35
+ code = code.decode(enc or DEFAULT_ENCODING)
36
+ self.value = code + '\n'
37
+
38
+ def __str__(self):
39
+ return self.value
40
+
41
+ def __repr__(self):
42
+ return 'IPython.macro.Macro(%s)' % repr(self.value)
43
+
44
+ def __getstate__(self):
45
+ """ needed for safe pickling via %store """
46
+ return {'value': self.value}
47
+
48
+ def __add__(self, other):
49
+ if isinstance(other, Macro):
50
+ return Macro(self.value + other.value)
51
+ elif isinstance(other, str):
52
+ return Macro(self.value + other)
53
+ raise TypeError
temp_venv/lib/python3.13/site-packages/IPython/core/magic_arguments.py ADDED
@@ -0,0 +1,310 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''A decorator-based method of constructing IPython magics with `argparse`
2
+ option handling.
3
+
4
+ New magic functions can be defined like so::
5
+
6
+ from IPython.core.magic_arguments import (argument, magic_arguments,
7
+ parse_argstring)
8
+
9
+ @magic_arguments()
10
+ @argument('-o', '--option', help='An optional argument.')
11
+ @argument('arg', type=int, help='An integer positional argument.')
12
+ def magic_cool(self, arg):
13
+ """ A really cool magic command.
14
+
15
+ """
16
+ args = parse_argstring(magic_cool, arg)
17
+ ...
18
+
19
+ The `@magic_arguments` decorator marks the function as having argparse arguments.
20
+ The `@argument` decorator adds an argument using the same syntax as argparse's
21
+ `add_argument()` method. More sophisticated uses may also require the
22
+ `@argument_group` or `@kwds` decorator to customize the formatting and the
23
+ parsing.
24
+
25
+ Help text for the magic is automatically generated from the docstring and the
26
+ arguments::
27
+
28
+ In[1]: %cool?
29
+ %cool [-o OPTION] arg
30
+
31
+ A really cool magic command.
32
+
33
+ positional arguments:
34
+ arg An integer positional argument.
35
+
36
+ optional arguments:
37
+ -o OPTION, --option OPTION
38
+ An optional argument.
39
+
40
+ Here is an elaborated example that uses default parameters in `argument` and calls the `args` in the cell magic::
41
+
42
+ from IPython.core.magic import register_cell_magic
43
+ from IPython.core.magic_arguments import (argument, magic_arguments,
44
+ parse_argstring)
45
+
46
+
47
+ @magic_arguments()
48
+ @argument(
49
+ "--option",
50
+ "-o",
51
+ help=("Add an option here"),
52
+ )
53
+ @argument(
54
+ "--style",
55
+ "-s",
56
+ default="foo",
57
+ help=("Add some style arguments"),
58
+ )
59
+ @register_cell_magic
60
+ def my_cell_magic(line, cell):
61
+ args = parse_argstring(my_cell_magic, line)
62
+ print(f"{args.option=}")
63
+ print(f"{args.style=}")
64
+ print(f"{cell=}")
65
+
66
+ In a jupyter notebook, this cell magic can be executed like this::
67
+
68
+ %%my_cell_magic -o Hello
69
+ print("bar")
70
+ i = 42
71
+
72
+ Inheritance diagram:
73
+
74
+ .. inheritance-diagram:: IPython.core.magic_arguments
75
+ :parts: 3
76
+
77
+ '''
78
+ #-----------------------------------------------------------------------------
79
+ # Copyright (C) 2010-2011, IPython Development Team.
80
+ #
81
+ # Distributed under the terms of the Modified BSD License.
82
+ #
83
+ # The full license is in the file COPYING.txt, distributed with this software.
84
+ #-----------------------------------------------------------------------------
85
+ import argparse
86
+ import re
87
+
88
+ # Our own imports
89
+ from IPython.core.error import UsageError
90
+ from IPython.utils.decorators import undoc
91
+ from IPython.utils.process import arg_split
92
+ from IPython.utils.text import dedent
93
+
94
+ NAME_RE = re.compile(r"[a-zA-Z][a-zA-Z0-9_-]*$")
95
+
96
+ @undoc
97
+ class MagicHelpFormatter(argparse.RawDescriptionHelpFormatter):
98
+ """A HelpFormatter with a couple of changes to meet our needs.
99
+ """
100
+ # Modified to dedent text.
101
+ def _fill_text(self, text, width, indent):
102
+ return argparse.RawDescriptionHelpFormatter._fill_text(self, dedent(text), width, indent)
103
+
104
+ # Modified to wrap argument placeholders in <> where necessary.
105
+ def _format_action_invocation(self, action):
106
+ if not action.option_strings:
107
+ metavar, = self._metavar_formatter(action, action.dest)(1)
108
+ return metavar
109
+
110
+ else:
111
+ parts = []
112
+
113
+ # if the Optional doesn't take a value, format is:
114
+ # -s, --long
115
+ if action.nargs == 0:
116
+ parts.extend(action.option_strings)
117
+
118
+ # if the Optional takes a value, format is:
119
+ # -s ARGS, --long ARGS
120
+ else:
121
+ default = action.dest.upper()
122
+ args_string = self._format_args(action, default)
123
+ # IPYTHON MODIFICATION: If args_string is not a plain name, wrap
124
+ # it in <> so it's valid RST.
125
+ if not NAME_RE.match(args_string):
126
+ args_string = "<%s>" % args_string
127
+ for option_string in action.option_strings:
128
+ parts.append('%s %s' % (option_string, args_string))
129
+
130
+ return ', '.join(parts)
131
+
132
+ # Override the default prefix ('usage') to our % magic escape,
133
+ # in a code block.
134
+ def add_usage(self, usage, actions, groups, prefix="::\n\n %"):
135
+ super(MagicHelpFormatter, self).add_usage(usage, actions, groups, prefix)
136
+
137
+ class MagicArgumentParser(argparse.ArgumentParser):
138
+ """ An ArgumentParser tweaked for use by IPython magics.
139
+ """
140
+ def __init__(self,
141
+ prog=None,
142
+ usage=None,
143
+ description=None,
144
+ epilog=None,
145
+ parents=None,
146
+ formatter_class=MagicHelpFormatter,
147
+ prefix_chars='-',
148
+ argument_default=None,
149
+ conflict_handler='error',
150
+ add_help=False):
151
+ if parents is None:
152
+ parents = []
153
+ super(MagicArgumentParser, self).__init__(prog=prog, usage=usage,
154
+ description=description, epilog=epilog,
155
+ parents=parents, formatter_class=formatter_class,
156
+ prefix_chars=prefix_chars, argument_default=argument_default,
157
+ conflict_handler=conflict_handler, add_help=add_help)
158
+
159
+ def error(self, message):
160
+ """ Raise a catchable error instead of exiting.
161
+ """
162
+ raise UsageError(message)
163
+
164
+ def parse_argstring(self, argstring):
165
+ """ Split a string into an argument list and parse that argument list.
166
+ """
167
+ argv = arg_split(argstring)
168
+ return self.parse_args(argv)
169
+
170
+
171
+ def construct_parser(magic_func):
172
+ """ Construct an argument parser using the function decorations.
173
+ """
174
+ kwds = getattr(magic_func, 'argcmd_kwds', {})
175
+ if 'description' not in kwds:
176
+ kwds['description'] = getattr(magic_func, '__doc__', None)
177
+ arg_name = real_name(magic_func)
178
+ parser = MagicArgumentParser(arg_name, **kwds)
179
+ # Reverse the list of decorators in order to apply them in the
180
+ # order in which they appear in the source.
181
+ group = None
182
+ for deco in magic_func.decorators[::-1]:
183
+ result = deco.add_to_parser(parser, group)
184
+ if result is not None:
185
+ group = result
186
+
187
+ # Replace the magic function's docstring with the full help text.
188
+ magic_func.__doc__ = parser.format_help()
189
+
190
+ return parser
191
+
192
+
193
+ def parse_argstring(magic_func, argstring):
194
+ """ Parse the string of arguments for the given magic function.
195
+ """
196
+ return magic_func.parser.parse_argstring(argstring)
197
+
198
+
199
+ def real_name(magic_func):
200
+ """ Find the real name of the magic.
201
+ """
202
+ magic_name = magic_func.__name__
203
+ if magic_name.startswith('magic_'):
204
+ magic_name = magic_name[len('magic_'):]
205
+ return getattr(magic_func, 'argcmd_name', magic_name)
206
+
207
+
208
+ class ArgDecorator:
209
+ """ Base class for decorators to add ArgumentParser information to a method.
210
+ """
211
+
212
+ def __call__(self, func):
213
+ if not getattr(func, 'has_arguments', False):
214
+ func.has_arguments = True
215
+ func.decorators = []
216
+ func.decorators.append(self)
217
+ return func
218
+
219
+ def add_to_parser(self, parser, group):
220
+ """ Add this object's information to the parser, if necessary.
221
+ """
222
+ pass
223
+
224
+
225
+ class magic_arguments(ArgDecorator):
226
+ """ Mark the magic as having argparse arguments and possibly adjust the
227
+ name.
228
+ """
229
+
230
+ def __init__(self, name=None):
231
+ self.name = name
232
+
233
+ def __call__(self, func):
234
+ if not getattr(func, 'has_arguments', False):
235
+ func.has_arguments = True
236
+ func.decorators = []
237
+ if self.name is not None:
238
+ func.argcmd_name = self.name
239
+ # This should be the first decorator in the list of decorators, thus the
240
+ # last to execute. Build the parser.
241
+ func.parser = construct_parser(func)
242
+ return func
243
+
244
+
245
+ class ArgMethodWrapper(ArgDecorator):
246
+
247
+ """
248
+ Base class to define a wrapper for ArgumentParser method.
249
+
250
+ Child class must define either `_method_name` or `add_to_parser`.
251
+
252
+ """
253
+
254
+ _method_name: str
255
+
256
+ def __init__(self, *args, **kwds):
257
+ self.args = args
258
+ self.kwds = kwds
259
+
260
+ def add_to_parser(self, parser, group):
261
+ """ Add this object's information to the parser.
262
+ """
263
+ if group is not None:
264
+ parser = group
265
+ getattr(parser, self._method_name)(*self.args, **self.kwds)
266
+ return None
267
+
268
+
269
+ class argument(ArgMethodWrapper):
270
+ """ Store arguments and keywords to pass to add_argument().
271
+
272
+ Instances also serve to decorate command methods.
273
+ """
274
+ _method_name = 'add_argument'
275
+
276
+
277
+ class defaults(ArgMethodWrapper):
278
+ """ Store arguments and keywords to pass to set_defaults().
279
+
280
+ Instances also serve to decorate command methods.
281
+ """
282
+ _method_name = 'set_defaults'
283
+
284
+
285
+ class argument_group(ArgMethodWrapper):
286
+ """ Store arguments and keywords to pass to add_argument_group().
287
+
288
+ Instances also serve to decorate command methods.
289
+ """
290
+
291
+ def add_to_parser(self, parser, group):
292
+ """ Add this object's information to the parser.
293
+ """
294
+ return parser.add_argument_group(*self.args, **self.kwds)
295
+
296
+
297
+ class kwds(ArgDecorator):
298
+ """ Provide other keywords to the sub-parser constructor.
299
+ """
300
+ def __init__(self, **kwds):
301
+ self.kwds = kwds
302
+
303
+ def __call__(self, func):
304
+ func = super(kwds, self).__call__(func)
305
+ func.argcmd_kwds = self.kwds
306
+ return func
307
+
308
+
309
+ __all__ = ['magic_arguments', 'argument', 'argument_group', 'kwds',
310
+ 'parse_argstring']
temp_venv/lib/python3.13/site-packages/IPython/core/oinspect.py ADDED
@@ -0,0 +1,1216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tools for inspecting Python objects.
2
+
3
+ Uses syntax highlighting for presenting the various information elements.
4
+
5
+ Similar in spirit to the inspect module, but all calls take a name argument to
6
+ reference the name under which an object is being read.
7
+ """
8
+
9
+ # Copyright (c) IPython Development Team.
10
+ # Distributed under the terms of the Modified BSD License.
11
+
12
+ __all__ = ["Inspector"]
13
+
14
+ # stdlib modules
15
+ from dataclasses import dataclass
16
+ from inspect import signature
17
+ from textwrap import dedent
18
+ import ast
19
+ import html
20
+ import inspect
21
+ import io as stdlib_io
22
+ import linecache
23
+ import os
24
+ import types
25
+ import warnings
26
+ from pygments.token import Token
27
+
28
+
29
+ from typing import (
30
+ cast,
31
+ Any,
32
+ Optional,
33
+ Dict,
34
+ Union,
35
+ List,
36
+ TypedDict,
37
+ TypeAlias,
38
+ Tuple,
39
+ )
40
+
41
+ import traitlets
42
+ from traitlets.config import Configurable
43
+
44
+ # IPython's own
45
+ from IPython.core import page
46
+ from IPython.lib.pretty import pretty
47
+ from IPython.testing.skipdoctest import skip_doctest
48
+ from IPython.utils import PyColorize, openpy
49
+ from IPython.utils.dir2 import safe_hasattr
50
+ from IPython.utils.path import compress_user
51
+ from IPython.utils.text import indent
52
+ from IPython.utils.wildcard import list_namespace, typestr2type
53
+ from IPython.utils.decorators import undoc
54
+
55
+ from pygments import highlight
56
+ from pygments.lexers import PythonLexer
57
+ from pygments.formatters import HtmlFormatter
58
+
59
+ HOOK_NAME = "__custom_documentations__"
60
+
61
+
62
+ UnformattedBundle: TypeAlias = Dict[str, List[Tuple[str, str]]] # List of (title, body)
63
+ Bundle: TypeAlias = Dict[str, str]
64
+
65
+
66
+ @dataclass
67
+ class OInfo:
68
+ ismagic: bool
69
+ isalias: bool
70
+ found: bool
71
+ namespace: Optional[str]
72
+ parent: Any
73
+ obj: Any
74
+
75
+ def get(self, field):
76
+ """Get a field from the object for backward compatibility with before 8.12
77
+
78
+ see https://github.com/h5py/h5py/issues/2253
79
+ """
80
+ # We need to deprecate this at some point, but the warning will show in completion.
81
+ # Let's comment this for now and uncomment end of 2023 ish
82
+ # Jan 2025: decomenting for IPython 9.0
83
+ warnings.warn(
84
+ f"OInfo dataclass with fields access since IPython 8.12 please use OInfo.{field} instead."
85
+ "OInfo used to be a dict but a dataclass provide static fields verification with mypy."
86
+ "This warning and backward compatibility `get()` method were added in 8.13.",
87
+ DeprecationWarning,
88
+ stacklevel=2,
89
+ )
90
+ return getattr(self, field)
91
+
92
+
93
+ def pylight(code):
94
+ return highlight(code, PythonLexer(), HtmlFormatter(noclasses=True))
95
+
96
+ # builtin docstrings to ignore
97
+ _func_call_docstring = types.FunctionType.__call__.__doc__
98
+ _object_init_docstring = object.__init__.__doc__
99
+ _builtin_type_docstrings = {
100
+ inspect.getdoc(t) for t in (types.ModuleType, types.MethodType,
101
+ types.FunctionType, property)
102
+ }
103
+
104
+ _builtin_func_type = type(all)
105
+ _builtin_meth_type = type(str.upper) # Bound methods have the same type as builtin functions
106
+ #****************************************************************************
107
+ # Builtin color schemes
108
+
109
+
110
+ #****************************************************************************
111
+ # Auxiliary functions and objects
112
+
113
+
114
+ class InfoDict(TypedDict):
115
+ type_name: Optional[str]
116
+ base_class: Optional[str]
117
+ string_form: Optional[str]
118
+ namespace: Optional[str]
119
+ length: Optional[str]
120
+ file: Optional[str]
121
+ definition: Optional[str]
122
+ docstring: Optional[str]
123
+ source: Optional[str]
124
+ init_definition: Optional[str]
125
+ class_docstring: Optional[str]
126
+ init_docstring: Optional[str]
127
+ call_def: Optional[str]
128
+ call_docstring: Optional[str]
129
+ subclasses: Optional[str]
130
+ # These won't be printed but will be used to determine how to
131
+ # format the object
132
+ ismagic: bool
133
+ isalias: bool
134
+ isclass: bool
135
+ found: bool
136
+ name: str
137
+
138
+
139
+ _info_fields = list(InfoDict.__annotations__.keys())
140
+
141
+
142
+ def __getattr__(name):
143
+ if name == "info_fields":
144
+ warnings.warn(
145
+ "IPython.core.oinspect's `info_fields` is considered for deprecation and may be removed in the Future. ",
146
+ DeprecationWarning,
147
+ stacklevel=2,
148
+ )
149
+ return _info_fields
150
+
151
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
152
+
153
+
154
+ @dataclass
155
+ class InspectorHookData:
156
+ """Data passed to the mime hook"""
157
+
158
+ obj: Any
159
+ info: Optional[OInfo]
160
+ info_dict: InfoDict
161
+ detail_level: int
162
+ omit_sections: list[str]
163
+
164
+
165
+ @undoc
166
+ def object_info(
167
+ *,
168
+ name: str,
169
+ found: bool,
170
+ isclass: bool = False,
171
+ isalias: bool = False,
172
+ ismagic: bool = False,
173
+ **kw,
174
+ ) -> InfoDict:
175
+ """Make an object info dict with all fields present."""
176
+ infodict = dict(kw)
177
+ infodict.update({k: None for k in _info_fields if k not in infodict})
178
+ infodict["name"] = name # type: ignore
179
+ infodict["found"] = found # type: ignore
180
+ infodict["isclass"] = isclass # type: ignore
181
+ infodict["isalias"] = isalias # type: ignore
182
+ infodict["ismagic"] = ismagic # type: ignore
183
+
184
+ return InfoDict(**infodict) # type:ignore
185
+
186
+
187
+ def get_encoding(obj):
188
+ """Get encoding for python source file defining obj
189
+
190
+ Returns None if obj is not defined in a sourcefile.
191
+ """
192
+ ofile = find_file(obj)
193
+ # run contents of file through pager starting at line where the object
194
+ # is defined, as long as the file isn't binary and is actually on the
195
+ # filesystem.
196
+ if ofile is None:
197
+ return None
198
+ elif ofile.endswith(('.so', '.dll', '.pyd')):
199
+ return None
200
+ elif not os.path.isfile(ofile):
201
+ return None
202
+ else:
203
+ # Print only text files, not extension binaries. Note that
204
+ # getsourcelines returns lineno with 1-offset and page() uses
205
+ # 0-offset, so we must adjust.
206
+ with stdlib_io.open(ofile, 'rb') as buffer: # Tweaked to use io.open for Python 2
207
+ encoding, _lines = openpy.detect_encoding(buffer.readline)
208
+ return encoding
209
+
210
+
211
+ def getdoc(obj) -> Union[str, None]:
212
+ """Stable wrapper around inspect.getdoc.
213
+
214
+ This can't crash because of attribute problems.
215
+
216
+ It also attempts to call a getdoc() method on the given object. This
217
+ allows objects which provide their docstrings via non-standard mechanisms
218
+ (like Pyro proxies) to still be inspected by ipython's ? system.
219
+ """
220
+ # Allow objects to offer customized documentation via a getdoc method:
221
+ try:
222
+ ds = obj.getdoc()
223
+ except Exception:
224
+ pass
225
+ else:
226
+ if isinstance(ds, str):
227
+ return inspect.cleandoc(ds)
228
+ docstr = inspect.getdoc(obj)
229
+ return docstr
230
+
231
+
232
+ def getsource(obj, oname='') -> Union[str,None]:
233
+ """Wrapper around inspect.getsource.
234
+
235
+ This can be modified by other projects to provide customized source
236
+ extraction.
237
+
238
+ Parameters
239
+ ----------
240
+ obj : object
241
+ an object whose source code we will attempt to extract
242
+ oname : str
243
+ (optional) a name under which the object is known
244
+
245
+ Returns
246
+ -------
247
+ src : unicode or None
248
+
249
+ """
250
+
251
+ if isinstance(obj, property):
252
+ sources = []
253
+ for attrname in ['fget', 'fset', 'fdel']:
254
+ fn = getattr(obj, attrname)
255
+ if fn is not None:
256
+ oname_prefix = ('%s.' % oname) if oname else ''
257
+ sources.append(''.join(('# ', oname_prefix, attrname)))
258
+ if inspect.isfunction(fn):
259
+ _src = getsource(fn)
260
+ if _src:
261
+ # assert _src is not None, "please mypy"
262
+ sources.append(dedent(_src))
263
+ else:
264
+ # Default str/repr only prints function name,
265
+ # pretty.pretty prints module name too.
266
+ sources.append(
267
+ '%s%s = %s\n' % (oname_prefix, attrname, pretty(fn))
268
+ )
269
+ if sources:
270
+ return '\n'.join(sources)
271
+ else:
272
+ return None
273
+
274
+ else:
275
+ # Get source for non-property objects.
276
+
277
+ obj = _get_wrapped(obj)
278
+
279
+ try:
280
+ src = inspect.getsource(obj)
281
+ except TypeError:
282
+ # The object itself provided no meaningful source, try looking for
283
+ # its class definition instead.
284
+ try:
285
+ src = inspect.getsource(obj.__class__)
286
+ except (OSError, TypeError):
287
+ return None
288
+ except OSError:
289
+ return None
290
+
291
+ return src
292
+
293
+
294
+ def is_simple_callable(obj):
295
+ """True if obj is a function ()"""
296
+ return (inspect.isfunction(obj) or inspect.ismethod(obj) or \
297
+ isinstance(obj, _builtin_func_type) or isinstance(obj, _builtin_meth_type))
298
+
299
+ def _get_wrapped(obj):
300
+ """Get the original object if wrapped in one or more @decorators
301
+
302
+ Some objects automatically construct similar objects on any unrecognised
303
+ attribute access (e.g. unittest.mock.call). To protect against infinite loops,
304
+ this will arbitrarily cut off after 100 levels of obj.__wrapped__
305
+ attribute access. --TK, Jan 2016
306
+ """
307
+ orig_obj = obj
308
+ i = 0
309
+ while safe_hasattr(obj, '__wrapped__'):
310
+ obj = obj.__wrapped__
311
+ i += 1
312
+ if i > 100:
313
+ # __wrapped__ is probably a lie, so return the thing we started with
314
+ return orig_obj
315
+ return obj
316
+
317
+ def find_file(obj) -> Optional[str]:
318
+ """Find the absolute path to the file where an object was defined.
319
+
320
+ This is essentially a robust wrapper around `inspect.getabsfile`.
321
+
322
+ Returns None if no file can be found.
323
+
324
+ Parameters
325
+ ----------
326
+ obj : any Python object
327
+
328
+ Returns
329
+ -------
330
+ fname : str
331
+ The absolute path to the file where the object was defined.
332
+ """
333
+ obj = _get_wrapped(obj)
334
+
335
+ fname: Optional[str] = None
336
+ try:
337
+ fname = inspect.getabsfile(obj)
338
+ except TypeError:
339
+ # For an instance, the file that matters is where its class was
340
+ # declared.
341
+ try:
342
+ fname = inspect.getabsfile(obj.__class__)
343
+ except (OSError, TypeError):
344
+ # Can happen for builtins
345
+ pass
346
+ except OSError:
347
+ pass
348
+
349
+ return fname
350
+
351
+
352
+ def find_source_lines(obj):
353
+ """Find the line number in a file where an object was defined.
354
+
355
+ This is essentially a robust wrapper around `inspect.getsourcelines`.
356
+
357
+ Returns None if no file can be found.
358
+
359
+ Parameters
360
+ ----------
361
+ obj : any Python object
362
+
363
+ Returns
364
+ -------
365
+ lineno : int
366
+ The line number where the object definition starts.
367
+ """
368
+ obj = _get_wrapped(obj)
369
+
370
+ try:
371
+ lineno = inspect.getsourcelines(obj)[1]
372
+ except TypeError:
373
+ # For instances, try the class object like getsource() does
374
+ try:
375
+ lineno = inspect.getsourcelines(obj.__class__)[1]
376
+ except (OSError, TypeError):
377
+ return None
378
+ except OSError:
379
+ return None
380
+
381
+ return lineno
382
+
383
+
384
+ _sentinel = object()
385
+
386
+
387
+ class Inspector(Configurable):
388
+
389
+ mime_hooks = traitlets.Dict(
390
+ config=True,
391
+ help="dictionary of mime to callable to add information into help mimebundle dict",
392
+ ).tag(config=True)
393
+
394
+ _theme_name: str
395
+
396
+ def __init__(
397
+ self,
398
+ *,
399
+ theme_name: str,
400
+ str_detail_level=0,
401
+ parent=None,
402
+ config=None,
403
+ ):
404
+ if theme_name in ["Linux", "LightBG", "Neutral", "NoColor"]:
405
+ warnings.warn(
406
+ f"Theme names and color schemes are lowercase in IPython 9.0 use {theme_name.lower()} instead",
407
+ DeprecationWarning,
408
+ stacklevel=2,
409
+ )
410
+ theme_name = theme_name.lower()
411
+ self._theme_name = theme_name
412
+ super(Inspector, self).__init__(parent=parent, config=config)
413
+ self.parser = PyColorize.Parser(out="str", theme_name=theme_name)
414
+ self.str_detail_level = str_detail_level
415
+ self.set_theme_name(theme_name)
416
+
417
+ def format(self, *args, **kwargs):
418
+ return self.parser.format(*args, **kwargs)
419
+
420
+ def _getdef(self,obj,oname='') -> Union[str,None]:
421
+ """Return the call signature for any callable object.
422
+
423
+ If any exception is generated, None is returned instead and the
424
+ exception is suppressed."""
425
+ if not callable(obj):
426
+ return None
427
+ try:
428
+ return _render_signature(signature(obj), oname)
429
+ except:
430
+ return None
431
+
432
+ def __head(self, h: str) -> str:
433
+ """Return a header string with proper colors."""
434
+ return PyColorize.theme_table[self._theme_name].format([(Token.Header, h)])
435
+
436
+ def set_theme_name(self, name: str):
437
+ assert name == name.lower()
438
+ assert name in PyColorize.theme_table.keys()
439
+ self._theme_name = name
440
+ self.parser.theme_name = name
441
+
442
+ def set_active_scheme(self, scheme: str):
443
+ warnings.warn(
444
+ "set_active_scheme is deprecated and replaced by set_theme_name as of IPython 9.0",
445
+ DeprecationWarning,
446
+ stacklevel=2,
447
+ )
448
+ assert scheme == scheme.lower()
449
+ if scheme is not None and self._theme_name != scheme:
450
+ self._theme_name = scheme
451
+ self.parser.theme_name = scheme
452
+
453
+ def noinfo(self, msg, oname):
454
+ """Generic message when no information is found."""
455
+ print('No %s found' % msg, end=' ')
456
+ if oname:
457
+ print('for %s' % oname)
458
+ else:
459
+ print()
460
+
461
+ def pdef(self, obj, oname=''):
462
+ """Print the call signature for any callable object.
463
+
464
+ If the object is a class, print the constructor information."""
465
+
466
+ if not callable(obj):
467
+ print('Object is not callable.')
468
+ return
469
+
470
+ header = ''
471
+
472
+ if inspect.isclass(obj):
473
+ header = self.__head('Class constructor information:\n')
474
+
475
+
476
+ output = self._getdef(obj,oname)
477
+ if output is None:
478
+ self.noinfo('definition header',oname)
479
+ else:
480
+ print(header,self.format(output), end=' ')
481
+
482
+ # In Python 3, all classes are new-style, so they all have __init__.
483
+ @skip_doctest
484
+ def pdoc(self, obj, oname='', formatter=None):
485
+ """Print the docstring for any object.
486
+
487
+ Optional:
488
+ -formatter: a function to run the docstring through for specially
489
+ formatted docstrings.
490
+
491
+ Examples
492
+ --------
493
+ In [1]: class NoInit:
494
+ ...: pass
495
+
496
+ In [2]: class NoDoc:
497
+ ...: def __init__(self):
498
+ ...: pass
499
+
500
+ In [3]: %pdoc NoDoc
501
+ No documentation found for NoDoc
502
+
503
+ In [4]: %pdoc NoInit
504
+ No documentation found for NoInit
505
+
506
+ In [5]: obj = NoInit()
507
+
508
+ In [6]: %pdoc obj
509
+ No documentation found for obj
510
+
511
+ In [5]: obj2 = NoDoc()
512
+
513
+ In [6]: %pdoc obj2
514
+ No documentation found for obj2
515
+ """
516
+
517
+ lines = []
518
+ ds = getdoc(obj)
519
+ if formatter:
520
+ ds = formatter(ds).get('plain/text', ds)
521
+ if ds:
522
+ lines.append(self.__head("Class docstring:"))
523
+ lines.append(indent(ds))
524
+ if inspect.isclass(obj) and hasattr(obj, '__init__'):
525
+ init_ds = getdoc(obj.__init__)
526
+ if init_ds is not None:
527
+ lines.append(self.__head("Init docstring:"))
528
+ lines.append(indent(init_ds))
529
+ elif hasattr(obj,'__call__'):
530
+ call_ds = getdoc(obj.__call__)
531
+ if call_ds:
532
+ lines.append(self.__head("Call docstring:"))
533
+ lines.append(indent(call_ds))
534
+
535
+ if not lines:
536
+ self.noinfo('documentation',oname)
537
+ else:
538
+ page.page('\n'.join(lines))
539
+
540
+ def psource(self, obj, oname=''):
541
+ """Print the source code for an object."""
542
+
543
+ # Flush the source cache because inspect can return out-of-date source
544
+ linecache.checkcache()
545
+ try:
546
+ src = getsource(obj, oname=oname)
547
+ except Exception:
548
+ src = None
549
+
550
+ if src is None:
551
+ self.noinfo('source', oname)
552
+ else:
553
+ page.page(self.format(src))
554
+
555
+ def pfile(self, obj, oname=''):
556
+ """Show the whole file where an object was defined."""
557
+
558
+ lineno = find_source_lines(obj)
559
+ if lineno is None:
560
+ self.noinfo('file', oname)
561
+ return
562
+
563
+ ofile = find_file(obj)
564
+ # run contents of file through pager starting at line where the object
565
+ # is defined, as long as the file isn't binary and is actually on the
566
+ # filesystem.
567
+ if ofile is None:
568
+ print("Could not find file for object")
569
+ elif ofile.endswith((".so", ".dll", ".pyd")):
570
+ print("File %r is binary, not printing." % ofile)
571
+ elif not os.path.isfile(ofile):
572
+ print('File %r does not exist, not printing.' % ofile)
573
+ else:
574
+ # Print only text files, not extension binaries. Note that
575
+ # getsourcelines returns lineno with 1-offset and page() uses
576
+ # 0-offset, so we must adjust.
577
+ page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
578
+
579
+
580
+ def _mime_format(self, text:str, formatter=None) -> dict:
581
+ """Return a mime bundle representation of the input text.
582
+
583
+ - if `formatter` is None, the returned mime bundle has
584
+ a ``text/plain`` field, with the input text.
585
+ a ``text/html`` field with a ``<pre>`` tag containing the input text.
586
+
587
+ - if ``formatter`` is not None, it must be a callable transforming the
588
+ input text into a mime bundle. Default values for ``text/plain`` and
589
+ ``text/html`` representations are the ones described above.
590
+
591
+ Note:
592
+
593
+ Formatters returning strings are supported but this behavior is deprecated.
594
+
595
+ """
596
+ defaults = {
597
+ "text/plain": text,
598
+ "text/html": f"<pre>{html.escape(text)}</pre>",
599
+ }
600
+
601
+ if formatter is None:
602
+ return defaults
603
+ else:
604
+ formatted = formatter(text)
605
+
606
+ if not isinstance(formatted, dict):
607
+ # Handle the deprecated behavior of a formatter returning
608
+ # a string instead of a mime bundle.
609
+ return {"text/plain": formatted, "text/html": f"<pre>{formatted}</pre>"}
610
+
611
+ else:
612
+ return dict(defaults, **formatted)
613
+
614
+ def format_mime(self, bundle: UnformattedBundle) -> Bundle:
615
+ """Format a mimebundle being created by _make_info_unformatted into a real mimebundle"""
616
+ # Format text/plain mimetype
617
+ assert isinstance(bundle["text/plain"], list)
618
+ for item in bundle["text/plain"]:
619
+ assert isinstance(item, tuple)
620
+
621
+ new_b: Bundle = {}
622
+ lines = []
623
+ _len = max(len(h) for h, _ in bundle["text/plain"])
624
+
625
+ for head, body in bundle["text/plain"]:
626
+ body = body.strip("\n")
627
+ delim = "\n" if "\n" in body else " "
628
+ lines.append(
629
+ f"{self.__head(head+':')}{(_len - len(head))*' '}{delim}{body}"
630
+ )
631
+
632
+ new_b["text/plain"] = "\n".join(lines)
633
+
634
+ if "text/html" in bundle:
635
+ assert isinstance(bundle["text/html"], list)
636
+ for item in bundle["text/html"]:
637
+ assert isinstance(item, tuple)
638
+ # Format the text/html mimetype
639
+ if isinstance(bundle["text/html"], (list, tuple)):
640
+ # bundle['text/html'] is a list of (head, formatted body) pairs
641
+ new_b["text/html"] = "\n".join(
642
+ f"<h1>{head}</h1>\n{body}" for (head, body) in bundle["text/html"]
643
+ )
644
+
645
+ for k in bundle.keys():
646
+ if k in ("text/html", "text/plain"):
647
+ continue
648
+ else:
649
+ new_b[k] = bundle[k] # type:ignore
650
+ return new_b
651
+
652
+ def _append_info_field(
653
+ self,
654
+ bundle: UnformattedBundle,
655
+ title: str,
656
+ key: str,
657
+ info,
658
+ omit_sections: List[str],
659
+ formatter,
660
+ ):
661
+ """Append an info value to the unformatted mimebundle being constructed by _make_info_unformatted"""
662
+ if title in omit_sections or key in omit_sections:
663
+ return
664
+ field = info[key]
665
+ if field is not None:
666
+ formatted_field = self._mime_format(field, formatter)
667
+ bundle["text/plain"].append((title, formatted_field["text/plain"]))
668
+ bundle["text/html"].append((title, formatted_field["text/html"]))
669
+
670
+ def _make_info_unformatted(
671
+ self, obj, info, formatter, detail_level, omit_sections
672
+ ) -> UnformattedBundle:
673
+ """Assemble the mimebundle as unformatted lists of information"""
674
+ bundle: UnformattedBundle = {
675
+ "text/plain": [],
676
+ "text/html": [],
677
+ }
678
+
679
+ # A convenience function to simplify calls below
680
+ def append_field(
681
+ bundle: UnformattedBundle, title: str, key: str, formatter=None
682
+ ):
683
+ self._append_info_field(
684
+ bundle,
685
+ title=title,
686
+ key=key,
687
+ info=info,
688
+ omit_sections=omit_sections,
689
+ formatter=formatter,
690
+ )
691
+
692
+ def code_formatter(text) -> Bundle:
693
+ return {
694
+ 'text/plain': self.format(text),
695
+ 'text/html': pylight(text)
696
+ }
697
+
698
+ if info["isalias"]:
699
+ append_field(bundle, "Repr", "string_form")
700
+
701
+ elif info['ismagic']:
702
+ if detail_level > 0:
703
+ append_field(bundle, "Source", "source", code_formatter)
704
+ else:
705
+ append_field(bundle, "Docstring", "docstring", formatter)
706
+ append_field(bundle, "File", "file")
707
+
708
+ elif info['isclass'] or is_simple_callable(obj):
709
+ # Functions, methods, classes
710
+ append_field(bundle, "Signature", "definition", code_formatter)
711
+ append_field(bundle, "Init signature", "init_definition", code_formatter)
712
+ append_field(bundle, "Docstring", "docstring", formatter)
713
+ if detail_level > 0 and info["source"]:
714
+ append_field(bundle, "Source", "source", code_formatter)
715
+ else:
716
+ append_field(bundle, "Init docstring", "init_docstring", formatter)
717
+
718
+ append_field(bundle, "File", "file")
719
+ append_field(bundle, "Type", "type_name")
720
+ append_field(bundle, "Subclasses", "subclasses")
721
+
722
+ else:
723
+ # General Python objects
724
+ append_field(bundle, "Signature", "definition", code_formatter)
725
+ append_field(bundle, "Call signature", "call_def", code_formatter)
726
+ append_field(bundle, "Type", "type_name")
727
+ append_field(bundle, "String form", "string_form")
728
+
729
+ # Namespace
730
+ if info["namespace"] != "Interactive":
731
+ append_field(bundle, "Namespace", "namespace")
732
+
733
+ append_field(bundle, "Length", "length")
734
+ append_field(bundle, "File", "file")
735
+
736
+ # Source or docstring, depending on detail level and whether
737
+ # source found.
738
+ if detail_level > 0 and info["source"]:
739
+ append_field(bundle, "Source", "source", code_formatter)
740
+ else:
741
+ append_field(bundle, "Docstring", "docstring", formatter)
742
+
743
+ append_field(bundle, "Class docstring", "class_docstring", formatter)
744
+ append_field(bundle, "Init docstring", "init_docstring", formatter)
745
+ append_field(bundle, "Call docstring", "call_docstring", formatter)
746
+ return bundle
747
+
748
+
749
+ def _get_info(
750
+ self,
751
+ obj: Any,
752
+ oname: str = "",
753
+ formatter=None,
754
+ info: Optional[OInfo] = None,
755
+ detail_level: int = 0,
756
+ omit_sections: Union[List[str], Tuple[()]] = (),
757
+ ) -> Bundle:
758
+ """Retrieve an info dict and format it.
759
+
760
+ Parameters
761
+ ----------
762
+ obj : any
763
+ Object to inspect and return info from
764
+ oname : str (default: ''):
765
+ Name of the variable pointing to `obj`.
766
+ formatter : callable
767
+ info
768
+ already computed information
769
+ detail_level : integer
770
+ Granularity of detail level, if set to 1, give more information.
771
+ omit_sections : list[str]
772
+ Titles or keys to omit from output (can be set, tuple, etc., anything supporting `in`)
773
+ """
774
+
775
+ info_dict = self.info(obj, oname=oname, info=info, detail_level=detail_level)
776
+ omit_sections = list(omit_sections)
777
+
778
+ bundle = self._make_info_unformatted(
779
+ obj,
780
+ info_dict,
781
+ formatter,
782
+ detail_level=detail_level,
783
+ omit_sections=omit_sections,
784
+ )
785
+ if self.mime_hooks:
786
+ hook_data = InspectorHookData(
787
+ obj=obj,
788
+ info=info,
789
+ info_dict=info_dict,
790
+ detail_level=detail_level,
791
+ omit_sections=omit_sections,
792
+ )
793
+ for key, hook in self.mime_hooks.items(): # type:ignore
794
+ required_parameters = [
795
+ parameter
796
+ for parameter in inspect.signature(hook).parameters.values()
797
+ if parameter.default != inspect.Parameter.default
798
+ ]
799
+ if len(required_parameters) == 1:
800
+ res = hook(hook_data)
801
+ else:
802
+ warnings.warn(
803
+ "MIME hook format changed in IPython 8.22; hooks should now accept"
804
+ " a single parameter (InspectorHookData); support for hooks requiring"
805
+ " two-parameters (obj and info) will be removed in a future version",
806
+ DeprecationWarning,
807
+ stacklevel=2,
808
+ )
809
+ res = hook(obj, info)
810
+ if res is not None:
811
+ bundle[key] = res
812
+ return self.format_mime(bundle)
813
+
814
+ def pinfo(
815
+ self,
816
+ obj,
817
+ oname="",
818
+ formatter=None,
819
+ info: Optional[OInfo] = None,
820
+ detail_level=0,
821
+ enable_html_pager=True,
822
+ omit_sections=(),
823
+ ):
824
+ """Show detailed information about an object.
825
+
826
+ Optional arguments:
827
+
828
+ - oname: name of the variable pointing to the object.
829
+
830
+ - formatter: callable (optional)
831
+ A special formatter for docstrings.
832
+
833
+ The formatter is a callable that takes a string as an input
834
+ and returns either a formatted string or a mime type bundle
835
+ in the form of a dictionary.
836
+
837
+ Although the support of custom formatter returning a string
838
+ instead of a mime type bundle is deprecated.
839
+
840
+ - info: a structure with some information fields which may have been
841
+ precomputed already.
842
+
843
+ - detail_level: if set to 1, more information is given.
844
+
845
+ - omit_sections: set of section keys and titles to omit
846
+ """
847
+ assert info is not None
848
+ info_b: Bundle = self._get_info(
849
+ obj, oname, formatter, info, detail_level, omit_sections=omit_sections
850
+ )
851
+ if not enable_html_pager:
852
+ del info_b["text/html"]
853
+ page.page(info_b)
854
+
855
+ def info(self, obj, oname="", info=None, detail_level=0) -> InfoDict:
856
+ """Compute a dict with detailed information about an object.
857
+
858
+ Parameters
859
+ ----------
860
+ obj : any
861
+ An object to find information about
862
+ oname : str (default: '')
863
+ Name of the variable pointing to `obj`.
864
+ info : (default: None)
865
+ A struct (dict like with attr access) with some information fields
866
+ which may have been precomputed already.
867
+ detail_level : int (default:0)
868
+ If set to 1, more information is given.
869
+
870
+ Returns
871
+ -------
872
+ An object info dict with known fields from `info_fields` (see `InfoDict`).
873
+ """
874
+
875
+ if info is None:
876
+ ismagic = False
877
+ isalias = False
878
+ ospace = ''
879
+ else:
880
+ ismagic = info.ismagic
881
+ isalias = info.isalias
882
+ ospace = info.namespace
883
+
884
+ # Get docstring, special-casing aliases:
885
+ att_name = oname.split(".")[-1]
886
+ parents_docs = None
887
+ prelude = ""
888
+ if info and info.parent is not None and hasattr(info.parent, HOOK_NAME):
889
+ parents_docs_dict = getattr(info.parent, HOOK_NAME)
890
+ parents_docs = parents_docs_dict.get(att_name, None)
891
+ out: InfoDict = cast(
892
+ InfoDict,
893
+ {
894
+ **{field: None for field in _info_fields},
895
+ **{
896
+ "name": oname,
897
+ "found": True,
898
+ "isalias": isalias,
899
+ "ismagic": ismagic,
900
+ "subclasses": None,
901
+ },
902
+ },
903
+ )
904
+
905
+ if parents_docs:
906
+ ds = parents_docs
907
+ elif isalias:
908
+ if not callable(obj):
909
+ try:
910
+ ds = "Alias to the system command:\n %s" % obj[1]
911
+ except:
912
+ ds = "Alias: " + str(obj)
913
+ else:
914
+ ds = "Alias to " + str(obj)
915
+ if obj.__doc__:
916
+ ds += "\nDocstring:\n" + obj.__doc__
917
+ else:
918
+ ds_or_None = getdoc(obj)
919
+ if ds_or_None is None:
920
+ ds = '<no docstring>'
921
+ else:
922
+ ds = ds_or_None
923
+
924
+ ds = prelude + ds
925
+
926
+ # store output in a dict, we initialize it here and fill it as we go
927
+
928
+ string_max = 200 # max size of strings to show (snipped if longer)
929
+ shalf = int((string_max - 5) / 2)
930
+
931
+ if ismagic:
932
+ out['type_name'] = 'Magic function'
933
+ elif isalias:
934
+ out['type_name'] = 'System alias'
935
+ else:
936
+ out['type_name'] = type(obj).__name__
937
+
938
+ try:
939
+ bclass = obj.__class__
940
+ out['base_class'] = str(bclass)
941
+ except:
942
+ pass
943
+
944
+ # String form, but snip if too long in ? form (full in ??)
945
+ if detail_level >= self.str_detail_level:
946
+ try:
947
+ ostr = str(obj)
948
+ if not detail_level and len(ostr) > string_max:
949
+ ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
950
+ # TODO: `'string_form'.expandtabs()` seems wrong, but
951
+ # it was (nearly) like this since the first commit ever.
952
+ ostr = ("\n" + " " * len("string_form".expandtabs())).join(
953
+ q.strip() for q in ostr.split("\n")
954
+ )
955
+ out["string_form"] = ostr
956
+ except:
957
+ pass
958
+
959
+ if ospace:
960
+ out['namespace'] = ospace
961
+
962
+ # Length (for strings and lists)
963
+ try:
964
+ out['length'] = str(len(obj))
965
+ except Exception:
966
+ pass
967
+
968
+ # Filename where object was defined
969
+ binary_file = False
970
+ fname = find_file(obj)
971
+ if fname is None:
972
+ # if anything goes wrong, we don't want to show source, so it's as
973
+ # if the file was binary
974
+ binary_file = True
975
+ else:
976
+ if fname.endswith(('.so', '.dll', '.pyd')):
977
+ binary_file = True
978
+ elif fname.endswith('<string>'):
979
+ fname = 'Dynamically generated function. No source code available.'
980
+ out['file'] = compress_user(fname)
981
+
982
+ # Original source code for a callable, class or property.
983
+ if detail_level:
984
+ # Flush the source cache because inspect can return out-of-date
985
+ # source
986
+ linecache.checkcache()
987
+ try:
988
+ if isinstance(obj, property) or not binary_file:
989
+ src = getsource(obj, oname)
990
+ if src is not None:
991
+ src = src.rstrip()
992
+ out['source'] = src
993
+
994
+ except Exception:
995
+ pass
996
+
997
+ # Add docstring only if no source is to be shown (avoid repetitions).
998
+ if ds and not self._source_contains_docstring(out.get('source'), ds):
999
+ out['docstring'] = ds
1000
+
1001
+ # Constructor docstring for classes
1002
+ if inspect.isclass(obj):
1003
+ out['isclass'] = True
1004
+
1005
+ # get the init signature:
1006
+ try:
1007
+ init_def = self._getdef(obj, oname)
1008
+ except AttributeError:
1009
+ init_def = None
1010
+
1011
+ # get the __init__ docstring
1012
+ try:
1013
+ obj_init = obj.__init__
1014
+ except AttributeError:
1015
+ init_ds = None
1016
+ else:
1017
+ if init_def is None:
1018
+ # Get signature from init if top-level sig failed.
1019
+ # Can happen for built-in types (list, etc.).
1020
+ try:
1021
+ init_def = self._getdef(obj_init, oname)
1022
+ except AttributeError:
1023
+ pass
1024
+ init_ds = getdoc(obj_init)
1025
+ # Skip Python's auto-generated docstrings
1026
+ if init_ds == _object_init_docstring:
1027
+ init_ds = None
1028
+
1029
+ if init_def:
1030
+ out['init_definition'] = init_def
1031
+
1032
+ if init_ds:
1033
+ out['init_docstring'] = init_ds
1034
+
1035
+ names = [sub.__name__ for sub in type.__subclasses__(obj)]
1036
+ if len(names) < 10:
1037
+ all_names = ', '.join(names)
1038
+ else:
1039
+ all_names = ', '.join(names[:10]+['...'])
1040
+ out['subclasses'] = all_names
1041
+ # and class docstring for instances:
1042
+ else:
1043
+ # reconstruct the function definition and print it:
1044
+ defln = self._getdef(obj, oname)
1045
+ if defln:
1046
+ out['definition'] = defln
1047
+
1048
+ # First, check whether the instance docstring is identical to the
1049
+ # class one, and print it separately if they don't coincide. In
1050
+ # most cases they will, but it's nice to print all the info for
1051
+ # objects which use instance-customized docstrings.
1052
+ if ds:
1053
+ try:
1054
+ cls = getattr(obj,'__class__')
1055
+ except:
1056
+ class_ds = None
1057
+ else:
1058
+ class_ds = getdoc(cls)
1059
+ # Skip Python's auto-generated docstrings
1060
+ if class_ds in _builtin_type_docstrings:
1061
+ class_ds = None
1062
+ if class_ds and ds != class_ds:
1063
+ out['class_docstring'] = class_ds
1064
+
1065
+ # Next, try to show constructor docstrings
1066
+ try:
1067
+ init_ds = getdoc(obj.__init__)
1068
+ # Skip Python's auto-generated docstrings
1069
+ if init_ds == _object_init_docstring:
1070
+ init_ds = None
1071
+ except AttributeError:
1072
+ init_ds = None
1073
+ if init_ds:
1074
+ out['init_docstring'] = init_ds
1075
+
1076
+ # Call form docstring for callable instances
1077
+ if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
1078
+ call_def = self._getdef(obj.__call__, oname)
1079
+ if call_def and (call_def != out.get('definition')):
1080
+ # it may never be the case that call def and definition differ,
1081
+ # but don't include the same signature twice
1082
+ out['call_def'] = call_def
1083
+ call_ds = getdoc(obj.__call__)
1084
+ # Skip Python's auto-generated docstrings
1085
+ if call_ds == _func_call_docstring:
1086
+ call_ds = None
1087
+ if call_ds:
1088
+ out['call_docstring'] = call_ds
1089
+
1090
+ return out
1091
+
1092
+ @staticmethod
1093
+ def _source_contains_docstring(src, doc):
1094
+ """
1095
+ Check whether the source *src* contains the docstring *doc*.
1096
+
1097
+ This is is helper function to skip displaying the docstring if the
1098
+ source already contains it, avoiding repetition of information.
1099
+ """
1100
+ try:
1101
+ (def_node,) = ast.parse(dedent(src)).body
1102
+ return ast.get_docstring(def_node) == doc # type: ignore[arg-type]
1103
+ except Exception:
1104
+ # The source can become invalid or even non-existent (because it
1105
+ # is re-fetched from the source file) so the above code fail in
1106
+ # arbitrary ways.
1107
+ return False
1108
+
1109
+ def psearch(self,pattern,ns_table,ns_search=[],
1110
+ ignore_case=False,show_all=False, *, list_types=False):
1111
+ """Search namespaces with wildcards for objects.
1112
+
1113
+ Arguments:
1114
+
1115
+ - pattern: string containing shell-like wildcards to use in namespace
1116
+ searches and optionally a type specification to narrow the search to
1117
+ objects of that type.
1118
+
1119
+ - ns_table: dict of name->namespaces for search.
1120
+
1121
+ Optional arguments:
1122
+
1123
+ - ns_search: list of namespace names to include in search.
1124
+
1125
+ - ignore_case(False): make the search case-insensitive.
1126
+
1127
+ - show_all(False): show all names, including those starting with
1128
+ underscores.
1129
+
1130
+ - list_types(False): list all available object types for object matching.
1131
+ """
1132
+ # print('ps pattern:<%r>' % pattern) # dbg
1133
+
1134
+ # defaults
1135
+ type_pattern = 'all'
1136
+ filter = ''
1137
+
1138
+ # list all object types
1139
+ if list_types:
1140
+ page.page('\n'.join(sorted(typestr2type)))
1141
+ return
1142
+
1143
+ cmds = pattern.split()
1144
+ len_cmds = len(cmds)
1145
+ if len_cmds == 1:
1146
+ # Only filter pattern given
1147
+ filter = cmds[0]
1148
+ elif len_cmds == 2:
1149
+ # Both filter and type specified
1150
+ filter,type_pattern = cmds
1151
+ else:
1152
+ raise ValueError('invalid argument string for psearch: <%s>' %
1153
+ pattern)
1154
+
1155
+ # filter search namespaces
1156
+ for name in ns_search:
1157
+ if name not in ns_table:
1158
+ raise ValueError('invalid namespace <%s>. Valid names: %s' %
1159
+ (name,ns_table.keys()))
1160
+
1161
+ # print('type_pattern:',type_pattern) # dbg
1162
+ search_result, namespaces_seen = set(), set()
1163
+ for ns_name in ns_search:
1164
+ ns = ns_table[ns_name]
1165
+ # Normally, locals and globals are the same, so we just check one.
1166
+ if id(ns) in namespaces_seen:
1167
+ continue
1168
+ namespaces_seen.add(id(ns))
1169
+ tmp_res = list_namespace(ns, type_pattern, filter,
1170
+ ignore_case=ignore_case, show_all=show_all)
1171
+ search_result.update(tmp_res)
1172
+
1173
+ page.page('\n'.join(sorted(search_result)))
1174
+
1175
+
1176
+ def _render_signature(obj_signature, obj_name) -> str:
1177
+ """
1178
+ This was mostly taken from inspect.Signature.__str__.
1179
+ Look there for the comments.
1180
+ The only change is to add linebreaks when this gets too long.
1181
+ """
1182
+ result = []
1183
+ pos_only = False
1184
+ kw_only = True
1185
+ for param in obj_signature.parameters.values():
1186
+ if param.kind == inspect.Parameter.POSITIONAL_ONLY:
1187
+ pos_only = True
1188
+ elif pos_only:
1189
+ result.append('/')
1190
+ pos_only = False
1191
+
1192
+ if param.kind == inspect.Parameter.VAR_POSITIONAL:
1193
+ kw_only = False
1194
+ elif param.kind == inspect.Parameter.KEYWORD_ONLY and kw_only:
1195
+ result.append('*')
1196
+ kw_only = False
1197
+
1198
+ result.append(str(param))
1199
+
1200
+ if pos_only:
1201
+ result.append('/')
1202
+
1203
+ # add up name, parameters, braces (2), and commas
1204
+ if len(obj_name) + sum(len(r) + 2 for r in result) > 75:
1205
+ # This doesn’t fit behind “Signature: ” in an inspect window.
1206
+ rendered = '{}(\n{})'.format(obj_name, ''.join(
1207
+ ' {},\n'.format(r) for r in result)
1208
+ )
1209
+ else:
1210
+ rendered = '{}({})'.format(obj_name, ', '.join(result))
1211
+
1212
+ if obj_signature.return_annotation is not inspect._empty:
1213
+ anno = inspect.formatannotation(obj_signature.return_annotation)
1214
+ rendered += ' -> {}'.format(anno)
1215
+
1216
+ return rendered
temp_venv/lib/python3.13/site-packages/IPython/core/page.py ADDED
@@ -0,0 +1,348 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # encoding: utf-8
2
+ """
3
+ Paging capabilities for IPython.core
4
+
5
+ Notes
6
+ -----
7
+
8
+ For now this uses IPython hooks, so it can't be in IPython.utils. If we can get
9
+ rid of that dependency, we could move it there.
10
+ -----
11
+ """
12
+
13
+ # Copyright (c) IPython Development Team.
14
+ # Distributed under the terms of the Modified BSD License.
15
+
16
+
17
+ import os
18
+ import io
19
+ import re
20
+ import sys
21
+ import tempfile
22
+ import subprocess
23
+
24
+ from io import UnsupportedOperation
25
+ from pathlib import Path
26
+
27
+ from IPython import get_ipython
28
+ from IPython.display import display
29
+ from IPython.core.error import TryNext
30
+ from IPython.utils.data import chop
31
+ from IPython.utils.process import system
32
+ from IPython.utils.terminal import get_terminal_size
33
+ from IPython.utils import py3compat
34
+
35
+
36
+ def display_page(strng, start=0, screen_lines=25):
37
+ """Just display, no paging. screen_lines is ignored."""
38
+ if isinstance(strng, dict):
39
+ data = strng
40
+ else:
41
+ if start:
42
+ strng = u'\n'.join(strng.splitlines()[start:])
43
+ data = { 'text/plain': strng }
44
+ display(data, raw=True)
45
+
46
+
47
+ def as_hook(page_func):
48
+ """Wrap a pager func to strip the `self` arg
49
+
50
+ so it can be called as a hook.
51
+ """
52
+ return lambda self, *args, **kwargs: page_func(*args, **kwargs)
53
+
54
+
55
+ esc_re = re.compile(r"(\x1b[^m]+m)")
56
+
57
+ def page_dumb(strng, start=0, screen_lines=25):
58
+ """Very dumb 'pager' in Python, for when nothing else works.
59
+
60
+ Only moves forward, same interface as page(), except for pager_cmd and
61
+ mode.
62
+ """
63
+ if isinstance(strng, dict):
64
+ strng = strng.get('text/plain', '')
65
+ out_ln = strng.splitlines()[start:]
66
+ screens = chop(out_ln,screen_lines-1)
67
+ if len(screens) == 1:
68
+ print(os.linesep.join(screens[0]))
69
+ else:
70
+ last_escape = ""
71
+ for scr in screens[0:-1]:
72
+ hunk = os.linesep.join(scr)
73
+ print(last_escape + hunk)
74
+ if not page_more():
75
+ return
76
+ esc_list = esc_re.findall(hunk)
77
+ if len(esc_list) > 0:
78
+ last_escape = esc_list[-1]
79
+ print(last_escape + os.linesep.join(screens[-1]))
80
+
81
+ def _detect_screen_size(screen_lines_def):
82
+ """Attempt to work out the number of lines on the screen.
83
+
84
+ This is called by page(). It can raise an error (e.g. when run in the
85
+ test suite), so it's separated out so it can easily be called in a try block.
86
+ """
87
+ TERM = os.environ.get('TERM',None)
88
+ if not((TERM=='xterm' or TERM=='xterm-color') and sys.platform != 'sunos5'):
89
+ # curses causes problems on many terminals other than xterm, and
90
+ # some termios calls lock up on Sun OS5.
91
+ return screen_lines_def
92
+
93
+ try:
94
+ import termios
95
+ import curses
96
+ except ImportError:
97
+ return screen_lines_def
98
+
99
+ # There is a bug in curses, where *sometimes* it fails to properly
100
+ # initialize, and then after the endwin() call is made, the
101
+ # terminal is left in an unusable state. Rather than trying to
102
+ # check every time for this (by requesting and comparing termios
103
+ # flags each time), we just save the initial terminal state and
104
+ # unconditionally reset it every time. It's cheaper than making
105
+ # the checks.
106
+ try:
107
+ term_flags = termios.tcgetattr(sys.stdout)
108
+ except termios.error as err:
109
+ # can fail on Linux 2.6, pager_page will catch the TypeError
110
+ raise TypeError('termios error: {0}'.format(err)) from err
111
+
112
+ try:
113
+ scr = curses.initscr()
114
+ except AttributeError:
115
+ # Curses on Solaris may not be complete, so we can't use it there
116
+ return screen_lines_def
117
+
118
+ screen_lines_real,screen_cols = scr.getmaxyx()
119
+ curses.endwin()
120
+
121
+ # Restore terminal state in case endwin() didn't.
122
+ termios.tcsetattr(sys.stdout,termios.TCSANOW,term_flags)
123
+ # Now we have what we needed: the screen size in rows/columns
124
+ return screen_lines_real
125
+ # print('***Screen size:',screen_lines_real,'lines x',
126
+ # screen_cols,'columns.') # dbg
127
+
128
+ def pager_page(strng, start=0, screen_lines=0, pager_cmd=None) -> None:
129
+ """Display a string, piping through a pager after a certain length.
130
+
131
+ strng can be a mime-bundle dict, supplying multiple representations,
132
+ keyed by mime-type.
133
+
134
+ The screen_lines parameter specifies the number of *usable* lines of your
135
+ terminal screen (total lines minus lines you need to reserve to show other
136
+ information).
137
+
138
+ If you set screen_lines to a number <=0, page() will try to auto-determine
139
+ your screen size and will only use up to (screen_size+screen_lines) for
140
+ printing, paging after that. That is, if you want auto-detection but need
141
+ to reserve the bottom 3 lines of the screen, use screen_lines = -3, and for
142
+ auto-detection without any lines reserved simply use screen_lines = 0.
143
+
144
+ If a string won't fit in the allowed lines, it is sent through the
145
+ specified pager command. If none given, look for PAGER in the environment,
146
+ and ultimately default to less.
147
+
148
+ If no system pager works, the string is sent through a 'dumb pager'
149
+ written in python, very simplistic.
150
+ """
151
+
152
+ # for compatibility with mime-bundle form:
153
+ if isinstance(strng, dict):
154
+ strng = strng['text/plain']
155
+
156
+ # Ugly kludge, but calling curses.initscr() flat out crashes in emacs
157
+ TERM = os.environ.get('TERM','dumb')
158
+ if TERM in ['dumb','emacs'] and os.name != 'nt':
159
+ print(strng)
160
+ return
161
+ # chop off the topmost part of the string we don't want to see
162
+ str_lines = strng.splitlines()[start:]
163
+ str_toprint = os.linesep.join(str_lines)
164
+ num_newlines = len(str_lines)
165
+ len_str = len(str_toprint)
166
+
167
+ # Dumb heuristics to guesstimate number of on-screen lines the string
168
+ # takes. Very basic, but good enough for docstrings in reasonable
169
+ # terminals. If someone later feels like refining it, it's not hard.
170
+ numlines = max(num_newlines,int(len_str/80)+1)
171
+
172
+ screen_lines_def = get_terminal_size()[1]
173
+
174
+ # auto-determine screen size
175
+ if screen_lines <= 0:
176
+ try:
177
+ screen_lines += _detect_screen_size(screen_lines_def)
178
+ except (TypeError, UnsupportedOperation):
179
+ print(str_toprint)
180
+ return
181
+
182
+ # print('numlines',numlines,'screenlines',screen_lines) # dbg
183
+ if numlines <= screen_lines :
184
+ # print('*** normal print') # dbg
185
+ print(str_toprint)
186
+ else:
187
+ # Try to open pager and default to internal one if that fails.
188
+ # All failure modes are tagged as 'retval=1', to match the return
189
+ # value of a failed system command. If any intermediate attempt
190
+ # sets retval to 1, at the end we resort to our own page_dumb() pager.
191
+ pager_cmd = get_pager_cmd(pager_cmd)
192
+ pager_cmd += ' ' + get_pager_start(pager_cmd,start)
193
+ if os.name == 'nt':
194
+ if pager_cmd.startswith('type'):
195
+ # The default WinXP 'type' command is failing on complex strings.
196
+ retval = 1
197
+ else:
198
+ fd, tmpname = tempfile.mkstemp('.txt')
199
+ tmppath = Path(tmpname)
200
+ try:
201
+ os.close(fd)
202
+ with tmppath.open("wt", encoding="utf-8") as tmpfile:
203
+ tmpfile.write(strng)
204
+ cmd = "%s < %s" % (pager_cmd, tmppath)
205
+ # tmpfile needs to be closed for windows
206
+ if os.system(cmd):
207
+ retval = 1
208
+ else:
209
+ retval = None
210
+ finally:
211
+ Path.unlink(tmppath)
212
+ else:
213
+ try:
214
+ retval = None
215
+ # Emulate os.popen, but redirect stderr
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
+ )
225
+ try:
226
+ pager_encoding = pager.encoding or sys.stdout.encoding
227
+ pager.write(strng)
228
+ finally:
229
+ retval = pager.close()
230
+ except IOError as msg: # broken pipe when user quits
231
+ if msg.args == (32, 'Broken pipe'):
232
+ retval = None
233
+ else:
234
+ retval = 1
235
+ except OSError:
236
+ # Other strange problems, sometimes seen in Win2k/cygwin
237
+ retval = 1
238
+ if retval is not None:
239
+ page_dumb(strng,screen_lines=screen_lines)
240
+
241
+
242
+ def page(data, start: int = 0, screen_lines: int = 0, pager_cmd=None):
243
+ """Display content in a pager, piping through a pager after a certain length.
244
+
245
+ data can be a mime-bundle dict, supplying multiple representations,
246
+ keyed by mime-type, or text.
247
+
248
+ Pager is dispatched via the `show_in_pager` IPython hook.
249
+ If no hook is registered, `pager_page` will be used.
250
+ """
251
+ # Some routines may auto-compute start offsets incorrectly and pass a
252
+ # negative value. Offset to 0 for robustness.
253
+ start = max(0, start)
254
+
255
+ # first, try the hook
256
+ ip = get_ipython()
257
+ if ip:
258
+ try:
259
+ ip.hooks.show_in_pager(data, start=start, screen_lines=screen_lines)
260
+ return
261
+ except TryNext:
262
+ pass
263
+
264
+ # fallback on default pager
265
+ return pager_page(data, start, screen_lines, pager_cmd)
266
+
267
+
268
+ def page_file(fname, start=0, pager_cmd=None):
269
+ """Page a file, using an optional pager command and starting line.
270
+ """
271
+
272
+ pager_cmd = get_pager_cmd(pager_cmd)
273
+ pager_cmd += ' ' + get_pager_start(pager_cmd,start)
274
+
275
+ try:
276
+ if os.environ['TERM'] in ['emacs','dumb']:
277
+ raise EnvironmentError
278
+ system(pager_cmd + ' ' + fname)
279
+ except:
280
+ try:
281
+ if start > 0:
282
+ start -= 1
283
+ page(open(fname, encoding="utf-8").read(), start)
284
+ except:
285
+ print('Unable to show file',repr(fname))
286
+
287
+
288
+ def get_pager_cmd(pager_cmd=None):
289
+ """Return a pager command.
290
+
291
+ Makes some attempts at finding an OS-correct one.
292
+ """
293
+ if os.name == 'posix':
294
+ default_pager_cmd = 'less -R' # -R for color control sequences
295
+ elif os.name in ['nt','dos']:
296
+ default_pager_cmd = 'type'
297
+
298
+ if pager_cmd is None:
299
+ try:
300
+ pager_cmd = os.environ['PAGER']
301
+ except:
302
+ pager_cmd = default_pager_cmd
303
+
304
+ if pager_cmd == 'less' and '-r' not in os.environ.get('LESS', '').lower():
305
+ pager_cmd += ' -R'
306
+
307
+ return pager_cmd
308
+
309
+
310
+ def get_pager_start(pager, start):
311
+ """Return the string for paging files with an offset.
312
+
313
+ This is the '+N' argument which less and more (under Unix) accept.
314
+ """
315
+
316
+ if pager in ['less','more']:
317
+ if start:
318
+ start_string = '+' + str(start)
319
+ else:
320
+ start_string = ''
321
+ else:
322
+ start_string = ''
323
+ return start_string
324
+
325
+
326
+ # (X)emacs on win32 doesn't like to be bypassed with msvcrt.getch()
327
+ if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
328
+ import msvcrt
329
+ def page_more():
330
+ """ Smart pausing between pages
331
+
332
+ @return: True if need print more lines, False if quit
333
+ """
334
+ sys.stdout.write('---Return to continue, q to quit--- ')
335
+ ans = msvcrt.getwch()
336
+ if ans in ("q", "Q"):
337
+ result = False
338
+ else:
339
+ result = True
340
+ sys.stdout.write("\b"*37 + " "*37 + "\b"*37)
341
+ return result
342
+ else:
343
+ def page_more():
344
+ ans = py3compat.input('---Return to continue, q to quit--- ')
345
+ if ans.lower().startswith('q'):
346
+ return False
347
+ else:
348
+ return True
temp_venv/lib/python3.13/site-packages/IPython/core/payload.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """Payload system for IPython.
3
+
4
+ Authors:
5
+
6
+ * Fernando Perez
7
+ * Brian Granger
8
+ """
9
+
10
+ #-----------------------------------------------------------------------------
11
+ # Copyright (C) 2008-2011 The IPython Development Team
12
+ #
13
+ # Distributed under the terms of the BSD License. The full license is in
14
+ # the file COPYING, distributed as part of this software.
15
+ #-----------------------------------------------------------------------------
16
+
17
+ #-----------------------------------------------------------------------------
18
+ # Imports
19
+ #-----------------------------------------------------------------------------
20
+
21
+ from traitlets.config.configurable import Configurable
22
+ from traitlets import List
23
+
24
+ #-----------------------------------------------------------------------------
25
+ # Main payload class
26
+ #-----------------------------------------------------------------------------
27
+
28
+ class PayloadManager(Configurable):
29
+ _payload: List = List([])
30
+
31
+ def write_payload(self, data, single=True):
32
+ """Include or update the specified `data` payload in the PayloadManager.
33
+
34
+ If a previous payload with the same source exists and `single` is True,
35
+ it will be overwritten with the new one.
36
+ """
37
+
38
+ if not isinstance(data, dict):
39
+ raise TypeError('Each payload write must be a dict, got: %r' % data)
40
+
41
+ if single and 'source' in data:
42
+ source = data['source']
43
+ for i, pl in enumerate(self._payload):
44
+ if 'source' in pl and pl['source'] == source:
45
+ self._payload[i] = data
46
+ return
47
+
48
+ self._payload.append(data)
49
+
50
+ def read_payload(self):
51
+ return self._payload
52
+
53
+ def clear_payload(self):
54
+ self._payload = []
temp_venv/lib/python3.13/site-packages/IPython/core/payloadpage.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """A payload based version of page."""
2
+
3
+ # Copyright (c) IPython Development Team.
4
+ # Distributed under the terms of the Modified BSD License.
5
+
6
+ import warnings
7
+ from IPython.core.getipython import get_ipython
8
+
9
+ # see https://github.com/ipython/ipykernel/issues/1304
10
+ # this should be moved to ipykernel and removed in the long run.
11
+
12
+
13
+ def page(strng, start=0, screen_lines=0, pager_cmd=None):
14
+ """Print a string, piping through a pager.
15
+
16
+ This version ignores the screen_lines and pager_cmd arguments and uses
17
+ IPython's payload system instead.
18
+
19
+ Parameters
20
+ ----------
21
+ strng : str or mime-dict
22
+ Text to page, or a mime-type keyed dict of already formatted data.
23
+ start : int
24
+ Starting line at which to place the display.
25
+ """
26
+
27
+ # Some routines may auto-compute start offsets incorrectly and pass a
28
+ # negative value. Offset to 0 for robustness.
29
+ start = max(0, start)
30
+ shell = get_ipython()
31
+
32
+ if isinstance(strng, dict):
33
+ data = strng
34
+ else:
35
+ data = {"text/plain": strng}
36
+ payload = dict(
37
+ source="page",
38
+ data=data,
39
+ start=start,
40
+ )
41
+ shell.payload_manager.write_payload(payload)
temp_venv/lib/python3.13/site-packages/IPython/core/profileapp.py ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # encoding: utf-8
2
+ """
3
+ An application for managing IPython profiles.
4
+
5
+ To be invoked as the `ipython profile` subcommand.
6
+
7
+ Authors:
8
+
9
+ * Min RK
10
+
11
+ """
12
+
13
+ #-----------------------------------------------------------------------------
14
+ # Copyright (C) 2008 The IPython Development Team
15
+ #
16
+ # Distributed under the terms of the BSD License. The full license is in
17
+ # the file COPYING, distributed as part of this software.
18
+ #-----------------------------------------------------------------------------
19
+
20
+ #-----------------------------------------------------------------------------
21
+ # Imports
22
+ #-----------------------------------------------------------------------------
23
+
24
+ import os
25
+
26
+ from traitlets.config.application import Application
27
+ from IPython.core.application import (
28
+ BaseIPythonApplication, base_flags
29
+ )
30
+ from IPython.core.profiledir import ProfileDir
31
+ from IPython.utils.importstring import import_item
32
+ from IPython.paths import get_ipython_dir, get_ipython_package_dir
33
+ from traitlets import Unicode, Bool, Dict, observe
34
+
35
+ #-----------------------------------------------------------------------------
36
+ # Constants
37
+ #-----------------------------------------------------------------------------
38
+
39
+ create_help = """Create an IPython profile by name
40
+
41
+ Create an ipython profile directory by its name or
42
+ profile directory path. Profile directories contain
43
+ configuration, log and security related files and are named
44
+ using the convention 'profile_<name>'. By default they are
45
+ located in your ipython directory. Once created, you will
46
+ can edit the configuration files in the profile
47
+ directory to configure IPython. Most users will create a
48
+ profile directory by name,
49
+ `ipython profile create myprofile`, which will put the directory
50
+ in `<ipython_dir>/profile_myprofile`.
51
+ """
52
+ list_help = """List available IPython profiles
53
+
54
+ List all available profiles, by profile location, that can
55
+ be found in the current working directly or in the ipython
56
+ directory. Profile directories are named using the convention
57
+ 'profile_<profile>'.
58
+ """
59
+ profile_help = """Manage IPython profiles
60
+
61
+ Profile directories contain
62
+ configuration, log and security related files and are named
63
+ using the convention 'profile_<name>'. By default they are
64
+ located in your ipython directory. You can create profiles
65
+ with `ipython profile create <name>`, or see the profiles you
66
+ already have with `ipython profile list`
67
+
68
+ To get started configuring IPython, simply do:
69
+
70
+ $> ipython profile create
71
+
72
+ and IPython will create the default profile in <ipython_dir>/profile_default,
73
+ where you can edit ipython_config.py to start configuring IPython.
74
+
75
+ """
76
+
77
+ _list_examples = "ipython profile list # list all profiles"
78
+
79
+ _create_examples = """
80
+ ipython profile create foo # create profile foo w/ default config files
81
+ ipython profile create foo --reset # restage default config files over current
82
+ ipython profile create foo --parallel # also stage parallel config files
83
+ """
84
+
85
+ _main_examples = """
86
+ ipython profile create -h # show the help string for the create subcommand
87
+ ipython profile list -h # show the help string for the list subcommand
88
+
89
+ ipython locate profile foo # print the path to the directory for profile 'foo'
90
+ """
91
+
92
+ #-----------------------------------------------------------------------------
93
+ # Profile Application Class (for `ipython profile` subcommand)
94
+ #-----------------------------------------------------------------------------
95
+
96
+
97
+ def list_profiles_in(path):
98
+ """list profiles in a given root directory"""
99
+ profiles = []
100
+
101
+ # for python 3.6+ rewrite to: with os.scandir(path) as dirlist:
102
+ files = os.scandir(path)
103
+ for f in files:
104
+ if f.is_dir() and f.name.startswith('profile_'):
105
+ profiles.append(f.name.split('_', 1)[-1])
106
+ return profiles
107
+
108
+
109
+ def list_bundled_profiles():
110
+ """list profiles that are bundled with IPython."""
111
+ path = os.path.join(get_ipython_package_dir(), u'core', u'profile')
112
+ profiles = []
113
+
114
+ # for python 3.6+ rewrite to: with os.scandir(path) as dirlist:
115
+ files = os.scandir(path)
116
+ for profile in files:
117
+ if profile.is_dir() and profile.name != "__pycache__":
118
+ profiles.append(profile.name)
119
+ return profiles
120
+
121
+
122
+ class ProfileLocate(BaseIPythonApplication):
123
+ description = """print the path to an IPython profile dir"""
124
+
125
+ def parse_command_line(self, argv=None):
126
+ super(ProfileLocate, self).parse_command_line(argv)
127
+ if self.extra_args:
128
+ self.profile = self.extra_args[0]
129
+
130
+ def start(self):
131
+ print(self.profile_dir.location)
132
+
133
+
134
+ class ProfileList(Application):
135
+ name = u'ipython-profile'
136
+ description = list_help
137
+ examples = _list_examples
138
+
139
+ aliases = Dict({
140
+ 'ipython-dir' : 'ProfileList.ipython_dir',
141
+ 'log-level' : 'Application.log_level',
142
+ })
143
+ flags = Dict(dict(
144
+ debug = ({'Application' : {'log_level' : 0}},
145
+ "Set Application.log_level to 0, maximizing log output."
146
+ )
147
+ ))
148
+
149
+ ipython_dir = Unicode(get_ipython_dir(),
150
+ help="""
151
+ The name of the IPython directory. This directory is used for logging
152
+ configuration (through profiles), history storage, etc. The default
153
+ is usually $HOME/.ipython. This options can also be specified through
154
+ the environment variable IPYTHONDIR.
155
+ """
156
+ ).tag(config=True)
157
+
158
+
159
+ def _print_profiles(self, profiles):
160
+ """print list of profiles, indented."""
161
+ for profile in profiles:
162
+ print(' %s' % profile)
163
+
164
+ def list_profile_dirs(self):
165
+ profiles = list_bundled_profiles()
166
+ if profiles:
167
+ print()
168
+ print("Available profiles in IPython:")
169
+ self._print_profiles(profiles)
170
+ print()
171
+ print(" The first request for a bundled profile will copy it")
172
+ print(" into your IPython directory (%s)," % self.ipython_dir)
173
+ print(" where you can customize it.")
174
+
175
+ profiles = list_profiles_in(self.ipython_dir)
176
+ if profiles:
177
+ print()
178
+ print("Available profiles in %s:" % self.ipython_dir)
179
+ self._print_profiles(profiles)
180
+
181
+ profiles = list_profiles_in(os.getcwd())
182
+ if profiles:
183
+ print()
184
+ print(
185
+ "Profiles from CWD have been removed for security reason, see CVE-2022-21699:"
186
+ )
187
+
188
+ print()
189
+ print("To use any of the above profiles, start IPython with:")
190
+ print(" ipython --profile=<name>")
191
+ print()
192
+
193
+ def start(self):
194
+ self.list_profile_dirs()
195
+
196
+
197
+ create_flags = {}
198
+ create_flags.update(base_flags)
199
+ # don't include '--init' flag, which implies running profile create in other apps
200
+ create_flags.pop('init')
201
+ create_flags['reset'] = ({'ProfileCreate': {'overwrite' : True}},
202
+ "reset config files in this profile to the defaults.")
203
+ create_flags['parallel'] = ({'ProfileCreate': {'parallel' : True}},
204
+ "Include the config files for parallel "
205
+ "computing apps (ipengine, ipcontroller, etc.)")
206
+
207
+
208
+ class ProfileCreate(BaseIPythonApplication):
209
+ name = u'ipython-profile'
210
+ description = create_help
211
+ examples = _create_examples
212
+ auto_create = Bool(True).tag(config=True)
213
+ def _log_format_default(self):
214
+ return "[%(name)s] %(message)s"
215
+
216
+ def _copy_config_files_default(self):
217
+ return True
218
+
219
+ parallel = Bool(False,
220
+ help="whether to include parallel computing config files"
221
+ ).tag(config=True)
222
+
223
+ @observe('parallel')
224
+ def _parallel_changed(self, change):
225
+ parallel_files = [ 'ipcontroller_config.py',
226
+ 'ipengine_config.py',
227
+ 'ipcluster_config.py'
228
+ ]
229
+ if change['new']:
230
+ for cf in parallel_files:
231
+ self.config_files.append(cf)
232
+ else:
233
+ for cf in parallel_files:
234
+ if cf in self.config_files:
235
+ self.config_files.remove(cf)
236
+
237
+ def parse_command_line(self, argv):
238
+ super(ProfileCreate, self).parse_command_line(argv)
239
+ # accept positional arg as profile name
240
+ if self.extra_args:
241
+ self.profile = self.extra_args[0]
242
+
243
+ flags = Dict(create_flags)
244
+
245
+ classes = [ProfileDir]
246
+
247
+ def _import_app(self, app_path):
248
+ """import an app class"""
249
+ app = None
250
+ name = app_path.rsplit('.', 1)[-1]
251
+ try:
252
+ app = import_item(app_path)
253
+ except ImportError:
254
+ self.log.info("Couldn't import %s, config file will be excluded", name)
255
+ except Exception:
256
+ self.log.warning('Unexpected error importing %s', name, exc_info=True)
257
+ return app
258
+
259
+ def init_config_files(self):
260
+ super(ProfileCreate, self).init_config_files()
261
+ # use local imports, since these classes may import from here
262
+ from IPython.terminal.ipapp import TerminalIPythonApp
263
+ apps = [TerminalIPythonApp]
264
+ for app_path in (
265
+ 'ipykernel.kernelapp.IPKernelApp',
266
+ ):
267
+ app = self._import_app(app_path)
268
+ if app is not None:
269
+ apps.append(app)
270
+ if self.parallel:
271
+ from ipyparallel.apps.ipcontrollerapp import IPControllerApp
272
+ from ipyparallel.apps.ipengineapp import IPEngineApp
273
+ from ipyparallel.apps.ipclusterapp import IPClusterStart
274
+ apps.extend([
275
+ IPControllerApp,
276
+ IPEngineApp,
277
+ IPClusterStart,
278
+ ])
279
+ for App in apps:
280
+ app = App()
281
+ app.config.update(self.config)
282
+ app.log = self.log
283
+ app.overwrite = self.overwrite
284
+ app.copy_config_files=True
285
+ app.ipython_dir=self.ipython_dir
286
+ app.profile_dir=self.profile_dir
287
+ app.init_config_files()
288
+
289
+ def stage_default_config_file(self):
290
+ pass
291
+
292
+
293
+ class ProfileApp(Application):
294
+ name = u'ipython profile'
295
+ description = profile_help
296
+ examples = _main_examples
297
+
298
+ subcommands = Dict(dict(
299
+ create = (ProfileCreate, ProfileCreate.description.splitlines()[0]),
300
+ list = (ProfileList, ProfileList.description.splitlines()[0]),
301
+ locate = (ProfileLocate, ProfileLocate.description.splitlines()[0]),
302
+ ))
303
+
304
+ def start(self):
305
+ if self.subapp is None:
306
+ print(
307
+ "No subcommand specified. Must specify one of: "
308
+ + ", ".join(map(repr, self.subcommands))
309
+ + ".\n"
310
+ )
311
+ self.print_description()
312
+ self.print_subcommands()
313
+ self.exit(1)
314
+ else:
315
+ return self.subapp.start()
temp_venv/lib/python3.13/site-packages/IPython/core/pylabtools.py ADDED
@@ -0,0 +1,515 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """Pylab (matplotlib) support utilities."""
3
+
4
+ # Copyright (c) IPython Development Team.
5
+ # Distributed under the terms of the Modified BSD License.
6
+
7
+ from io import BytesIO
8
+ from binascii import b2a_base64
9
+ from functools import partial
10
+ import warnings
11
+
12
+ from IPython.core.display import _pngxy
13
+ from IPython.utils.decorators import flag_calls
14
+
15
+
16
+ # Matplotlib backend resolution functionality moved from IPython to Matplotlib
17
+ # in IPython 8.24 and Matplotlib 3.9.0. Need to keep `backends` and `backend2gui`
18
+ # here for earlier Matplotlib and for external backend libraries such as
19
+ # mplcairo that might rely upon it.
20
+ _deprecated_backends = {
21
+ "tk": "TkAgg",
22
+ "gtk": "GTKAgg",
23
+ "gtk3": "GTK3Agg",
24
+ "gtk4": "GTK4Agg",
25
+ "wx": "WXAgg",
26
+ "qt4": "Qt4Agg",
27
+ "qt5": "Qt5Agg",
28
+ "qt6": "QtAgg",
29
+ "qt": "QtAgg",
30
+ "osx": "MacOSX",
31
+ "nbagg": "nbAgg",
32
+ "webagg": "WebAgg",
33
+ "notebook": "nbAgg",
34
+ "agg": "agg",
35
+ "svg": "svg",
36
+ "pdf": "pdf",
37
+ "ps": "ps",
38
+ "inline": "module://matplotlib_inline.backend_inline",
39
+ "ipympl": "module://ipympl.backend_nbagg",
40
+ "widget": "module://ipympl.backend_nbagg",
41
+ }
42
+
43
+ # We also need a reverse backends2guis mapping that will properly choose which
44
+ # GUI support to activate based on the desired matplotlib backend. For the
45
+ # most part it's just a reverse of the above dict, but we also need to add a
46
+ # few others that map to the same GUI manually:
47
+ _deprecated_backend2gui = dict(
48
+ zip(_deprecated_backends.values(), _deprecated_backends.keys())
49
+ )
50
+ # In the reverse mapping, there are a few extra valid matplotlib backends that
51
+ # map to the same GUI support
52
+ _deprecated_backend2gui["GTK"] = _deprecated_backend2gui["GTKCairo"] = "gtk"
53
+ _deprecated_backend2gui["GTK3Cairo"] = "gtk3"
54
+ _deprecated_backend2gui["GTK4Cairo"] = "gtk4"
55
+ _deprecated_backend2gui["WX"] = "wx"
56
+ _deprecated_backend2gui["CocoaAgg"] = "osx"
57
+ # There needs to be a hysteresis here as the new QtAgg Matplotlib backend
58
+ # supports either Qt5 or Qt6 and the IPython qt event loop support Qt4, Qt5,
59
+ # and Qt6.
60
+ _deprecated_backend2gui["QtAgg"] = "qt"
61
+ _deprecated_backend2gui["Qt4Agg"] = "qt4"
62
+ _deprecated_backend2gui["Qt5Agg"] = "qt5"
63
+
64
+ # And some backends that don't need GUI integration
65
+ del _deprecated_backend2gui["nbAgg"]
66
+ del _deprecated_backend2gui["agg"]
67
+ del _deprecated_backend2gui["svg"]
68
+ del _deprecated_backend2gui["pdf"]
69
+ del _deprecated_backend2gui["ps"]
70
+ del _deprecated_backend2gui["module://matplotlib_inline.backend_inline"]
71
+ del _deprecated_backend2gui["module://ipympl.backend_nbagg"]
72
+
73
+
74
+ # Deprecated attributes backends and backend2gui mostly following PEP 562.
75
+ def __getattr__(name):
76
+ if name in ("backends", "backend2gui"):
77
+ warnings.warn(
78
+ f"{name} is deprecated since IPython 8.24, backends are managed "
79
+ "in matplotlib and can be externally registered.",
80
+ DeprecationWarning,
81
+ )
82
+ return globals()[f"_deprecated_{name}"]
83
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
84
+
85
+
86
+ #-----------------------------------------------------------------------------
87
+ # Matplotlib utilities
88
+ #-----------------------------------------------------------------------------
89
+
90
+
91
+ def getfigs(*fig_nums):
92
+ """Get a list of matplotlib figures by figure numbers.
93
+
94
+ If no arguments are given, all available figures are returned. If the
95
+ argument list contains references to invalid figures, a warning is printed
96
+ but the function continues pasting further figures.
97
+
98
+ Parameters
99
+ ----------
100
+ figs : tuple
101
+ A tuple of ints giving the figure numbers of the figures to return.
102
+ """
103
+ from matplotlib._pylab_helpers import Gcf
104
+ if not fig_nums:
105
+ fig_managers = Gcf.get_all_fig_managers()
106
+ return [fm.canvas.figure for fm in fig_managers]
107
+ else:
108
+ figs = []
109
+ for num in fig_nums:
110
+ f = Gcf.figs.get(num)
111
+ if f is None:
112
+ print('Warning: figure %s not available.' % num)
113
+ else:
114
+ figs.append(f.canvas.figure)
115
+ return figs
116
+
117
+
118
+ def figsize(sizex, sizey):
119
+ """Set the default figure size to be [sizex, sizey].
120
+
121
+ This is just an easy to remember, convenience wrapper that sets::
122
+
123
+ matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
124
+ """
125
+ import matplotlib
126
+ matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
127
+
128
+
129
+ def print_figure(fig, fmt="png", bbox_inches="tight", base64=False, **kwargs):
130
+ """Print a figure to an image, and return the resulting file data
131
+
132
+ Returned data will be bytes unless ``fmt='svg'``,
133
+ in which case it will be unicode.
134
+
135
+ Any keyword args are passed to fig.canvas.print_figure,
136
+ such as ``quality`` or ``bbox_inches``.
137
+
138
+ If `base64` is True, return base64-encoded str instead of raw bytes
139
+ for binary-encoded image formats
140
+
141
+ .. versionadded:: 7.29
142
+ base64 argument
143
+ """
144
+ # When there's an empty figure, we shouldn't return anything, otherwise we
145
+ # get big blank areas in the qt console.
146
+ if not fig.axes and not fig.lines:
147
+ return
148
+
149
+ dpi = fig.dpi
150
+ if fmt == 'retina':
151
+ dpi = dpi * 2
152
+ fmt = 'png'
153
+
154
+ # build keyword args
155
+ kw = {
156
+ "format":fmt,
157
+ "facecolor":fig.get_facecolor(),
158
+ "edgecolor":fig.get_edgecolor(),
159
+ "dpi":dpi,
160
+ "bbox_inches":bbox_inches,
161
+ }
162
+ # **kwargs get higher priority
163
+ kw.update(kwargs)
164
+
165
+ bytes_io = BytesIO()
166
+ if fig.canvas is None:
167
+ from matplotlib.backend_bases import FigureCanvasBase
168
+ FigureCanvasBase(fig)
169
+
170
+ fig.canvas.print_figure(bytes_io, **kw)
171
+ data = bytes_io.getvalue()
172
+ if fmt == 'svg':
173
+ data = data.decode('utf-8')
174
+ elif base64:
175
+ data = b2a_base64(data, newline=False).decode("ascii")
176
+ return data
177
+
178
+ def retina_figure(fig, base64=False, **kwargs):
179
+ """format a figure as a pixel-doubled (retina) PNG
180
+
181
+ If `base64` is True, return base64-encoded str instead of raw bytes
182
+ for binary-encoded image formats
183
+
184
+ .. versionadded:: 7.29
185
+ base64 argument
186
+ """
187
+ pngdata = print_figure(fig, fmt="retina", base64=False, **kwargs)
188
+ # Make sure that retina_figure acts just like print_figure and returns
189
+ # None when the figure is empty.
190
+ if pngdata is None:
191
+ return
192
+ w, h = _pngxy(pngdata)
193
+ metadata = {"width": w//2, "height":h//2}
194
+ if base64:
195
+ pngdata = b2a_base64(pngdata, newline=False).decode("ascii")
196
+ return pngdata, metadata
197
+
198
+
199
+ # We need a little factory function here to create the closure where
200
+ # safe_execfile can live.
201
+ def mpl_runner(safe_execfile):
202
+ """Factory to return a matplotlib-enabled runner for %run.
203
+
204
+ Parameters
205
+ ----------
206
+ safe_execfile : function
207
+ This must be a function with the same interface as the
208
+ :meth:`safe_execfile` method of IPython.
209
+
210
+ Returns
211
+ -------
212
+ A function suitable for use as the ``runner`` argument of the %run magic
213
+ function.
214
+ """
215
+
216
+ def mpl_execfile(fname,*where,**kw):
217
+ """matplotlib-aware wrapper around safe_execfile.
218
+
219
+ Its interface is identical to that of the :func:`execfile` builtin.
220
+
221
+ This is ultimately a call to execfile(), but wrapped in safeties to
222
+ properly handle interactive rendering."""
223
+
224
+ import matplotlib
225
+ import matplotlib.pyplot as plt
226
+
227
+ # print('*** Matplotlib runner ***') # dbg
228
+ # turn off rendering until end of script
229
+ with matplotlib.rc_context({"interactive": False}):
230
+ safe_execfile(fname, *where, **kw)
231
+
232
+ if matplotlib.is_interactive():
233
+ plt.show()
234
+
235
+ # make rendering call now, if the user tried to do it
236
+ if plt.draw_if_interactive.called:
237
+ plt.draw()
238
+ plt.draw_if_interactive.called = False
239
+
240
+ # re-draw everything that is stale
241
+ try:
242
+ da = plt.draw_all
243
+ except AttributeError:
244
+ pass
245
+ else:
246
+ da()
247
+
248
+ return mpl_execfile
249
+
250
+
251
+ def _reshow_nbagg_figure(fig):
252
+ """reshow an nbagg figure"""
253
+ try:
254
+ reshow = fig.canvas.manager.reshow
255
+ except AttributeError as e:
256
+ raise NotImplementedError() from e
257
+ else:
258
+ reshow()
259
+
260
+
261
+ def select_figure_formats(shell, formats, **kwargs):
262
+ """Select figure formats for the inline backend.
263
+
264
+ Parameters
265
+ ----------
266
+ shell : InteractiveShell
267
+ The main IPython instance.
268
+ formats : str or set
269
+ One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
270
+ **kwargs : any
271
+ Extra keyword arguments to be passed to fig.canvas.print_figure.
272
+ """
273
+ import matplotlib
274
+ from matplotlib.figure import Figure
275
+
276
+ svg_formatter = shell.display_formatter.formatters['image/svg+xml']
277
+ png_formatter = shell.display_formatter.formatters['image/png']
278
+ jpg_formatter = shell.display_formatter.formatters['image/jpeg']
279
+ pdf_formatter = shell.display_formatter.formatters['application/pdf']
280
+
281
+ if isinstance(formats, str):
282
+ formats = {formats}
283
+ # cast in case of list / tuple
284
+ formats = set(formats)
285
+
286
+ [ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ]
287
+ mplbackend = matplotlib.get_backend().lower()
288
+ if mplbackend in ("nbagg", "ipympl", "widget", "module://ipympl.backend_nbagg"):
289
+ formatter = shell.display_formatter.ipython_display_formatter
290
+ formatter.for_type(Figure, _reshow_nbagg_figure)
291
+
292
+ supported = {'png', 'png2x', 'retina', 'jpg', 'jpeg', 'svg', 'pdf'}
293
+ bad = formats.difference(supported)
294
+ if bad:
295
+ bs = "%s" % ','.join([repr(f) for f in bad])
296
+ gs = "%s" % ','.join([repr(f) for f in supported])
297
+ raise ValueError("supported formats are: %s not %s" % (gs, bs))
298
+
299
+ if "png" in formats:
300
+ png_formatter.for_type(
301
+ Figure, partial(print_figure, fmt="png", base64=True, **kwargs)
302
+ )
303
+ if "retina" in formats or "png2x" in formats:
304
+ png_formatter.for_type(Figure, partial(retina_figure, base64=True, **kwargs))
305
+ if "jpg" in formats or "jpeg" in formats:
306
+ jpg_formatter.for_type(
307
+ Figure, partial(print_figure, fmt="jpg", base64=True, **kwargs)
308
+ )
309
+ if "svg" in formats:
310
+ svg_formatter.for_type(Figure, partial(print_figure, fmt="svg", **kwargs))
311
+ if "pdf" in formats:
312
+ pdf_formatter.for_type(
313
+ Figure, partial(print_figure, fmt="pdf", base64=True, **kwargs)
314
+ )
315
+
316
+ #-----------------------------------------------------------------------------
317
+ # Code for initializing matplotlib and importing pylab
318
+ #-----------------------------------------------------------------------------
319
+
320
+
321
+ def find_gui_and_backend(gui=None, gui_select=None):
322
+ """Given a gui string return the gui and mpl backend.
323
+
324
+ Parameters
325
+ ----------
326
+ gui : str
327
+ Can be one of ('tk','gtk','wx','qt','qt4','inline','agg').
328
+ gui_select : str
329
+ Can be one of ('tk','gtk','wx','qt','qt4','inline').
330
+ This is any gui already selected by the shell.
331
+
332
+ Returns
333
+ -------
334
+ A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
335
+ 'WXAgg','Qt4Agg','module://matplotlib_inline.backend_inline','agg').
336
+ """
337
+
338
+ import matplotlib
339
+
340
+ if _matplotlib_manages_backends():
341
+ backend_registry = matplotlib.backends.registry.backend_registry
342
+
343
+ # gui argument may be a gui event loop or may be a backend name.
344
+ if gui in ("auto", None):
345
+ backend = matplotlib.rcParamsOrig["backend"]
346
+ backend, gui = backend_registry.resolve_backend(backend)
347
+ else:
348
+ gui = _convert_gui_to_matplotlib(gui)
349
+ backend, gui = backend_registry.resolve_gui_or_backend(gui)
350
+
351
+ gui = _convert_gui_from_matplotlib(gui)
352
+ return gui, backend
353
+
354
+ # Fallback to previous behaviour (Matplotlib < 3.9)
355
+ mpl_version_info = getattr(matplotlib, "__version_info__", (0, 0))
356
+ has_unified_qt_backend = mpl_version_info >= (3, 5)
357
+
358
+ from IPython.core.pylabtools import backends
359
+
360
+ backends_ = dict(backends)
361
+ if not has_unified_qt_backend:
362
+ backends_["qt"] = "qt5agg"
363
+
364
+ if gui and gui != 'auto':
365
+ # select backend based on requested gui
366
+ backend = backends_[gui]
367
+ if gui == 'agg':
368
+ gui = None
369
+ else:
370
+ # We need to read the backend from the original data structure, *not*
371
+ # from mpl.rcParams, since a prior invocation of %matplotlib may have
372
+ # overwritten that.
373
+ # WARNING: this assumes matplotlib 1.1 or newer!!
374
+ backend = matplotlib.rcParamsOrig['backend']
375
+ # In this case, we need to find what the appropriate gui selection call
376
+ # should be for IPython, so we can activate inputhook accordingly
377
+ from IPython.core.pylabtools import backend2gui
378
+ gui = backend2gui.get(backend, None)
379
+
380
+ # If we have already had a gui active, we need it and inline are the
381
+ # ones allowed.
382
+ if gui_select and gui != gui_select:
383
+ gui = gui_select
384
+ backend = backends_[gui]
385
+
386
+ # Matplotlib before _matplotlib_manages_backends() can return "inline" for
387
+ # no gui event loop rather than the None that IPython >= 8.24.0 expects.
388
+ if gui == "inline":
389
+ gui = None
390
+
391
+ return gui, backend
392
+
393
+
394
+ def activate_matplotlib(backend):
395
+ """Activate the given backend and set interactive to True."""
396
+
397
+ import matplotlib
398
+ matplotlib.interactive(True)
399
+
400
+ # Matplotlib had a bug where even switch_backend could not force
401
+ # the rcParam to update. This needs to be set *before* the module
402
+ # magic of switch_backend().
403
+ matplotlib.rcParams['backend'] = backend
404
+
405
+ # Due to circular imports, pyplot may be only partially initialised
406
+ # when this function runs.
407
+ # So avoid needing matplotlib attribute-lookup to access pyplot.
408
+ from matplotlib import pyplot as plt
409
+
410
+ plt.switch_backend(backend)
411
+
412
+ plt.show._needmain = False
413
+ # We need to detect at runtime whether show() is called by the user.
414
+ # For this, we wrap it into a decorator which adds a 'called' flag.
415
+ plt.draw_if_interactive = flag_calls(plt.draw_if_interactive)
416
+
417
+
418
+ def import_pylab(user_ns, import_all=True):
419
+ """Populate the namespace with pylab-related values.
420
+
421
+ Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
422
+
423
+ Also imports a few names from IPython (figsize, display, getfigs)
424
+
425
+ """
426
+
427
+ # Import numpy as np/pyplot as plt are conventions we're trying to
428
+ # somewhat standardize on. Making them available to users by default
429
+ # will greatly help this.
430
+ s = ("import numpy\n"
431
+ "import matplotlib\n"
432
+ "from matplotlib import pylab, mlab, pyplot\n"
433
+ "np = numpy\n"
434
+ "plt = pyplot\n"
435
+ )
436
+ exec(s, user_ns)
437
+
438
+ if import_all:
439
+ s = ("from matplotlib.pylab import *\n"
440
+ "from numpy import *\n")
441
+ exec(s, user_ns)
442
+
443
+ # IPython symbols to add
444
+ user_ns['figsize'] = figsize
445
+ from IPython.display import display
446
+ # Add display and getfigs to the user's namespace
447
+ user_ns['display'] = display
448
+ user_ns['getfigs'] = getfigs
449
+
450
+
451
+ # Determine if Matplotlib manages backends only if needed, and cache result.
452
+ # Do not read this directly, instead use _matplotlib_manages_backends().
453
+ _matplotlib_manages_backends_value: bool | None = None
454
+
455
+
456
+ def _matplotlib_manages_backends() -> bool:
457
+ """Return True if Matplotlib manages backends, False otherwise.
458
+
459
+ If it returns True, the caller can be sure that
460
+ matplotlib.backends.registry.backend_registry is available along with
461
+ member functions resolve_gui_or_backend, resolve_backend, list_all, and
462
+ list_gui_frameworks.
463
+
464
+ This function can be removed as it will always return True when Python
465
+ 3.12, the latest version supported by Matplotlib < 3.9, reaches
466
+ end-of-life in late 2028.
467
+ """
468
+ global _matplotlib_manages_backends_value
469
+ if _matplotlib_manages_backends_value is None:
470
+ try:
471
+ from matplotlib.backends.registry import backend_registry
472
+
473
+ _matplotlib_manages_backends_value = hasattr(
474
+ backend_registry, "resolve_gui_or_backend"
475
+ )
476
+ except ImportError:
477
+ _matplotlib_manages_backends_value = False
478
+
479
+ return _matplotlib_manages_backends_value
480
+
481
+
482
+ def _list_matplotlib_backends_and_gui_loops() -> list[str]:
483
+ """Return list of all Matplotlib backends and GUI event loops.
484
+
485
+ This is the list returned by
486
+ %matplotlib --list
487
+ """
488
+ if _matplotlib_manages_backends():
489
+ from matplotlib.backends.registry import backend_registry
490
+
491
+ ret = backend_registry.list_all() + [
492
+ _convert_gui_from_matplotlib(gui)
493
+ for gui in backend_registry.list_gui_frameworks()
494
+ ]
495
+ else:
496
+ from IPython.core import pylabtools
497
+
498
+ ret = list(pylabtools.backends.keys())
499
+
500
+ return sorted(["auto"] + ret)
501
+
502
+
503
+ # Matplotlib and IPython do not always use the same gui framework name.
504
+ # Always use the appropriate one of these conversion functions when passing a
505
+ # gui framework name to/from Matplotlib.
506
+ def _convert_gui_to_matplotlib(gui: str | None) -> str | None:
507
+ if gui and gui.lower() == "osx":
508
+ return "macosx"
509
+ return gui
510
+
511
+
512
+ def _convert_gui_from_matplotlib(gui: str | None) -> str | None:
513
+ if gui and gui.lower() == "macosx":
514
+ return "osx"
515
+ return gui
temp_venv/lib/python3.13/site-packages/IPython/core/shellapp.py ADDED
@@ -0,0 +1,496 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # encoding: utf-8
2
+ """
3
+ A mixin for :class:`~IPython.core.application.Application` classes that
4
+ launch InteractiveShell instances, load extensions, etc.
5
+ """
6
+
7
+ # Copyright (c) IPython Development Team.
8
+ # Distributed under the terms of the Modified BSD License.
9
+
10
+ import glob
11
+ from itertools import chain
12
+ import os
13
+ import sys
14
+ import typing as t
15
+
16
+ from traitlets.config.application import boolean_flag
17
+ from traitlets.config.configurable import Configurable
18
+ from traitlets.config.loader import Config
19
+ from IPython.core.application import SYSTEM_CONFIG_DIRS, ENV_CONFIG_DIRS
20
+ from IPython.utils.contexts import preserve_keys
21
+ from IPython.utils.path import filefind
22
+ from traitlets import (
23
+ Unicode,
24
+ Instance,
25
+ List,
26
+ Bool,
27
+ CaselessStrEnum,
28
+ observe,
29
+ DottedObjectName,
30
+ Undefined,
31
+ )
32
+ from IPython.terminal import pt_inputhooks
33
+
34
+ # -----------------------------------------------------------------------------
35
+ # Aliases and Flags
36
+ # -----------------------------------------------------------------------------
37
+
38
+ gui_keys = tuple(sorted(pt_inputhooks.backends) + sorted(pt_inputhooks.aliases))
39
+
40
+ shell_flags = {}
41
+
42
+ addflag = lambda *args: shell_flags.update(boolean_flag(*args))
43
+ addflag(
44
+ "autoindent",
45
+ "InteractiveShell.autoindent",
46
+ "Turn on autoindenting.",
47
+ "Turn off autoindenting.",
48
+ )
49
+ addflag(
50
+ "automagic",
51
+ "InteractiveShell.automagic",
52
+ """Turn on the auto calling of magic commands. Type %%magic at the
53
+ IPython prompt for more information.""",
54
+ 'Turn off the auto calling of magic commands.'
55
+ )
56
+ addflag('pdb', 'InteractiveShell.pdb',
57
+ "Enable auto calling the pdb debugger after every exception.",
58
+ "Disable auto calling the pdb debugger after every exception."
59
+ )
60
+ addflag('pprint', 'PlainTextFormatter.pprint',
61
+ "Enable auto pretty printing of results.",
62
+ "Disable auto pretty printing of results."
63
+ )
64
+ addflag('color-info', 'InteractiveShell.color_info',
65
+ """IPython can display information about objects via a set of functions,
66
+ and optionally can use colors for this, syntax highlighting
67
+ source code and various other elements. This is on by default, but can cause
68
+ problems with some pagers. If you see such problems, you can disable the
69
+ colours.""",
70
+ "Disable using colors for info related things."
71
+ )
72
+ addflag('ignore-cwd', 'InteractiveShellApp.ignore_cwd',
73
+ "Exclude the current working directory from sys.path",
74
+ "Include the current working directory in sys.path",
75
+ )
76
+ nosep_config = Config()
77
+ nosep_config.InteractiveShell.separate_in = ''
78
+ nosep_config.InteractiveShell.separate_out = ''
79
+ nosep_config.InteractiveShell.separate_out2 = ''
80
+
81
+ shell_flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.")
82
+ shell_flags['pylab'] = (
83
+ {'InteractiveShellApp' : {'pylab' : 'auto'}},
84
+ """Pre-load matplotlib and numpy for interactive use with
85
+ the default matplotlib backend. The exact options available
86
+ depend on what Matplotlib provides at runtime.""",
87
+ )
88
+ shell_flags['matplotlib'] = (
89
+ {'InteractiveShellApp' : {'matplotlib' : 'auto'}},
90
+ """Configure matplotlib for interactive use with
91
+ the default matplotlib backend. The exact options available
92
+ depend on what Matplotlib provides at runtime.""",
93
+ )
94
+
95
+ # it's possible we don't want short aliases for *all* of these:
96
+ shell_aliases = dict(
97
+ autocall="InteractiveShell.autocall",
98
+ colors="InteractiveShell.colors",
99
+ theme="InteractiveShell.colors",
100
+ logfile="InteractiveShell.logfile",
101
+ logappend="InteractiveShell.logappend",
102
+ c="InteractiveShellApp.code_to_run",
103
+ m="InteractiveShellApp.module_to_run",
104
+ ext="InteractiveShellApp.extra_extensions",
105
+ gui='InteractiveShellApp.gui',
106
+ pylab='InteractiveShellApp.pylab',
107
+ matplotlib='InteractiveShellApp.matplotlib',
108
+ )
109
+ shell_aliases['cache-size'] = 'InteractiveShell.cache_size'
110
+
111
+
112
+ # -----------------------------------------------------------------------------
113
+ # Traitlets
114
+ # -----------------------------------------------------------------------------
115
+
116
+
117
+ class MatplotlibBackendCaselessStrEnum(CaselessStrEnum):
118
+ """An enum of Matplotlib backend strings where the case should be ignored.
119
+
120
+ Prior to Matplotlib 3.9.0 the list of valid backends is hardcoded in
121
+ pylabtools.backends. After that, Matplotlib manages backends.
122
+
123
+ The list of valid backends is determined when it is first needed to avoid
124
+ wasting unnecessary initialisation time.
125
+ """
126
+
127
+ def __init__(
128
+ self: CaselessStrEnum[t.Any],
129
+ default_value: t.Any = Undefined,
130
+ **kwargs: t.Any,
131
+ ) -> None:
132
+ super().__init__(None, default_value=default_value, **kwargs)
133
+
134
+ def __getattribute__(self, name):
135
+ if name == "values" and object.__getattribute__(self, name) is None:
136
+ from IPython.core.pylabtools import _list_matplotlib_backends_and_gui_loops
137
+
138
+ self.values = _list_matplotlib_backends_and_gui_loops()
139
+ return object.__getattribute__(self, name)
140
+
141
+
142
+ #-----------------------------------------------------------------------------
143
+ # Main classes and functions
144
+ #-----------------------------------------------------------------------------
145
+
146
+ class InteractiveShellApp(Configurable):
147
+ """A Mixin for applications that start InteractiveShell instances.
148
+
149
+ Provides configurables for loading extensions and executing files
150
+ as part of configuring a Shell environment.
151
+
152
+ The following methods should be called by the :meth:`initialize` method
153
+ of the subclass:
154
+
155
+ - :meth:`init_path`
156
+ - :meth:`init_shell` (to be implemented by the subclass)
157
+ - :meth:`init_gui_pylab`
158
+ - :meth:`init_extensions`
159
+ - :meth:`init_code`
160
+ """
161
+ extensions = List(Unicode(),
162
+ help="A list of dotted module names of IPython extensions to load."
163
+ ).tag(config=True)
164
+
165
+ extra_extensions = List(
166
+ DottedObjectName(),
167
+ help="""
168
+ Dotted module name(s) of one or more IPython extensions to load.
169
+
170
+ For specifying extra extensions to load on the command-line.
171
+
172
+ .. versionadded:: 7.10
173
+ """,
174
+ ).tag(config=True)
175
+
176
+ reraise_ipython_extension_failures = Bool(False,
177
+ help="Reraise exceptions encountered loading IPython extensions?",
178
+ ).tag(config=True)
179
+
180
+ # Extensions that are always loaded (not configurable)
181
+ default_extensions = List(Unicode(), [u'storemagic']).tag(config=False)
182
+
183
+ hide_initial_ns = Bool(True,
184
+ help="""Should variables loaded at startup (by startup files, exec_lines, etc.)
185
+ be hidden from tools like %who?"""
186
+ ).tag(config=True)
187
+
188
+ exec_files = List(Unicode(),
189
+ help="""List of files to run at IPython startup."""
190
+ ).tag(config=True)
191
+ exec_PYTHONSTARTUP = Bool(True,
192
+ help="""Run the file referenced by the PYTHONSTARTUP environment
193
+ variable at IPython startup."""
194
+ ).tag(config=True)
195
+ file_to_run = Unicode('',
196
+ help="""A file to be run""").tag(config=True)
197
+
198
+ exec_lines = List(Unicode(),
199
+ help="""lines of code to run at IPython startup."""
200
+ ).tag(config=True)
201
+ code_to_run = Unicode("", help="Execute the given command string.").tag(config=True)
202
+ module_to_run = Unicode("", help="Run the module as a script.").tag(config=True)
203
+ gui = CaselessStrEnum(
204
+ gui_keys,
205
+ allow_none=True,
206
+ help="Enable GUI event loop integration with any of {0}.".format(gui_keys),
207
+ ).tag(config=True)
208
+ matplotlib = MatplotlibBackendCaselessStrEnum(
209
+ allow_none=True,
210
+ help="""Configure matplotlib for interactive use with
211
+ the default matplotlib backend. The exact options available
212
+ depend on what Matplotlib provides at runtime.""",
213
+ ).tag(config=True)
214
+ pylab = MatplotlibBackendCaselessStrEnum(
215
+ allow_none=True,
216
+ help="""Pre-load matplotlib and numpy for interactive use,
217
+ selecting a particular matplotlib backend and loop integration.
218
+ The exact options available depend on what Matplotlib provides at runtime.
219
+ """,
220
+ ).tag(config=True)
221
+ pylab_import_all = Bool(
222
+ True,
223
+ help="""If true, IPython will populate the user namespace with numpy, pylab, etc.
224
+ and an ``import *`` is done from numpy and pylab, when using pylab mode.
225
+
226
+ When False, pylab mode should not import any names into the user namespace.
227
+ """,
228
+ ).tag(config=True)
229
+ ignore_cwd = Bool(
230
+ False,
231
+ help="""If True, IPython will not add the current working directory to sys.path.
232
+ When False, the current working directory is added to sys.path, allowing imports
233
+ of modules defined in the current directory."""
234
+ ).tag(config=True)
235
+ shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
236
+ allow_none=True)
237
+ # whether interact-loop should start
238
+ interact = Bool(True)
239
+
240
+ user_ns = Instance(dict, args=None, allow_none=True)
241
+ @observe('user_ns')
242
+ def _user_ns_changed(self, change):
243
+ if self.shell is not None:
244
+ self.shell.user_ns = change['new']
245
+ self.shell.init_user_ns()
246
+
247
+ def init_path(self):
248
+ """Add current working directory, '', to sys.path
249
+
250
+ Unlike Python's default, we insert before the first `site-packages`
251
+ or `dist-packages` directory,
252
+ so that it is after the standard library.
253
+
254
+ .. versionchanged:: 7.2
255
+ Try to insert after the standard library, instead of first.
256
+ .. versionchanged:: 8.0
257
+ Allow optionally not including the current directory in sys.path
258
+ """
259
+ if '' in sys.path or self.ignore_cwd:
260
+ return
261
+ for idx, path in enumerate(sys.path):
262
+ parent, last_part = os.path.split(path)
263
+ if last_part in {'site-packages', 'dist-packages'}:
264
+ break
265
+ else:
266
+ # no site-packages or dist-packages found (?!)
267
+ # back to original behavior of inserting at the front
268
+ idx = 0
269
+ sys.path.insert(idx, '')
270
+
271
+ def init_shell(self):
272
+ raise NotImplementedError("Override in subclasses")
273
+
274
+ def init_gui_pylab(self):
275
+ """Enable GUI event loop integration, taking pylab into account."""
276
+ enable = False
277
+ shell = self.shell
278
+ if self.pylab:
279
+ enable = lambda key: shell.enable_pylab(key, import_all=self.pylab_import_all)
280
+ key = self.pylab
281
+ elif self.matplotlib:
282
+ enable = shell.enable_matplotlib
283
+ key = self.matplotlib
284
+ elif self.gui:
285
+ enable = shell.enable_gui
286
+ key = self.gui
287
+
288
+ if not enable:
289
+ return
290
+
291
+ try:
292
+ r = enable(key)
293
+ except ImportError:
294
+ self.log.warning("Eventloop or matplotlib integration failed. Is matplotlib installed?")
295
+ self.shell.showtraceback()
296
+ return
297
+ except Exception:
298
+ self.log.warning("GUI event loop or pylab initialization failed")
299
+ self.shell.showtraceback()
300
+ return
301
+
302
+ if isinstance(r, tuple):
303
+ gui, backend = r[:2]
304
+ self.log.info("Enabling GUI event loop integration, "
305
+ "eventloop=%s, matplotlib=%s", gui, backend)
306
+ if key == "auto":
307
+ print("Using matplotlib backend: %s" % backend)
308
+ else:
309
+ gui = r
310
+ self.log.info("Enabling GUI event loop integration, "
311
+ "eventloop=%s", gui)
312
+
313
+ def init_extensions(self):
314
+ """Load all IPython extensions in IPythonApp.extensions.
315
+
316
+ This uses the :meth:`ExtensionManager.load_extensions` to load all
317
+ the extensions listed in ``self.extensions``.
318
+ """
319
+ try:
320
+ self.log.debug("Loading IPython extensions...")
321
+ extensions = (
322
+ self.default_extensions + self.extensions + self.extra_extensions
323
+ )
324
+ for ext in extensions:
325
+ try:
326
+ self.log.info("Loading IPython extension: %s", ext)
327
+ self.shell.extension_manager.load_extension(ext)
328
+ except:
329
+ if self.reraise_ipython_extension_failures:
330
+ raise
331
+ msg = ("Error in loading extension: {ext}\n"
332
+ "Check your config files in {location}".format(
333
+ ext=ext,
334
+ location=self.profile_dir.location
335
+ ))
336
+ self.log.warning(msg, exc_info=True)
337
+ except:
338
+ if self.reraise_ipython_extension_failures:
339
+ raise
340
+ self.log.warning("Unknown error in loading extensions:", exc_info=True)
341
+
342
+ def init_code(self):
343
+ """run the pre-flight code, specified via exec_lines"""
344
+ self._run_startup_files()
345
+ self._run_exec_lines()
346
+ self._run_exec_files()
347
+
348
+ # Hide variables defined here from %who etc.
349
+ if self.hide_initial_ns:
350
+ self.shell.user_ns_hidden.update(self.shell.user_ns)
351
+
352
+ # command-line execution (ipython -i script.py, ipython -m module)
353
+ # should *not* be excluded from %whos
354
+ self._run_cmd_line_code()
355
+ self._run_module()
356
+
357
+ # flush output, so itwon't be attached to the first cell
358
+ sys.stdout.flush()
359
+ sys.stderr.flush()
360
+ self.shell._sys_modules_keys = set(sys.modules.keys())
361
+
362
+ def _run_exec_lines(self):
363
+ """Run lines of code in IPythonApp.exec_lines in the user's namespace."""
364
+ if not self.exec_lines:
365
+ return
366
+ try:
367
+ self.log.debug("Running code from IPythonApp.exec_lines...")
368
+ for line in self.exec_lines:
369
+ try:
370
+ self.log.info("Running code in user namespace: %s" %
371
+ line)
372
+ self.shell.run_cell(line, store_history=False)
373
+ except:
374
+ self.log.warning("Error in executing line in user "
375
+ "namespace: %s" % line)
376
+ self.shell.showtraceback()
377
+ except:
378
+ self.log.warning("Unknown error in handling IPythonApp.exec_lines:")
379
+ self.shell.showtraceback()
380
+
381
+ def _exec_file(self, fname, shell_futures=False):
382
+ try:
383
+ full_filename = filefind(fname, [u'.', self.ipython_dir])
384
+ except IOError:
385
+ self.log.warning("File not found: %r"%fname)
386
+ return
387
+ # Make sure that the running script gets a proper sys.argv as if it
388
+ # were run from a system shell.
389
+ save_argv = sys.argv
390
+ sys.argv = [full_filename] + self.extra_args[1:]
391
+ try:
392
+ if os.path.isfile(full_filename):
393
+ self.log.info("Running file in user namespace: %s" %
394
+ full_filename)
395
+ # Ensure that __file__ is always defined to match Python
396
+ # behavior.
397
+ with preserve_keys(self.shell.user_ns, '__file__'):
398
+ self.shell.user_ns['__file__'] = fname
399
+ if full_filename.endswith('.ipy') or full_filename.endswith('.ipynb'):
400
+ self.shell.safe_execfile_ipy(full_filename,
401
+ shell_futures=shell_futures)
402
+ else:
403
+ # default to python, even without extension
404
+ self.shell.safe_execfile(full_filename,
405
+ self.shell.user_ns,
406
+ shell_futures=shell_futures,
407
+ raise_exceptions=True)
408
+ finally:
409
+ sys.argv = save_argv
410
+
411
+ def _run_startup_files(self):
412
+ """Run files from profile startup directory"""
413
+ startup_dirs = [self.profile_dir.startup_dir] + [
414
+ os.path.join(p, 'startup') for p in chain(ENV_CONFIG_DIRS, SYSTEM_CONFIG_DIRS)
415
+ ]
416
+ startup_files = []
417
+
418
+ if self.exec_PYTHONSTARTUP and os.environ.get('PYTHONSTARTUP', False) and \
419
+ not (self.file_to_run or self.code_to_run or self.module_to_run):
420
+ python_startup = os.environ['PYTHONSTARTUP']
421
+ self.log.debug("Running PYTHONSTARTUP file %s...", python_startup)
422
+ try:
423
+ self._exec_file(python_startup)
424
+ except:
425
+ self.log.warning("Unknown error in handling PYTHONSTARTUP file %s:", python_startup)
426
+ self.shell.showtraceback()
427
+ for startup_dir in startup_dirs[::-1]:
428
+ startup_files += glob.glob(os.path.join(startup_dir, '*.py'))
429
+ startup_files += glob.glob(os.path.join(startup_dir, '*.ipy'))
430
+ if not startup_files:
431
+ return
432
+
433
+ self.log.debug("Running startup files from %s...", startup_dir)
434
+ try:
435
+ for fname in sorted(startup_files):
436
+ self._exec_file(fname)
437
+ except:
438
+ self.log.warning("Unknown error in handling startup files:")
439
+ self.shell.showtraceback()
440
+
441
+ def _run_exec_files(self):
442
+ """Run files from IPythonApp.exec_files"""
443
+ if not self.exec_files:
444
+ return
445
+
446
+ self.log.debug("Running files in IPythonApp.exec_files...")
447
+ try:
448
+ for fname in self.exec_files:
449
+ self._exec_file(fname)
450
+ except:
451
+ self.log.warning("Unknown error in handling IPythonApp.exec_files:")
452
+ self.shell.showtraceback()
453
+
454
+ def _run_cmd_line_code(self):
455
+ """Run code or file specified at the command-line"""
456
+ if self.code_to_run:
457
+ line = self.code_to_run
458
+ try:
459
+ self.log.info("Running code given at command line (c=): %s" %
460
+ line)
461
+ self.shell.run_cell(line, store_history=False)
462
+ except:
463
+ self.log.warning("Error in executing line in user namespace: %s" %
464
+ line)
465
+ self.shell.showtraceback()
466
+ if not self.interact:
467
+ self.exit(1)
468
+
469
+ # Like Python itself, ignore the second if the first of these is present
470
+ elif self.file_to_run:
471
+ fname = self.file_to_run
472
+ if os.path.isdir(fname):
473
+ fname = os.path.join(fname, "__main__.py")
474
+ if not os.path.exists(fname):
475
+ self.log.warning("File '%s' doesn't exist", fname)
476
+ if not self.interact:
477
+ self.exit(2)
478
+ try:
479
+ self._exec_file(fname, shell_futures=True)
480
+ except:
481
+ self.shell.showtraceback(tb_offset=4)
482
+ if not self.interact:
483
+ self.exit(1)
484
+
485
+ def _run_module(self):
486
+ """Run module specified at the command-line."""
487
+ if self.module_to_run:
488
+ # Make sure that the module gets a proper sys.argv as if it were
489
+ # run using `python -m`.
490
+ save_argv = sys.argv
491
+ sys.argv = [sys.executable] + self.extra_args
492
+ try:
493
+ self.shell.safe_run_module(self.module_to_run,
494
+ self.shell.user_ns)
495
+ finally:
496
+ sys.argv = save_argv
temp_venv/lib/python3.13/site-packages/IPython/core/splitinput.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # encoding: utf-8
2
+ """
3
+ Simple utility for splitting user input. This is used by both inputsplitter and
4
+ prefilter.
5
+
6
+ Authors:
7
+
8
+ * Brian Granger
9
+ * Fernando Perez
10
+ """
11
+
12
+ #-----------------------------------------------------------------------------
13
+ # Copyright (C) 2008-2011 The IPython Development Team
14
+ #
15
+ # Distributed under the terms of the BSD License. The full license is in
16
+ # the file COPYING, distributed as part of this software.
17
+ #-----------------------------------------------------------------------------
18
+
19
+ #-----------------------------------------------------------------------------
20
+ # Imports
21
+ #-----------------------------------------------------------------------------
22
+
23
+ import re
24
+ import sys
25
+
26
+ from IPython.utils import py3compat
27
+ from IPython.utils.encoding import get_stream_enc
28
+ from IPython.core.oinspect import OInfo
29
+
30
+ #-----------------------------------------------------------------------------
31
+ # Main function
32
+ #-----------------------------------------------------------------------------
33
+
34
+ # RegExp for splitting line contents into pre-char//first word-method//rest.
35
+ # For clarity, each group in on one line.
36
+
37
+ # WARNING: update the regexp if the escapes in interactiveshell are changed, as
38
+ # they are hardwired in.
39
+
40
+ # Although it's not solely driven by the regex, note that:
41
+ # ,;/% only trigger if they are the first character on the line
42
+ # ! and !! trigger if they are first char(s) *or* follow an indent
43
+ # ? triggers as first or last char.
44
+
45
+ line_split = re.compile(r"""
46
+ ^(\s*) # any leading space
47
+ ([,;/%]|!!?|\?\??)? # escape character or characters
48
+ \s*(%{0,2}[\w\.\*]*) # function/method, possibly with leading %
49
+ # to correctly treat things like '?%magic'
50
+ (.*?$|$) # rest of line
51
+ """, re.VERBOSE)
52
+
53
+
54
+ def split_user_input(line, pattern=None):
55
+ """Split user input into initial whitespace, escape character, function part
56
+ and the rest.
57
+ """
58
+ # We need to ensure that the rest of this routine deals only with unicode
59
+ encoding = get_stream_enc(sys.stdin, 'utf-8')
60
+ line = py3compat.cast_unicode(line, encoding)
61
+
62
+ if pattern is None:
63
+ pattern = line_split
64
+ match = pattern.match(line)
65
+ if not match:
66
+ # print("match failed for line '%s'" % line)
67
+ try:
68
+ ifun, the_rest = line.split(None,1)
69
+ except ValueError:
70
+ # print("split failed for line '%s'" % line)
71
+ ifun, the_rest = line, u''
72
+ pre = re.match(r'^(\s*)(.*)',line).groups()[0]
73
+ esc = ""
74
+ else:
75
+ pre, esc, ifun, the_rest = match.groups()
76
+
77
+ # print('line:<%s>' % line) # dbg
78
+ # print('pre <%s> ifun <%s> rest <%s>' % (pre,ifun.strip(),the_rest)) # dbg
79
+ return pre, esc or "", ifun.strip(), the_rest
80
+
81
+
82
+ class LineInfo:
83
+ """A single line of input and associated info.
84
+
85
+ Includes the following as properties:
86
+
87
+ line
88
+ The original, raw line
89
+
90
+ continue_prompt
91
+ Is this line a continuation in a sequence of multiline input?
92
+
93
+ pre
94
+ Any leading whitespace.
95
+
96
+ esc
97
+ The escape character(s) in pre or the empty string if there isn't one.
98
+ Note that '!!' and '??' are possible values for esc. Otherwise it will
99
+ always be a single character.
100
+
101
+ ifun
102
+ The 'function part', which is basically the maximal initial sequence
103
+ of valid python identifiers and the '.' character. This is what is
104
+ checked for alias and magic transformations, used for auto-calling,
105
+ etc. In contrast to Python identifiers, it may start with "%" and contain
106
+ "*".
107
+
108
+ the_rest
109
+ Everything else on the line.
110
+
111
+ raw_the_rest
112
+ the_rest without whitespace stripped.
113
+ """
114
+ def __init__(self, line, continue_prompt=False):
115
+ self.line = line
116
+ self.continue_prompt = continue_prompt
117
+ self.pre, self.esc, self.ifun, self.raw_the_rest = split_user_input(line)
118
+ self.the_rest = self.raw_the_rest.lstrip()
119
+
120
+ self.pre_char = self.pre.strip()
121
+ if self.pre_char:
122
+ self.pre_whitespace = '' # No whitespace allowed before esc chars
123
+ else:
124
+ self.pre_whitespace = self.pre
125
+
126
+ def ofind(self, ip) -> OInfo:
127
+ """Do a full, attribute-walking lookup of the ifun in the various
128
+ namespaces for the given IPython InteractiveShell instance.
129
+
130
+ Return a dict with keys: {found, obj, ospace, ismagic}
131
+
132
+ Note: can cause state changes because of calling getattr, but should
133
+ only be run if autocall is on and if the line hasn't matched any
134
+ other, less dangerous handlers.
135
+
136
+ Does cache the results of the call, so can be called multiple times
137
+ without worrying about *further* damaging state.
138
+ """
139
+ return ip._ofind(self.ifun)
140
+
141
+ def __str__(self):
142
+ return "LineInfo [%s|%s|%s|%s]" %(self.pre, self.esc, self.ifun, self.the_rest)
143
+
144
+ def __repr__(self):
145
+ return "<" + str(self) + ">"
temp_venv/lib/python3.13/site-packages/IPython/core/tbtools.py ADDED
@@ -0,0 +1,554 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import functools
2
+ import inspect
3
+ import pydoc
4
+ import sys
5
+ import types
6
+ import warnings
7
+ from types import TracebackType
8
+ from typing import Any, Callable, Optional, Tuple
9
+
10
+ import stack_data
11
+ from pygments.token import Token
12
+
13
+ from IPython import get_ipython
14
+ from IPython.core import debugger
15
+ from IPython.utils import path as util_path
16
+ from IPython.utils import py3compat
17
+ from IPython.utils.PyColorize import Theme, TokenStream, theme_table
18
+
19
+ _sentinel = object()
20
+ INDENT_SIZE = 8
21
+
22
+
23
+ @functools.lru_cache
24
+ def count_lines_in_py_file(filename: str) -> int:
25
+ """
26
+ Given a filename, returns the number of lines in the file
27
+ if it ends with the extension ".py". Otherwise, returns 0.
28
+ """
29
+ if not filename.endswith(".py"):
30
+ return 0
31
+ else:
32
+ try:
33
+ with open(filename, "r") as file:
34
+ s = sum(1 for line in file)
35
+ except UnicodeError:
36
+ return 0
37
+ return s
38
+
39
+
40
+ def get_line_number_of_frame(frame: types.FrameType) -> int:
41
+ """
42
+ Given a frame object, returns the total number of lines in the file
43
+ containing the frame's code object, or the number of lines in the
44
+ frame's source code if the file is not available.
45
+
46
+ Parameters
47
+ ----------
48
+ frame : FrameType
49
+ The frame object whose line number is to be determined.
50
+
51
+ Returns
52
+ -------
53
+ int
54
+ The total number of lines in the file containing the frame's
55
+ code object, or the number of lines in the frame's source code
56
+ if the file is not available.
57
+ """
58
+ filename = frame.f_code.co_filename
59
+ if filename is None:
60
+ print("No file....")
61
+ lines, first = inspect.getsourcelines(frame)
62
+ return first + len(lines)
63
+ return count_lines_in_py_file(filename)
64
+
65
+
66
+ def _safe_string(value: Any, what: Any, func: Any = str) -> str:
67
+ # Copied from cpython/Lib/traceback.py
68
+ try:
69
+ return func(value)
70
+ except:
71
+ return f"<{what} {func.__name__}() failed>"
72
+
73
+
74
+ def _format_traceback_lines(
75
+ lines: list[stack_data.Line],
76
+ theme: Theme,
77
+ has_colors: bool,
78
+ lvals_toks: list[TokenStream],
79
+ ) -> TokenStream:
80
+ """
81
+ Format tracebacks lines with pointing arrow, leading numbers,
82
+ this assumes the stack have been extracted using stackdata.
83
+
84
+
85
+ Parameters
86
+ ----------
87
+ lines : list[Line]
88
+ """
89
+ numbers_width = INDENT_SIZE - 1
90
+ tokens: TokenStream = []
91
+
92
+ for stack_line in lines:
93
+ if stack_line is stack_data.LINE_GAP:
94
+ toks = [(Token.LinenoEm, " (...)")]
95
+ tokens.extend(toks)
96
+ continue
97
+
98
+ lineno = stack_line.lineno
99
+ line = stack_line.render(pygmented=has_colors).rstrip("\n") + "\n"
100
+ if stack_line.is_current:
101
+ # This is the line with the error
102
+ pad = numbers_width - len(str(lineno))
103
+ toks = [
104
+ (Token.LinenoEm, theme.make_arrow(pad)),
105
+ (Token.LinenoEm, str(lineno)),
106
+ (Token, " "),
107
+ (Token, line),
108
+ ]
109
+ else:
110
+ num = "%*s" % (numbers_width, lineno)
111
+ toks = [
112
+ (Token.LinenoEm, str(num)),
113
+ (Token, " "),
114
+ (Token, line),
115
+ ]
116
+
117
+ tokens.extend(toks)
118
+ if lvals_toks and stack_line.is_current:
119
+ for lv in lvals_toks:
120
+ tokens.append((Token, " " * INDENT_SIZE))
121
+ tokens.extend(lv)
122
+ tokens.append((Token, "\n"))
123
+ # strip the last newline
124
+ tokens = tokens[:-1]
125
+
126
+ return tokens
127
+
128
+
129
+ # some internal-use functions
130
+ def text_repr(value: Any) -> str:
131
+ """Hopefully pretty robust repr equivalent."""
132
+ # this is pretty horrible but should always return *something*
133
+ try:
134
+ return pydoc.text.repr(value) # type: ignore[call-arg]
135
+ except KeyboardInterrupt:
136
+ raise
137
+ except:
138
+ try:
139
+ return repr(value)
140
+ except KeyboardInterrupt:
141
+ raise
142
+ except:
143
+ try:
144
+ # all still in an except block so we catch
145
+ # getattr raising
146
+ name = getattr(value, "__name__", None)
147
+ if name:
148
+ # ick, recursion
149
+ return text_repr(name)
150
+ klass = getattr(value, "__class__", None)
151
+ if klass:
152
+ return "%s instance" % text_repr(klass)
153
+ return "UNRECOVERABLE REPR FAILURE"
154
+ except KeyboardInterrupt:
155
+ raise
156
+ except:
157
+ return "UNRECOVERABLE REPR FAILURE"
158
+
159
+
160
+ def eqrepr(value: Any, repr: Callable[[Any], str] = text_repr) -> str:
161
+ return "=%s" % repr(value)
162
+
163
+
164
+ def nullrepr(value: Any, repr: Callable[[Any], str] = text_repr) -> str:
165
+ return ""
166
+
167
+
168
+ def _tokens_filename(
169
+ em: bool,
170
+ file: str | None,
171
+ *,
172
+ lineno: int | None = None,
173
+ ) -> TokenStream:
174
+ """
175
+ Format filename lines with custom formatting from caching compiler or `File *.py` by default
176
+
177
+ Parameters
178
+ ----------
179
+ em: wether bold or not
180
+ file : str
181
+ """
182
+ Normal = Token.NormalEm if em else Token.Normal
183
+ Filename = Token.FilenameEm if em else Token.Filename
184
+ ipinst = get_ipython()
185
+ if (
186
+ ipinst is not None
187
+ and (data := ipinst.compile.format_code_name(file)) is not None
188
+ ):
189
+ label, name = data
190
+ if lineno is None:
191
+ return [
192
+ (Normal, label),
193
+ (Normal, " "),
194
+ (Filename, name),
195
+ ]
196
+ else:
197
+ return [
198
+ (Normal, label),
199
+ (Normal, " "),
200
+ (Filename, name),
201
+ (Filename, f", line {lineno}"),
202
+ ]
203
+ else:
204
+ name = util_path.compress_user(
205
+ py3compat.cast_unicode(file, util_path.fs_encoding)
206
+ )
207
+ if lineno is None:
208
+ return [
209
+ (Normal, "File "),
210
+ (Filename, name),
211
+ ]
212
+ else:
213
+ return [
214
+ (Normal, "File "),
215
+ (Filename, f"{name}:{lineno}"),
216
+ ]
217
+
218
+
219
+ def _simple_format_traceback_lines(
220
+ lnum: int,
221
+ index: int,
222
+ lines: list[tuple[str, tuple[str, bool]]],
223
+ lvals_toks: list[TokenStream],
224
+ theme: Theme,
225
+ ) -> TokenStream:
226
+ """
227
+ Format tracebacks lines with pointing arrow, leading numbers
228
+
229
+ This should be equivalent to _format_traceback_lines, but does not rely on stackdata
230
+ to format the lines
231
+
232
+ This is due to the fact that stackdata may be slow on super long and complex files.
233
+
234
+ Parameters
235
+ ==========
236
+
237
+ lnum: int
238
+ number of the target line of code.
239
+ index: int
240
+ which line in the list should be highlighted.
241
+ lines: list[string]
242
+ lvals_toks: pairs of token type and str
243
+ Values of local variables, already colored, to inject just after the error line.
244
+ """
245
+ for item in lvals_toks:
246
+ assert isinstance(item, list)
247
+ for subit in item:
248
+ assert isinstance(subit[1], str)
249
+
250
+ numbers_width = INDENT_SIZE - 1
251
+ res_toks: TokenStream = []
252
+ for i, (line, (new_line, err)) in enumerate(lines, lnum - index):
253
+ if not err:
254
+ line = new_line
255
+
256
+ colored_line = line
257
+ if i == lnum:
258
+ # This is the line with the error
259
+ pad = numbers_width - len(str(i))
260
+ line_toks = [
261
+ (Token.LinenoEm, theme.make_arrow(pad)),
262
+ (Token.LinenoEm, str(lnum)),
263
+ (Token, " "),
264
+ (Token, colored_line),
265
+ ]
266
+ else:
267
+ padding_num = "%*s" % (numbers_width, i)
268
+
269
+ line_toks = [
270
+ (Token.LinenoEm, padding_num),
271
+ (Token, " "),
272
+ (Token, colored_line),
273
+ ]
274
+ res_toks.extend(line_toks)
275
+
276
+ if lvals_toks and i == lnum:
277
+ for lv in lvals_toks:
278
+ res_toks.extend(lv)
279
+ # res_toks.extend(lvals_toks)
280
+ return res_toks
281
+
282
+
283
+ class FrameInfo:
284
+ """
285
+ Mirror of stack data's FrameInfo, but so that we can bypass highlighting on
286
+ really long frames.
287
+ """
288
+
289
+ description: Optional[str]
290
+ filename: Optional[str]
291
+ lineno: int
292
+ # number of context lines to use
293
+ context: Optional[int]
294
+ raw_lines: list[str]
295
+ _sd: stack_data.core.FrameInfo
296
+ frame: Any
297
+
298
+ @classmethod
299
+ def _from_stack_data_FrameInfo(
300
+ cls, frame_info: stack_data.core.FrameInfo | stack_data.core.RepeatedFrames
301
+ ) -> "FrameInfo":
302
+ return cls(
303
+ getattr(frame_info, "description", None),
304
+ getattr(frame_info, "filename", None), # type: ignore[arg-type]
305
+ getattr(frame_info, "lineno", None), # type: ignore[arg-type]
306
+ getattr(frame_info, "frame", None),
307
+ getattr(frame_info, "code", None),
308
+ sd=frame_info,
309
+ context=None,
310
+ )
311
+
312
+ def __init__(
313
+ self,
314
+ description: Optional[str],
315
+ filename: str,
316
+ lineno: int,
317
+ frame: Any,
318
+ code: Optional[types.CodeType],
319
+ *,
320
+ sd: Any = None,
321
+ context: int | None = None,
322
+ ):
323
+ assert isinstance(lineno, (int, type(None))), lineno
324
+ self.description = description
325
+ self.filename = filename
326
+ self.lineno = lineno
327
+ self.frame = frame
328
+ self.code = code
329
+ self._sd = sd
330
+ self.context = context
331
+
332
+ # self.lines = []
333
+ if sd is None:
334
+ try:
335
+ # return a list of source lines and a starting line number
336
+ self.raw_lines = inspect.getsourcelines(frame)[0]
337
+ except OSError:
338
+ self.raw_lines = [
339
+ "'Could not get source, probably due dynamically evaluated source code.'"
340
+ ]
341
+
342
+ @property
343
+ def variables_in_executing_piece(self) -> list[Any]:
344
+ if self._sd is not None:
345
+ return self._sd.variables_in_executing_piece # type:ignore[misc]
346
+ else:
347
+ return []
348
+
349
+ @property
350
+ def lines(self) -> list[Any]:
351
+ from executing.executing import NotOneValueFound
352
+
353
+ assert self._sd is not None
354
+ try:
355
+ return self._sd.lines # type: ignore[misc]
356
+ except NotOneValueFound:
357
+
358
+ class Dummy:
359
+ lineno = 0
360
+ is_current = False
361
+
362
+ def render(self, *, pygmented: bool) -> str:
363
+ return "<Error retrieving source code with stack_data see ipython/ipython#13598>"
364
+
365
+ return [Dummy()]
366
+
367
+ @property
368
+ def executing(self) -> Any:
369
+ if self._sd is not None:
370
+ return self._sd.executing
371
+ else:
372
+ return None
373
+
374
+
375
+ class TBTools:
376
+ """Basic tools used by all traceback printer classes."""
377
+
378
+ # Number of frames to skip when reporting tracebacks
379
+ tb_offset = 0
380
+ _theme_name: str
381
+ _old_theme_name: str
382
+ call_pdb: bool
383
+ ostream: Any
384
+ debugger_cls: Any
385
+ pdb: Any
386
+
387
+ def __init__(
388
+ self,
389
+ color_scheme: Any = _sentinel,
390
+ call_pdb: bool = False,
391
+ ostream: Any = None,
392
+ *,
393
+ debugger_cls: type | None = None,
394
+ theme_name: str = "nocolor",
395
+ ):
396
+ if color_scheme is not _sentinel:
397
+ assert isinstance(color_scheme, str), color_scheme
398
+ warnings.warn(
399
+ "color_scheme is deprecated since IPython 9.0, use theme_name instead, all lowercase",
400
+ DeprecationWarning,
401
+ stacklevel=2,
402
+ )
403
+ theme_name = color_scheme
404
+ if theme_name in ["Linux", "LightBG", "Neutral", "NoColor"]:
405
+ warnings.warn(
406
+ f"Theme names and color schemes are lowercase in IPython 9.0 use {theme_name.lower()} instead",
407
+ DeprecationWarning,
408
+ stacklevel=2,
409
+ )
410
+ theme_name = theme_name.lower()
411
+ # Whether to call the interactive pdb debugger after printing
412
+ # tracebacks or not
413
+ super().__init__()
414
+ self.call_pdb = call_pdb
415
+
416
+ # Output stream to write to. Note that we store the original value in
417
+ # a private attribute and then make the public ostream a property, so
418
+ # that we can delay accessing sys.stdout until runtime. The way
419
+ # things are written now, the sys.stdout object is dynamically managed
420
+ # so a reference to it should NEVER be stored statically. This
421
+ # property approach confines this detail to a single location, and all
422
+ # subclasses can simply access self.ostream for writing.
423
+ self._ostream = ostream
424
+
425
+ # Create color table
426
+ self.set_theme_name(theme_name)
427
+ self.debugger_cls = debugger_cls or debugger.Pdb
428
+
429
+ if call_pdb:
430
+ self.pdb = self.debugger_cls()
431
+ else:
432
+ self.pdb = None
433
+
434
+ def _get_ostream(self) -> Any:
435
+ """Output stream that exceptions are written to.
436
+
437
+ Valid values are:
438
+
439
+ - None: the default, which means that IPython will dynamically resolve
440
+ to sys.stdout. This ensures compatibility with most tools, including
441
+ Windows (where plain stdout doesn't recognize ANSI escapes).
442
+
443
+ - Any object with 'write' and 'flush' attributes.
444
+ """
445
+ return sys.stdout if self._ostream is None else self._ostream
446
+
447
+ def _set_ostream(self, val) -> None: # type:ignore[no-untyped-def]
448
+ assert val is None or (hasattr(val, "write") and hasattr(val, "flush"))
449
+ self._ostream = val
450
+
451
+ ostream = property(_get_ostream, _set_ostream)
452
+
453
+ @staticmethod
454
+ def _get_chained_exception(exception_value: Any) -> Any:
455
+ cause = getattr(exception_value, "__cause__", None)
456
+ if cause:
457
+ return cause
458
+ if getattr(exception_value, "__suppress_context__", False):
459
+ return None
460
+ return getattr(exception_value, "__context__", None)
461
+
462
+ def get_parts_of_chained_exception(
463
+ self, evalue: BaseException | None
464
+ ) -> Optional[Tuple[type, BaseException, TracebackType]]:
465
+ chained_evalue = self._get_chained_exception(evalue)
466
+
467
+ if chained_evalue:
468
+ return (
469
+ chained_evalue.__class__,
470
+ chained_evalue,
471
+ chained_evalue.__traceback__,
472
+ )
473
+ return None
474
+
475
+ def prepare_chained_exception_message(
476
+ self, cause: BaseException | None
477
+ ) -> list[list[str]]:
478
+ direct_cause = (
479
+ "\nThe above exception was the direct cause of the following exception:\n"
480
+ )
481
+ exception_during_handling = (
482
+ "\nDuring handling of the above exception, another exception occurred:\n"
483
+ )
484
+
485
+ if cause:
486
+ message = [[direct_cause]]
487
+ else:
488
+ message = [[exception_during_handling]]
489
+ return message
490
+
491
+ @property
492
+ def has_colors(self) -> bool:
493
+ assert self._theme_name == self._theme_name.lower()
494
+ return self._theme_name != "nocolor"
495
+
496
+ def set_theme_name(self, name: str) -> None:
497
+ assert name in theme_table
498
+ assert name.lower() == name
499
+ self._theme_name = name
500
+ # Also set colors of debugger
501
+ if hasattr(self, "pdb") and self.pdb is not None:
502
+ self.pdb.set_theme_name(name)
503
+
504
+ def set_colors(self, name: str) -> None:
505
+ """Shorthand access to the color table scheme selector method."""
506
+
507
+ # todo emit deprecation
508
+ warnings.warn(
509
+ "set_colors is deprecated since IPython 9.0, use set_theme_name instead",
510
+ DeprecationWarning,
511
+ stacklevel=2,
512
+ )
513
+ self.set_theme_name(name)
514
+
515
+ def color_toggle(self) -> None:
516
+ """Toggle between the currently active color scheme and nocolor."""
517
+ if self._theme_name == "nocolor":
518
+ self._theme_name = self._old_theme_name
519
+ else:
520
+ self._old_theme_name = self._theme_name
521
+ self._theme_name = "nocolor"
522
+
523
+ def stb2text(self, stb: list[str]) -> str:
524
+ """Convert a structured traceback (a list) to a string."""
525
+ return "\n".join(stb)
526
+
527
+ def text(
528
+ self,
529
+ etype: type,
530
+ value: BaseException | None,
531
+ tb: TracebackType | None,
532
+ tb_offset: Optional[int] = None,
533
+ context: int = 5,
534
+ ) -> str:
535
+ """Return formatted traceback.
536
+
537
+ Subclasses may override this if they add extra arguments.
538
+ """
539
+ tb_list = self.structured_traceback(etype, value, tb, tb_offset, context)
540
+ return self.stb2text(tb_list)
541
+
542
+ def structured_traceback(
543
+ self,
544
+ etype: type,
545
+ evalue: BaseException | None,
546
+ etb: Optional[TracebackType] = None,
547
+ tb_offset: Optional[int] = None,
548
+ context: int = 5,
549
+ ) -> list[str]:
550
+ """Return a list of traceback frames.
551
+
552
+ Must be implemented by each class.
553
+ """
554
+ raise NotImplementedError()
temp_venv/lib/python3.13/site-packages/IPython/core/tips.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ import os
3
+ from random import choice
4
+ from typing import Any
5
+
6
+ _tips: Any = {
7
+ # (month, day)
8
+ "every_year": {
9
+ (1, 1): "Happy new year!",
10
+ # European time:
11
+ # [2/8/25, 23:28:24] Fernando Perez: Hi! Yes, this was my first public
12
+ # announcement:
13
+ # [2/8/25, 23:28:25] Fernando Perez:
14
+ # https://mail.python.org/pipermail/python-list/2001-December/093408.html
15
+ # [2/8/25, 23:28:55] Fernando Perez: All that started two months earlier
16
+ # - in October 2001 I read this article:
17
+ # [2/8/25, 23:28:55] Fernando Perez:
18
+ # https://web.archive.org/web/20011202000624/http://www.onlamp.com/pub/a/python/2001/10/11/pythonnews.html
19
+ # [2/8/25, 23:29:05] Fernando Perez: Which is also archived here:
20
+ # [2/8/25, 23:29:05] Fernando Perez:
21
+ # https://docstore.mik.ua/orelly/weblinux2/orn/interactive_python.html
22
+ # [2/8/25, 23:29:48] Fernando Perez: That article put me on the trail of
23
+ # LazyPython, and I also found out (can’t remember where) about IPP
24
+ # (Interactive Python Prompt), another similar projecd by Janko Hauser.
25
+ # [2/8/25, 23:30:20] Fernando Perez: A also read an article around that
26
+ # time, about new features in Python 2.0, which spoke of how
27
+ # sys.displayhook could be programmed to call any object you wanted.
28
+ # [2/8/25, 23:31:01] Fernando Perez: Those things together gave me the
29
+ # idea of implementing a stateful object, the “IPython Prompt”, that
30
+ # could store a cache of old results, Mathematica-style, and that could
31
+ # have all kinds of other useful tricks up its sleeve.
32
+ # [2/8/25, 23:31:17] Fernando Perez: I can’t remember if I did the
33
+ # prompt stuff first and then read about LazyPython/IPP.
34
+ # [2/8/25, 23:31:41] Fernando Perez: I do know that, implementation
35
+ # wise, at first I just did the tiny IPython 0.0.1 that I posted much
36
+ # later on github as a gist:
37
+ # [2/8/25, 23:31:55] Fernando Perez:
38
+ # https://gist.github.com/fperez/1579699
39
+ # [2/8/25, 23:32:03] Fernando Perez: But I only shared that publicly
40
+ # much later.
41
+ # [2/8/25, 23:33:19] Fernando Perez: I did find out about IPP/LazyPython
42
+ # sometime in October, contacted Janko and Nathan who told me to use
43
+ # their code at will but said they were busy with other things, and got
44
+ # cranking for a few mad weeks on what became the IPython 0.2.0 that I
45
+ # posted about in that comp.lang.python thread of December 2001.
46
+ # [2/8/25, 23:33:52] Fernando Perez: That period from Oct 11 to Dec 9
47
+ # 2001 was maniacal coding, with very little sleep 🙂
48
+ (
49
+ 10,
50
+ 11,
51
+ ): "IPython's first line of code was written {} years ago by Fernando Pérez".format(
52
+ datetime.now().year - 2001
53
+ ),
54
+ (
55
+ 12,
56
+ 9,
57
+ ): "IPython 0.0.2 was announced {} years ago: https://mail.python.org/pipermail/python-list/2001-December/093408.html".format(
58
+ datetime.now().year - 2001
59
+ ),
60
+ (
61
+ 3,
62
+ 8,
63
+ ): "Today is International Women's Day: https://www.internationalwomensday.com/",
64
+ (
65
+ 3,
66
+ 31,
67
+ ): "Happy International Transgender Day of Visibility! You are valid. You matter. https://en.wikipedia.org/wiki/International_Transgender_Day_of_Visibility",
68
+ },
69
+ "random": [
70
+ "Use `F2` or %edit with no arguments to open an empty editor with a temporary file.",
71
+ "Run your doctests from within IPython for development and debugging. The special %doctest_mode command toggles a mode where the prompt, output and exceptions display matches as closely as possible that of the default Python interpreter.",
72
+ "You can use `files = !ls *.png`",
73
+ "Use the IPython.lib.demo.Demo class to load any Python script as an interactive demo.",
74
+ "Put a ';' at the end of a line to suppress the printing of output.",
75
+ "You can use Ctrl-O to force a new line in terminal IPython",
76
+ "Use `object?` to see the help on `object`, `object??` to view its source",
77
+ "`?` alone on a line will brings up IPython's help",
78
+ "You can use `%hist` to view history, see the options with `%history?`",
79
+ "You can change the editing mode of IPython to behave more like vi, or emacs.",
80
+ "IPython 9.0+ has hooks to integrate AI/LLM completions.",
81
+ "Use `%timeit` or `%%timeit`, and the `-r`, `-n`, and `-o` options to easily profile your code.",
82
+ "Use `ipython --help-all | less` to view all the IPython configuration options.",
83
+ "Use `--theme`, or the `%colors` magic to change IPython's themes and colors.",
84
+ "The `%timeit` magic has a `-o` flag, which returns the results, making it easy to plot. See `%timeit?`.",
85
+ ],
86
+ }
87
+
88
+ if os.name == "nt":
89
+ _tips["random"].extend(
90
+ [
91
+ "We can't show you all tips on Windows as sometimes Unicode characters crash the Windows console, please help us debug it."
92
+ ]
93
+ )
94
+ # unicode may crash windows console, so we filter out tips with non-ASCII characters.
95
+ _tips["every_year"] = {
96
+ k: v
97
+ for k, v in _tips["every_year"].items()
98
+ if all(ord(char) < 128 for char in v)
99
+ }
100
+ else:
101
+ _tips["random"].extend(
102
+ [
103
+ "You can use LaTeX or Unicode completion, `\\alpha<tab>` will insert the α symbol.",
104
+ "You can find how to type a LaTeX symbol by back-completing it, eg `\\θ<tab>` will expand to `\\theta`.",
105
+ "You can find how to type a Unicode symbol by back-completing it, eg `\\Ⅷ<tab>` will expand to `\\ROMAN NUMERAL EIGHT`.",
106
+ "IPython supports combining unicode identifiers, eg F\\vec<tab> will become F⃗, useful for physics equations. Play with \\dot \\ddot and others.",
107
+ ]
108
+ )
109
+
110
+
111
+ def pick_tip() -> str:
112
+ current_date = datetime.now()
113
+ month, day = current_date.month, current_date.day
114
+
115
+ if (month, day) in _tips["every_year"]:
116
+ return _tips["every_year"][(month, day)]
117
+
118
+ return choice(_tips["random"])
temp_venv/lib/python3.13/site-packages/IPython/core/ultratb.py ADDED
@@ -0,0 +1,1252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Verbose and colourful traceback formatting.
3
+
4
+ **ColorTB**
5
+
6
+ I've always found it a bit hard to visually parse tracebacks in Python. The
7
+ ColorTB class is a solution to that problem. It colors the different parts of a
8
+ traceback in a manner similar to what you would expect from a syntax-highlighting
9
+ text editor.
10
+
11
+ Installation instructions for ColorTB::
12
+
13
+ import sys,ultratb
14
+ sys.excepthook = ultratb.ColorTB()
15
+
16
+ **VerboseTB**
17
+
18
+ I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
19
+ of useful info when a traceback occurs. Ping originally had it spit out HTML
20
+ and intended it for CGI programmers, but why should they have all the fun? I
21
+ altered it to spit out colored text to the terminal. It's a bit overwhelming,
22
+ but kind of neat, and maybe useful for long-running programs that you believe
23
+ are bug-free. If a crash *does* occur in that type of program you want details.
24
+ Give it a shot--you'll love it or you'll hate it.
25
+
26
+ .. note::
27
+
28
+ The Verbose mode prints the variables currently visible where the exception
29
+ happened (shortening their strings if too long). This can potentially be
30
+ very slow, if you happen to have a huge data structure whose string
31
+ representation is complex to compute. Your computer may appear to freeze for
32
+ a while with cpu usage at 100%. If this occurs, you can cancel the traceback
33
+ with Ctrl-C (maybe hitting it more than once).
34
+
35
+ If you encounter this kind of situation often, you may want to use the
36
+ Verbose_novars mode instead of the regular Verbose, which avoids formatting
37
+ variables (but otherwise includes the information and context given by
38
+ Verbose).
39
+
40
+ .. note::
41
+
42
+ The verbose mode print all variables in the stack, which means it can
43
+ potentially leak sensitive information like access keys, or unencrypted
44
+ password.
45
+
46
+ Installation instructions for VerboseTB::
47
+
48
+ import sys,ultratb
49
+ sys.excepthook = ultratb.VerboseTB()
50
+
51
+ Note: Much of the code in this module was lifted verbatim from the standard
52
+ library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
53
+
54
+
55
+ Inheritance diagram:
56
+
57
+ .. inheritance-diagram:: IPython.core.ultratb
58
+ :parts: 3
59
+ """
60
+
61
+ # *****************************************************************************
62
+ # Copyright (C) 2001 Nathaniel Gray <[email protected]>
63
+ # Copyright (C) 2001-2004 Fernando Perez <[email protected]>
64
+ #
65
+ # Distributed under the terms of the BSD License. The full license is in
66
+ # the file COPYING, distributed as part of this software.
67
+ # *****************************************************************************
68
+
69
+ import functools
70
+ import inspect
71
+ import linecache
72
+ import sys
73
+ import time
74
+ import traceback
75
+ import types
76
+ import warnings
77
+ from collections.abc import Sequence
78
+ from types import TracebackType
79
+ from typing import Any, Callable, List, Optional, Tuple
80
+
81
+ import stack_data
82
+ from pygments.formatters.terminal256 import Terminal256Formatter
83
+ from pygments.token import Token
84
+
85
+ from IPython import get_ipython
86
+ from IPython.utils import path as util_path
87
+ from IPython.utils import py3compat
88
+ from IPython.utils.PyColorize import Parser, Theme, TokenStream, theme_table
89
+ from IPython.utils.terminal import get_terminal_size
90
+
91
+ from .display_trap import DisplayTrap
92
+ from .doctb import DocTB
93
+ from .tbtools import (
94
+ FrameInfo,
95
+ TBTools,
96
+ _format_traceback_lines,
97
+ _safe_string,
98
+ _simple_format_traceback_lines,
99
+ _tokens_filename,
100
+ eqrepr,
101
+ get_line_number_of_frame,
102
+ nullrepr,
103
+ text_repr,
104
+ )
105
+
106
+ # Globals
107
+ # amount of space to put line numbers before verbose tracebacks
108
+ INDENT_SIZE = 8
109
+
110
+ # When files are too long do not use stackdata to get frames.
111
+ # it is too long.
112
+ FAST_THRESHOLD = 10_000
113
+
114
+ # ---------------------------------------------------------------------------
115
+ class ListTB(TBTools):
116
+ """Print traceback information from a traceback list, with optional color.
117
+
118
+ Calling requires 3 arguments: (etype, evalue, elist)
119
+ as would be obtained by::
120
+
121
+ etype, evalue, tb = sys.exc_info()
122
+ if tb:
123
+ elist = traceback.extract_tb(tb)
124
+ else:
125
+ elist = None
126
+
127
+ It can thus be used by programs which need to process the traceback before
128
+ printing (such as console replacements based on the code module from the
129
+ standard library).
130
+
131
+ Because they are meant to be called without a full traceback (only a
132
+ list), instances of this class can't call the interactive pdb debugger."""
133
+
134
+ def __call__(
135
+ self,
136
+ etype: type[BaseException],
137
+ evalue: BaseException | None,
138
+ etb: TracebackType | None,
139
+ ) -> None:
140
+ self.ostream.flush()
141
+ self.ostream.write(self.text(etype, evalue, etb))
142
+ self.ostream.write("\n")
143
+
144
+ def _extract_tb(self, tb: TracebackType | None) -> traceback.StackSummary | None:
145
+ if tb:
146
+ return traceback.extract_tb(tb)
147
+ else:
148
+ return None
149
+
150
+ def structured_traceback(
151
+ self,
152
+ etype: type,
153
+ evalue: Optional[BaseException],
154
+ etb: Optional[TracebackType] = None,
155
+ tb_offset: Optional[int] = None,
156
+ context: int = 5,
157
+ ) -> list[str]:
158
+ """Return a color formatted string with the traceback info.
159
+
160
+ Parameters
161
+ ----------
162
+ etype : exception type
163
+ Type of the exception raised.
164
+ evalue : object
165
+ Data stored in the exception
166
+ etb : list | TracebackType | None
167
+ If list: List of frames, see class docstring for details.
168
+ If Traceback: Traceback of the exception.
169
+ tb_offset : int, optional
170
+ Number of frames in the traceback to skip. If not given, the
171
+ instance evalue is used (set in constructor).
172
+ context : int, optional
173
+ Number of lines of context information to print.
174
+
175
+ Returns
176
+ -------
177
+ String with formatted exception.
178
+ """
179
+ # This is a workaround to get chained_exc_ids in recursive calls
180
+ # etb should not be a tuple if structured_traceback is not recursive
181
+ if isinstance(etb, tuple):
182
+ etb, chained_exc_ids = etb
183
+ else:
184
+ chained_exc_ids = set()
185
+ elist: list[Any]
186
+ if isinstance(etb, list):
187
+ elist = etb
188
+ elif etb is not None:
189
+ elist = self._extract_tb(etb) # type: ignore[assignment]
190
+ else:
191
+ elist = []
192
+ tb_offset = self.tb_offset if tb_offset is None else tb_offset
193
+ assert isinstance(tb_offset, int)
194
+ out_list: list[str] = []
195
+ if elist:
196
+ if tb_offset and len(elist) > tb_offset:
197
+ elist = elist[tb_offset:]
198
+
199
+ out_list.append(
200
+ theme_table[self._theme_name].format(
201
+ [
202
+ (Token, "Traceback"),
203
+ (Token, " "),
204
+ (Token.NormalEm, "(most recent call last)"),
205
+ (Token, ":"),
206
+ (Token, "\n"),
207
+ ]
208
+ ),
209
+ )
210
+ out_list.extend(self._format_list(elist))
211
+ # The exception info should be a single entry in the list.
212
+ lines = "".join(self._format_exception_only(etype, evalue))
213
+ out_list.append(lines)
214
+
215
+ # Find chained exceptions if we have a traceback (not for exception-only mode)
216
+ if etb is not None:
217
+ exception = self.get_parts_of_chained_exception(evalue)
218
+
219
+ if exception and (id(exception[1]) not in chained_exc_ids):
220
+ chained_exception_message: list[str] = (
221
+ self.prepare_chained_exception_message(evalue.__cause__)[0]
222
+ if evalue is not None
223
+ else [""]
224
+ )
225
+ etype, evalue, etb = exception
226
+ # Trace exception to avoid infinite 'cause' loop
227
+ chained_exc_ids.add(id(exception[1]))
228
+ chained_exceptions_tb_offset = 0
229
+ ol1 = self.structured_traceback(
230
+ etype,
231
+ evalue,
232
+ (etb, chained_exc_ids), # type: ignore[arg-type]
233
+ chained_exceptions_tb_offset,
234
+ context,
235
+ )
236
+ ol2 = chained_exception_message
237
+
238
+ out_list = ol1 + ol2 + out_list
239
+
240
+ return out_list
241
+
242
+ def _format_list(self, extracted_list: list[Any]) -> list[str]:
243
+ """Format a list of traceback entry tuples for printing.
244
+
245
+ Given a list of tuples as returned by extract_tb() or
246
+ extract_stack(), return a list of strings ready for printing.
247
+ Each string in the resulting list corresponds to the item with the
248
+ same index in the argument list. Each string ends in a newline;
249
+ the strings may contain internal newlines as well, for those items
250
+ whose source text line is not None.
251
+
252
+ Lifted almost verbatim from traceback.py
253
+ """
254
+
255
+ output_list = []
256
+ for ind, (filename, lineno, name, line) in enumerate(extracted_list):
257
+ # Will emphasize the last entry
258
+ em = True if ind == len(extracted_list) - 1 else False
259
+
260
+ item = theme_table[self._theme_name].format(
261
+ [(Token.NormalEm if em else Token.Normal, " ")]
262
+ + _tokens_filename(em, filename, lineno=lineno)
263
+ )
264
+
265
+ # This seem to be only in xmode plain (%run sinpleer), investigate why not share with verbose.
266
+ # look at _tokens_filename in forma_record.
267
+ if name != "<module>":
268
+ item += theme_table[self._theme_name].format(
269
+ [
270
+ (Token.NormalEm if em else Token.Normal, " in "),
271
+ (Token.TB.NameEm if em else Token.TB.Name, name),
272
+ ]
273
+ )
274
+ item += theme_table[self._theme_name].format(
275
+ [(Token.NormalEm if em else Token, "\n")]
276
+ )
277
+ if line:
278
+ item += theme_table[self._theme_name].format(
279
+ [
280
+ (Token.Line if em else Token, " "),
281
+ (Token.Line if em else Token, line.strip()),
282
+ (Token, "\n"),
283
+ ]
284
+ )
285
+ output_list.append(item)
286
+
287
+ return output_list
288
+
289
+ def _format_exception_only(
290
+ self, etype: type[BaseException], value: BaseException | None
291
+ ) -> list[str]:
292
+ """Format the exception part of a traceback.
293
+
294
+ The arguments are the exception type and value such as given by
295
+ sys.exc_info()[:2]. The return value is a list of strings, each ending
296
+ in a newline. Normally, the list contains a single string; however,
297
+ for SyntaxError exceptions, it contains several lines that (when
298
+ printed) display detailed information about where the syntax error
299
+ occurred. The message indicating which exception occurred is the
300
+ always last string in the list.
301
+
302
+ Also lifted nearly verbatim from traceback.py
303
+ """
304
+ have_filedata = False
305
+ output_list = []
306
+ stype_tokens = [(Token.ExcName, etype.__name__)]
307
+ stype: str = theme_table[self._theme_name].format(stype_tokens)
308
+ if value is None:
309
+ # Not sure if this can still happen in Python 2.6 and above
310
+ output_list.append(stype + "\n")
311
+ else:
312
+ if issubclass(etype, SyntaxError):
313
+ assert hasattr(value, "filename")
314
+ assert hasattr(value, "lineno")
315
+ assert hasattr(value, "text")
316
+ assert hasattr(value, "offset")
317
+ assert hasattr(value, "msg")
318
+ have_filedata = True
319
+ if not value.filename:
320
+ value.filename = "<string>"
321
+ if value.lineno:
322
+ lineno = value.lineno
323
+ textline = linecache.getline(value.filename, value.lineno)
324
+ else:
325
+ lineno = "unknown"
326
+ textline = ""
327
+ output_list.append(
328
+ theme_table[self._theme_name].format(
329
+ [(Token, " ")]
330
+ + _tokens_filename(
331
+ True,
332
+ value.filename,
333
+ lineno=(None if lineno == "unknown" else lineno),
334
+ )
335
+ + [(Token, "\n")]
336
+ )
337
+ )
338
+ if textline == "":
339
+ textline = py3compat.cast_unicode(value.text, "utf-8")
340
+
341
+ if textline is not None:
342
+ i = 0
343
+ while i < len(textline) and textline[i].isspace():
344
+ i += 1
345
+ output_list.append(
346
+ theme_table[self._theme_name].format(
347
+ [
348
+ (Token.Line, " "),
349
+ (Token.Line, textline.strip()),
350
+ (Token, "\n"),
351
+ ]
352
+ )
353
+ )
354
+ if value.offset is not None:
355
+ s = " "
356
+ for c in textline[i : value.offset - 1]:
357
+ if c.isspace():
358
+ s += c
359
+ else:
360
+ s += " "
361
+ output_list.append(
362
+ theme_table[self._theme_name].format(
363
+ [(Token.Caret, s + "^"), (Token, "\n")]
364
+ )
365
+ )
366
+
367
+ try:
368
+ assert hasattr(value, "msg")
369
+ s = value.msg
370
+ except Exception:
371
+ s = self._some_str(value)
372
+ if s:
373
+ output_list.append(
374
+ theme_table[self._theme_name].format(
375
+ stype_tokens
376
+ + [
377
+ (Token.ExcName, ":"),
378
+ (Token, " "),
379
+ (Token, s),
380
+ (Token, "\n"),
381
+ ]
382
+ )
383
+ )
384
+ else:
385
+ output_list.append("%s\n" % stype)
386
+
387
+ # PEP-678 notes
388
+ output_list.extend(f"{x}\n" for x in getattr(value, "__notes__", []))
389
+
390
+ # sync with user hooks
391
+ if have_filedata:
392
+ ipinst = get_ipython()
393
+ if ipinst is not None:
394
+ assert value is not None
395
+ assert hasattr(value, "lineno")
396
+ assert hasattr(value, "filename")
397
+ ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
398
+
399
+ return output_list
400
+
401
+ def get_exception_only(self, etype, value):
402
+ """Only print the exception type and message, without a traceback.
403
+
404
+ Parameters
405
+ ----------
406
+ etype : exception type
407
+ value : exception value
408
+ """
409
+ return ListTB.structured_traceback(self, etype, value)
410
+
411
+ def show_exception_only(
412
+ self, etype: BaseException | None, evalue: TracebackType | None
413
+ ) -> None:
414
+ """Only print the exception type and message, without a traceback.
415
+
416
+ Parameters
417
+ ----------
418
+ etype : exception type
419
+ evalue : exception value
420
+ """
421
+ # This method needs to use __call__ from *this* class, not the one from
422
+ # a subclass whose signature or behavior may be different
423
+ ostream = self.ostream
424
+ ostream.flush()
425
+ ostream.write("\n".join(self.get_exception_only(etype, evalue)))
426
+ ostream.flush()
427
+
428
+ def _some_str(self, value: Any) -> str:
429
+ # Lifted from traceback.py
430
+ try:
431
+ return py3compat.cast_unicode(str(value))
432
+ except:
433
+ return "<unprintable %s object>" % type(value).__name__
434
+
435
+
436
+ _sentinel = object()
437
+ _default = "default"
438
+
439
+
440
+ # ----------------------------------------------------------------------------
441
+ class VerboseTB(TBTools):
442
+ """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
443
+ of HTML. Requires inspect and pydoc. Crazy, man.
444
+
445
+ Modified version which optionally strips the topmost entries from the
446
+ traceback, to be used with alternate interpreters (because their own code
447
+ would appear in the traceback)."""
448
+
449
+ tb_highlight = "bg:ansiyellow"
450
+ tb_highlight_style = "default"
451
+
452
+ _mode: str
453
+
454
+ def __init__(
455
+ self,
456
+ # TODO: no default ?
457
+ theme_name: str = _default,
458
+ call_pdb: bool = False,
459
+ ostream: Any = None,
460
+ tb_offset: int = 0,
461
+ long_header: bool = False,
462
+ include_vars: bool = True,
463
+ check_cache: Callable[[], None] | None = None,
464
+ debugger_cls: type | None = None,
465
+ *,
466
+ color_scheme: Any = _sentinel,
467
+ ):
468
+ """Specify traceback offset, headers and color scheme.
469
+
470
+ Define how many frames to drop from the tracebacks. Calling it with
471
+ tb_offset=1 allows use of this handler in interpreters which will have
472
+ their own code at the top of the traceback (VerboseTB will first
473
+ remove that frame before printing the traceback info)."""
474
+ if color_scheme is not _sentinel:
475
+ assert isinstance(color_scheme, str)
476
+ theme_name = color_scheme.lower()
477
+
478
+ warnings.warn(
479
+ "color_scheme is deprecated as of IPython 9.0 and replaced by "
480
+ "theme_name (which should be lowercase). As you passed a "
481
+ "color_scheme value I will try to see if I have corresponding "
482
+ "theme.",
483
+ stacklevel=2,
484
+ category=DeprecationWarning,
485
+ )
486
+
487
+ if theme_name != _default:
488
+ warnings.warn(
489
+ "You passed both `theme_name` and `color_scheme` "
490
+ "(deprecated) to VerboseTB constructor. `theme_name` will "
491
+ "be ignored for the time being.",
492
+ stacklevel=2,
493
+ category=DeprecationWarning,
494
+ )
495
+
496
+ if theme_name == _default:
497
+ theme_name = "linux"
498
+
499
+ assert isinstance(theme_name, str)
500
+ super().__init__(
501
+ theme_name=theme_name,
502
+ call_pdb=call_pdb,
503
+ ostream=ostream,
504
+ debugger_cls=debugger_cls,
505
+ )
506
+ self.tb_offset = tb_offset
507
+ self.long_header = long_header
508
+ self.include_vars = include_vars
509
+ # By default we use linecache.checkcache, but the user can provide a
510
+ # different check_cache implementation. This was formerly used by the
511
+ # IPython kernel for interactive code, but is no longer necessary.
512
+ if check_cache is None:
513
+ check_cache = linecache.checkcache
514
+ self.check_cache = check_cache
515
+
516
+ self.skip_hidden = True
517
+
518
+ def format_record(self, frame_info: FrameInfo) -> str:
519
+ """Format a single stack frame"""
520
+ assert isinstance(frame_info, FrameInfo)
521
+
522
+ if isinstance(frame_info._sd, stack_data.RepeatedFrames):
523
+ return theme_table[self._theme_name].format(
524
+ [
525
+ (Token, " "),
526
+ (
527
+ Token.ExcName,
528
+ "[... skipping similar frames: %s]" % frame_info.description,
529
+ ),
530
+ (Token, "\n"),
531
+ ]
532
+ )
533
+
534
+ indent: str = " " * INDENT_SIZE
535
+
536
+ assert isinstance(frame_info.lineno, int)
537
+ args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
538
+ if frame_info.executing is not None:
539
+ func = frame_info.executing.code_qualname()
540
+ else:
541
+ func = "?"
542
+ if func == "<module>":
543
+ call = ""
544
+ else:
545
+ # Decide whether to include variable details or not
546
+ var_repr = eqrepr if self.include_vars else nullrepr
547
+ try:
548
+ scope = inspect.formatargvalues(
549
+ args, varargs, varkw, locals_, formatvalue=var_repr
550
+ )
551
+ assert isinstance(scope, str)
552
+ call = theme_table[self._theme_name].format(
553
+ [(Token, "in "), (Token.VName, func), (Token.ValEm, scope)]
554
+ )
555
+ except KeyError:
556
+ # This happens in situations like errors inside generator
557
+ # expressions, where local variables are listed in the
558
+ # line, but can't be extracted from the frame. I'm not
559
+ # 100% sure this isn't actually a bug in inspect itself,
560
+ # but since there's no info for us to compute with, the
561
+ # best we can do is report the failure and move on. Here
562
+ # we must *not* call any traceback construction again,
563
+ # because that would mess up use of %debug later on. So we
564
+ # simply report the failure and move on. The only
565
+ # limitation will be that this frame won't have locals
566
+ # listed in the call signature. Quite subtle problem...
567
+ # I can't think of a good way to validate this in a unit
568
+ # test, but running a script consisting of:
569
+ # dict( (k,v.strip()) for (k,v) in range(10) )
570
+ # will illustrate the error, if this exception catch is
571
+ # disabled.
572
+ call = theme_table[self._theme_name].format(
573
+ [
574
+ (Token, "in "),
575
+ (Token.VName, func),
576
+ (Token.ValEm, "(***failed resolving arguments***)"),
577
+ ]
578
+ )
579
+
580
+ lvals_toks: list[TokenStream] = []
581
+ if self.include_vars:
582
+ try:
583
+ # we likely want to fix stackdata at some point, but
584
+ # still need a workaround.
585
+ fibp = frame_info.variables_in_executing_piece
586
+ for var in fibp:
587
+ lvals_toks.append(
588
+ [
589
+ (Token, var.name),
590
+ (Token, " "),
591
+ (Token.ValEm, "= "),
592
+ (Token.ValEm, repr(var.value)),
593
+ ]
594
+ )
595
+ except Exception:
596
+ lvals_toks.append(
597
+ [
598
+ (
599
+ Token,
600
+ "Exception trying to inspect frame. No more locals available.",
601
+ ),
602
+ ]
603
+ )
604
+
605
+ if frame_info._sd is None:
606
+ # fast fallback if file is too long
607
+ assert frame_info.filename is not None
608
+ level_tokens = [
609
+ (Token.FilenameEm, util_path.compress_user(frame_info.filename)),
610
+ (Token, " "),
611
+ (Token, call),
612
+ (Token, "\n"),
613
+ ]
614
+
615
+ _line_format = Parser(theme_name=self._theme_name).format2
616
+ assert isinstance(frame_info.code, types.CodeType)
617
+ first_line: int = frame_info.code.co_firstlineno
618
+ current_line: int = frame_info.lineno
619
+ raw_lines: list[str] = frame_info.raw_lines
620
+ index: int = current_line - first_line
621
+ assert frame_info.context is not None
622
+ if index >= frame_info.context:
623
+ start = max(index - frame_info.context, 0)
624
+ stop = index + frame_info.context
625
+ index = frame_info.context
626
+ else:
627
+ start = 0
628
+ stop = index + frame_info.context
629
+ raw_lines = raw_lines[start:stop]
630
+
631
+ # Jan 2025: may need _line_format(py3ompat.cast_unicode(s))
632
+ raw_color_err = [(s, _line_format(s, "str")) for s in raw_lines]
633
+
634
+ tb_tokens = _simple_format_traceback_lines(
635
+ current_line,
636
+ index,
637
+ raw_color_err,
638
+ lvals_toks,
639
+ theme=theme_table[self._theme_name],
640
+ )
641
+ _tb_lines: str = theme_table[self._theme_name].format(tb_tokens)
642
+
643
+ return theme_table[self._theme_name].format(level_tokens + tb_tokens)
644
+ else:
645
+ result = theme_table[self._theme_name].format(
646
+ _tokens_filename(True, frame_info.filename, lineno=frame_info.lineno)
647
+ )
648
+ result += ", " if call else ""
649
+ result += f"{call}\n"
650
+ result += theme_table[self._theme_name].format(
651
+ _format_traceback_lines(
652
+ frame_info.lines,
653
+ theme_table[self._theme_name],
654
+ self.has_colors,
655
+ lvals_toks,
656
+ )
657
+ )
658
+ return result
659
+
660
+ def prepare_header(self, etype: str, long_version: bool = False) -> str:
661
+ width = min(75, get_terminal_size()[0])
662
+ if long_version:
663
+ # Header with the exception type, python version, and date
664
+ pyver = "Python " + sys.version.split()[0] + ": " + sys.executable
665
+ date = time.ctime(time.time())
666
+ theme = theme_table[self._theme_name]
667
+ head = theme.format(
668
+ [
669
+ (Token.Topline, theme.symbols["top_line"] * width),
670
+ (Token, "\n"),
671
+ (Token.ExcName, etype),
672
+ (Token, " " * (width - len(etype) - len(pyver))),
673
+ (Token, pyver),
674
+ (Token, "\n"),
675
+ (Token, date.rjust(width)),
676
+ ]
677
+ )
678
+ head += (
679
+ "\nA problem occurred executing Python code. Here is the sequence of function"
680
+ "\ncalls leading up to the error, with the most recent (innermost) call last."
681
+ )
682
+ else:
683
+ # Simplified header
684
+ head = theme_table[self._theme_name].format(
685
+ [
686
+ (Token.ExcName, etype),
687
+ (
688
+ Token,
689
+ "Traceback (most recent call last)".rjust(width - len(etype)),
690
+ ),
691
+ ]
692
+ )
693
+
694
+ return head
695
+
696
+ def format_exception(self, etype, evalue):
697
+ # Get (safely) a string form of the exception info
698
+ try:
699
+ etype_str, evalue_str = map(str, (etype, evalue))
700
+ except:
701
+ # User exception is improperly defined.
702
+ etype, evalue = str, sys.exc_info()[:2]
703
+ etype_str, evalue_str = map(str, (etype, evalue))
704
+
705
+ # PEP-678 notes
706
+ notes = getattr(evalue, "__notes__", [])
707
+ if not isinstance(notes, Sequence) or isinstance(notes, (str, bytes)):
708
+ notes = [_safe_string(notes, "__notes__", func=repr)]
709
+
710
+ # ... and format it
711
+ return [
712
+ theme_table[self._theme_name].format(
713
+ [(Token.ExcName, etype_str), (Token, ": "), (Token, evalue_str)]
714
+ ),
715
+ *(
716
+ theme_table[self._theme_name].format(
717
+ [(Token, _safe_string(py3compat.cast_unicode(n), "note"))]
718
+ )
719
+ for n in notes
720
+ ),
721
+ ]
722
+
723
+ def format_exception_as_a_whole(
724
+ self,
725
+ etype: type,
726
+ evalue: Optional[BaseException],
727
+ etb: Optional[TracebackType],
728
+ context: int,
729
+ tb_offset: Optional[int],
730
+ ) -> list[list[str]]:
731
+ """Formats the header, traceback and exception message for a single exception.
732
+
733
+ This may be called multiple times by Python 3 exception chaining
734
+ (PEP 3134).
735
+ """
736
+ # some locals
737
+ orig_etype = etype
738
+ try:
739
+ etype = etype.__name__ # type: ignore[assignment]
740
+ except AttributeError:
741
+ pass
742
+
743
+ tb_offset = self.tb_offset if tb_offset is None else tb_offset
744
+ assert isinstance(tb_offset, int)
745
+ head = self.prepare_header(str(etype), self.long_header)
746
+ records = self.get_records(etb, context, tb_offset) if etb else []
747
+
748
+ frames = []
749
+ skipped = 0
750
+ lastrecord = len(records) - 1
751
+ for i, record in enumerate(records):
752
+ if (
753
+ not isinstance(record._sd, stack_data.RepeatedFrames)
754
+ and self.skip_hidden
755
+ ):
756
+ if (
757
+ record.frame.f_locals.get("__tracebackhide__", 0)
758
+ and i != lastrecord
759
+ ):
760
+ skipped += 1
761
+ continue
762
+ if skipped:
763
+ frames.append(
764
+ theme_table[self._theme_name].format(
765
+ [
766
+ (Token, " "),
767
+ (Token.ExcName, "[... skipping hidden %s frame]" % skipped),
768
+ (Token, "\n"),
769
+ ]
770
+ )
771
+ )
772
+ skipped = 0
773
+ frames.append(self.format_record(record))
774
+ if skipped:
775
+ frames.append(
776
+ theme_table[self._theme_name].format(
777
+ [
778
+ (Token, " "),
779
+ (Token.ExcName, "[... skipping hidden %s frame]" % skipped),
780
+ (Token, "\n"),
781
+ ]
782
+ )
783
+ )
784
+
785
+ formatted_exception = self.format_exception(etype, evalue)
786
+ if records:
787
+ frame_info = records[-1]
788
+ ipinst = get_ipython()
789
+ if ipinst is not None:
790
+ ipinst.hooks.synchronize_with_editor(
791
+ frame_info.filename, frame_info.lineno, 0
792
+ )
793
+
794
+ return [[head] + frames + formatted_exception]
795
+
796
+ def get_records(self, etb: TracebackType, context: int, tb_offset: int) -> Any:
797
+ assert etb is not None
798
+ context = context - 1
799
+ after = context // 2
800
+ before = context - after
801
+ if self.has_colors:
802
+ base_style = theme_table[self._theme_name].as_pygments_style()
803
+ style = stack_data.style_with_executing_node(base_style, self.tb_highlight)
804
+ formatter = Terminal256Formatter(style=style)
805
+ else:
806
+ formatter = None
807
+ options = stack_data.Options(
808
+ before=before,
809
+ after=after,
810
+ pygments_formatter=formatter,
811
+ )
812
+
813
+ # Let's estimate the amount of code we will have to parse/highlight.
814
+ cf: Optional[TracebackType] = etb
815
+ max_len = 0
816
+ tbs = []
817
+ while cf is not None:
818
+ try:
819
+ mod = inspect.getmodule(cf.tb_frame)
820
+ if mod is not None:
821
+ mod_name = mod.__name__
822
+ root_name, *_ = mod_name.split(".")
823
+ if root_name == "IPython":
824
+ cf = cf.tb_next
825
+ continue
826
+ max_len = get_line_number_of_frame(cf.tb_frame)
827
+
828
+ except OSError:
829
+ max_len = 0
830
+ max_len = max(max_len, max_len)
831
+ tbs.append(cf)
832
+ cf = getattr(cf, "tb_next", None)
833
+
834
+ if max_len > FAST_THRESHOLD:
835
+ FIs: list[FrameInfo] = []
836
+ for tb in tbs:
837
+ frame = tb.tb_frame # type: ignore[union-attr]
838
+ lineno = frame.f_lineno
839
+ code = frame.f_code
840
+ filename = code.co_filename
841
+ # TODO: Here we need to use before/after/
842
+ FIs.append(
843
+ FrameInfo(
844
+ "Raw frame", filename, lineno, frame, code, context=context
845
+ )
846
+ )
847
+ return FIs
848
+ res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
849
+ res2 = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
850
+ return res2
851
+
852
+ def structured_traceback(
853
+ self,
854
+ etype: type,
855
+ evalue: Optional[BaseException],
856
+ etb: Optional[TracebackType] = None,
857
+ tb_offset: Optional[int] = None,
858
+ context: int = 5,
859
+ ) -> list[str]:
860
+ """Return a nice text document describing the traceback."""
861
+ formatted_exceptions: list[list[str]] = self.format_exception_as_a_whole(
862
+ etype, evalue, etb, context, tb_offset
863
+ )
864
+
865
+ termsize = min(75, get_terminal_size()[0])
866
+ theme = theme_table[self._theme_name]
867
+ head: str = theme.format(
868
+ [
869
+ (
870
+ Token.Topline,
871
+ theme.symbols["top_line"] * termsize,
872
+ ),
873
+ ]
874
+ )
875
+ structured_traceback_parts: list[str] = [head]
876
+ chained_exceptions_tb_offset = 0
877
+ lines_of_context = 3
878
+ exception = self.get_parts_of_chained_exception(evalue)
879
+ if exception:
880
+ assert evalue is not None
881
+ formatted_exceptions += self.prepare_chained_exception_message(
882
+ evalue.__cause__
883
+ )
884
+ etype, evalue, etb = exception
885
+ else:
886
+ evalue = None
887
+ chained_exc_ids = set()
888
+ while evalue:
889
+ formatted_exceptions += self.format_exception_as_a_whole(
890
+ etype, evalue, etb, lines_of_context, chained_exceptions_tb_offset
891
+ )
892
+ exception = self.get_parts_of_chained_exception(evalue)
893
+
894
+ if exception and id(exception[1]) not in chained_exc_ids:
895
+ chained_exc_ids.add(
896
+ id(exception[1])
897
+ ) # trace exception to avoid infinite 'cause' loop
898
+ formatted_exceptions += self.prepare_chained_exception_message(
899
+ evalue.__cause__
900
+ )
901
+ etype, evalue, etb = exception
902
+ else:
903
+ evalue = None
904
+
905
+ # we want to see exceptions in a reversed order:
906
+ # the first exception should be on top
907
+ for fx in reversed(formatted_exceptions):
908
+ structured_traceback_parts += fx
909
+
910
+ return structured_traceback_parts
911
+
912
+ def debugger(self, force: bool = False) -> None:
913
+ """Call up the pdb debugger if desired, always clean up the tb
914
+ reference.
915
+
916
+ Keywords:
917
+
918
+ - force(False): by default, this routine checks the instance call_pdb
919
+ flag and does not actually invoke the debugger if the flag is false.
920
+ The 'force' option forces the debugger to activate even if the flag
921
+ is false.
922
+
923
+ If the call_pdb flag is set, the pdb interactive debugger is
924
+ invoked. In all cases, the self.tb reference to the current traceback
925
+ is deleted to prevent lingering references which hamper memory
926
+ management.
927
+
928
+ Note that each call to pdb() does an 'import readline', so if your app
929
+ requires a special setup for the readline completers, you'll have to
930
+ fix that by hand after invoking the exception handler."""
931
+
932
+ if force or self.call_pdb:
933
+ if self.pdb is None:
934
+ self.pdb = self.debugger_cls()
935
+ # the system displayhook may have changed, restore the original
936
+ # for pdb
937
+ display_trap = DisplayTrap(hook=sys.__displayhook__)
938
+ with display_trap:
939
+ self.pdb.reset()
940
+ # Find the right frame so we don't pop up inside ipython itself
941
+ if hasattr(self, "tb") and self.tb is not None: # type: ignore[has-type]
942
+ etb = self.tb # type: ignore[has-type]
943
+ else:
944
+ etb = self.tb = sys.last_traceback
945
+ while self.tb is not None and self.tb.tb_next is not None:
946
+ assert self.tb.tb_next is not None
947
+ self.tb = self.tb.tb_next
948
+ if etb and etb.tb_next:
949
+ etb = etb.tb_next
950
+ self.pdb.botframe = etb.tb_frame
951
+ # last_value should be deprecated, but last-exc sometimme not set
952
+ # please check why later and remove the getattr.
953
+ exc = (
954
+ sys.last_value
955
+ if sys.version_info < (3, 12)
956
+ else getattr(sys, "last_exc", sys.last_value)
957
+ ) # type: ignore[attr-defined]
958
+ if exc:
959
+ self.pdb.interaction(None, exc)
960
+ else:
961
+ self.pdb.interaction(None, etb)
962
+
963
+ if hasattr(self, "tb"):
964
+ del self.tb
965
+
966
+ def handler(self, info=None):
967
+ (etype, evalue, etb) = info or sys.exc_info()
968
+ self.tb = etb
969
+ ostream = self.ostream
970
+ ostream.flush()
971
+ ostream.write(self.text(etype, evalue, etb)) # type:ignore[arg-type]
972
+ ostream.write("\n")
973
+ ostream.flush()
974
+
975
+ # Changed so an instance can just be called as VerboseTB_inst() and print
976
+ # out the right info on its own.
977
+ def __call__(self, etype=None, evalue=None, etb=None):
978
+ """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
979
+ if etb is None:
980
+ self.handler()
981
+ else:
982
+ self.handler((etype, evalue, etb))
983
+ try:
984
+ self.debugger()
985
+ except KeyboardInterrupt:
986
+ print("\nKeyboardInterrupt")
987
+
988
+
989
+ # ----------------------------------------------------------------------------
990
+ class FormattedTB(VerboseTB, ListTB):
991
+ """Subclass ListTB but allow calling with a traceback.
992
+
993
+ It can thus be used as a sys.excepthook for Python > 2.1.
994
+
995
+ Also adds 'Context' and 'Verbose' modes, not available in ListTB.
996
+
997
+ Allows a tb_offset to be specified. This is useful for situations where
998
+ one needs to remove a number of topmost frames from the traceback (such as
999
+ occurs with python programs that themselves execute other python code,
1000
+ like Python shells)."""
1001
+
1002
+ mode: str
1003
+
1004
+ def __init__(
1005
+ self,
1006
+ mode="Plain",
1007
+ # TODO: no default
1008
+ theme_name="linux",
1009
+ call_pdb=False,
1010
+ ostream=None,
1011
+ tb_offset=0,
1012
+ long_header=False,
1013
+ include_vars=False,
1014
+ check_cache=None,
1015
+ debugger_cls=None,
1016
+ ):
1017
+ # NEVER change the order of this list. Put new modes at the end:
1018
+ self.valid_modes = ["Plain", "Context", "Verbose", "Minimal", "Docs"]
1019
+ self.verbose_modes = self.valid_modes[1:3]
1020
+
1021
+ VerboseTB.__init__(
1022
+ self,
1023
+ theme_name=theme_name,
1024
+ call_pdb=call_pdb,
1025
+ ostream=ostream,
1026
+ tb_offset=tb_offset,
1027
+ long_header=long_header,
1028
+ include_vars=include_vars,
1029
+ check_cache=check_cache,
1030
+ debugger_cls=debugger_cls,
1031
+ )
1032
+
1033
+ # Different types of tracebacks are joined with different separators to
1034
+ # form a single string. They are taken from this dict
1035
+ self._join_chars = dict(
1036
+ Plain="", Context="\n", Verbose="\n", Minimal="", Docs=""
1037
+ )
1038
+ # set_mode also sets the tb_join_char attribute
1039
+ self.set_mode(mode)
1040
+
1041
+ def structured_traceback(
1042
+ self,
1043
+ etype: type,
1044
+ evalue: BaseException | None,
1045
+ etb: TracebackType | None = None,
1046
+ tb_offset: int | None = None,
1047
+ context: int = 5,
1048
+ ) -> list[str]:
1049
+ tb_offset = self.tb_offset if tb_offset is None else tb_offset
1050
+ mode = self.mode
1051
+ if mode in self.verbose_modes:
1052
+ # Verbose modes need a full traceback
1053
+ return VerboseTB.structured_traceback(
1054
+ self, etype, evalue, etb, tb_offset, context
1055
+ )
1056
+ elif mode == "Docs":
1057
+ # return DocTB
1058
+ return DocTB(
1059
+ theme_name=self._theme_name,
1060
+ call_pdb=self.call_pdb,
1061
+ ostream=self.ostream,
1062
+ tb_offset=tb_offset,
1063
+ long_header=self.long_header,
1064
+ include_vars=self.include_vars,
1065
+ check_cache=self.check_cache,
1066
+ debugger_cls=self.debugger_cls,
1067
+ ).structured_traceback(
1068
+ etype, evalue, etb, tb_offset, 1
1069
+ ) # type: ignore[arg-type]
1070
+
1071
+ elif mode == "Minimal":
1072
+ return ListTB.get_exception_only(self, etype, evalue)
1073
+ else:
1074
+ # We must check the source cache because otherwise we can print
1075
+ # out-of-date source code.
1076
+ self.check_cache()
1077
+ # Now we can extract and format the exception
1078
+ return ListTB.structured_traceback(
1079
+ self, etype, evalue, etb, tb_offset, context
1080
+ )
1081
+
1082
+ def stb2text(self, stb: list[str]) -> str:
1083
+ """Convert a structured traceback (a list) to a string."""
1084
+ return self.tb_join_char.join(stb)
1085
+
1086
+ def set_mode(self, mode: Optional[str] = None) -> None:
1087
+ """Switch to the desired mode.
1088
+
1089
+ If mode is not specified, cycles through the available modes."""
1090
+
1091
+ if not mode:
1092
+ new_idx = (self.valid_modes.index(self.mode) + 1) % len(self.valid_modes)
1093
+ self.mode = self.valid_modes[new_idx]
1094
+ elif mode not in self.valid_modes:
1095
+ raise ValueError(
1096
+ "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1097
+ "Valid modes: " + str(self.valid_modes)
1098
+ )
1099
+ else:
1100
+ assert isinstance(mode, str)
1101
+ self.mode = mode
1102
+ # include variable details only in 'Verbose' mode
1103
+ self.include_vars = self.mode == self.valid_modes[2]
1104
+ # Set the join character for generating text tracebacks
1105
+ self.tb_join_char = self._join_chars[self.mode]
1106
+
1107
+ # some convenient shortcuts
1108
+ def plain(self) -> None:
1109
+ self.set_mode(self.valid_modes[0])
1110
+
1111
+ def context(self) -> None:
1112
+ self.set_mode(self.valid_modes[1])
1113
+
1114
+ def verbose(self) -> None:
1115
+ self.set_mode(self.valid_modes[2])
1116
+
1117
+ def minimal(self) -> None:
1118
+ self.set_mode(self.valid_modes[3])
1119
+
1120
+
1121
+ # ----------------------------------------------------------------------------
1122
+ class AutoFormattedTB(FormattedTB):
1123
+ """A traceback printer which can be called on the fly.
1124
+
1125
+ It will find out about exceptions by itself.
1126
+
1127
+ A brief example::
1128
+
1129
+ AutoTB = AutoFormattedTB(mode = 'Verbose', theme_name='linux')
1130
+ try:
1131
+ ...
1132
+ except:
1133
+ AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1134
+ """
1135
+
1136
+ def __call__(
1137
+ self,
1138
+ etype: type | None = None,
1139
+ evalue: BaseException | None = None,
1140
+ etb: TracebackType | None = None,
1141
+ out: Any = None,
1142
+ tb_offset: int | None = None,
1143
+ ) -> None:
1144
+ """Print out a formatted exception traceback.
1145
+
1146
+ Optional arguments:
1147
+ - out: an open file-like object to direct output to.
1148
+
1149
+ - tb_offset: the number of frames to skip over in the stack, on a
1150
+ per-call basis (this overrides temporarily the instance's tb_offset
1151
+ given at initialization time."""
1152
+
1153
+ if out is None:
1154
+ out = self.ostream
1155
+ out.flush()
1156
+ out.write(self.text(etype, evalue, etb, tb_offset)) # type:ignore[arg-type]
1157
+ out.write("\n")
1158
+ out.flush()
1159
+ # FIXME: we should remove the auto pdb behavior from here and leave
1160
+ # that to the clients.
1161
+ try:
1162
+ self.debugger()
1163
+ except KeyboardInterrupt:
1164
+ print("\nKeyboardInterrupt")
1165
+
1166
+ def structured_traceback(
1167
+ self,
1168
+ etype: type,
1169
+ evalue: Optional[BaseException],
1170
+ etb: Optional[TracebackType] = None,
1171
+ tb_offset: Optional[int] = None,
1172
+ context: int = 5,
1173
+ ) -> list[str]:
1174
+ # tb: TracebackType or tupleof tb types ?
1175
+ if etype is None:
1176
+ etype, evalue, etb = sys.exc_info()
1177
+ if isinstance(etb, tuple):
1178
+ # tb is a tuple if this is a chained exception.
1179
+ self.tb = etb[0]
1180
+ else:
1181
+ self.tb = etb
1182
+ return FormattedTB.structured_traceback(
1183
+ self, etype, evalue, etb, tb_offset, context
1184
+ )
1185
+
1186
+
1187
+ # ---------------------------------------------------------------------------
1188
+
1189
+
1190
+ # A simple class to preserve Nathan's original functionality.
1191
+ class ColorTB(FormattedTB):
1192
+ """Deprecated since IPython 9.0."""
1193
+
1194
+ def __init__(self, *args, **kwargs):
1195
+ warnings.warn(
1196
+ "Deprecated since IPython 9.0 use FormattedTB directly ColorTB is just an alias",
1197
+ DeprecationWarning,
1198
+ stacklevel=2,
1199
+ )
1200
+
1201
+ super().__init__(*args, **kwargs)
1202
+
1203
+
1204
+ class SyntaxTB(ListTB):
1205
+ """Extension which holds some state: the last exception value"""
1206
+
1207
+ last_syntax_error: BaseException | None
1208
+
1209
+ def __init__(self, *, theme_name):
1210
+ super().__init__(theme_name=theme_name)
1211
+ self.last_syntax_error = None
1212
+
1213
+ def __call__(self, etype, value, elist):
1214
+ self.last_syntax_error = value
1215
+
1216
+ super().__call__(etype, value, elist)
1217
+
1218
+ def structured_traceback(
1219
+ self,
1220
+ etype: type,
1221
+ evalue: BaseException | None,
1222
+ etb: TracebackType | None = None,
1223
+ tb_offset: int | None = None,
1224
+ context: int = 5,
1225
+ ) -> list[str]:
1226
+ value = evalue
1227
+ # If the source file has been edited, the line in the syntax error can
1228
+ # be wrong (retrieved from an outdated cache). This replaces it with
1229
+ # the current value.
1230
+ if (
1231
+ isinstance(value, SyntaxError)
1232
+ and isinstance(value.filename, str)
1233
+ and isinstance(value.lineno, int)
1234
+ ):
1235
+ linecache.checkcache(value.filename)
1236
+ newtext = linecache.getline(value.filename, value.lineno)
1237
+ if newtext:
1238
+ value.text = newtext
1239
+ self.last_syntax_error = value
1240
+ return super(SyntaxTB, self).structured_traceback(
1241
+ etype, value, etb, tb_offset=tb_offset, context=context
1242
+ )
1243
+
1244
+ def clear_err_state(self) -> Any | None:
1245
+ """Return the current error state and clear it"""
1246
+ e = self.last_syntax_error
1247
+ self.last_syntax_error = None
1248
+ return e
1249
+
1250
+ def stb2text(self, stb: list[str]) -> str:
1251
+ """Convert a structured traceback (a list) to a string."""
1252
+ return "".join(stb)
temp_venv/lib/python3.13/site-packages/IPython/core/usage.py ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """Usage information for the main IPython applications.
3
+ """
4
+ #-----------------------------------------------------------------------------
5
+ # Copyright (C) 2008-2011 The IPython Development Team
6
+ # Copyright (C) 2001-2007 Fernando Perez. <[email protected]>
7
+ #
8
+ # Distributed under the terms of the BSD License. The full license is in
9
+ # the file COPYING, distributed as part of this software.
10
+ #-----------------------------------------------------------------------------
11
+
12
+ import sys
13
+ from IPython.core import release
14
+
15
+ cl_usage = """\
16
+ =========
17
+ IPython
18
+ =========
19
+
20
+ Tools for Interactive Computing in Python
21
+ =========================================
22
+
23
+ A Python shell with automatic history (input and output), dynamic object
24
+ introspection, easier configuration, command completion, access to the
25
+ system shell and more. IPython can also be embedded in running programs.
26
+
27
+
28
+ Usage
29
+
30
+ ipython [subcommand] [options] [-c cmd | -m mod | file] [--] [arg] ...
31
+
32
+ If invoked with no options, it executes the file and exits, passing the
33
+ remaining arguments to the script, just as if you had specified the same
34
+ command with python. You may need to specify `--` before args to be passed
35
+ to the script, to prevent IPython from attempting to parse them. If you
36
+ specify the option `-i` before the filename, it will enter an interactive
37
+ IPython session after running the script, rather than exiting. Files ending
38
+ in .py will be treated as normal Python, but files ending in .ipy can
39
+ contain special IPython syntax (magic commands, shell expansions, etc.).
40
+
41
+ Almost all configuration in IPython is available via the command-line. Do
42
+ `ipython --help-all` to see all available options. For persistent
43
+ configuration, look into your `ipython_config.py` configuration file for
44
+ details.
45
+
46
+ This file is typically installed in the `IPYTHONDIR` directory, and there
47
+ is a separate configuration directory for each profile. The default profile
48
+ directory will be located in $IPYTHONDIR/profile_default. IPYTHONDIR
49
+ defaults to to `$HOME/.ipython`. For Windows users, $HOME resolves to
50
+ C:\\Users\\YourUserName in most instances.
51
+
52
+ To initialize a profile with the default configuration file, do::
53
+
54
+ $> ipython profile create
55
+
56
+ and start editing `IPYTHONDIR/profile_default/ipython_config.py`
57
+
58
+ In IPython's documentation, we will refer to this directory as
59
+ `IPYTHONDIR`, you can change its default location by creating an
60
+ environment variable with this name and setting it to the desired path.
61
+
62
+ For more information, see the manual available in HTML and PDF in your
63
+ installation, or online at https://ipython.org/documentation.html.
64
+ """
65
+
66
+ interactive_usage = """
67
+ IPython -- An enhanced Interactive Python
68
+ =========================================
69
+
70
+ IPython offers a fully compatible replacement for the standard Python
71
+ interpreter, with convenient shell features, special commands, command
72
+ history mechanism and output results caching.
73
+
74
+ At your system command line, type 'ipython -h' to see the command line
75
+ options available. This document only describes interactive features.
76
+
77
+ GETTING HELP
78
+ ------------
79
+
80
+ Within IPython you have various way to access help:
81
+
82
+ ? -> Introduction and overview of IPython's features (this screen).
83
+ object? -> Details about 'object'.
84
+ object?? -> More detailed, verbose information about 'object'.
85
+ %quickref -> Quick reference of all IPython specific syntax and magics.
86
+ help -> Access Python's own help system.
87
+
88
+ If you are in terminal IPython you can quit this screen by pressing `q`.
89
+
90
+
91
+ MAIN FEATURES
92
+ -------------
93
+
94
+ * Access to the standard Python help with object docstrings and the Python
95
+ manuals. Simply type 'help' (no quotes) to invoke it.
96
+
97
+ * Magic commands: type %magic for information on the magic subsystem.
98
+
99
+ * System command aliases, via the %alias command or the configuration file(s).
100
+
101
+ * Dynamic object information:
102
+
103
+ Typing ?word or word? prints detailed information about an object. Certain
104
+ long strings (code, etc.) get snipped in the center for brevity.
105
+
106
+ Typing ??word or word?? gives access to the full information without
107
+ snipping long strings. Strings that are longer than the screen are printed
108
+ through the less pager.
109
+
110
+ The ?/?? system gives access to the full source code for any object (if
111
+ available), shows function prototypes and other useful information.
112
+
113
+ If you just want to see an object's docstring, type '%pdoc object' (without
114
+ quotes, and without % if you have automagic on).
115
+
116
+ * Tab completion in the local namespace:
117
+
118
+ At any time, hitting tab will complete any available python commands or
119
+ variable names, and show you a list of the possible completions if there's
120
+ no unambiguous one. It will also complete filenames in the current directory.
121
+
122
+ * Search previous command history in multiple ways:
123
+
124
+ - Start typing, and then use arrow keys up/down or (Ctrl-p/Ctrl-n) to search
125
+ through the history items that match what you've typed so far.
126
+
127
+ - Hit Ctrl-r: opens a search prompt. Begin typing and the system searches
128
+ your history for lines that match what you've typed so far, completing as
129
+ much as it can.
130
+
131
+ - %hist: search history by index.
132
+
133
+ * Persistent command history across sessions.
134
+
135
+ * Logging of input with the ability to save and restore a working session.
136
+
137
+ * System shell with !. Typing !ls will run 'ls' in the current directory.
138
+
139
+ * The reload command does a 'deep' reload of a module: changes made to the
140
+ module since you imported will actually be available without having to exit.
141
+
142
+ * Verbose and colored exception traceback printouts. See the magic xmode and
143
+ xcolor functions for details (just type %magic).
144
+
145
+ * Input caching system:
146
+
147
+ IPython offers numbered prompts (In/Out) with input and output caching. All
148
+ input is saved and can be retrieved as variables (besides the usual arrow
149
+ key recall).
150
+
151
+ The following GLOBAL variables always exist (so don't overwrite them!):
152
+ _i: stores previous input.
153
+ _ii: next previous.
154
+ _iii: next-next previous.
155
+ _ih : a list of all input _ih[n] is the input from line n.
156
+
157
+ Additionally, global variables named _i<n> are dynamically created (<n>
158
+ being the prompt counter), such that _i<n> == _ih[<n>]
159
+
160
+ For example, what you typed at prompt 14 is available as _i14 and _ih[14].
161
+
162
+ You can create macros which contain multiple input lines from this history,
163
+ for later re-execution, with the %macro function.
164
+
165
+ The history function %hist allows you to see any part of your input history
166
+ by printing a range of the _i variables. Note that inputs which contain
167
+ magic functions (%) appear in the history with a prepended comment. This is
168
+ because they aren't really valid Python code, so you can't exec them.
169
+
170
+ * Output caching system:
171
+
172
+ For output that is returned from actions, a system similar to the input
173
+ cache exists but using _ instead of _i. Only actions that produce a result
174
+ (NOT assignments, for example) are cached. If you are familiar with
175
+ Mathematica, IPython's _ variables behave exactly like Mathematica's %
176
+ variables.
177
+
178
+ The following GLOBAL variables always exist (so don't overwrite them!):
179
+ _ (one underscore): previous output.
180
+ __ (two underscores): next previous.
181
+ ___ (three underscores): next-next previous.
182
+
183
+ Global variables named _<n> are dynamically created (<n> being the prompt
184
+ counter), such that the result of output <n> is always available as _<n>.
185
+
186
+ Finally, a global dictionary named _oh exists with entries for all lines
187
+ which generated output.
188
+
189
+ * Directory history:
190
+
191
+ Your history of visited directories is kept in the global list _dh, and the
192
+ magic %cd command can be used to go to any entry in that list.
193
+
194
+ * Auto-parentheses and auto-quotes (adapted from Nathan Gray's LazyPython)
195
+
196
+ 1. Auto-parentheses
197
+
198
+ Callable objects (i.e. functions, methods, etc) can be invoked like
199
+ this (notice the commas between the arguments)::
200
+
201
+ In [1]: callable_ob arg1, arg2, arg3
202
+
203
+ and the input will be translated to this::
204
+
205
+ callable_ob(arg1, arg2, arg3)
206
+
207
+ This feature is off by default (in rare cases it can produce
208
+ undesirable side-effects), but you can activate it at the command-line
209
+ by starting IPython with `--autocall 1`, set it permanently in your
210
+ configuration file, or turn on at runtime with `%autocall 1`.
211
+
212
+ You can force auto-parentheses by using '/' as the first character
213
+ of a line. For example::
214
+
215
+ In [1]: /globals # becomes 'globals()'
216
+
217
+ Note that the '/' MUST be the first character on the line! This
218
+ won't work::
219
+
220
+ In [2]: print /globals # syntax error
221
+
222
+ In most cases the automatic algorithm should work, so you should
223
+ rarely need to explicitly invoke /. One notable exception is if you
224
+ are trying to call a function with a list of tuples as arguments (the
225
+ parenthesis will confuse IPython)::
226
+
227
+ In [1]: zip (1,2,3),(4,5,6) # won't work
228
+
229
+ but this will work::
230
+
231
+ In [2]: /zip (1,2,3),(4,5,6)
232
+ ------> zip ((1,2,3),(4,5,6))
233
+ Out[2]= [(1, 4), (2, 5), (3, 6)]
234
+
235
+ IPython tells you that it has altered your command line by
236
+ displaying the new command line preceded by -->. e.g.::
237
+
238
+ In [18]: callable list
239
+ -------> callable (list)
240
+
241
+ 2. Auto-Quoting
242
+
243
+ You can force auto-quoting of a function's arguments by using ',' as
244
+ the first character of a line. For example::
245
+
246
+ In [1]: ,my_function /home/me # becomes my_function("/home/me")
247
+
248
+ If you use ';' instead, the whole argument is quoted as a single
249
+ string (while ',' splits on whitespace)::
250
+
251
+ In [2]: ,my_function a b c # becomes my_function("a","b","c")
252
+ In [3]: ;my_function a b c # becomes my_function("a b c")
253
+
254
+ Note that the ',' MUST be the first character on the line! This
255
+ won't work::
256
+
257
+ In [4]: x = ,my_function /home/me # syntax error
258
+ """
259
+
260
+ interactive_usage_min = """\
261
+ An enhanced console for Python.
262
+ Some of its features are:
263
+ - Tab completion in the local namespace.
264
+ - Logging of input, see command-line options.
265
+ - System shell escape via ! , eg !ls.
266
+ - Magic commands, starting with a % (like %ls, %pwd, %cd, etc.)
267
+ - Keeps track of locally defined variables via %who, %whos.
268
+ - Show object information with a ? eg ?x or x? (use ?? for more info).
269
+ """
270
+
271
+ quick_reference = r"""
272
+ IPython -- An enhanced Interactive Python - Quick Reference Card
273
+ ================================================================
274
+
275
+ obj?, obj?? : Get help, or more help for object (also works as
276
+ ?obj, ??obj).
277
+ ?foo.*abc* : List names in 'foo' containing 'abc' in them.
278
+ %magic : Information about IPython's 'magic' % functions.
279
+
280
+ Magic functions are prefixed by % or %%, and typically take their arguments
281
+ without parentheses, quotes or even commas for convenience. Line magics take a
282
+ single % and cell magics are prefixed with two %%.
283
+
284
+ Example magic function calls:
285
+
286
+ %alias d ls -F : 'd' is now an alias for 'ls -F'
287
+ alias d ls -F : Works if 'alias' not a python name
288
+ alist = %alias : Get list of aliases to 'alist'
289
+ cd /usr/share : Obvious. cd -<tab> to choose from visited dirs.
290
+ %cd?? : See help AND source for magic %cd
291
+ %timeit x=10 : time the 'x=10' statement with high precision.
292
+ %%timeit x=2**100
293
+ x**100 : time 'x**100' with a setup of 'x=2**100'; setup code is not
294
+ counted. This is an example of a cell magic.
295
+
296
+ System commands:
297
+
298
+ !cp a.txt b/ : System command escape, calls os.system()
299
+ cp a.txt b/ : after %rehashx, most system commands work without !
300
+ cp ${f}.txt $bar : Variable expansion in magics and system commands
301
+ files = !ls /usr : Capture system command output
302
+ files.s, files.l, files.n: "a b c", ['a','b','c'], 'a\nb\nc'
303
+
304
+ History:
305
+
306
+ _i, _ii, _iii : Previous, next previous, next next previous input
307
+ _i4, _ih[2:5] : Input history line 4, lines 2-4
308
+ exec(_i81) : Execute input history line #81 again
309
+ %rep 81 : Edit input history line #81
310
+ _, __, ___ : previous, next previous, next next previous output
311
+ _dh : Directory history
312
+ _oh : Output history
313
+ %hist : Command history of current session.
314
+ %hist -g foo : Search command history of (almost) all sessions for 'foo'.
315
+ %hist -g : Command history of (almost) all sessions.
316
+ %hist 1/2-8 : Command history containing lines 2-8 of session 1.
317
+ %hist 1/ ~2/ : Command history of session 1 and 2 sessions before current.
318
+ %hist ~8/1-~6/5 : Command history from line 1 of 8 sessions ago to
319
+ line 5 of 6 sessions ago.
320
+ %edit 0/ : Open editor to execute code with history of current session.
321
+
322
+ Autocall:
323
+
324
+ f 1,2 : f(1,2) # Off by default, enable with %autocall magic.
325
+ /f 1,2 : f(1,2) (forced autoparen)
326
+ ,f 1 2 : f("1","2")
327
+ ;f 1 2 : f("1 2")
328
+
329
+ Remember: TAB completion works in many contexts, not just file names
330
+ or python names.
331
+
332
+ The following magic functions are currently available:
333
+
334
+ """
335
+
336
+ default_banner_parts = ["Python %s\n"%sys.version.split("\n")[0],
337
+ "Type 'copyright', 'credits' or 'license' for more information\n" ,
338
+ "IPython {version} -- An enhanced Interactive Python. Type '?' for help.\n".format(version=release.version),
339
+ ]
340
+
341
+ default_banner = ''.join(default_banner_parts)
temp_venv/lib/python3.13/site-packages/IPython/sphinxext/__init__.py ADDED
File without changes
temp_venv/lib/python3.13/site-packages/IPython/sphinxext/custom_doctests.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Handlers for IPythonDirective's @doctest pseudo-decorator.
3
+
4
+ The Sphinx extension that provides support for embedded IPython code provides
5
+ a pseudo-decorator @doctest, which treats the input/output block as a
6
+ doctest, raising a RuntimeError during doc generation if the actual output
7
+ (after running the input) does not match the expected output.
8
+
9
+ An example usage is:
10
+
11
+ .. code-block:: rst
12
+
13
+ .. ipython::
14
+
15
+ In [1]: x = 1
16
+
17
+ @doctest
18
+ In [2]: x + 2
19
+ Out[3]: 3
20
+
21
+ One can also provide arguments to the decorator. The first argument should be
22
+ the name of a custom handler. The specification of any other arguments is
23
+ determined by the handler. For example,
24
+
25
+ .. code-block:: rst
26
+
27
+ .. ipython::
28
+
29
+ @doctest float
30
+ In [154]: 0.1 + 0.2
31
+ Out[154]: 0.3
32
+
33
+ allows the actual output ``0.30000000000000004`` to match the expected output
34
+ due to a comparison with `np.allclose`.
35
+
36
+ This module contains handlers for the @doctest pseudo-decorator. Handlers
37
+ should have the following function signature::
38
+
39
+ handler(sphinx_shell, args, input_lines, found, submitted)
40
+
41
+ where `sphinx_shell` is the embedded Sphinx shell, `args` contains the list
42
+ of arguments that follow: '@doctest handler_name', `input_lines` contains
43
+ a list of the lines relevant to the current doctest, `found` is a string
44
+ containing the output from the IPython shell, and `submitted` is a string
45
+ containing the expected output from the IPython shell.
46
+
47
+ Handlers must be registered in the `doctests` dict at the end of this module.
48
+
49
+ """
50
+
51
+ def str_to_array(s):
52
+ """
53
+ Simplistic converter of strings from repr to float NumPy arrays.
54
+
55
+ If the repr representation has ellipsis in it, then this will fail.
56
+
57
+ Parameters
58
+ ----------
59
+ s : str
60
+ The repr version of a NumPy array.
61
+
62
+ Examples
63
+ --------
64
+ >>> s = "array([ 0.3, inf, nan])"
65
+ >>> a = str_to_array(s)
66
+
67
+ """
68
+ import numpy as np
69
+
70
+ # Need to make sure eval() knows about inf and nan.
71
+ # This also assumes default printoptions for NumPy.
72
+ from numpy import inf, nan
73
+
74
+ if s.startswith(u'array'):
75
+ # Remove array( and )
76
+ s = s[6:-1]
77
+
78
+ if s.startswith(u'['):
79
+ a = np.array(eval(s), dtype=float)
80
+ else:
81
+ # Assume its a regular float. Force 1D so we can index into it.
82
+ a = np.atleast_1d(float(s))
83
+ return a
84
+
85
+ def float_doctest(sphinx_shell, args, input_lines, found, submitted):
86
+ """
87
+ Doctest which allow the submitted output to vary slightly from the input.
88
+
89
+ Here is how it might appear in an rst file:
90
+
91
+ .. code-block:: rst
92
+
93
+ .. ipython::
94
+
95
+ @doctest float
96
+ In [1]: 0.1 + 0.2
97
+ Out[1]: 0.3
98
+
99
+ """
100
+ import numpy as np
101
+
102
+ if len(args) == 2:
103
+ rtol = 1e-05
104
+ atol = 1e-08
105
+ else:
106
+ # Both must be specified if any are specified.
107
+ try:
108
+ rtol = float(args[2])
109
+ atol = float(args[3])
110
+ except IndexError:
111
+ e = ("Both `rtol` and `atol` must be specified "
112
+ "if either are specified: {0}".format(args))
113
+ raise IndexError(e) from e
114
+
115
+ try:
116
+ submitted = str_to_array(submitted)
117
+ found = str_to_array(found)
118
+ except:
119
+ # For example, if the array is huge and there are ellipsis in it.
120
+ error = True
121
+ else:
122
+ found_isnan = np.isnan(found)
123
+ submitted_isnan = np.isnan(submitted)
124
+ error = not np.allclose(found_isnan, submitted_isnan)
125
+ error |= not np.allclose(found[~found_isnan],
126
+ submitted[~submitted_isnan],
127
+ rtol=rtol, atol=atol)
128
+
129
+ TAB = ' ' * 4
130
+ directive = sphinx_shell.directive
131
+ if directive is None:
132
+ source = 'Unavailable'
133
+ content = 'Unavailable'
134
+ else:
135
+ source = directive.state.document.current_source
136
+ # Add tabs and make into a single string.
137
+ content = '\n'.join([TAB + line for line in directive.content])
138
+
139
+ if error:
140
+
141
+ e = ('doctest float comparison failure\n\n'
142
+ 'Document source: {0}\n\n'
143
+ 'Raw content: \n{1}\n\n'
144
+ 'On input line(s):\n{TAB}{2}\n\n'
145
+ 'we found output:\n{TAB}{3}\n\n'
146
+ 'instead of the expected:\n{TAB}{4}\n\n')
147
+ e = e.format(source, content, '\n'.join(input_lines), repr(found),
148
+ repr(submitted), TAB=TAB)
149
+ raise RuntimeError(e)
150
+
151
+ # dict of allowable doctest handlers. The key represents the first argument
152
+ # that must be given to @doctest in order to activate the handler.
153
+ doctests = {
154
+ 'float': float_doctest,
155
+ }
temp_venv/lib/python3.13/site-packages/IPython/sphinxext/ipython_console_highlighting.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ reST directive for syntax-highlighting ipython interactive sessions.
3
+
4
+ """
5
+
6
+ from sphinx import highlighting
7
+ from ipython_pygments_lexers import IPyLexer
8
+
9
+
10
+ def setup(app):
11
+ """Setup as a sphinx extension."""
12
+
13
+ # This is only a lexer, so adding it below to pygments appears sufficient.
14
+ # But if somebody knows what the right API usage should be to do that via
15
+ # sphinx, by all means fix it here. At least having this setup.py
16
+ # suppresses the sphinx warning we'd get without it.
17
+ metadata = {"parallel_read_safe": True, "parallel_write_safe": True}
18
+ return metadata
19
+
20
+
21
+ # Register the extension as a valid pygments lexer.
22
+ # Alternatively, we could register the lexer with pygments instead. This would
23
+ # require using setuptools entrypoints: http://pygments.org/docs/plugins
24
+
25
+ ipy3 = IPyLexer()
26
+
27
+ highlighting.lexers["ipython"] = ipy3
28
+ highlighting.lexers["ipython3"] = ipy3
temp_venv/lib/python3.13/site-packages/IPython/sphinxext/ipython_directive.py ADDED
@@ -0,0 +1,1278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Sphinx directive to support embedded IPython code.
4
+
5
+ IPython provides an extension for `Sphinx <http://www.sphinx-doc.org/>`_ to
6
+ highlight and run code.
7
+
8
+ This directive allows pasting of entire interactive IPython sessions, prompts
9
+ and all, and their code will actually get re-executed at doc build time, with
10
+ all prompts renumbered sequentially. It also allows you to input code as a pure
11
+ python input by giving the argument python to the directive. The output looks
12
+ like an interactive ipython section.
13
+
14
+ Here is an example of how the IPython directive can
15
+ **run** python code, at build time.
16
+
17
+ .. ipython::
18
+
19
+ In [1]: 1+1
20
+
21
+ In [1]: import datetime
22
+ ...: datetime.date.fromisoformat('2022-02-22')
23
+
24
+ It supports IPython construct that plain
25
+ Python does not understand (like magics):
26
+
27
+ .. ipython::
28
+
29
+ In [0]: import time
30
+
31
+ In [0]: %pdoc time.sleep
32
+
33
+ This will also support top-level async when using IPython 7.0+
34
+
35
+ .. ipython::
36
+
37
+ In [2]: import asyncio
38
+ ...: print('before')
39
+ ...: await asyncio.sleep(1)
40
+ ...: print('after')
41
+
42
+
43
+ The namespace will persist across multiple code chucks, Let's define a variable:
44
+
45
+ .. ipython::
46
+
47
+ In [0]: who = "World"
48
+
49
+ And now say hello:
50
+
51
+ .. ipython::
52
+
53
+ In [0]: print('Hello,', who)
54
+
55
+ If the current section raises an exception, you can add the ``:okexcept:`` flag
56
+ to the current block, otherwise the build will fail.
57
+
58
+ .. ipython::
59
+ :okexcept:
60
+
61
+ In [1]: 1/0
62
+
63
+ IPython Sphinx directive module
64
+ ===============================
65
+
66
+ To enable this directive, simply list it in your Sphinx ``conf.py`` file
67
+ (making sure the directory where you placed it is visible to sphinx, as is
68
+ needed for all Sphinx directives). For example, to enable syntax highlighting
69
+ and the IPython directive::
70
+
71
+ extensions = ['IPython.sphinxext.ipython_console_highlighting',
72
+ 'IPython.sphinxext.ipython_directive']
73
+
74
+ The IPython directive outputs code-blocks with the language 'ipython'. So
75
+ if you do not have the syntax highlighting extension enabled as well, then
76
+ all rendered code-blocks will be uncolored. By default this directive assumes
77
+ that your prompts are unchanged IPython ones, but this can be customized.
78
+ The configurable options that can be placed in conf.py are:
79
+
80
+ ipython_savefig_dir:
81
+ The directory in which to save the figures. This is relative to the
82
+ Sphinx source directory. The default is `html_static_path`.
83
+ ipython_rgxin:
84
+ The compiled regular expression to denote the start of IPython input
85
+ lines. The default is ``re.compile('In \\[(\\d+)\\]:\\s?(.*)\\s*')``. You
86
+ shouldn't need to change this.
87
+ ipython_warning_is_error: [default to True]
88
+ Fail the build if something unexpected happen, for example if a block raise
89
+ an exception but does not have the `:okexcept:` flag. The exact behavior of
90
+ what is considered strict, may change between the sphinx directive version.
91
+ ipython_rgxout:
92
+ The compiled regular expression to denote the start of IPython output
93
+ lines. The default is ``re.compile('Out\\[(\\d+)\\]:\\s?(.*)\\s*')``. You
94
+ shouldn't need to change this.
95
+ ipython_promptin:
96
+ The string to represent the IPython input prompt in the generated ReST.
97
+ The default is ``'In [%d]:'``. This expects that the line numbers are used
98
+ in the prompt.
99
+ ipython_promptout:
100
+ The string to represent the IPython prompt in the generated ReST. The
101
+ default is ``'Out [%d]:'``. This expects that the line numbers are used
102
+ in the prompt.
103
+ ipython_mplbackend:
104
+ The string which specifies if the embedded Sphinx shell should import
105
+ Matplotlib and set the backend. The value specifies a backend that is
106
+ passed to `matplotlib.use()` before any lines in `ipython_execlines` are
107
+ executed. If not specified in conf.py, then the default value of 'agg' is
108
+ used. To use the IPython directive without matplotlib as a dependency, set
109
+ the value to `None`. It may end up that matplotlib is still imported
110
+ if the user specifies so in `ipython_execlines` or makes use of the
111
+ @savefig pseudo decorator.
112
+ ipython_execlines:
113
+ A list of strings to be exec'd in the embedded Sphinx shell. Typical
114
+ usage is to make certain packages always available. Set this to an empty
115
+ list if you wish to have no imports always available. If specified in
116
+ ``conf.py`` as `None`, then it has the effect of making no imports available.
117
+ If omitted from conf.py altogether, then the default value of
118
+ ['import numpy as np', 'import matplotlib.pyplot as plt'] is used.
119
+ ipython_holdcount
120
+ When the @suppress pseudo-decorator is used, the execution count can be
121
+ incremented or not. The default behavior is to hold the execution count,
122
+ corresponding to a value of `True`. Set this to `False` to increment
123
+ the execution count after each suppressed command.
124
+
125
+ As an example, to use the IPython directive when `matplotlib` is not available,
126
+ one sets the backend to `None`::
127
+
128
+ ipython_mplbackend = None
129
+
130
+ An example usage of the directive is:
131
+
132
+ .. code-block:: rst
133
+
134
+ .. ipython::
135
+
136
+ In [1]: x = 1
137
+
138
+ In [2]: y = x**2
139
+
140
+ In [3]: print(y)
141
+
142
+ See http://matplotlib.org/sampledoc/ipython_directive.html for additional
143
+ documentation.
144
+
145
+ Pseudo-Decorators
146
+ =================
147
+
148
+ Note: Only one decorator is supported per input. If more than one decorator
149
+ is specified, then only the last one is used.
150
+
151
+ In addition to the Pseudo-Decorators/options described at the above link,
152
+ several enhancements have been made. The directive will emit a message to the
153
+ console at build-time if code-execution resulted in an exception or warning.
154
+ You can suppress these on a per-block basis by specifying the :okexcept:
155
+ or :okwarning: options:
156
+
157
+ .. code-block:: rst
158
+
159
+ .. ipython::
160
+ :okexcept:
161
+ :okwarning:
162
+
163
+ In [1]: 1/0
164
+ In [2]: # raise warning.
165
+
166
+ To Do
167
+ =====
168
+
169
+ - Turn the ad-hoc test() function into a real test suite.
170
+ - Break up ipython-specific functionality from matplotlib stuff into better
171
+ separated code.
172
+
173
+ """
174
+
175
+ # Authors
176
+ # =======
177
+ #
178
+ # - John D Hunter: original author.
179
+ # - Fernando Perez: refactoring, documentation, cleanups, port to 0.11.
180
+ # - VáclavŠmilauer <eudoxos-AT-arcig.cz>: Prompt generalizations.
181
+ # - Skipper Seabold, refactoring, cleanups, pure python addition
182
+
183
+ #-----------------------------------------------------------------------------
184
+ # Imports
185
+ #-----------------------------------------------------------------------------
186
+
187
+ # Stdlib
188
+ import atexit
189
+ import errno
190
+ import os
191
+ import pathlib
192
+ import re
193
+ import sys
194
+ import tempfile
195
+ import ast
196
+ import warnings
197
+ import shutil
198
+ from io import StringIO
199
+ from typing import Any, Dict, Set
200
+
201
+ # Third-party
202
+ from docutils.parsers.rst import directives
203
+ from docutils.parsers.rst import Directive
204
+ from sphinx.util import logging
205
+
206
+ # Our own
207
+ from traitlets.config import Config
208
+ from IPython import InteractiveShell
209
+ from IPython.core.profiledir import ProfileDir
210
+
211
+ use_matplotlib = False
212
+ try:
213
+ import matplotlib
214
+ use_matplotlib = True
215
+ except Exception:
216
+ pass
217
+
218
+ #-----------------------------------------------------------------------------
219
+ # Globals
220
+ #-----------------------------------------------------------------------------
221
+ # for tokenizing blocks
222
+ COMMENT, INPUT, OUTPUT = range(3)
223
+
224
+ PSEUDO_DECORATORS = ["suppress", "verbatim", "savefig", "doctest"]
225
+
226
+ #-----------------------------------------------------------------------------
227
+ # Functions and class declarations
228
+ #-----------------------------------------------------------------------------
229
+
230
+ def block_parser(part, rgxin, rgxout, fmtin, fmtout):
231
+ """
232
+ part is a string of ipython text, comprised of at most one
233
+ input, one output, comments, and blank lines. The block parser
234
+ parses the text into a list of::
235
+
236
+ blocks = [ (TOKEN0, data0), (TOKEN1, data1), ...]
237
+
238
+ where TOKEN is one of [COMMENT | INPUT | OUTPUT ] and
239
+ data is, depending on the type of token::
240
+
241
+ COMMENT : the comment string
242
+
243
+ INPUT: the (DECORATOR, INPUT_LINE, REST) where
244
+ DECORATOR: the input decorator (or None)
245
+ INPUT_LINE: the input as string (possibly multi-line)
246
+ REST : any stdout generated by the input line (not OUTPUT)
247
+
248
+ OUTPUT: the output string, possibly multi-line
249
+
250
+ """
251
+ block = []
252
+ lines = part.split('\n')
253
+ N = len(lines)
254
+ i = 0
255
+ decorator = None
256
+ while 1:
257
+
258
+ if i==N:
259
+ # nothing left to parse -- the last line
260
+ break
261
+
262
+ line = lines[i]
263
+ i += 1
264
+ line_stripped = line.strip()
265
+ if line_stripped.startswith('#'):
266
+ block.append((COMMENT, line))
267
+ continue
268
+
269
+ if any(
270
+ line_stripped.startswith("@" + pseudo_decorator)
271
+ for pseudo_decorator in PSEUDO_DECORATORS
272
+ ):
273
+ if decorator:
274
+ raise RuntimeError(
275
+ "Applying multiple pseudo-decorators on one line is not supported"
276
+ )
277
+ else:
278
+ decorator = line_stripped
279
+ continue
280
+
281
+ # does this look like an input line?
282
+ matchin = rgxin.match(line)
283
+ if matchin:
284
+ lineno, inputline = int(matchin.group(1)), matchin.group(2)
285
+
286
+ # the ....: continuation string
287
+ continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
288
+ Nc = len(continuation)
289
+ # input lines can continue on for more than one line, if
290
+ # we have a '\' line continuation char or a function call
291
+ # echo line 'print'. The input line can only be
292
+ # terminated by the end of the block or an output line, so
293
+ # we parse out the rest of the input line if it is
294
+ # multiline as well as any echo text
295
+
296
+ rest = []
297
+ while i<N:
298
+
299
+ # look ahead; if the next line is blank, or a comment, or
300
+ # an output line, we're done
301
+
302
+ nextline = lines[i]
303
+ matchout = rgxout.match(nextline)
304
+ # print("nextline=%s, continuation=%s, starts=%s"%(nextline, continuation, nextline.startswith(continuation)))
305
+ if matchout or nextline.startswith('#'):
306
+ break
307
+ elif nextline.startswith(continuation):
308
+ # The default ipython_rgx* treat the space following the colon as optional.
309
+ # However, If the space is there we must consume it or code
310
+ # employing the cython_magic extension will fail to execute.
311
+ #
312
+ # This works with the default ipython_rgx* patterns,
313
+ # If you modify them, YMMV.
314
+ nextline = nextline[Nc:]
315
+ if nextline and nextline[0] == ' ':
316
+ nextline = nextline[1:]
317
+
318
+ inputline += '\n' + nextline
319
+ else:
320
+ rest.append(nextline)
321
+ i+= 1
322
+
323
+ block.append((INPUT, (decorator, inputline, '\n'.join(rest))))
324
+ continue
325
+
326
+ # if it looks like an output line grab all the text to the end
327
+ # of the block
328
+ matchout = rgxout.match(line)
329
+ if matchout:
330
+ lineno, output = int(matchout.group(1)), matchout.group(2)
331
+ if i<N-1:
332
+ output = '\n'.join([output] + lines[i:])
333
+
334
+ block.append((OUTPUT, output))
335
+ break
336
+
337
+ return block
338
+
339
+
340
+ class EmbeddedSphinxShell:
341
+ """An embedded IPython instance to run inside Sphinx"""
342
+
343
+ def __init__(self, exec_lines=None):
344
+
345
+ self.cout = StringIO()
346
+
347
+ if exec_lines is None:
348
+ exec_lines = []
349
+
350
+ # Create config object for IPython
351
+ config = Config()
352
+ config.HistoryManager.hist_file = ':memory:'
353
+ config.InteractiveShell.autocall = False
354
+ config.InteractiveShell.autoindent = False
355
+ config.InteractiveShell.colors = "nocolor"
356
+
357
+ # create a profile so instance history isn't saved
358
+ tmp_profile_dir = tempfile.mkdtemp(prefix='profile_')
359
+ profname = 'auto_profile_sphinx_build'
360
+ pdir = os.path.join(tmp_profile_dir,profname)
361
+ profile = ProfileDir.create_profile_dir(pdir)
362
+
363
+ # Create and initialize global ipython, but don't start its mainloop.
364
+ # This will persist across different EmbeddedSphinxShell instances.
365
+ IP = InteractiveShell.instance(config=config, profile_dir=profile)
366
+ atexit.register(self.cleanup)
367
+
368
+ # Store a few parts of IPython we'll need.
369
+ self.IP = IP
370
+ self.user_ns = self.IP.user_ns
371
+ self.user_global_ns = self.IP.user_global_ns
372
+
373
+ self.input = ''
374
+ self.output = ''
375
+ self.tmp_profile_dir = tmp_profile_dir
376
+
377
+ self.is_verbatim = False
378
+ self.is_doctest = False
379
+ self.is_suppress = False
380
+
381
+ # Optionally, provide more detailed information to shell.
382
+ # this is assigned by the SetUp method of IPythonDirective
383
+ # to point at itself.
384
+ #
385
+ # So, you can access handy things at self.directive.state
386
+ self.directive = None
387
+
388
+ # on the first call to the savefig decorator, we'll import
389
+ # pyplot as plt so we can make a call to the plt.gcf().savefig
390
+ self._pyplot_imported = False
391
+
392
+ # Prepopulate the namespace.
393
+ for line in exec_lines:
394
+ self.process_input_line(line, store_history=False)
395
+
396
+ def cleanup(self):
397
+ shutil.rmtree(self.tmp_profile_dir, ignore_errors=True)
398
+
399
+ def clear_cout(self):
400
+ self.cout.seek(0)
401
+ self.cout.truncate(0)
402
+
403
+ def process_input_line(self, line, store_history):
404
+ return self.process_input_lines([line], store_history=store_history)
405
+
406
+ def process_input_lines(self, lines, store_history=True):
407
+ """process the input, capturing stdout"""
408
+ stdout = sys.stdout
409
+ source_raw = '\n'.join(lines)
410
+ try:
411
+ sys.stdout = self.cout
412
+ self.IP.run_cell(source_raw, store_history=store_history)
413
+ finally:
414
+ sys.stdout = stdout
415
+
416
+ def process_image(self, decorator):
417
+ """
418
+ # build out an image directive like
419
+ # .. image:: somefile.png
420
+ # :width 4in
421
+ #
422
+ # from an input like
423
+ # savefig somefile.png width=4in
424
+ """
425
+ savefig_dir = self.savefig_dir
426
+ source_dir = self.source_dir
427
+ saveargs = decorator.split(' ')
428
+ filename = saveargs[1]
429
+ # insert relative path to image file in source
430
+ # as absolute path for Sphinx
431
+ # sphinx expects a posix path, even on Windows
432
+ path = pathlib.Path(savefig_dir, filename)
433
+ outfile = '/' + path.relative_to(source_dir).as_posix()
434
+
435
+ imagerows = ['.. image:: %s' % outfile]
436
+
437
+ for kwarg in saveargs[2:]:
438
+ arg, val = kwarg.split('=')
439
+ arg = arg.strip()
440
+ val = val.strip()
441
+ imagerows.append(' :%s: %s'%(arg, val))
442
+
443
+ image_file = os.path.basename(outfile) # only return file name
444
+ image_directive = '\n'.join(imagerows)
445
+ return image_file, image_directive
446
+
447
+ # Callbacks for each type of token
448
+ def process_input(self, data, input_prompt, lineno):
449
+ """
450
+ Process data block for INPUT token.
451
+
452
+ """
453
+ decorator, input, rest = data
454
+ image_file = None
455
+ image_directive = None
456
+
457
+ is_verbatim = decorator=='@verbatim' or self.is_verbatim
458
+ is_doctest = (decorator is not None and \
459
+ decorator.startswith('@doctest')) or self.is_doctest
460
+ is_suppress = decorator=='@suppress' or self.is_suppress
461
+ is_okexcept = decorator=='@okexcept' or self.is_okexcept
462
+ is_okwarning = decorator=='@okwarning' or self.is_okwarning
463
+ is_savefig = decorator is not None and \
464
+ decorator.startswith('@savefig')
465
+
466
+ input_lines = input.split('\n')
467
+ if len(input_lines) > 1:
468
+ if input_lines[-1] != "":
469
+ input_lines.append('') # make sure there's a blank line
470
+ # so splitter buffer gets reset
471
+
472
+ continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
473
+
474
+ if is_savefig:
475
+ image_file, image_directive = self.process_image(decorator)
476
+
477
+ ret = []
478
+ is_semicolon = False
479
+
480
+ # Hold the execution count, if requested to do so.
481
+ if is_suppress and self.hold_count:
482
+ store_history = False
483
+ else:
484
+ store_history = True
485
+
486
+ # Note: catch_warnings is not thread safe
487
+ with warnings.catch_warnings(record=True) as ws:
488
+ if input_lines[0].endswith(';'):
489
+ is_semicolon = True
490
+ #for i, line in enumerate(input_lines):
491
+
492
+ # process the first input line
493
+ if is_verbatim:
494
+ self.process_input_lines([''])
495
+ self.IP.execution_count += 1 # increment it anyway
496
+ else:
497
+ # only submit the line in non-verbatim mode
498
+ self.process_input_lines(input_lines, store_history=store_history)
499
+
500
+ if not is_suppress:
501
+ for i, line in enumerate(input_lines):
502
+ if i == 0:
503
+ formatted_line = '%s %s'%(input_prompt, line)
504
+ else:
505
+ formatted_line = '%s %s'%(continuation, line)
506
+ ret.append(formatted_line)
507
+
508
+ if not is_suppress and len(rest.strip()) and is_verbatim:
509
+ # The "rest" is the standard output of the input. This needs to be
510
+ # added when in verbatim mode. If there is no "rest", then we don't
511
+ # add it, as the new line will be added by the processed output.
512
+ ret.append(rest)
513
+
514
+ # Fetch the processed output. (This is not the submitted output.)
515
+ self.cout.seek(0)
516
+ processed_output = self.cout.read()
517
+ if not is_suppress and not is_semicolon:
518
+ #
519
+ # In IPythonDirective.run, the elements of `ret` are eventually
520
+ # combined such that '' entries correspond to newlines. So if
521
+ # `processed_output` is equal to '', then the adding it to `ret`
522
+ # ensures that there is a blank line between consecutive inputs
523
+ # that have no outputs, as in:
524
+ #
525
+ # In [1]: x = 4
526
+ #
527
+ # In [2]: x = 5
528
+ #
529
+ # When there is processed output, it has a '\n' at the tail end. So
530
+ # adding the output to `ret` will provide the necessary spacing
531
+ # between consecutive input/output blocks, as in:
532
+ #
533
+ # In [1]: x
534
+ # Out[1]: 5
535
+ #
536
+ # In [2]: x
537
+ # Out[2]: 5
538
+ #
539
+ # When there is stdout from the input, it also has a '\n' at the
540
+ # tail end, and so this ensures proper spacing as well. E.g.:
541
+ #
542
+ # In [1]: print(x)
543
+ # 5
544
+ #
545
+ # In [2]: x = 5
546
+ #
547
+ # When in verbatim mode, `processed_output` is empty (because
548
+ # nothing was passed to IP. Sometimes the submitted code block has
549
+ # an Out[] portion and sometimes it does not. When it does not, we
550
+ # need to ensure proper spacing, so we have to add '' to `ret`.
551
+ # However, if there is an Out[] in the submitted code, then we do
552
+ # not want to add a newline as `process_output` has stuff to add.
553
+ # The difficulty is that `process_input` doesn't know if
554
+ # `process_output` will be called---so it doesn't know if there is
555
+ # Out[] in the code block. The requires that we include a hack in
556
+ # `process_block`. See the comments there.
557
+ #
558
+ ret.append(processed_output)
559
+ elif is_semicolon:
560
+ # Make sure there is a newline after the semicolon.
561
+ ret.append('')
562
+
563
+ # context information
564
+ filename = "Unknown"
565
+ lineno = 0
566
+ if self.directive.state:
567
+ filename = self.directive.state.document.current_source
568
+ lineno = self.directive.state.document.current_line
569
+
570
+ # Use sphinx logger for warnings
571
+ logger = logging.getLogger(__name__)
572
+
573
+ # output any exceptions raised during execution to stdout
574
+ # unless :okexcept: has been specified.
575
+ if not is_okexcept and (
576
+ ("Traceback" in processed_output) or ("SyntaxError" in processed_output)
577
+ ):
578
+ s = "\n>>>" + ("-" * 73) + "\n"
579
+ s += "Exception in %s at block ending on line %s\n" % (filename, lineno)
580
+ s += "Specify :okexcept: as an option in the ipython:: block to suppress this message\n"
581
+ s += processed_output + "\n"
582
+ s += "<<<" + ("-" * 73)
583
+ logger.warning(s)
584
+ if self.warning_is_error:
585
+ raise RuntimeError(
586
+ "Unexpected exception in `{}` line {}".format(filename, lineno)
587
+ )
588
+
589
+ # output any warning raised during execution to stdout
590
+ # unless :okwarning: has been specified.
591
+ if not is_okwarning:
592
+ for w in ws:
593
+ s = "\n>>>" + ("-" * 73) + "\n"
594
+ s += "Warning in %s at block ending on line %s\n" % (filename, lineno)
595
+ s += "Specify :okwarning: as an option in the ipython:: block to suppress this message\n"
596
+ s += ("-" * 76) + "\n"
597
+ s += warnings.formatwarning(
598
+ w.message, w.category, w.filename, w.lineno, w.line
599
+ )
600
+ s += "<<<" + ("-" * 73)
601
+ logger.warning(s)
602
+ if self.warning_is_error:
603
+ raise RuntimeError(
604
+ "Unexpected warning in `{}` line {}".format(filename, lineno)
605
+ )
606
+
607
+ self.clear_cout()
608
+ return (ret, input_lines, processed_output,
609
+ is_doctest, decorator, image_file, image_directive)
610
+
611
+
612
+ def process_output(self, data, output_prompt, input_lines, output,
613
+ is_doctest, decorator, image_file):
614
+ """
615
+ Process data block for OUTPUT token.
616
+
617
+ """
618
+ # Recall: `data` is the submitted output, and `output` is the processed
619
+ # output from `input_lines`.
620
+
621
+ TAB = ' ' * 4
622
+
623
+ if is_doctest and output is not None:
624
+
625
+ found = output # This is the processed output
626
+ found = found.strip()
627
+ submitted = data.strip()
628
+
629
+ if self.directive is None:
630
+ source = 'Unavailable'
631
+ content = 'Unavailable'
632
+ else:
633
+ source = self.directive.state.document.current_source
634
+ content = self.directive.content
635
+ # Add tabs and join into a single string.
636
+ content = '\n'.join([TAB + line for line in content])
637
+
638
+ # Make sure the output contains the output prompt.
639
+ ind = found.find(output_prompt)
640
+ if ind < 0:
641
+ e = ('output does not contain output prompt\n\n'
642
+ 'Document source: {0}\n\n'
643
+ 'Raw content: \n{1}\n\n'
644
+ 'Input line(s):\n{TAB}{2}\n\n'
645
+ 'Output line(s):\n{TAB}{3}\n\n')
646
+ e = e.format(source, content, '\n'.join(input_lines),
647
+ repr(found), TAB=TAB)
648
+ raise RuntimeError(e)
649
+ found = found[len(output_prompt):].strip()
650
+
651
+ # Handle the actual doctest comparison.
652
+ if decorator.strip() == '@doctest':
653
+ # Standard doctest
654
+ if found != submitted:
655
+ e = ('doctest failure\n\n'
656
+ 'Document source: {0}\n\n'
657
+ 'Raw content: \n{1}\n\n'
658
+ 'On input line(s):\n{TAB}{2}\n\n'
659
+ 'we found output:\n{TAB}{3}\n\n'
660
+ 'instead of the expected:\n{TAB}{4}\n\n')
661
+ e = e.format(source, content, '\n'.join(input_lines),
662
+ repr(found), repr(submitted), TAB=TAB)
663
+ raise RuntimeError(e)
664
+ else:
665
+ self.custom_doctest(decorator, input_lines, found, submitted)
666
+
667
+ # When in verbatim mode, this holds additional submitted output
668
+ # to be written in the final Sphinx output.
669
+ # https://github.com/ipython/ipython/issues/5776
670
+ out_data = []
671
+
672
+ is_verbatim = decorator=='@verbatim' or self.is_verbatim
673
+ if is_verbatim and data.strip():
674
+ # Note that `ret` in `process_block` has '' as its last element if
675
+ # the code block was in verbatim mode. So if there is no submitted
676
+ # output, then we will have proper spacing only if we do not add
677
+ # an additional '' to `out_data`. This is why we condition on
678
+ # `and data.strip()`.
679
+
680
+ # The submitted output has no output prompt. If we want the
681
+ # prompt and the code to appear, we need to join them now
682
+ # instead of adding them separately---as this would create an
683
+ # undesired newline. How we do this ultimately depends on the
684
+ # format of the output regex. I'll do what works for the default
685
+ # prompt for now, and we might have to adjust if it doesn't work
686
+ # in other cases. Finally, the submitted output does not have
687
+ # a trailing newline, so we must add it manually.
688
+ out_data.append("{0} {1}\n".format(output_prompt, data))
689
+
690
+ return out_data
691
+
692
+ def process_comment(self, data):
693
+ """Process data fPblock for COMMENT token."""
694
+ if not self.is_suppress:
695
+ return [data]
696
+
697
+ def save_image(self, image_file):
698
+ """
699
+ Saves the image file to disk.
700
+ """
701
+ self.ensure_pyplot()
702
+ command = 'plt.gcf().savefig("%s")'%image_file
703
+ # print('SAVEFIG', command) # dbg
704
+ self.process_input_line('bookmark ipy_thisdir', store_history=False)
705
+ self.process_input_line('cd -b ipy_savedir', store_history=False)
706
+ self.process_input_line(command, store_history=False)
707
+ self.process_input_line('cd -b ipy_thisdir', store_history=False)
708
+ self.process_input_line('bookmark -d ipy_thisdir', store_history=False)
709
+ self.clear_cout()
710
+
711
+ def process_block(self, block):
712
+ """
713
+ process block from the block_parser and return a list of processed lines
714
+ """
715
+ ret = []
716
+ output = None
717
+ input_lines = None
718
+ lineno = self.IP.execution_count
719
+
720
+ input_prompt = self.promptin % lineno
721
+ output_prompt = self.promptout % lineno
722
+ image_file = None
723
+ image_directive = None
724
+
725
+ found_input = False
726
+ for token, data in block:
727
+ if token == COMMENT:
728
+ out_data = self.process_comment(data)
729
+ elif token == INPUT:
730
+ found_input = True
731
+ (out_data, input_lines, output, is_doctest,
732
+ decorator, image_file, image_directive) = \
733
+ self.process_input(data, input_prompt, lineno)
734
+ elif token == OUTPUT:
735
+ if not found_input:
736
+
737
+ TAB = ' ' * 4
738
+ linenumber = 0
739
+ source = 'Unavailable'
740
+ content = 'Unavailable'
741
+ if self.directive:
742
+ linenumber = self.directive.state.document.current_line
743
+ source = self.directive.state.document.current_source
744
+ content = self.directive.content
745
+ # Add tabs and join into a single string.
746
+ content = '\n'.join([TAB + line for line in content])
747
+
748
+ e = ('\n\nInvalid block: Block contains an output prompt '
749
+ 'without an input prompt.\n\n'
750
+ 'Document source: {0}\n\n'
751
+ 'Content begins at line {1}: \n\n{2}\n\n'
752
+ 'Problematic block within content: \n\n{TAB}{3}\n\n')
753
+ e = e.format(source, linenumber, content, block, TAB=TAB)
754
+
755
+ # Write, rather than include in exception, since Sphinx
756
+ # will truncate tracebacks.
757
+ sys.stdout.write(e)
758
+ raise RuntimeError('An invalid block was detected.')
759
+ out_data = \
760
+ self.process_output(data, output_prompt, input_lines,
761
+ output, is_doctest, decorator,
762
+ image_file)
763
+ if out_data:
764
+ # Then there was user submitted output in verbatim mode.
765
+ # We need to remove the last element of `ret` that was
766
+ # added in `process_input`, as it is '' and would introduce
767
+ # an undesirable newline.
768
+ assert(ret[-1] == '')
769
+ del ret[-1]
770
+
771
+ if out_data:
772
+ ret.extend(out_data)
773
+
774
+ # save the image files
775
+ if image_file is not None:
776
+ self.save_image(image_file)
777
+
778
+ return ret, image_directive
779
+
780
+ def ensure_pyplot(self):
781
+ """
782
+ Ensures that pyplot has been imported into the embedded IPython shell.
783
+
784
+ Also, makes sure to set the backend appropriately if not set already.
785
+
786
+ """
787
+ # We are here if the @figure pseudo decorator was used. Thus, it's
788
+ # possible that we could be here even if python_mplbackend were set to
789
+ # `None`. That's also strange and perhaps worthy of raising an
790
+ # exception, but for now, we just set the backend to 'agg'.
791
+
792
+ if not self._pyplot_imported:
793
+ if 'matplotlib.backends' not in sys.modules:
794
+ # Then ipython_matplotlib was set to None but there was a
795
+ # call to the @figure decorator (and ipython_execlines did
796
+ # not set a backend).
797
+ #raise Exception("No backend was set, but @figure was used!")
798
+ import matplotlib
799
+ matplotlib.use('agg')
800
+
801
+ # Always import pyplot into embedded shell.
802
+ self.process_input_line('import matplotlib.pyplot as plt',
803
+ store_history=False)
804
+ self._pyplot_imported = True
805
+
806
+ def process_pure_python(self, content):
807
+ """
808
+ content is a list of strings. it is unedited directive content
809
+
810
+ This runs it line by line in the InteractiveShell, prepends
811
+ prompts as needed capturing stderr and stdout, then returns
812
+ the content as a list as if it were ipython code
813
+ """
814
+ output = []
815
+ savefig = False # keep up with this to clear figure
816
+ multiline = False # to handle line continuation
817
+ multiline_start = None
818
+ fmtin = self.promptin
819
+
820
+ ct = 0
821
+
822
+ for lineno, line in enumerate(content):
823
+
824
+ line_stripped = line.strip()
825
+ if not len(line):
826
+ output.append(line)
827
+ continue
828
+
829
+ # handle pseudo-decorators, whilst ensuring real python decorators are treated as input
830
+ if any(
831
+ line_stripped.startswith("@" + pseudo_decorator)
832
+ for pseudo_decorator in PSEUDO_DECORATORS
833
+ ):
834
+ output.extend([line])
835
+ if 'savefig' in line:
836
+ savefig = True # and need to clear figure
837
+ continue
838
+
839
+ # handle comments
840
+ if line_stripped.startswith('#'):
841
+ output.extend([line])
842
+ continue
843
+
844
+ # deal with lines checking for multiline
845
+ continuation = u' %s:'% ''.join(['.']*(len(str(ct))+2))
846
+ if not multiline:
847
+ modified = u"%s %s" % (fmtin % ct, line_stripped)
848
+ output.append(modified)
849
+ ct += 1
850
+ try:
851
+ ast.parse(line_stripped)
852
+ output.append(u'')
853
+ except Exception: # on a multiline
854
+ multiline = True
855
+ multiline_start = lineno
856
+ else: # still on a multiline
857
+ modified = u'%s %s' % (continuation, line)
858
+ output.append(modified)
859
+
860
+ # if the next line is indented, it should be part of multiline
861
+ if len(content) > lineno + 1:
862
+ nextline = content[lineno + 1]
863
+ if len(nextline) - len(nextline.lstrip()) > 3:
864
+ continue
865
+ try:
866
+ mod = ast.parse(
867
+ '\n'.join(content[multiline_start:lineno+1]))
868
+ if isinstance(mod.body[0], ast.FunctionDef):
869
+ # check to see if we have the whole function
870
+ for element in mod.body[0].body:
871
+ if isinstance(element, ast.Return):
872
+ multiline = False
873
+ else:
874
+ output.append(u'')
875
+ multiline = False
876
+ except Exception:
877
+ pass
878
+
879
+ if savefig: # clear figure if plotted
880
+ self.ensure_pyplot()
881
+ self.process_input_line('plt.clf()', store_history=False)
882
+ self.clear_cout()
883
+ savefig = False
884
+
885
+ return output
886
+
887
+ def custom_doctest(self, decorator, input_lines, found, submitted):
888
+ """
889
+ Perform a specialized doctest.
890
+
891
+ """
892
+ from .custom_doctests import doctests
893
+
894
+ args = decorator.split()
895
+ doctest_type = args[1]
896
+ if doctest_type in doctests:
897
+ doctests[doctest_type](self, args, input_lines, found, submitted)
898
+ else:
899
+ e = "Invalid option to @doctest: {0}".format(doctest_type)
900
+ raise Exception(e)
901
+
902
+
903
+ class IPythonDirective(Directive):
904
+
905
+ has_content: bool = True
906
+ required_arguments: int = 0
907
+ optional_arguments: int = 4 # python, suppress, verbatim, doctest
908
+ final_argumuent_whitespace: bool = True
909
+ option_spec: Dict[str, Any] = {
910
+ "python": directives.unchanged,
911
+ "suppress": directives.flag,
912
+ "verbatim": directives.flag,
913
+ "doctest": directives.flag,
914
+ "okexcept": directives.flag,
915
+ "okwarning": directives.flag,
916
+ }
917
+
918
+ shell = None
919
+
920
+ seen_docs: Set = set()
921
+
922
+ def get_config_options(self):
923
+ # contains sphinx configuration variables
924
+ config = self.state.document.settings.env.config
925
+
926
+ # get config variables to set figure output directory
927
+ savefig_dir = config.ipython_savefig_dir
928
+ source_dir = self.state.document.settings.env.srcdir
929
+ savefig_dir = os.path.join(source_dir, savefig_dir)
930
+
931
+ # get regex and prompt stuff
932
+ rgxin = config.ipython_rgxin
933
+ rgxout = config.ipython_rgxout
934
+ warning_is_error= config.ipython_warning_is_error
935
+ promptin = config.ipython_promptin
936
+ promptout = config.ipython_promptout
937
+ mplbackend = config.ipython_mplbackend
938
+ exec_lines = config.ipython_execlines
939
+ hold_count = config.ipython_holdcount
940
+
941
+ return (savefig_dir, source_dir, rgxin, rgxout,
942
+ promptin, promptout, mplbackend, exec_lines, hold_count, warning_is_error)
943
+
944
+ def setup(self):
945
+ # Get configuration values.
946
+ (savefig_dir, source_dir, rgxin, rgxout, promptin, promptout,
947
+ mplbackend, exec_lines, hold_count, warning_is_error) = self.get_config_options()
948
+
949
+ try:
950
+ os.makedirs(savefig_dir)
951
+ except OSError as e:
952
+ if e.errno != errno.EEXIST:
953
+ raise
954
+
955
+ if self.shell is None:
956
+ # We will be here many times. However, when the
957
+ # EmbeddedSphinxShell is created, its interactive shell member
958
+ # is the same for each instance.
959
+
960
+ if mplbackend and 'matplotlib.backends' not in sys.modules and use_matplotlib:
961
+ import matplotlib
962
+ matplotlib.use(mplbackend)
963
+
964
+ # Must be called after (potentially) importing matplotlib and
965
+ # setting its backend since exec_lines might import pylab.
966
+ self.shell = EmbeddedSphinxShell(exec_lines)
967
+
968
+ # Store IPython directive to enable better error messages
969
+ self.shell.directive = self
970
+
971
+ # reset the execution count if we haven't processed this doc
972
+ #NOTE: this may be borked if there are multiple seen_doc tmp files
973
+ #check time stamp?
974
+ if self.state.document.current_source not in self.seen_docs:
975
+ self.shell.IP.history_manager.reset()
976
+ self.shell.IP.execution_count = 1
977
+ self.seen_docs.add(self.state.document.current_source)
978
+
979
+ # and attach to shell so we don't have to pass them around
980
+ self.shell.rgxin = rgxin
981
+ self.shell.rgxout = rgxout
982
+ self.shell.promptin = promptin
983
+ self.shell.promptout = promptout
984
+ self.shell.savefig_dir = savefig_dir
985
+ self.shell.source_dir = source_dir
986
+ self.shell.hold_count = hold_count
987
+ self.shell.warning_is_error = warning_is_error
988
+
989
+ # setup bookmark for saving figures directory
990
+ self.shell.process_input_line(
991
+ 'bookmark ipy_savedir "%s"' % savefig_dir, store_history=False
992
+ )
993
+ self.shell.clear_cout()
994
+
995
+ return rgxin, rgxout, promptin, promptout
996
+
997
+ def teardown(self):
998
+ # delete last bookmark
999
+ self.shell.process_input_line('bookmark -d ipy_savedir',
1000
+ store_history=False)
1001
+ self.shell.clear_cout()
1002
+
1003
+ def run(self):
1004
+ debug = False
1005
+
1006
+ #TODO, any reason block_parser can't be a method of embeddable shell
1007
+ # then we wouldn't have to carry these around
1008
+ rgxin, rgxout, promptin, promptout = self.setup()
1009
+
1010
+ options = self.options
1011
+ self.shell.is_suppress = 'suppress' in options
1012
+ self.shell.is_doctest = 'doctest' in options
1013
+ self.shell.is_verbatim = 'verbatim' in options
1014
+ self.shell.is_okexcept = 'okexcept' in options
1015
+ self.shell.is_okwarning = 'okwarning' in options
1016
+
1017
+ # handle pure python code
1018
+ if 'python' in self.arguments:
1019
+ content = self.content
1020
+ self.content = self.shell.process_pure_python(content)
1021
+
1022
+ # parts consists of all text within the ipython-block.
1023
+ # Each part is an input/output block.
1024
+ parts = '\n'.join(self.content).split('\n\n')
1025
+
1026
+ lines = ['.. code-block:: ipython', '']
1027
+ figures = []
1028
+
1029
+ # Use sphinx logger for warnings
1030
+ logger = logging.getLogger(__name__)
1031
+
1032
+ for part in parts:
1033
+ block = block_parser(part, rgxin, rgxout, promptin, promptout)
1034
+ if len(block):
1035
+ rows, figure = self.shell.process_block(block)
1036
+ for row in rows:
1037
+ lines.extend([' {0}'.format(line)
1038
+ for line in row.split('\n')])
1039
+
1040
+ if figure is not None:
1041
+ figures.append(figure)
1042
+ else:
1043
+ message = 'Code input with no code at {}, line {}'\
1044
+ .format(
1045
+ self.state.document.current_source,
1046
+ self.state.document.current_line)
1047
+ if self.shell.warning_is_error:
1048
+ raise RuntimeError(message)
1049
+ else:
1050
+ logger.warning(message)
1051
+
1052
+ for figure in figures:
1053
+ lines.append('')
1054
+ lines.extend(figure.split('\n'))
1055
+ lines.append('')
1056
+
1057
+ if len(lines) > 2:
1058
+ if debug:
1059
+ print('\n'.join(lines))
1060
+ else:
1061
+ # This has to do with input, not output. But if we comment
1062
+ # these lines out, then no IPython code will appear in the
1063
+ # final output.
1064
+ self.state_machine.insert_input(
1065
+ lines, self.state_machine.input_lines.source(0))
1066
+
1067
+ # cleanup
1068
+ self.teardown()
1069
+
1070
+ return []
1071
+
1072
+ # Enable as a proper Sphinx directive
1073
+ def setup(app):
1074
+ setup.app = app
1075
+
1076
+ app.add_directive('ipython', IPythonDirective)
1077
+ app.add_config_value('ipython_savefig_dir', 'savefig', 'env')
1078
+ app.add_config_value('ipython_warning_is_error', True, 'env')
1079
+ app.add_config_value('ipython_rgxin',
1080
+ re.compile(r'In \[(\d+)\]:\s?(.*)\s*'), 'env')
1081
+ app.add_config_value('ipython_rgxout',
1082
+ re.compile(r'Out\[(\d+)\]:\s?(.*)\s*'), 'env')
1083
+ app.add_config_value('ipython_promptin', 'In [%d]:', 'env')
1084
+ app.add_config_value('ipython_promptout', 'Out[%d]:', 'env')
1085
+
1086
+ # We could just let matplotlib pick whatever is specified as the default
1087
+ # backend in the matplotlibrc file, but this would cause issues if the
1088
+ # backend didn't work in headless environments. For this reason, 'agg'
1089
+ # is a good default backend choice.
1090
+ app.add_config_value('ipython_mplbackend', 'agg', 'env')
1091
+
1092
+ # If the user sets this config value to `None`, then EmbeddedSphinxShell's
1093
+ # __init__ method will treat it as [].
1094
+ execlines = ['import numpy as np']
1095
+ if use_matplotlib:
1096
+ execlines.append('import matplotlib.pyplot as plt')
1097
+ app.add_config_value('ipython_execlines', execlines, 'env')
1098
+
1099
+ app.add_config_value('ipython_holdcount', True, 'env')
1100
+
1101
+ metadata = {'parallel_read_safe': True, 'parallel_write_safe': True}
1102
+ return metadata
1103
+
1104
+ # Simple smoke test, needs to be converted to a proper automatic test.
1105
+ def test():
1106
+
1107
+ examples = [
1108
+ r"""
1109
+ In [9]: pwd
1110
+ Out[9]: '/home/jdhunter/py4science/book'
1111
+
1112
+ In [10]: cd bookdata/
1113
+ /home/jdhunter/py4science/book/bookdata
1114
+
1115
+ In [2]: from pylab import *
1116
+
1117
+ In [2]: ion()
1118
+
1119
+ In [3]: im = imread('stinkbug.png')
1120
+
1121
+ @savefig mystinkbug.png width=4in
1122
+ In [4]: imshow(im)
1123
+ Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
1124
+
1125
+ """,
1126
+ r"""
1127
+
1128
+ In [1]: x = 'hello world'
1129
+
1130
+ # string methods can be
1131
+ # used to alter the string
1132
+ @doctest
1133
+ In [2]: x.upper()
1134
+ Out[2]: 'HELLO WORLD'
1135
+
1136
+ @verbatim
1137
+ In [3]: x.st<TAB>
1138
+ x.startswith x.strip
1139
+ """,
1140
+ r"""
1141
+
1142
+ In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\
1143
+ .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv'
1144
+
1145
+ In [131]: print url.split('&')
1146
+ ['http://ichart.finance.yahoo.com/table.csv?s=CROX', 'd=9', 'e=22', 'f=2009', 'g=d', 'a=1', 'b=8', 'c=2006', 'ignore=.csv']
1147
+
1148
+ In [60]: import urllib
1149
+
1150
+ """,
1151
+ r"""\
1152
+
1153
+ In [133]: import numpy.random
1154
+
1155
+ @suppress
1156
+ In [134]: numpy.random.seed(2358)
1157
+
1158
+ @doctest
1159
+ In [135]: numpy.random.rand(10,2)
1160
+ Out[135]:
1161
+ array([[ 0.64524308, 0.59943846],
1162
+ [ 0.47102322, 0.8715456 ],
1163
+ [ 0.29370834, 0.74776844],
1164
+ [ 0.99539577, 0.1313423 ],
1165
+ [ 0.16250302, 0.21103583],
1166
+ [ 0.81626524, 0.1312433 ],
1167
+ [ 0.67338089, 0.72302393],
1168
+ [ 0.7566368 , 0.07033696],
1169
+ [ 0.22591016, 0.77731835],
1170
+ [ 0.0072729 , 0.34273127]])
1171
+
1172
+ """,
1173
+
1174
+ r"""
1175
+ In [106]: print x
1176
+ jdh
1177
+
1178
+ In [109]: for i in range(10):
1179
+ .....: print i
1180
+ .....:
1181
+ .....:
1182
+ 0
1183
+ 1
1184
+ 2
1185
+ 3
1186
+ 4
1187
+ 5
1188
+ 6
1189
+ 7
1190
+ 8
1191
+ 9
1192
+ """,
1193
+
1194
+ r"""
1195
+
1196
+ In [144]: from pylab import *
1197
+
1198
+ In [145]: ion()
1199
+
1200
+ # use a semicolon to suppress the output
1201
+ @savefig test_hist.png width=4in
1202
+ In [151]: hist(np.random.randn(10000), 100);
1203
+
1204
+
1205
+ @savefig test_plot.png width=4in
1206
+ In [151]: plot(np.random.randn(10000), 'o');
1207
+ """,
1208
+
1209
+ r"""
1210
+ # use a semicolon to suppress the output
1211
+ In [151]: plt.clf()
1212
+
1213
+ @savefig plot_simple.png width=4in
1214
+ In [151]: plot([1,2,3])
1215
+
1216
+ @savefig hist_simple.png width=4in
1217
+ In [151]: hist(np.random.randn(10000), 100);
1218
+
1219
+ """,
1220
+ r"""
1221
+ # update the current fig
1222
+ In [151]: ylabel('number')
1223
+
1224
+ In [152]: title('normal distribution')
1225
+
1226
+
1227
+ @savefig hist_with_text.png
1228
+ In [153]: grid(True)
1229
+
1230
+ @doctest float
1231
+ In [154]: 0.1 + 0.2
1232
+ Out[154]: 0.3
1233
+
1234
+ @doctest float
1235
+ In [155]: np.arange(16).reshape(4,4)
1236
+ Out[155]:
1237
+ array([[ 0, 1, 2, 3],
1238
+ [ 4, 5, 6, 7],
1239
+ [ 8, 9, 10, 11],
1240
+ [12, 13, 14, 15]])
1241
+
1242
+ In [1]: x = np.arange(16, dtype=float).reshape(4,4)
1243
+
1244
+ In [2]: x[0,0] = np.inf
1245
+
1246
+ In [3]: x[0,1] = np.nan
1247
+
1248
+ @doctest float
1249
+ In [4]: x
1250
+ Out[4]:
1251
+ array([[ inf, nan, 2., 3.],
1252
+ [ 4., 5., 6., 7.],
1253
+ [ 8., 9., 10., 11.],
1254
+ [ 12., 13., 14., 15.]])
1255
+
1256
+
1257
+ """,
1258
+ ]
1259
+ # skip local-file depending first example:
1260
+ examples = examples[1:]
1261
+
1262
+ #ipython_directive.DEBUG = True # dbg
1263
+ #options = dict(suppress=True) # dbg
1264
+ options = {}
1265
+ for example in examples:
1266
+ content = example.split('\n')
1267
+ IPythonDirective('debug', arguments=None, options=options,
1268
+ content=content, lineno=0,
1269
+ content_offset=None, block_text=None,
1270
+ state=None, state_machine=None,
1271
+ )
1272
+
1273
+ # Run test suite as a script
1274
+ if __name__=='__main__':
1275
+ if not os.path.isdir('_static'):
1276
+ os.mkdir('_static')
1277
+ test()
1278
+ print('All OK? Check figures in _static/')
temp_venv/lib/python3.13/site-packages/IPython/terminal/__init__.py ADDED
File without changes
temp_venv/lib/python3.13/site-packages/IPython/terminal/debugger.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import os
3
+ import sys
4
+
5
+ from IPython.core.debugger import Pdb
6
+ from IPython.core.completer import IPCompleter
7
+ from .ptutils import IPythonPTCompleter
8
+ from .shortcuts import create_ipython_shortcuts
9
+ from . import embed
10
+
11
+ from pathlib import Path
12
+ from pygments.token import Token
13
+ from prompt_toolkit.application import create_app_session
14
+ from prompt_toolkit.shortcuts.prompt import PromptSession
15
+ from prompt_toolkit.enums import EditingMode
16
+ from prompt_toolkit.formatted_text import PygmentsTokens
17
+ from prompt_toolkit.history import InMemoryHistory, FileHistory
18
+ from concurrent.futures import ThreadPoolExecutor
19
+
20
+ # we want to avoid ptk as much as possible when using subprocesses
21
+ # as it uses cursor positioning requests, deletes color ....
22
+ _use_simple_prompt = "IPY_TEST_SIMPLE_PROMPT" in os.environ
23
+
24
+
25
+ class TerminalPdb(Pdb):
26
+ """Standalone IPython debugger."""
27
+
28
+ def __init__(self, *args, pt_session_options=None, **kwargs):
29
+ Pdb.__init__(self, *args, **kwargs)
30
+ self._ptcomp = None
31
+ self.pt_init(pt_session_options)
32
+ self.thread_executor = ThreadPoolExecutor(1)
33
+
34
+ def pt_init(self, pt_session_options=None):
35
+ """Initialize the prompt session and the prompt loop
36
+ and store them in self.pt_app and self.pt_loop.
37
+
38
+ Additional keyword arguments for the PromptSession class
39
+ can be specified in pt_session_options.
40
+ """
41
+ if pt_session_options is None:
42
+ pt_session_options = {}
43
+
44
+ def get_prompt_tokens():
45
+ return [(Token.Prompt, self.prompt)]
46
+
47
+ if self._ptcomp is None:
48
+ compl = IPCompleter(
49
+ shell=self.shell, namespace={}, global_namespace={}, parent=self.shell
50
+ )
51
+ # add a completer for all the do_ methods
52
+ methods_names = [m[3:] for m in dir(self) if m.startswith("do_")]
53
+
54
+ def gen_comp(self, text):
55
+ return [m for m in methods_names if m.startswith(text)]
56
+ import types
57
+ newcomp = types.MethodType(gen_comp, compl)
58
+ compl.custom_matchers.insert(0, newcomp)
59
+ # end add completer.
60
+
61
+ self._ptcomp = IPythonPTCompleter(compl)
62
+
63
+ # setup history only when we start pdb
64
+ if self.shell.debugger_history is None:
65
+ if self.shell.debugger_history_file is not None:
66
+ p = Path(self.shell.debugger_history_file).expanduser()
67
+ if not p.exists():
68
+ p.touch()
69
+ self.debugger_history = FileHistory(os.path.expanduser(str(p)))
70
+ else:
71
+ self.debugger_history = InMemoryHistory()
72
+ else:
73
+ self.debugger_history = self.shell.debugger_history
74
+
75
+ options = dict(
76
+ message=(lambda: PygmentsTokens(get_prompt_tokens())),
77
+ editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
78
+ key_bindings=create_ipython_shortcuts(self.shell),
79
+ history=self.debugger_history,
80
+ completer=self._ptcomp,
81
+ enable_history_search=True,
82
+ mouse_support=self.shell.mouse_support,
83
+ complete_style=self.shell.pt_complete_style,
84
+ style=getattr(self.shell, "style", None),
85
+ color_depth=self.shell.color_depth,
86
+ )
87
+
88
+ options.update(pt_session_options)
89
+ if not _use_simple_prompt:
90
+ self.pt_loop = asyncio.new_event_loop()
91
+ self.pt_app = PromptSession(**options)
92
+
93
+ def _prompt(self):
94
+ """
95
+ In case other prompt_toolkit apps have to run in parallel to this one (e.g. in madbg),
96
+ create_app_session must be used to prevent mixing up between them. According to the prompt_toolkit docs:
97
+
98
+ > If you need multiple applications running at the same time, you have to create a separate
99
+ > `AppSession` using a `with create_app_session():` block.
100
+ """
101
+ with create_app_session():
102
+ return self.pt_app.prompt()
103
+
104
+ def cmdloop(self, intro=None):
105
+ """Repeatedly issue a prompt, accept input, parse an initial prefix
106
+ off the received input, and dispatch to action methods, passing them
107
+ the remainder of the line as argument.
108
+
109
+ override the same methods from cmd.Cmd to provide prompt toolkit replacement.
110
+ """
111
+ if not self.use_rawinput:
112
+ raise ValueError('Sorry ipdb does not support use_rawinput=False')
113
+
114
+ # In order to make sure that prompt, which uses asyncio doesn't
115
+ # interfere with applications in which it's used, we always run the
116
+ # prompt itself in a different thread (we can't start an event loop
117
+ # within an event loop). This new thread won't have any event loop
118
+ # running, and here we run our prompt-loop.
119
+ self.preloop()
120
+
121
+ try:
122
+ if intro is not None:
123
+ self.intro = intro
124
+ if self.intro:
125
+ print(self.intro, file=self.stdout)
126
+ stop = None
127
+ while not stop:
128
+ if self.cmdqueue:
129
+ line = self.cmdqueue.pop(0)
130
+ else:
131
+ self._ptcomp.ipy_completer.namespace = self.curframe_locals
132
+ self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
133
+
134
+ # Run the prompt in a different thread.
135
+ if not _use_simple_prompt:
136
+ try:
137
+ line = self.thread_executor.submit(self._prompt).result()
138
+ except EOFError:
139
+ line = "EOF"
140
+ else:
141
+ line = input("ipdb> ")
142
+
143
+ line = self.precmd(line)
144
+ stop = self.onecmd(line)
145
+ stop = self.postcmd(stop, line)
146
+ self.postloop()
147
+ except Exception:
148
+ raise
149
+
150
+ def do_interact(self, arg):
151
+ ipshell = embed.InteractiveShellEmbed(
152
+ config=self.shell.config,
153
+ banner1="*interactive*",
154
+ exit_msg="*exiting interactive console...*",
155
+ )
156
+ global_ns = self.curframe.f_globals
157
+ ipshell(
158
+ module=sys.modules.get(global_ns["__name__"], None),
159
+ local_ns=self.curframe_locals,
160
+ )
161
+
162
+
163
+ def set_trace(frame=None):
164
+ """
165
+ Start debugging from `frame`.
166
+
167
+ If frame is not specified, debugging starts from caller's frame.
168
+ """
169
+ TerminalPdb().set_trace(frame or sys._getframe().f_back)
170
+
171
+
172
+ if __name__ == '__main__':
173
+ import pdb
174
+ # IPython.core.debugger.Pdb.trace_dispatch shall not catch
175
+ # bdb.BdbQuit. When started through __main__ and an exception
176
+ # happened after hitting "c", this is needed in order to
177
+ # be able to quit the debugging session (see #9950).
178
+ old_trace_dispatch = pdb.Pdb.trace_dispatch
179
+ pdb.Pdb = TerminalPdb # type: ignore
180
+ pdb.Pdb.trace_dispatch = old_trace_dispatch # type: ignore
181
+ pdb.main()
temp_venv/lib/python3.13/site-packages/IPython/terminal/embed.py ADDED
@@ -0,0 +1,436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # encoding: utf-8
2
+ """
3
+ An embedded IPython shell.
4
+ """
5
+ # Copyright (c) IPython Development Team.
6
+ # Distributed under the terms of the Modified BSD License.
7
+
8
+
9
+ import sys
10
+ import warnings
11
+
12
+ from IPython.core import ultratb, compilerop
13
+ from IPython.core import magic_arguments
14
+ from IPython.core.magic import Magics, magics_class, line_magic
15
+ from IPython.core.interactiveshell import InteractiveShell, make_main_module_type
16
+ from IPython.terminal.interactiveshell import TerminalInteractiveShell
17
+ from IPython.terminal.ipapp import load_default_config
18
+
19
+ from traitlets import Bool, CBool, Unicode
20
+ from IPython.utils.io import ask_yes_no
21
+
22
+ from typing import Set
23
+
24
+ class KillEmbedded(Exception):pass
25
+
26
+ # kept for backward compatibility as IPython 6 was released with
27
+ # the typo. See https://github.com/ipython/ipython/pull/10706
28
+ KillEmbeded = KillEmbedded
29
+
30
+ # This is an additional magic that is exposed in embedded shells.
31
+ @magics_class
32
+ class EmbeddedMagics(Magics):
33
+
34
+ @line_magic
35
+ @magic_arguments.magic_arguments()
36
+ @magic_arguments.argument('-i', '--instance', action='store_true',
37
+ help='Kill instance instead of call location')
38
+ @magic_arguments.argument('-x', '--exit', action='store_true',
39
+ help='Also exit the current session')
40
+ @magic_arguments.argument('-y', '--yes', action='store_true',
41
+ help='Do not ask confirmation')
42
+ def kill_embedded(self, parameter_s=''):
43
+ """%kill_embedded : deactivate for good the current embedded IPython
44
+
45
+ This function (after asking for confirmation) sets an internal flag so
46
+ that an embedded IPython will never activate again for the given call
47
+ location. This is useful to permanently disable a shell that is being
48
+ called inside a loop: once you've figured out what you needed from it,
49
+ you may then kill it and the program will then continue to run without
50
+ the interactive shell interfering again.
51
+
52
+ Kill Instance Option:
53
+
54
+ If for some reasons you need to kill the location where the instance
55
+ is created and not called, for example if you create a single
56
+ instance in one place and debug in many locations, you can use the
57
+ ``--instance`` option to kill this specific instance. Like for the
58
+ ``call location`` killing an "instance" should work even if it is
59
+ recreated within a loop.
60
+
61
+ .. note::
62
+
63
+ This was the default behavior before IPython 5.2
64
+
65
+ """
66
+
67
+ args = magic_arguments.parse_argstring(self.kill_embedded, parameter_s)
68
+ print(args)
69
+ if args.instance:
70
+ # let no ask
71
+ if not args.yes:
72
+ kill = ask_yes_no(
73
+ "Are you sure you want to kill this embedded instance? [y/N] ", 'n')
74
+ else:
75
+ kill = True
76
+ if kill:
77
+ self.shell._disable_init_location()
78
+ print("This embedded IPython instance will not reactivate anymore "
79
+ "once you exit.")
80
+ else:
81
+ if not args.yes:
82
+ kill = ask_yes_no(
83
+ "Are you sure you want to kill this embedded call_location? [y/N] ", 'n')
84
+ else:
85
+ kill = True
86
+ if kill:
87
+ self.shell.embedded_active = False
88
+ print("This embedded IPython call location will not reactivate anymore "
89
+ "once you exit.")
90
+
91
+ if args.exit:
92
+ # Ask-exit does not really ask, it just set internals flags to exit
93
+ # on next loop.
94
+ self.shell.ask_exit()
95
+
96
+
97
+ @line_magic
98
+ def exit_raise(self, parameter_s=''):
99
+ """%exit_raise Make the current embedded kernel exit and raise and exception.
100
+
101
+ This function sets an internal flag so that an embedded IPython will
102
+ raise a `IPython.terminal.embed.KillEmbedded` Exception on exit, and then exit the current I. This is
103
+ useful to permanently exit a loop that create IPython embed instance.
104
+ """
105
+
106
+ self.shell.should_raise = True
107
+ self.shell.ask_exit()
108
+
109
+
110
+ class _Sentinel:
111
+ def __init__(self, repr):
112
+ assert isinstance(repr, str)
113
+ self.repr = repr
114
+
115
+ def __repr__(self):
116
+ return repr
117
+
118
+
119
+ class InteractiveShellEmbed(TerminalInteractiveShell):
120
+
121
+ dummy_mode = Bool(False)
122
+ exit_msg = Unicode('')
123
+ embedded = CBool(True)
124
+ should_raise = CBool(False)
125
+ # Like the base class display_banner is not configurable, but here it
126
+ # is True by default.
127
+ display_banner = CBool(True)
128
+ exit_msg = Unicode()
129
+
130
+ # When embedding, by default we don't change the terminal title
131
+ term_title = Bool(False,
132
+ help="Automatically set the terminal title"
133
+ ).tag(config=True)
134
+
135
+ _inactive_locations: Set[str] = set()
136
+
137
+ def _disable_init_location(self):
138
+ """Disable the current Instance creation location"""
139
+ InteractiveShellEmbed._inactive_locations.add(self._init_location_id)
140
+
141
+ @property
142
+ def embedded_active(self):
143
+ return (self._call_location_id not in InteractiveShellEmbed._inactive_locations)\
144
+ and (self._init_location_id not in InteractiveShellEmbed._inactive_locations)
145
+
146
+ @embedded_active.setter
147
+ def embedded_active(self, value):
148
+ if value:
149
+ InteractiveShellEmbed._inactive_locations.discard(
150
+ self._call_location_id)
151
+ InteractiveShellEmbed._inactive_locations.discard(
152
+ self._init_location_id)
153
+ else:
154
+ InteractiveShellEmbed._inactive_locations.add(
155
+ self._call_location_id)
156
+
157
+ def __init__(self, **kw):
158
+ assert (
159
+ "user_global_ns" not in kw
160
+ ), "Key word argument `user_global_ns` has been replaced by `user_module` since IPython 4.0."
161
+ # temporary fix for https://github.com/ipython/ipython/issues/14164
162
+ cls = type(self)
163
+ if cls._instance is None:
164
+ for subclass in cls._walk_mro():
165
+ subclass._instance = self
166
+ cls._instance = self
167
+
168
+ clid = kw.pop('_init_location_id', None)
169
+ if not clid:
170
+ frame = sys._getframe(1)
171
+ clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
172
+ self._init_location_id = clid
173
+
174
+ super(InteractiveShellEmbed,self).__init__(**kw)
175
+
176
+ # don't use the ipython crash handler so that user exceptions aren't
177
+ # trapped
178
+ sys.excepthook = ultratb.FormattedTB(
179
+ theme_name=self.colors,
180
+ mode=self.xmode,
181
+ call_pdb=self.pdb,
182
+ )
183
+
184
+ def init_sys_modules(self):
185
+ """
186
+ Explicitly overwrite :mod:`IPython.core.interactiveshell` to do nothing.
187
+ """
188
+ pass
189
+
190
+ def init_magics(self):
191
+ super(InteractiveShellEmbed, self).init_magics()
192
+ self.register_magics(EmbeddedMagics)
193
+
194
+ def __call__(
195
+ self,
196
+ header="",
197
+ local_ns=None,
198
+ module=None,
199
+ dummy=None,
200
+ stack_depth=1,
201
+ compile_flags=None,
202
+ **kw,
203
+ ):
204
+ """Activate the interactive interpreter.
205
+
206
+ __call__(self,header='',local_ns=None,module=None,dummy=None) -> Start
207
+ the interpreter shell with the given local and global namespaces, and
208
+ optionally print a header string at startup.
209
+
210
+ The shell can be globally activated/deactivated using the
211
+ dummy_mode attribute. This allows you to turn off a shell used
212
+ for debugging globally.
213
+
214
+ However, *each* time you call the shell you can override the current
215
+ state of dummy_mode with the optional keyword parameter 'dummy'. For
216
+ example, if you set dummy mode on with IPShell.dummy_mode = True, you
217
+ can still have a specific call work by making it as IPShell(dummy=False).
218
+ """
219
+
220
+ # we are called, set the underlying interactiveshell not to exit.
221
+ self.keep_running = True
222
+
223
+ # If the user has turned it off, go away
224
+ clid = kw.pop('_call_location_id', None)
225
+ if not clid:
226
+ frame = sys._getframe(1)
227
+ clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
228
+ self._call_location_id = clid
229
+
230
+ if not self.embedded_active:
231
+ return
232
+
233
+ # Normal exits from interactive mode set this flag, so the shell can't
234
+ # re-enter (it checks this variable at the start of interactive mode).
235
+ self.exit_now = False
236
+
237
+ # Allow the dummy parameter to override the global __dummy_mode
238
+ if dummy or (dummy != 0 and self.dummy_mode):
239
+ return
240
+
241
+ # self.banner is auto computed
242
+ if header:
243
+ self.old_banner2 = self.banner2
244
+ self.banner2 = self.banner2 + '\n' + header + '\n'
245
+ else:
246
+ self.old_banner2 = ''
247
+
248
+ if self.display_banner:
249
+ self.show_banner()
250
+
251
+ # Call the embedding code with a stack depth of 1 so it can skip over
252
+ # our call and get the original caller's namespaces.
253
+ self.mainloop(
254
+ local_ns, module, stack_depth=stack_depth, compile_flags=compile_flags
255
+ )
256
+
257
+ self.banner2 = self.old_banner2
258
+
259
+ if self.exit_msg is not None:
260
+ print(self.exit_msg)
261
+
262
+ if self.should_raise:
263
+ raise KillEmbedded('Embedded IPython raising error, as user requested.')
264
+
265
+ def mainloop(
266
+ self,
267
+ local_ns=None,
268
+ module=None,
269
+ stack_depth=0,
270
+ compile_flags=None,
271
+ ):
272
+ """Embeds IPython into a running python program.
273
+
274
+ Parameters
275
+ ----------
276
+ local_ns, module
277
+ Working local namespace (a dict) and module (a module or similar
278
+ object). If given as None, they are automatically taken from the scope
279
+ where the shell was called, so that program variables become visible.
280
+ stack_depth : int
281
+ How many levels in the stack to go to looking for namespaces (when
282
+ local_ns or module is None). This allows an intermediate caller to
283
+ make sure that this function gets the namespace from the intended
284
+ level in the stack. By default (0) it will get its locals and globals
285
+ from the immediate caller.
286
+ compile_flags
287
+ A bit field identifying the __future__ features
288
+ that are enabled, as passed to the builtin :func:`compile` function.
289
+ If given as None, they are automatically taken from the scope where
290
+ the shell was called.
291
+
292
+ """
293
+
294
+ # Get locals and globals from caller
295
+ if ((local_ns is None or module is None or compile_flags is None)
296
+ and self.default_user_namespaces):
297
+ call_frame = sys._getframe(stack_depth).f_back
298
+
299
+ if local_ns is None:
300
+ local_ns = call_frame.f_locals
301
+ if module is None:
302
+ global_ns = call_frame.f_globals
303
+ try:
304
+ module = sys.modules[global_ns['__name__']]
305
+ except KeyError:
306
+ warnings.warn("Failed to get module %s" % \
307
+ global_ns.get('__name__', 'unknown module')
308
+ )
309
+ module = make_main_module_type(global_ns)()
310
+ if compile_flags is None:
311
+ compile_flags = (call_frame.f_code.co_flags &
312
+ compilerop.PyCF_MASK)
313
+
314
+ # Save original namespace and module so we can restore them after
315
+ # embedding; otherwise the shell doesn't shut down correctly.
316
+ orig_user_module = self.user_module
317
+ orig_user_ns = self.user_ns
318
+ orig_compile_flags = self.compile.flags
319
+
320
+ # Update namespaces and fire up interpreter
321
+
322
+ # The global one is easy, we can just throw it in
323
+ if module is not None:
324
+ self.user_module = module
325
+
326
+ # But the user/local one is tricky: ipython needs it to store internal
327
+ # data, but we also need the locals. We'll throw our hidden variables
328
+ # like _ih and get_ipython() into the local namespace, but delete them
329
+ # later.
330
+ if local_ns is not None:
331
+ reentrant_local_ns = {k: v for (k, v) in local_ns.items() if k not in self.user_ns_hidden.keys()}
332
+ self.user_ns = reentrant_local_ns
333
+ self.init_user_ns()
334
+
335
+ # Compiler flags
336
+ if compile_flags is not None:
337
+ self.compile.flags = compile_flags
338
+
339
+ # make sure the tab-completer has the correct frame information, so it
340
+ # actually completes using the frame's locals/globals
341
+ self.set_completer_frame()
342
+
343
+ with self.builtin_trap, self.display_trap:
344
+ self.interact()
345
+
346
+ # now, purge out the local namespace of IPython's hidden variables.
347
+ if local_ns is not None:
348
+ local_ns.update({k: v for (k, v) in self.user_ns.items() if k not in self.user_ns_hidden.keys()})
349
+
350
+
351
+ # Restore original namespace so shell can shut down when we exit.
352
+ self.user_module = orig_user_module
353
+ self.user_ns = orig_user_ns
354
+ self.compile.flags = orig_compile_flags
355
+
356
+
357
+ def embed(*, header="", compile_flags=None, **kwargs):
358
+ """Call this to embed IPython at the current point in your program.
359
+
360
+ The first invocation of this will create a :class:`terminal.embed.InteractiveShellEmbed`
361
+ instance and then call it. Consecutive calls just call the already
362
+ created instance.
363
+
364
+ If you don't want the kernel to initialize the namespace
365
+ from the scope of the surrounding function,
366
+ and/or you want to load full IPython configuration,
367
+ you probably want `IPython.start_ipython()` instead.
368
+
369
+ Here is a simple example::
370
+
371
+ from IPython import embed
372
+ a = 10
373
+ b = 20
374
+ embed(header='First time')
375
+ c = 30
376
+ d = 40
377
+ embed()
378
+
379
+ Parameters
380
+ ----------
381
+
382
+ header : str
383
+ Optional header string to print at startup.
384
+ compile_flags
385
+ Passed to the `compile_flags` parameter of :py:meth:`terminal.embed.InteractiveShellEmbed.mainloop()`,
386
+ which is called when the :class:`terminal.embed.InteractiveShellEmbed` instance is called.
387
+ **kwargs : various, optional
388
+ Any other kwargs will be passed to the :class:`terminal.embed.InteractiveShellEmbed` constructor.
389
+ Full customization can be done by passing a traitlets :class:`Config` in as the
390
+ `config` argument (see :ref:`configure_start_ipython` and :ref:`terminal_options`).
391
+ """
392
+ config = kwargs.get('config')
393
+ if config is None:
394
+ config = load_default_config()
395
+ config.InteractiveShellEmbed = config.TerminalInteractiveShell
396
+ kwargs["config"] = config
397
+ using = kwargs.get("using", "sync")
398
+ colors = kwargs.pop("colors", "nocolor")
399
+ if using:
400
+ kwargs["config"].update(
401
+ {
402
+ "TerminalInteractiveShell": {
403
+ "loop_runner": using,
404
+ "colors": colors,
405
+ "autoawait": using != "sync",
406
+ }
407
+ }
408
+ )
409
+ # save ps1/ps2 if defined
410
+ ps1 = None
411
+ ps2 = None
412
+ try:
413
+ ps1 = sys.ps1
414
+ ps2 = sys.ps2
415
+ except AttributeError:
416
+ pass
417
+ #save previous instance
418
+ saved_shell_instance = InteractiveShell._instance
419
+ if saved_shell_instance is not None:
420
+ cls = type(saved_shell_instance)
421
+ cls.clear_instance()
422
+ frame = sys._getframe(1)
423
+ shell = InteractiveShellEmbed.instance(_init_location_id='%s:%s' % (
424
+ frame.f_code.co_filename, frame.f_lineno), **kwargs)
425
+ shell(header=header, stack_depth=2, compile_flags=compile_flags,
426
+ _call_location_id='%s:%s' % (frame.f_code.co_filename, frame.f_lineno))
427
+ InteractiveShellEmbed.clear_instance()
428
+ #restore previous instance
429
+ if saved_shell_instance is not None:
430
+ cls = type(saved_shell_instance)
431
+ cls.clear_instance()
432
+ for subclass in cls._walk_mro():
433
+ subclass._instance = saved_shell_instance
434
+ if ps1 is not None:
435
+ sys.ps1 = ps1
436
+ sys.ps2 = ps2
temp_venv/lib/python3.13/site-packages/IPython/terminal/interactiveshell.py ADDED
@@ -0,0 +1,1119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """IPython terminal interface using prompt_toolkit"""
2
+
3
+ import os
4
+ import sys
5
+ import inspect
6
+ from warnings import warn
7
+ from typing import Union as UnionType, Optional
8
+
9
+ from IPython.core.async_helpers import get_asyncio_loop
10
+ from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
11
+ from IPython.utils.py3compat import input
12
+ from IPython.utils.PyColorize import theme_table
13
+ from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title
14
+ from IPython.utils.process import abbrev_cwd
15
+ from traitlets import (
16
+ Any,
17
+ Bool,
18
+ Dict,
19
+ Enum,
20
+ Float,
21
+ Instance,
22
+ Integer,
23
+ List,
24
+ Type,
25
+ Unicode,
26
+ Union,
27
+ default,
28
+ observe,
29
+ validate,
30
+ DottedObjectName,
31
+ )
32
+ from traitlets.utils.importstring import import_item
33
+
34
+
35
+ from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
36
+ from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
37
+ from prompt_toolkit.filters import HasFocus, Condition, IsDone
38
+ from prompt_toolkit.formatted_text import PygmentsTokens
39
+ from prompt_toolkit.history import History
40
+ from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
41
+ from prompt_toolkit.output import ColorDepth
42
+ from prompt_toolkit.patch_stdout import patch_stdout
43
+ from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
44
+ from prompt_toolkit.styles import DynamicStyle, merge_styles
45
+ from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
46
+ from pygments.styles import get_style_by_name
47
+ from pygments.style import Style
48
+
49
+ from .debugger import TerminalPdb, Pdb
50
+ from .magics import TerminalMagics
51
+ from .pt_inputhooks import get_inputhook_name_and_func
52
+ from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
53
+ from .ptutils import IPythonPTCompleter, IPythonPTLexer
54
+ from .shortcuts import (
55
+ KEY_BINDINGS,
56
+ UNASSIGNED_ALLOWED_COMMANDS,
57
+ create_ipython_shortcuts,
58
+ create_identifier,
59
+ RuntimeBinding,
60
+ add_binding,
61
+ )
62
+ from .shortcuts.filters import KEYBINDING_FILTERS, filter_from_string
63
+ from .shortcuts.auto_suggest import (
64
+ NavigableAutoSuggestFromHistory,
65
+ AppendAutoSuggestionInAnyLine,
66
+ )
67
+
68
+
69
+ class _NoStyle(Style):
70
+ pass
71
+
72
+
73
+
74
+ def _backward_compat_continuation_prompt_tokens(
75
+ method, width: int, *, lineno: int, wrap_count: int
76
+ ):
77
+ """
78
+ Sagemath use custom prompt and we broke them in 8.19.
79
+
80
+ make sure to pass only width if method only support width
81
+ """
82
+ sig = inspect.signature(method)
83
+ extra = {}
84
+ params = inspect.signature(method).parameters
85
+ if "lineno" in inspect.signature(method).parameters or any(
86
+ [p.kind == p.VAR_KEYWORD for p in sig.parameters.values()]
87
+ ):
88
+ extra["lineno"] = lineno
89
+ if "line_number" in inspect.signature(method).parameters or any(
90
+ [p.kind == p.VAR_KEYWORD for p in sig.parameters.values()]
91
+ ):
92
+ extra["line_number"] = lineno
93
+
94
+ if "wrap_count" in inspect.signature(method).parameters or any(
95
+ [p.kind == p.VAR_KEYWORD for p in sig.parameters.values()]
96
+ ):
97
+ extra["wrap_count"] = wrap_count
98
+ return method(width, **extra)
99
+
100
+
101
+ def get_default_editor():
102
+ try:
103
+ return os.environ['EDITOR']
104
+ except KeyError:
105
+ pass
106
+ except UnicodeError:
107
+ warn("$EDITOR environment variable is not pure ASCII. Using platform "
108
+ "default editor.")
109
+
110
+ if os.name == 'posix':
111
+ return 'vi' # the only one guaranteed to be there!
112
+ else:
113
+ return "notepad" # same in Windows!
114
+
115
+
116
+ # conservatively check for tty
117
+ # overridden streams can result in things like:
118
+ # - sys.stdin = None
119
+ # - no isatty method
120
+ for _name in ('stdin', 'stdout', 'stderr'):
121
+ _stream = getattr(sys, _name)
122
+ try:
123
+ if not _stream or not hasattr(_stream, "isatty") or not _stream.isatty():
124
+ _is_tty = False
125
+ break
126
+ except ValueError:
127
+ # stream is closed
128
+ _is_tty = False
129
+ break
130
+ else:
131
+ _is_tty = True
132
+
133
+
134
+ _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
135
+
136
+ def black_reformat_handler(text_before_cursor):
137
+ """
138
+ We do not need to protect against error,
139
+ this is taken care at a higher level where any reformat error is ignored.
140
+ Indeed we may call reformatting on incomplete code.
141
+ """
142
+ import black
143
+
144
+ formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
145
+ if not text_before_cursor.endswith("\n") and formatted_text.endswith("\n"):
146
+ formatted_text = formatted_text[:-1]
147
+ return formatted_text
148
+
149
+
150
+ def yapf_reformat_handler(text_before_cursor):
151
+ from yapf.yapflib import file_resources
152
+ from yapf.yapflib import yapf_api
153
+
154
+ style_config = file_resources.GetDefaultStyleForDir(os.getcwd())
155
+ formatted_text, was_formatted = yapf_api.FormatCode(
156
+ text_before_cursor, style_config=style_config
157
+ )
158
+ if was_formatted:
159
+ if not text_before_cursor.endswith("\n") and formatted_text.endswith("\n"):
160
+ formatted_text = formatted_text[:-1]
161
+ return formatted_text
162
+ else:
163
+ return text_before_cursor
164
+
165
+
166
+ class PtkHistoryAdapter(History):
167
+ """
168
+ Prompt toolkit has it's own way of handling history, Where it assumes it can
169
+ Push/pull from history.
170
+
171
+ """
172
+
173
+ def __init__(self, shell):
174
+ super().__init__()
175
+ self.shell = shell
176
+ self._refresh()
177
+
178
+ def append_string(self, string):
179
+ # we rely on sql for that.
180
+ self._loaded = False
181
+ self._refresh()
182
+
183
+ def _refresh(self):
184
+ if not self._loaded:
185
+ self._loaded_strings = list(self.load_history_strings())
186
+
187
+ def load_history_strings(self):
188
+ last_cell = ""
189
+ res = []
190
+ for __, ___, cell in self.shell.history_manager.get_tail(
191
+ self.shell.history_load_length, include_latest=True
192
+ ):
193
+ # Ignore blank lines and consecutive duplicates
194
+ cell = cell.rstrip()
195
+ if cell and (cell != last_cell):
196
+ res.append(cell)
197
+ last_cell = cell
198
+ yield from res[::-1]
199
+
200
+ def store_string(self, string: str) -> None:
201
+ pass
202
+
203
+ class TerminalInteractiveShell(InteractiveShell):
204
+ mime_renderers = Dict().tag(config=True)
205
+
206
+ min_elide = Integer(
207
+ 30, help="minimum characters for filling with ellipsis in file completions"
208
+ ).tag(config=True)
209
+ space_for_menu = Integer(
210
+ 6,
211
+ help="Number of line at the bottom of the screen "
212
+ "to reserve for the tab completion menu, "
213
+ "search history, ...etc, the height of "
214
+ "these menus will at most this value. "
215
+ "Increase it is you prefer long and skinny "
216
+ "menus, decrease for short and wide.",
217
+ ).tag(config=True)
218
+
219
+ pt_app: UnionType[PromptSession, None] = None
220
+ auto_suggest: UnionType[
221
+ AutoSuggestFromHistory,
222
+ NavigableAutoSuggestFromHistory,
223
+ None,
224
+ ] = None
225
+ debugger_history = None
226
+
227
+ debugger_history_file = Unicode(
228
+ "~/.pdbhistory", help="File in which to store and read history"
229
+ ).tag(config=True)
230
+
231
+ simple_prompt = Bool(_use_simple_prompt,
232
+ help="""Use `raw_input` for the REPL, without completion and prompt colors.
233
+
234
+ Useful when controlling IPython as a subprocess, and piping
235
+ STDIN/OUT/ERR. Known usage are: IPython's own testing machinery,
236
+ and emacs' inferior-python subprocess (assuming you have set
237
+ `python-shell-interpreter` to "ipython") available through the
238
+ built-in `M-x run-python` and third party packages such as elpy.
239
+
240
+ This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
241
+ environment variable is set, or the current terminal is not a tty.
242
+ Thus the Default value reported in --help-all, or config will often
243
+ be incorrectly reported.
244
+ """,
245
+ ).tag(config=True)
246
+
247
+ @property
248
+ def debugger_cls(self):
249
+ return Pdb if self.simple_prompt else TerminalPdb
250
+
251
+ confirm_exit = Bool(True,
252
+ help="""
253
+ Set to confirm when you try to exit IPython with an EOF (Control-D
254
+ in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
255
+ you can force a direct exit without any confirmation.""",
256
+ ).tag(config=True)
257
+
258
+ editing_mode = Unicode('emacs',
259
+ help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
260
+ ).tag(config=True)
261
+
262
+ emacs_bindings_in_vi_insert_mode = Bool(
263
+ True,
264
+ help="Add shortcuts from 'emacs' insert mode to 'vi' insert mode.",
265
+ ).tag(config=True)
266
+
267
+ modal_cursor = Bool(
268
+ True,
269
+ help="""
270
+ Cursor shape changes depending on vi mode: beam in vi insert mode,
271
+ block in nav mode, underscore in replace mode.""",
272
+ ).tag(config=True)
273
+
274
+ ttimeoutlen = Float(
275
+ 0.01,
276
+ help="""The time in milliseconds that is waited for a key code
277
+ to complete.""",
278
+ ).tag(config=True)
279
+
280
+ timeoutlen = Float(
281
+ 0.5,
282
+ help="""The time in milliseconds that is waited for a mapped key
283
+ sequence to complete.""",
284
+ ).tag(config=True)
285
+
286
+ autoformatter = Unicode(
287
+ None,
288
+ help="Autoformatter to reformat Terminal code. Can be `'black'`, `'yapf'` or `None`",
289
+ allow_none=True
290
+ ).tag(config=True)
291
+
292
+ auto_match = Bool(
293
+ False,
294
+ help="""
295
+ Automatically add/delete closing bracket or quote when opening bracket or quote is entered/deleted.
296
+ Brackets: (), [], {}
297
+ Quotes: '', \"\"
298
+ """,
299
+ ).tag(config=True)
300
+
301
+ mouse_support = Bool(False,
302
+ help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
303
+ ).tag(config=True)
304
+
305
+ # We don't load the list of styles for the help string, because loading
306
+ # Pygments plugins takes time and can cause unexpected errors.
307
+ highlighting_style = Union(
308
+ [Unicode("legacy"), Type(klass=Style)],
309
+ help="""Deprecated, and has not effect, use IPython themes
310
+
311
+ The name or class of a Pygments style to use for syntax
312
+ highlighting. To see available styles, run `pygmentize -L styles`.""",
313
+ ).tag(config=True)
314
+
315
+ @validate('editing_mode')
316
+ def _validate_editing_mode(self, proposal):
317
+ if proposal['value'].lower() == 'vim':
318
+ proposal['value']= 'vi'
319
+ elif proposal['value'].lower() == 'default':
320
+ proposal['value']= 'emacs'
321
+
322
+ if hasattr(EditingMode, proposal['value'].upper()):
323
+ return proposal['value'].lower()
324
+
325
+ return self.editing_mode
326
+
327
+ @observe('editing_mode')
328
+ def _editing_mode(self, change):
329
+ if self.pt_app:
330
+ self.pt_app.editing_mode = getattr(EditingMode, change.new.upper())
331
+
332
+ def _set_formatter(self, formatter):
333
+ if formatter is None:
334
+ self.reformat_handler = lambda x:x
335
+ elif formatter == 'black':
336
+ self.reformat_handler = black_reformat_handler
337
+ elif formatter == "yapf":
338
+ self.reformat_handler = yapf_reformat_handler
339
+ else:
340
+ raise ValueError
341
+
342
+ @observe("autoformatter")
343
+ def _autoformatter_changed(self, change):
344
+ formatter = change.new
345
+ self._set_formatter(formatter)
346
+
347
+ @observe('highlighting_style')
348
+ @observe('colors')
349
+ def _highlighting_style_changed(self, change):
350
+ assert change.new == change.new.lower()
351
+ if change.new != "legacy":
352
+ warn(
353
+ "highlighting_style is deprecated since 9.0 and have no effect, use themeing."
354
+ )
355
+ return
356
+
357
+ def refresh_style(self):
358
+ self._style = self._make_style_from_name_or_cls("legacy")
359
+
360
+ # TODO: deprecate this
361
+ highlighting_style_overrides = Dict(
362
+ help="Override highlighting format for specific tokens"
363
+ ).tag(config=True)
364
+
365
+ true_color = Bool(False,
366
+ help="""Use 24bit colors instead of 256 colors in prompt highlighting.
367
+ If your terminal supports true color, the following command should
368
+ print ``TRUECOLOR`` in orange::
369
+
370
+ printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"
371
+ """,
372
+ ).tag(config=True)
373
+
374
+ editor = Unicode(get_default_editor(),
375
+ help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
376
+ ).tag(config=True)
377
+
378
+ prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
379
+
380
+ prompts = Instance(Prompts)
381
+
382
+ @default('prompts')
383
+ def _prompts_default(self):
384
+ return self.prompts_class(self)
385
+
386
+ # @observe('prompts')
387
+ # def _(self, change):
388
+ # self._update_layout()
389
+
390
+ @default('displayhook_class')
391
+ def _displayhook_class_default(self):
392
+ return RichPromptDisplayHook
393
+
394
+ term_title = Bool(True,
395
+ help="Automatically set the terminal title"
396
+ ).tag(config=True)
397
+
398
+ term_title_format = Unicode("IPython: {cwd}",
399
+ help="Customize the terminal title format. This is a python format string. " +
400
+ "Available substitutions are: {cwd}."
401
+ ).tag(config=True)
402
+
403
+ display_completions = Enum(('column', 'multicolumn','readlinelike'),
404
+ help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
405
+ "'readlinelike'. These options are for `prompt_toolkit`, see "
406
+ "`prompt_toolkit` documentation for more information."
407
+ ),
408
+ default_value='multicolumn').tag(config=True)
409
+
410
+ highlight_matching_brackets = Bool(True,
411
+ help="Highlight matching brackets.",
412
+ ).tag(config=True)
413
+
414
+ extra_open_editor_shortcuts = Bool(False,
415
+ help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
416
+ "This is in addition to the F2 binding, which is always enabled."
417
+ ).tag(config=True)
418
+
419
+ handle_return = Any(None,
420
+ help="Provide an alternative handler to be called when the user presses "
421
+ "Return. This is an advanced option intended for debugging, which "
422
+ "may be changed or removed in later releases."
423
+ ).tag(config=True)
424
+
425
+ enable_history_search = Bool(True,
426
+ help="Allows to enable/disable the prompt toolkit history search"
427
+ ).tag(config=True)
428
+
429
+ autosuggestions_provider = Unicode(
430
+ "NavigableAutoSuggestFromHistory",
431
+ help="Specifies from which source automatic suggestions are provided. "
432
+ "Can be set to ``'NavigableAutoSuggestFromHistory'`` (:kbd:`up` and "
433
+ ":kbd:`down` swap suggestions), ``'AutoSuggestFromHistory'``, "
434
+ " or ``None`` to disable automatic suggestions. "
435
+ "Default is `'NavigableAutoSuggestFromHistory`'.",
436
+ allow_none=True,
437
+ ).tag(config=True)
438
+ _autosuggestions_provider: Any
439
+
440
+ llm_constructor_kwargs = Dict(
441
+ {},
442
+ help="""
443
+ Extra arguments to pass to `llm_provider_class` constructor.
444
+
445
+ This is used to – for example – set the `model_id`""",
446
+ ).tag(config=True)
447
+
448
+ llm_prefix_from_history = DottedObjectName(
449
+ "input_history",
450
+ help="""\
451
+ Fully Qualifed name of a function that takes an IPython history manager and
452
+ return a prefix to pass the llm provider in addition to the current buffer
453
+ text.
454
+
455
+ You can use:
456
+
457
+ - no_prefix
458
+ - input_history
459
+
460
+ As default value. `input_history` (default), will use all the input history
461
+ of current IPython session
462
+
463
+ """,
464
+ ).tag(config=True)
465
+ _llm_prefix_from_history: Any
466
+
467
+ @observe("llm_prefix_from_history")
468
+ def _llm_prefix_from_history_changed(self, change):
469
+ name = change.new
470
+ self._llm_prefix_from_history = name
471
+ self._set_autosuggestions()
472
+
473
+ llm_provider_class = DottedObjectName(
474
+ None,
475
+ allow_none=True,
476
+ help="""\
477
+ Provisional:
478
+ This is a provisinal API in IPython 8.32, before stabilisation
479
+ in 9.0, it may change without warnings.
480
+
481
+ class to use for the `NavigableAutoSuggestFromHistory` to request
482
+ completions from a LLM, this should inherit from
483
+ `jupyter_ai_magics:BaseProvider` and implement
484
+ `stream_inline_completions`
485
+ """,
486
+ ).tag(config=True)
487
+ _llm_provider_class: Any = None
488
+
489
+ @observe("llm_provider_class")
490
+ def _llm_provider_class_changed(self, change):
491
+ provider_class = change.new
492
+ self._llm_provider_class = provider_class
493
+ self._set_autosuggestions()
494
+
495
+ def _set_autosuggestions(self, provider=None):
496
+ if provider is None:
497
+ provider = self.autosuggestions_provider
498
+ # disconnect old handler
499
+ if self.auto_suggest and isinstance(
500
+ self.auto_suggest, NavigableAutoSuggestFromHistory
501
+ ):
502
+ self.auto_suggest.disconnect()
503
+ if provider is None:
504
+ self.auto_suggest = None
505
+ elif provider == "AutoSuggestFromHistory":
506
+ self.auto_suggest = AutoSuggestFromHistory()
507
+ elif provider == "NavigableAutoSuggestFromHistory":
508
+ # LLM stuff are all Provisional in 8.32
509
+ if self._llm_provider_class:
510
+ llm_provider_constructor = import_item(self._llm_provider_class)
511
+ llm_provider = llm_provider_constructor(**self.llm_constructor_kwargs)
512
+ else:
513
+ llm_provider = None
514
+ self.auto_suggest = NavigableAutoSuggestFromHistory()
515
+ # Provisinal in 8.32
516
+ self.auto_suggest._llm_provider = llm_provider
517
+
518
+ name = self.llm_prefix_from_history
519
+
520
+ if name == "no_prefix":
521
+
522
+ def no_prefix(history_manager):
523
+ return ""
524
+
525
+ fun = no_prefix
526
+
527
+ elif name == "input_history":
528
+
529
+ def input_history(history_manager):
530
+ return "\n".join([s[2] for s in history_manager.get_range()]) + "\n"
531
+
532
+ fun = input_history
533
+
534
+ else:
535
+ fun = import_item(name)
536
+ self.auto_suggest._llm_prefixer = fun
537
+ else:
538
+ raise ValueError("No valid provider.")
539
+ if self.pt_app:
540
+ self.pt_app.auto_suggest = self.auto_suggest
541
+
542
+ @observe("autosuggestions_provider")
543
+ def _autosuggestions_provider_changed(self, change):
544
+ provider = change.new
545
+ self._set_autosuggestions(provider)
546
+
547
+ shortcuts = List(
548
+ trait=Dict(
549
+ key_trait=Enum(
550
+ [
551
+ "command",
552
+ "match_keys",
553
+ "match_filter",
554
+ "new_keys",
555
+ "new_filter",
556
+ "create",
557
+ ]
558
+ ),
559
+ per_key_traits={
560
+ "command": Unicode(),
561
+ "match_keys": List(Unicode()),
562
+ "match_filter": Unicode(),
563
+ "new_keys": List(Unicode()),
564
+ "new_filter": Unicode(),
565
+ "create": Bool(False),
566
+ },
567
+ ),
568
+ help="""
569
+ Add, disable or modifying shortcuts.
570
+
571
+ Each entry on the list should be a dictionary with ``command`` key
572
+ identifying the target function executed by the shortcut and at least
573
+ one of the following:
574
+
575
+ - ``match_keys``: list of keys used to match an existing shortcut,
576
+ - ``match_filter``: shortcut filter used to match an existing shortcut,
577
+ - ``new_keys``: list of keys to set,
578
+ - ``new_filter``: a new shortcut filter to set
579
+
580
+ The filters have to be composed of pre-defined verbs and joined by one
581
+ of the following conjunctions: ``&`` (and), ``|`` (or), ``~`` (not).
582
+ The pre-defined verbs are:
583
+
584
+ {filters}
585
+
586
+ To disable a shortcut set ``new_keys`` to an empty list.
587
+ To add a shortcut add key ``create`` with value ``True``.
588
+
589
+ When modifying/disabling shortcuts, ``match_keys``/``match_filter`` can
590
+ be omitted if the provided specification uniquely identifies a shortcut
591
+ to be modified/disabled. When modifying a shortcut ``new_filter`` or
592
+ ``new_keys`` can be omitted which will result in reuse of the existing
593
+ filter/keys.
594
+
595
+ Only shortcuts defined in IPython (and not default prompt-toolkit
596
+ shortcuts) can be modified or disabled. The full list of shortcuts,
597
+ command identifiers and filters is available under
598
+ :ref:`terminal-shortcuts-list`.
599
+
600
+ Here is an example:
601
+
602
+ .. code::
603
+
604
+ c.TerminalInteractiveShell.shortcuts = [
605
+ {{
606
+ "new_keys": ["c-q"],
607
+ "command": "prompt_toolkit:named_commands.capitalize_word",
608
+ "create": True,
609
+ }},
610
+ {{
611
+ "new_keys": ["c-j"],
612
+ "command": "prompt_toolkit:named_commands.beginning_of_line",
613
+ "create": True,
614
+ }},
615
+ ]
616
+
617
+
618
+ """.format(
619
+ filters="\n ".join([f" - ``{k}``" for k in KEYBINDING_FILTERS])
620
+ ),
621
+ ).tag(config=True)
622
+
623
+ @observe("shortcuts")
624
+ def _shortcuts_changed(self, change):
625
+ if self.pt_app:
626
+ self.pt_app.key_bindings = self._merge_shortcuts(user_shortcuts=change.new)
627
+
628
+ def _merge_shortcuts(self, user_shortcuts):
629
+ # rebuild the bindings list from scratch
630
+ key_bindings = create_ipython_shortcuts(self)
631
+
632
+ # for now we only allow adding shortcuts for a specific set of
633
+ # commands; this is a security precution.
634
+ allowed_commands = {
635
+ create_identifier(binding.command): binding.command
636
+ for binding in KEY_BINDINGS
637
+ }
638
+ allowed_commands.update(
639
+ {
640
+ create_identifier(command): command
641
+ for command in UNASSIGNED_ALLOWED_COMMANDS
642
+ }
643
+ )
644
+ shortcuts_to_skip = []
645
+ shortcuts_to_add = []
646
+
647
+ for shortcut in user_shortcuts:
648
+ command_id = shortcut["command"]
649
+ if command_id not in allowed_commands:
650
+ allowed_commands = "\n - ".join(allowed_commands)
651
+ raise ValueError(
652
+ f"{command_id} is not a known shortcut command."
653
+ f" Allowed commands are: \n - {allowed_commands}"
654
+ )
655
+ old_keys = shortcut.get("match_keys", None)
656
+ old_filter = (
657
+ filter_from_string(shortcut["match_filter"])
658
+ if "match_filter" in shortcut
659
+ else None
660
+ )
661
+ matching = [
662
+ binding
663
+ for binding in KEY_BINDINGS
664
+ if (
665
+ (old_filter is None or binding.filter == old_filter)
666
+ and (old_keys is None or [k for k in binding.keys] == old_keys)
667
+ and create_identifier(binding.command) == command_id
668
+ )
669
+ ]
670
+
671
+ new_keys = shortcut.get("new_keys", None)
672
+ new_filter = shortcut.get("new_filter", None)
673
+
674
+ command = allowed_commands[command_id]
675
+
676
+ creating_new = shortcut.get("create", False)
677
+ modifying_existing = not creating_new and (
678
+ new_keys is not None or new_filter
679
+ )
680
+
681
+ if creating_new and new_keys == []:
682
+ raise ValueError("Cannot add a shortcut without keys")
683
+
684
+ if modifying_existing:
685
+ specification = {
686
+ key: shortcut[key]
687
+ for key in ["command", "filter"]
688
+ if key in shortcut
689
+ }
690
+ if len(matching) == 0:
691
+ raise ValueError(
692
+ f"No shortcuts matching {specification} found in {KEY_BINDINGS}"
693
+ )
694
+ elif len(matching) > 1:
695
+ raise ValueError(
696
+ f"Multiple shortcuts matching {specification} found,"
697
+ f" please add keys/filter to select one of: {matching}"
698
+ )
699
+
700
+ matched = matching[0]
701
+ old_filter = matched.filter
702
+ old_keys = list(matched.keys)
703
+ shortcuts_to_skip.append(
704
+ RuntimeBinding(
705
+ command,
706
+ keys=old_keys,
707
+ filter=old_filter,
708
+ )
709
+ )
710
+
711
+ if new_keys != []:
712
+ shortcuts_to_add.append(
713
+ RuntimeBinding(
714
+ command,
715
+ keys=new_keys or old_keys,
716
+ filter=(
717
+ filter_from_string(new_filter)
718
+ if new_filter is not None
719
+ else (
720
+ old_filter
721
+ if old_filter is not None
722
+ else filter_from_string("always")
723
+ )
724
+ ),
725
+ )
726
+ )
727
+
728
+ # rebuild the bindings list from scratch
729
+ key_bindings = create_ipython_shortcuts(self, skip=shortcuts_to_skip)
730
+ for binding in shortcuts_to_add:
731
+ add_binding(key_bindings, binding)
732
+
733
+ return key_bindings
734
+
735
+ prompt_includes_vi_mode = Bool(True,
736
+ help="Display the current vi mode (when using vi editing mode)."
737
+ ).tag(config=True)
738
+
739
+ prompt_line_number_format = Unicode(
740
+ "",
741
+ help="The format for line numbering, will be passed `line` (int, 1 based)"
742
+ " the current line number and `rel_line` the relative line number."
743
+ " for example to display both you can use the following template string :"
744
+ " c.TerminalInteractiveShell.prompt_line_number_format='{line: 4d}/{rel_line:+03d} | '"
745
+ " This will display the current line number, with leading space and a width of at least 4"
746
+ " character, as well as the relative line number 0 padded and always with a + or - sign."
747
+ " Note that when using Emacs mode the prompt of the first line may not update.",
748
+ ).tag(config=True)
749
+
750
+ @observe('term_title')
751
+ def init_term_title(self, change=None):
752
+ # Enable or disable the terminal title.
753
+ if self.term_title and _is_tty:
754
+ toggle_set_term_title(True)
755
+ set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
756
+ else:
757
+ toggle_set_term_title(False)
758
+
759
+ def restore_term_title(self):
760
+ if self.term_title and _is_tty:
761
+ restore_term_title()
762
+
763
+ def init_display_formatter(self):
764
+ super(TerminalInteractiveShell, self).init_display_formatter()
765
+ # terminal only supports plain text
766
+ self.display_formatter.active_types = ["text/plain"]
767
+
768
+ def init_prompt_toolkit_cli(self):
769
+ if self.simple_prompt:
770
+ # Fall back to plain non-interactive output for tests.
771
+ # This is very limited.
772
+ def prompt():
773
+ prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
774
+ lines = [input(prompt_text)]
775
+ prompt_continuation = "".join(
776
+ x[1] for x in self.prompts.continuation_prompt_tokens()
777
+ )
778
+ while self.check_complete("\n".join(lines))[0] == "incomplete":
779
+ lines.append(input(prompt_continuation))
780
+ return "\n".join(lines)
781
+
782
+ self.prompt_for_code = prompt
783
+ return
784
+
785
+ # Set up keyboard shortcuts
786
+ key_bindings = self._merge_shortcuts(user_shortcuts=self.shortcuts)
787
+
788
+ # Pre-populate history from IPython's history database
789
+ history = PtkHistoryAdapter(self)
790
+
791
+ self.refresh_style()
792
+ ptk_s = DynamicStyle(lambda: self._style)
793
+
794
+ editing_mode = getattr(EditingMode, self.editing_mode.upper())
795
+
796
+ self._use_asyncio_inputhook = False
797
+ self.pt_app = PromptSession(
798
+ auto_suggest=self.auto_suggest,
799
+ editing_mode=editing_mode,
800
+ key_bindings=key_bindings,
801
+ history=history,
802
+ completer=IPythonPTCompleter(shell=self),
803
+ enable_history_search=self.enable_history_search,
804
+ style=ptk_s,
805
+ include_default_pygments_style=False,
806
+ mouse_support=self.mouse_support,
807
+ enable_open_in_editor=self.extra_open_editor_shortcuts,
808
+ color_depth=self.color_depth,
809
+ tempfile_suffix=".py",
810
+ **self._extra_prompt_options(),
811
+ )
812
+ if isinstance(self.auto_suggest, NavigableAutoSuggestFromHistory):
813
+ self.auto_suggest.connect(self.pt_app)
814
+
815
+ def _make_style_from_name_or_cls(self, name_or_cls):
816
+ """
817
+ Small wrapper that make an IPython compatible style from a style name
818
+
819
+ We need that to add style for prompt ... etc.
820
+ """
821
+ assert name_or_cls == "legacy"
822
+ legacy = self.colors.lower()
823
+
824
+ theme = theme_table.get(legacy, None)
825
+ assert theme is not None, legacy
826
+
827
+ if legacy == "nocolor":
828
+ style_overrides = {}
829
+ style_cls = _NoStyle
830
+ else:
831
+ style_overrides = {**theme.extra_style, **self.highlighting_style_overrides}
832
+ if theme.base is not None:
833
+ style_cls = get_style_by_name(theme.base)
834
+ else:
835
+ style_cls = _NoStyle
836
+
837
+ style = merge_styles(
838
+ [
839
+ style_from_pygments_cls(style_cls),
840
+ style_from_pygments_dict(style_overrides),
841
+ ]
842
+ )
843
+
844
+ return style
845
+
846
+ @property
847
+ def pt_complete_style(self):
848
+ return {
849
+ 'multicolumn': CompleteStyle.MULTI_COLUMN,
850
+ 'column': CompleteStyle.COLUMN,
851
+ 'readlinelike': CompleteStyle.READLINE_LIKE,
852
+ }[self.display_completions]
853
+
854
+ @property
855
+ def color_depth(self):
856
+ return (ColorDepth.TRUE_COLOR if self.true_color else None)
857
+
858
+ def _ptk_prompt_cont(self, width: int, line_number: int, wrap_count: int):
859
+ return PygmentsTokens(
860
+ _backward_compat_continuation_prompt_tokens(
861
+ self.prompts.continuation_prompt_tokens,
862
+ width,
863
+ lineno=line_number,
864
+ wrap_count=wrap_count,
865
+ )
866
+ )
867
+
868
+ def _extra_prompt_options(self):
869
+ """
870
+ Return the current layout option for the current Terminal InteractiveShell
871
+ """
872
+ def get_message():
873
+ return PygmentsTokens(self.prompts.in_prompt_tokens())
874
+
875
+ if self.editing_mode == "emacs" and self.prompt_line_number_format == "":
876
+ # with emacs mode the prompt is (usually) static, so we call only
877
+ # the function once. With VI mode it can toggle between [ins] and
878
+ # [nor] so we can't precompute.
879
+ # here I'm going to favor the default keybinding which almost
880
+ # everybody uses to decrease CPU usage.
881
+ # if we have issues with users with custom Prompts we can see how to
882
+ # work around this.
883
+ get_message = get_message()
884
+
885
+ options = {
886
+ "complete_in_thread": False,
887
+ "lexer": IPythonPTLexer(),
888
+ "reserve_space_for_menu": self.space_for_menu,
889
+ "message": get_message,
890
+ "prompt_continuation": self._ptk_prompt_cont,
891
+ "multiline": True,
892
+ "complete_style": self.pt_complete_style,
893
+ "input_processors": [
894
+ # Highlight matching brackets, but only when this setting is
895
+ # enabled, and only when the DEFAULT_BUFFER has the focus.
896
+ ConditionalProcessor(
897
+ processor=HighlightMatchingBracketProcessor(chars="[](){}"),
898
+ filter=HasFocus(DEFAULT_BUFFER)
899
+ & ~IsDone()
900
+ & Condition(lambda: self.highlight_matching_brackets),
901
+ ),
902
+ # Show auto-suggestion in lines other than the last line.
903
+ ConditionalProcessor(
904
+ processor=AppendAutoSuggestionInAnyLine(),
905
+ filter=HasFocus(DEFAULT_BUFFER)
906
+ & ~IsDone()
907
+ & Condition(
908
+ lambda: isinstance(
909
+ self.auto_suggest,
910
+ NavigableAutoSuggestFromHistory,
911
+ )
912
+ ),
913
+ ),
914
+ ],
915
+ }
916
+
917
+ return options
918
+
919
+ def prompt_for_code(self):
920
+ if self.rl_next_input:
921
+ default = self.rl_next_input
922
+ self.rl_next_input = None
923
+ else:
924
+ default = ''
925
+
926
+ # In order to make sure that asyncio code written in the
927
+ # interactive shell doesn't interfere with the prompt, we run the
928
+ # prompt in a different event loop.
929
+ # If we don't do this, people could spawn coroutine with a
930
+ # while/true inside which will freeze the prompt.
931
+
932
+ with patch_stdout(raw=True):
933
+ if self._use_asyncio_inputhook:
934
+ # When we integrate the asyncio event loop, run the UI in the
935
+ # same event loop as the rest of the code. don't use an actual
936
+ # input hook. (Asyncio is not made for nesting event loops.)
937
+ asyncio_loop = get_asyncio_loop()
938
+ text = asyncio_loop.run_until_complete(
939
+ self.pt_app.prompt_async(
940
+ default=default, **self._extra_prompt_options()
941
+ )
942
+ )
943
+ else:
944
+ text = self.pt_app.prompt(
945
+ default=default,
946
+ inputhook=self._inputhook,
947
+ **self._extra_prompt_options(),
948
+ )
949
+
950
+ return text
951
+
952
+ def init_io(self):
953
+ if sys.platform not in {'win32', 'cli'}:
954
+ return
955
+
956
+ import colorama
957
+ colorama.init()
958
+
959
+ def init_magics(self):
960
+ super(TerminalInteractiveShell, self).init_magics()
961
+ self.register_magics(TerminalMagics)
962
+
963
+ def init_alias(self):
964
+ # The parent class defines aliases that can be safely used with any
965
+ # frontend.
966
+ super(TerminalInteractiveShell, self).init_alias()
967
+
968
+ # Now define aliases that only make sense on the terminal, because they
969
+ # need direct access to the console in a way that we can't emulate in
970
+ # GUI or web frontend
971
+ if os.name == 'posix':
972
+ for cmd in ('clear', 'more', 'less', 'man'):
973
+ self.alias_manager.soft_define_alias(cmd, cmd)
974
+
975
+ def __init__(self, *args, **kwargs) -> None:
976
+ super().__init__(*args, **kwargs)
977
+ self._set_autosuggestions(self.autosuggestions_provider)
978
+ self.init_prompt_toolkit_cli()
979
+ self.init_term_title()
980
+ self.keep_running = True
981
+ self._set_formatter(self.autoformatter)
982
+
983
+ def ask_exit(self):
984
+ self.keep_running = False
985
+
986
+ rl_next_input = None
987
+
988
+ def interact(self):
989
+ self.keep_running = True
990
+ while self.keep_running:
991
+ print(self.separate_in, end='')
992
+
993
+ try:
994
+ code = self.prompt_for_code()
995
+ except EOFError:
996
+ if (not self.confirm_exit) \
997
+ or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
998
+ self.ask_exit()
999
+
1000
+ else:
1001
+ if code:
1002
+ self.run_cell(code, store_history=True)
1003
+
1004
+ def mainloop(self):
1005
+ # An extra layer of protection in case someone mashing Ctrl-C breaks
1006
+ # out of our internal code.
1007
+ while True:
1008
+ try:
1009
+ self.interact()
1010
+ break
1011
+ except KeyboardInterrupt as e:
1012
+ print("\n%s escaped interact()\n" % type(e).__name__)
1013
+ finally:
1014
+ # An interrupt during the eventloop will mess up the
1015
+ # internal state of the prompt_toolkit library.
1016
+ # Stopping the eventloop fixes this, see
1017
+ # https://github.com/ipython/ipython/pull/9867
1018
+ if hasattr(self, '_eventloop'):
1019
+ self._eventloop.stop()
1020
+
1021
+ self.restore_term_title()
1022
+
1023
+ # try to call some at-exit operation optimistically as some things can't
1024
+ # be done during interpreter shutdown. this is technically inaccurate as
1025
+ # this make mainlool not re-callable, but that should be a rare if not
1026
+ # in existent use case.
1027
+
1028
+ self._atexit_once()
1029
+
1030
+ _inputhook = None
1031
+ def inputhook(self, context):
1032
+ warn(
1033
+ "inputkook seem unused, and marked for deprecation/Removal as of IPython 9.0. "
1034
+ "Please open an issue if you are using it.",
1035
+ category=PendingDeprecationWarning,
1036
+ stacklevel=2,
1037
+ )
1038
+ if self._inputhook is not None:
1039
+ self._inputhook(context)
1040
+
1041
+ active_eventloop: Optional[str] = None
1042
+
1043
+ def enable_gui(self, gui: Optional[str] = None) -> None:
1044
+ if gui:
1045
+ from ..core.pylabtools import _convert_gui_from_matplotlib
1046
+
1047
+ gui = _convert_gui_from_matplotlib(gui)
1048
+
1049
+ if self.simple_prompt is True and gui is not None:
1050
+ print(
1051
+ f'Cannot install event loop hook for "{gui}" when running with `--simple-prompt`.'
1052
+ )
1053
+ print(
1054
+ "NOTE: Tk is supported natively; use Tk apps and Tk backends with `--simple-prompt`."
1055
+ )
1056
+ return
1057
+
1058
+ if self._inputhook is None and gui is None:
1059
+ print("No event loop hook running.")
1060
+ return
1061
+
1062
+ if self._inputhook is not None and gui is not None:
1063
+ newev, newinhook = get_inputhook_name_and_func(gui)
1064
+ if self._inputhook == newinhook:
1065
+ # same inputhook, do nothing
1066
+ self.log.info(
1067
+ f"Shell is already running the {self.active_eventloop} eventloop. Doing nothing"
1068
+ )
1069
+ return
1070
+ self.log.warning(
1071
+ f"Shell is already running a different gui event loop for {self.active_eventloop}. "
1072
+ "Call with no arguments to disable the current loop."
1073
+ )
1074
+ return
1075
+ if self._inputhook is not None and gui is None:
1076
+ self.active_eventloop = self._inputhook = None
1077
+
1078
+ if gui and (gui not in {None, "webagg"}):
1079
+ # This hook runs with each cycle of the `prompt_toolkit`'s event loop.
1080
+ self.active_eventloop, self._inputhook = get_inputhook_name_and_func(gui)
1081
+ else:
1082
+ self.active_eventloop = self._inputhook = None
1083
+
1084
+ self._use_asyncio_inputhook = gui == "asyncio"
1085
+
1086
+ # Run !system commands directly, not through pipes, so terminal programs
1087
+ # work correctly.
1088
+ system = InteractiveShell.system_raw
1089
+
1090
+ def auto_rewrite_input(self, cmd):
1091
+ """Overridden from the parent class to use fancy rewriting prompt"""
1092
+ if not self.show_rewritten_input:
1093
+ return
1094
+
1095
+ tokens = self.prompts.rewrite_prompt_tokens()
1096
+ if self.pt_app:
1097
+ print_formatted_text(PygmentsTokens(tokens), end='',
1098
+ style=self.pt_app.app.style)
1099
+ print(cmd)
1100
+ else:
1101
+ prompt = ''.join(s for t, s in tokens)
1102
+ print(prompt, cmd, sep='')
1103
+
1104
+ _prompts_before = None
1105
+ def switch_doctest_mode(self, mode):
1106
+ """Switch prompts to classic for %doctest_mode"""
1107
+ if mode:
1108
+ self._prompts_before = self.prompts
1109
+ self.prompts = ClassicPrompts(self)
1110
+ elif self._prompts_before:
1111
+ self.prompts = self._prompts_before
1112
+ self._prompts_before = None
1113
+ # self._update_layout()
1114
+
1115
+
1116
+ InteractiveShellABC.register(TerminalInteractiveShell)
1117
+
1118
+ if __name__ == '__main__':
1119
+ TerminalInteractiveShell.instance().interact()
temp_venv/lib/python3.13/site-packages/IPython/terminal/ipapp.py ADDED
@@ -0,0 +1,346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # encoding: utf-8
2
+ """
3
+ The :class:`~traitlets.config.application.Application` object for the command
4
+ line :command:`ipython` program.
5
+ """
6
+
7
+ # Copyright (c) IPython Development Team.
8
+ # Distributed under the terms of the Modified BSD License.
9
+
10
+
11
+ import logging
12
+ import os
13
+ import sys
14
+ import warnings
15
+
16
+ from traitlets.config.loader import Config
17
+ from traitlets.config.application import boolean_flag, catch_config_error
18
+ from IPython.core import release
19
+ from IPython.core import usage
20
+ from IPython.core.completer import IPCompleter
21
+ from IPython.core.crashhandler import CrashHandler
22
+ from IPython.core.formatters import PlainTextFormatter
23
+ from IPython.core.history import HistoryManager
24
+ from IPython.core.application import (
25
+ ProfileDir, BaseIPythonApplication, base_flags, base_aliases
26
+ )
27
+ from IPython.core.magic import MagicsManager
28
+ from IPython.core.magics import (
29
+ ScriptMagics, LoggingMagics
30
+ )
31
+ from IPython.core.shellapp import (
32
+ InteractiveShellApp, shell_flags, shell_aliases
33
+ )
34
+ from IPython.extensions.storemagic import StoreMagics
35
+ from .interactiveshell import TerminalInteractiveShell
36
+ from IPython.paths import get_ipython_dir
37
+ from traitlets import (
38
+ Bool, List, default, observe, Type
39
+ )
40
+
41
+ #-----------------------------------------------------------------------------
42
+ # Globals, utilities and helpers
43
+ #-----------------------------------------------------------------------------
44
+
45
+ _examples = """
46
+ ipython --matplotlib # enable matplotlib integration
47
+ ipython --matplotlib=qt # enable matplotlib integration with qt4 backend
48
+
49
+ ipython --log-level=DEBUG # set logging to DEBUG
50
+ ipython --profile=foo # start with profile foo
51
+
52
+ ipython profile create foo # create profile foo w/ default config files
53
+ ipython help profile # show the help for the profile subcmd
54
+
55
+ ipython locate # print the path to the IPython directory
56
+ ipython locate profile foo # print the path to the directory for profile `foo`
57
+ """
58
+
59
+ #-----------------------------------------------------------------------------
60
+ # Crash handler for this application
61
+ #-----------------------------------------------------------------------------
62
+
63
+ class IPAppCrashHandler(CrashHandler):
64
+ """sys.excepthook for IPython itself, leaves a detailed report on disk."""
65
+
66
+ def __init__(self, app):
67
+ contact_name = release.author
68
+ contact_email = release.author_email
69
+ bug_tracker = 'https://github.com/ipython/ipython/issues'
70
+ super(IPAppCrashHandler,self).__init__(
71
+ app, contact_name, contact_email, bug_tracker
72
+ )
73
+
74
+ def make_report(self,traceback):
75
+ """Return a string containing a crash report."""
76
+
77
+ sec_sep = self.section_sep
78
+ # Start with parent report
79
+ report = [super(IPAppCrashHandler, self).make_report(traceback)]
80
+ # Add interactive-specific info we may have
81
+ rpt_add = report.append
82
+ try:
83
+ rpt_add(sec_sep+"History of session input:")
84
+ for line in self.app.shell.user_ns['_ih']:
85
+ rpt_add(line)
86
+ rpt_add('\n*** Last line of input (may not be in above history):\n')
87
+ rpt_add(self.app.shell._last_input_line+'\n')
88
+ except:
89
+ pass
90
+
91
+ return ''.join(report)
92
+
93
+ #-----------------------------------------------------------------------------
94
+ # Aliases and Flags
95
+ #-----------------------------------------------------------------------------
96
+ flags = dict(base_flags)
97
+ flags.update(shell_flags)
98
+ frontend_flags = {}
99
+ addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
100
+ addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
101
+ 'Turn on auto editing of files with syntax errors.',
102
+ 'Turn off auto editing of files with syntax errors.'
103
+ )
104
+ addflag('simple-prompt', 'TerminalInteractiveShell.simple_prompt',
105
+ "Force simple minimal prompt using `raw_input`",
106
+ "Use a rich interactive prompt with prompt_toolkit",
107
+ )
108
+
109
+ addflag('banner', 'TerminalIPythonApp.display_banner',
110
+ "Display a banner upon starting IPython.",
111
+ "Don't display a banner upon starting IPython."
112
+ )
113
+ addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
114
+ """Set to confirm when you try to exit IPython with an EOF (Control-D
115
+ in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
116
+ you can force a direct exit without any confirmation.""",
117
+ "Don't prompt the user when exiting."
118
+ )
119
+ addflag(
120
+ "tip",
121
+ "TerminalInteractiveShell.enable_tip",
122
+ """Shows a tip when IPython starts.""",
123
+ "Don't show tip when IPython starts.",
124
+ )
125
+ addflag('term-title', 'TerminalInteractiveShell.term_title',
126
+ "Enable auto setting the terminal title.",
127
+ "Disable auto setting the terminal title."
128
+ )
129
+ classic_config = Config()
130
+ classic_config.InteractiveShell.cache_size = 0
131
+ classic_config.PlainTextFormatter.pprint = False
132
+ classic_config.TerminalInteractiveShell.prompts_class = (
133
+ "IPython.terminal.prompts.ClassicPrompts"
134
+ )
135
+ classic_config.InteractiveShell.separate_in = ""
136
+ classic_config.InteractiveShell.separate_out = ""
137
+ classic_config.InteractiveShell.separate_out2 = ""
138
+ classic_config.InteractiveShell.colors = "nocolor"
139
+ classic_config.InteractiveShell.xmode = "Plain"
140
+
141
+ frontend_flags['classic']=(
142
+ classic_config,
143
+ "Gives IPython a similar feel to the classic Python prompt."
144
+ )
145
+ # # log doesn't make so much sense this way anymore
146
+ # paa('--log','-l',
147
+ # action='store_true', dest='InteractiveShell.logstart',
148
+ # help="Start logging to the default log file (./ipython_log.py).")
149
+ #
150
+ # # quick is harder to implement
151
+ frontend_flags['quick']=(
152
+ {'TerminalIPythonApp' : {'quick' : True}},
153
+ "Enable quick startup with no config files."
154
+ )
155
+
156
+ frontend_flags['i'] = (
157
+ {'TerminalIPythonApp' : {'force_interact' : True}},
158
+ """If running code from the command line, become interactive afterwards.
159
+ It is often useful to follow this with `--` to treat remaining flags as
160
+ script arguments.
161
+ """
162
+ )
163
+ flags.update(frontend_flags)
164
+
165
+ aliases = dict(base_aliases)
166
+ aliases.update(shell_aliases) # type: ignore[arg-type]
167
+
168
+ #-----------------------------------------------------------------------------
169
+ # Main classes and functions
170
+ #-----------------------------------------------------------------------------
171
+
172
+
173
+ class LocateIPythonApp(BaseIPythonApplication):
174
+ description = """print the path to the IPython dir"""
175
+ subcommands = dict(
176
+ profile=('IPython.core.profileapp.ProfileLocate',
177
+ "print the path to an IPython profile directory",
178
+ ),
179
+ )
180
+ def start(self):
181
+ if self.subapp is not None:
182
+ return self.subapp.start()
183
+ else:
184
+ print(self.ipython_dir)
185
+
186
+
187
+ class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
188
+ name = "ipython"
189
+ description = usage.cl_usage
190
+ crash_handler_class = IPAppCrashHandler # typing: ignore[assignment]
191
+ examples = _examples
192
+
193
+ flags = flags
194
+ aliases = aliases
195
+ classes = List()
196
+
197
+ interactive_shell_class = Type(
198
+ klass=object, # use default_value otherwise which only allow subclasses.
199
+ default_value=TerminalInteractiveShell,
200
+ help="Class to use to instantiate the TerminalInteractiveShell object. Useful for custom Frontends"
201
+ ).tag(config=True)
202
+
203
+ @default('classes')
204
+ def _classes_default(self):
205
+ """This has to be in a method, for TerminalIPythonApp to be available."""
206
+ return [
207
+ InteractiveShellApp, # ShellApp comes before TerminalApp, because
208
+ self.__class__, # it will also affect subclasses (e.g. QtConsole)
209
+ TerminalInteractiveShell,
210
+ HistoryManager,
211
+ MagicsManager,
212
+ ProfileDir,
213
+ PlainTextFormatter,
214
+ IPCompleter,
215
+ ScriptMagics,
216
+ LoggingMagics,
217
+ StoreMagics,
218
+ ]
219
+
220
+ subcommands = dict(
221
+ profile = ("IPython.core.profileapp.ProfileApp",
222
+ "Create and manage IPython profiles."
223
+ ),
224
+ kernel = ("ipykernel.kernelapp.IPKernelApp",
225
+ "Start a kernel without an attached frontend."
226
+ ),
227
+ locate=('IPython.terminal.ipapp.LocateIPythonApp',
228
+ LocateIPythonApp.description
229
+ ),
230
+ history=('IPython.core.historyapp.HistoryApp',
231
+ "Manage the IPython history database."
232
+ ),
233
+ )
234
+
235
+ # *do* autocreate requested profile, but don't create the config file.
236
+ auto_create = Bool(True).tag(config=True)
237
+
238
+ # configurables
239
+ quick = Bool(False,
240
+ help="""Start IPython quickly by skipping the loading of config files."""
241
+ ).tag(config=True)
242
+ @observe('quick')
243
+ def _quick_changed(self, change):
244
+ if change['new']:
245
+ self.load_config_file = lambda *a, **kw: None
246
+
247
+ display_banner = Bool(True,
248
+ help="Whether to display a banner upon starting IPython."
249
+ ).tag(config=True)
250
+
251
+ # if there is code of files to run from the cmd line, don't interact
252
+ # unless the --i flag (App.force_interact) is true.
253
+ force_interact = Bool(False,
254
+ help="""If a command or file is given via the command-line,
255
+ e.g. 'ipython foo.py', start an interactive shell after executing the
256
+ file or command."""
257
+ ).tag(config=True)
258
+ @observe('force_interact')
259
+ def _force_interact_changed(self, change):
260
+ if change['new']:
261
+ self.interact = True
262
+
263
+ @observe('file_to_run', 'code_to_run', 'module_to_run')
264
+ def _file_to_run_changed(self, change):
265
+ new = change['new']
266
+ if new:
267
+ self.something_to_run = True
268
+ if new and not self.force_interact:
269
+ self.interact = False
270
+
271
+ # internal, not-configurable
272
+ something_to_run=Bool(False)
273
+
274
+ @catch_config_error
275
+ def initialize(self, argv=None):
276
+ """Do actions after construct, but before starting the app."""
277
+ super(TerminalIPythonApp, self).initialize(argv)
278
+ if self.subapp is not None:
279
+ # don't bother initializing further, starting subapp
280
+ return
281
+ # print(self.extra_args)
282
+ if self.extra_args and not self.something_to_run:
283
+ self.file_to_run = self.extra_args[0]
284
+ self.init_path()
285
+ # create the shell
286
+ self.init_shell()
287
+ # and draw the banner
288
+ self.init_banner()
289
+ # Now a variety of things that happen after the banner is printed.
290
+ self.init_gui_pylab()
291
+ self.init_extensions()
292
+ self.init_code()
293
+
294
+ def init_shell(self):
295
+ """initialize the InteractiveShell instance"""
296
+ # Create an InteractiveShell instance.
297
+ # shell.display_banner should always be False for the terminal
298
+ # based app, because we call shell.show_banner() by hand below
299
+ # so the banner shows *before* all extension loading stuff.
300
+ self.shell = self.interactive_shell_class.instance(parent=self,
301
+ profile_dir=self.profile_dir,
302
+ ipython_dir=self.ipython_dir, user_ns=self.user_ns)
303
+ self.shell.configurables.append(self)
304
+
305
+ def init_banner(self):
306
+ """optionally display the banner"""
307
+ if self.display_banner and self.interact:
308
+ self.shell.show_banner()
309
+ # Make sure there is a space below the banner.
310
+ if self.log_level <= logging.INFO: print()
311
+
312
+ def _pylab_changed(self, name, old, new):
313
+ """Replace --pylab='inline' with --pylab='auto'"""
314
+ if new == 'inline':
315
+ warnings.warn("'inline' not available as pylab backend, "
316
+ "using 'auto' instead.")
317
+ self.pylab = 'auto'
318
+
319
+ def start(self):
320
+ if self.subapp is not None:
321
+ return self.subapp.start()
322
+ # perform any prexec steps:
323
+ if self.interact:
324
+ self.log.debug("Starting IPython's mainloop...")
325
+ self.shell.mainloop()
326
+ else:
327
+ self.log.debug("IPython not interactive...")
328
+ self.shell.restore_term_title()
329
+ if not self.shell.last_execution_succeeded:
330
+ sys.exit(1)
331
+
332
+ def load_default_config(ipython_dir=None):
333
+ """Load the default config file from the default ipython_dir.
334
+
335
+ This is useful for embedded shells.
336
+ """
337
+ if ipython_dir is None:
338
+ ipython_dir = get_ipython_dir()
339
+
340
+ profile_dir = os.path.join(ipython_dir, 'profile_default')
341
+ app = TerminalIPythonApp()
342
+ app.config_file_paths.append(profile_dir)
343
+ app.load_config_file()
344
+ return app.config
345
+
346
+ launch_new_instance = TerminalIPythonApp.launch_instance
temp_venv/lib/python3.13/site-packages/IPython/terminal/magics.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Extra magics for terminal use."""
2
+
3
+ # Copyright (c) IPython Development Team.
4
+ # Distributed under the terms of the Modified BSD License.
5
+
6
+
7
+ from logging import error
8
+ import os
9
+ import sys
10
+
11
+ from IPython.core.error import TryNext, UsageError
12
+ from IPython.core.magic import Magics, magics_class, line_magic
13
+ from IPython.lib.clipboard import ClipboardEmpty
14
+ from IPython.testing.skipdoctest import skip_doctest
15
+ from IPython.utils.text import SList, strip_email_quotes
16
+ from IPython.utils import py3compat
17
+
18
+ def get_pasted_lines(sentinel, l_input=py3compat.input, quiet=False):
19
+ """ Yield pasted lines until the user enters the given sentinel value.
20
+ """
21
+ if not quiet:
22
+ print("Pasting code; enter '%s' alone on the line to stop or use Ctrl-D." \
23
+ % sentinel)
24
+ prompt = ":"
25
+ else:
26
+ prompt = ""
27
+ while True:
28
+ try:
29
+ l = l_input(prompt)
30
+ if l == sentinel:
31
+ return
32
+ else:
33
+ yield l
34
+ except EOFError:
35
+ print('<EOF>')
36
+ return
37
+
38
+
39
+ @magics_class
40
+ class TerminalMagics(Magics):
41
+ def __init__(self, shell):
42
+ super(TerminalMagics, self).__init__(shell)
43
+
44
+ def store_or_execute(self, block, name, store_history=False):
45
+ """ Execute a block, or store it in a variable, per the user's request.
46
+ """
47
+ if name:
48
+ # If storing it for further editing
49
+ self.shell.user_ns[name] = SList(block.splitlines())
50
+ print("Block assigned to '%s'" % name)
51
+ else:
52
+ b = self.preclean_input(block)
53
+ self.shell.user_ns['pasted_block'] = b
54
+ self.shell.using_paste_magics = True
55
+ try:
56
+ self.shell.run_cell(b, store_history)
57
+ finally:
58
+ self.shell.using_paste_magics = False
59
+
60
+ def preclean_input(self, block):
61
+ lines = block.splitlines()
62
+ while lines and not lines[0].strip():
63
+ lines = lines[1:]
64
+ return strip_email_quotes('\n'.join(lines))
65
+
66
+ def rerun_pasted(self, name='pasted_block'):
67
+ """ Rerun a previously pasted command.
68
+ """
69
+ b = self.shell.user_ns.get(name)
70
+
71
+ # Sanity checks
72
+ if b is None:
73
+ raise UsageError('No previous pasted block available')
74
+ if not isinstance(b, str):
75
+ raise UsageError(
76
+ "Variable 'pasted_block' is not a string, can't execute")
77
+
78
+ print("Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)))
79
+ self.shell.run_cell(b)
80
+
81
+ @line_magic
82
+ def autoindent(self, parameter_s = ''):
83
+ """Toggle autoindent on/off (deprecated)"""
84
+ self.shell.set_autoindent()
85
+ print("Automatic indentation is:",['OFF','ON'][self.shell.autoindent])
86
+
87
+ @skip_doctest
88
+ @line_magic
89
+ def cpaste(self, parameter_s=''):
90
+ """Paste & execute a pre-formatted code block from clipboard.
91
+
92
+ You must terminate the block with '--' (two minus-signs) or Ctrl-D
93
+ alone on the line. You can also provide your own sentinel with '%paste
94
+ -s %%' ('%%' is the new sentinel for this operation).
95
+
96
+ The block is dedented prior to execution to enable execution of method
97
+ definitions. '>' and '+' characters at the beginning of a line are
98
+ ignored, to allow pasting directly from e-mails, diff files and
99
+ doctests (the '...' continuation prompt is also stripped). The
100
+ executed block is also assigned to variable named 'pasted_block' for
101
+ later editing with '%edit pasted_block'.
102
+
103
+ You can also pass a variable name as an argument, e.g. '%cpaste foo'.
104
+ This assigns the pasted block to variable 'foo' as string, without
105
+ dedenting or executing it (preceding >>> and + is still stripped)
106
+
107
+ '%cpaste -r' re-executes the block previously entered by cpaste.
108
+ '%cpaste -q' suppresses any additional output messages.
109
+
110
+ Do not be alarmed by garbled output on Windows (it's a readline bug).
111
+ Just press enter and type -- (and press enter again) and the block
112
+ will be what was just pasted.
113
+
114
+ Shell escapes are not supported (yet).
115
+
116
+ See Also
117
+ --------
118
+ paste : automatically pull code from clipboard.
119
+
120
+ Examples
121
+ --------
122
+ ::
123
+
124
+ In [8]: %cpaste
125
+ Pasting code; enter '--' alone on the line to stop.
126
+ :>>> a = ["world!", "Hello"]
127
+ :>>> print(" ".join(sorted(a)))
128
+ :--
129
+ Hello world!
130
+
131
+ ::
132
+ In [8]: %cpaste
133
+ Pasting code; enter '--' alone on the line to stop.
134
+ :>>> %alias_magic t timeit
135
+ :>>> %t -n1 pass
136
+ :--
137
+ Created `%t` as an alias for `%timeit`.
138
+ Created `%%t` as an alias for `%%timeit`.
139
+ 354 ns ± 224 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)
140
+ """
141
+ opts, name = self.parse_options(parameter_s, 'rqs:', mode='string')
142
+ if 'r' in opts:
143
+ self.rerun_pasted()
144
+ return
145
+
146
+ quiet = ('q' in opts)
147
+
148
+ sentinel = opts.get('s', u'--')
149
+ block = '\n'.join(get_pasted_lines(sentinel, quiet=quiet))
150
+ self.store_or_execute(block, name, store_history=True)
151
+
152
+ @line_magic
153
+ def paste(self, parameter_s=''):
154
+ """Paste & execute a pre-formatted code block from clipboard.
155
+
156
+ The text is pulled directly from the clipboard without user
157
+ intervention and printed back on the screen before execution (unless
158
+ the -q flag is given to force quiet mode).
159
+
160
+ The block is dedented prior to execution to enable execution of method
161
+ definitions. '>' and '+' characters at the beginning of a line are
162
+ ignored, to allow pasting directly from e-mails, diff files and
163
+ doctests (the '...' continuation prompt is also stripped). The
164
+ executed block is also assigned to variable named 'pasted_block' for
165
+ later editing with '%edit pasted_block'.
166
+
167
+ You can also pass a variable name as an argument, e.g. '%paste foo'.
168
+ This assigns the pasted block to variable 'foo' as string, without
169
+ executing it (preceding >>> and + is still stripped).
170
+
171
+ Options:
172
+
173
+ -r: re-executes the block previously entered by cpaste.
174
+
175
+ -q: quiet mode: do not echo the pasted text back to the terminal.
176
+
177
+ IPython statements (magics, shell escapes) are not supported (yet).
178
+
179
+ See Also
180
+ --------
181
+ cpaste : manually paste code into terminal until you mark its end.
182
+ """
183
+ opts, name = self.parse_options(parameter_s, 'rq', mode='string')
184
+ if 'r' in opts:
185
+ self.rerun_pasted()
186
+ return
187
+ try:
188
+ block = self.shell.hooks.clipboard_get()
189
+ except TryNext as clipboard_exc:
190
+ message = getattr(clipboard_exc, 'args')
191
+ if message:
192
+ error(message[0])
193
+ else:
194
+ error('Could not get text from the clipboard.')
195
+ return
196
+ except ClipboardEmpty as e:
197
+ raise UsageError("The clipboard appears to be empty") from e
198
+
199
+ # By default, echo back to terminal unless quiet mode is requested
200
+ if 'q' not in opts:
201
+ sys.stdout.write(self.shell.pycolorize(block))
202
+ if not block.endswith("\n"):
203
+ sys.stdout.write("\n")
204
+ sys.stdout.write("## -- End pasted text --\n")
205
+
206
+ self.store_or_execute(block, name, store_history=True)
207
+
208
+ # Class-level: add a '%cls' magic only on Windows
209
+ if sys.platform == 'win32':
210
+ @line_magic
211
+ def cls(self, s):
212
+ """Clear screen.
213
+ """
214
+ os.system("cls")
temp_venv/lib/python3.13/site-packages/IPython/terminal/prompts.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Terminal input and output prompts."""
2
+
3
+ from pygments.token import Token
4
+ import sys
5
+
6
+ from IPython.core.displayhook import DisplayHook
7
+
8
+ from prompt_toolkit.formatted_text import fragment_list_width, PygmentsTokens
9
+ from prompt_toolkit.shortcuts import print_formatted_text
10
+ from prompt_toolkit.enums import EditingMode
11
+
12
+
13
+ class Prompts:
14
+ def __init__(self, shell):
15
+ self.shell = shell
16
+
17
+ def vi_mode(self):
18
+ if (getattr(self.shell.pt_app, 'editing_mode', None) == EditingMode.VI
19
+ and self.shell.prompt_includes_vi_mode):
20
+ mode = str(self.shell.pt_app.app.vi_state.input_mode)
21
+ if mode.startswith('InputMode.'):
22
+ mode = mode[10:13].lower()
23
+ elif mode.startswith('vi-'):
24
+ mode = mode[3:6]
25
+ return '['+mode+'] '
26
+ return ''
27
+
28
+ def current_line(self) -> int:
29
+ if self.shell.pt_app is not None:
30
+ return self.shell.pt_app.default_buffer.document.cursor_position_row or 0
31
+ return 0
32
+
33
+ def in_prompt_tokens(self):
34
+ return [
35
+ (Token.Prompt.Mode, self.vi_mode()),
36
+ (
37
+ Token.Prompt.LineNumber,
38
+ self.shell.prompt_line_number_format.format(
39
+ line=1, rel_line=-self.current_line()
40
+ ),
41
+ ),
42
+ (Token.Prompt, "In ["),
43
+ (Token.PromptNum, str(self.shell.execution_count)),
44
+ (Token.Prompt, ']: '),
45
+ ]
46
+
47
+ def _width(self):
48
+ return fragment_list_width(self.in_prompt_tokens())
49
+
50
+ def continuation_prompt_tokens(
51
+ self,
52
+ width: int | None = None,
53
+ *,
54
+ lineno: int | None = None,
55
+ wrap_count: int | None = None,
56
+ ):
57
+ if width is None:
58
+ width = self._width()
59
+ line = lineno + 1 if lineno is not None else 0
60
+ if wrap_count:
61
+ return [
62
+ (
63
+ Token.Prompt.Wrap,
64
+ # (" " * (width - 2)) + "\N{HORIZONTAL ELLIPSIS} ",
65
+ (" " * (width - 2)) + "\N{VERTICAL ELLIPSIS} ",
66
+ ),
67
+ ]
68
+ prefix = " " * len(
69
+ self.vi_mode()
70
+ ) + self.shell.prompt_line_number_format.format(
71
+ line=line, rel_line=line - self.current_line() - 1
72
+ )
73
+ return [
74
+ (
75
+ getattr(Token.Prompt.Continuation, f"L{lineno}"),
76
+ prefix + (" " * (width - len(prefix) - 5)) + "...:",
77
+ ),
78
+ (Token.Prompt.Padding, " "),
79
+ ]
80
+
81
+ def rewrite_prompt_tokens(self):
82
+ width = self._width()
83
+ return [
84
+ (Token.Prompt, ('-' * (width - 2)) + '> '),
85
+ ]
86
+
87
+ def out_prompt_tokens(self):
88
+ return [
89
+ (Token.OutPrompt, 'Out['),
90
+ (Token.OutPromptNum, str(self.shell.execution_count)),
91
+ (Token.OutPrompt, ']: '),
92
+ ]
93
+
94
+ class ClassicPrompts(Prompts):
95
+ def in_prompt_tokens(self):
96
+ return [
97
+ (Token.Prompt, '>>> '),
98
+ ]
99
+
100
+ def continuation_prompt_tokens(self, width=None):
101
+ return [(Token.Prompt.Continuation, "... ")]
102
+
103
+ def rewrite_prompt_tokens(self):
104
+ return []
105
+
106
+ def out_prompt_tokens(self):
107
+ return []
108
+
109
+ class RichPromptDisplayHook(DisplayHook):
110
+ """Subclass of base display hook using coloured prompt"""
111
+ def write_output_prompt(self):
112
+ sys.stdout.write(self.shell.separate_out)
113
+ # If we're not displaying a prompt, it effectively ends with a newline,
114
+ # because the output will be left-aligned.
115
+ self.prompt_end_newline = True
116
+
117
+ if self.do_full_cache:
118
+ tokens = self.shell.prompts.out_prompt_tokens()
119
+ prompt_txt = "".join(s for _, s in tokens)
120
+ if prompt_txt and not prompt_txt.endswith("\n"):
121
+ # Ask for a newline before multiline output
122
+ self.prompt_end_newline = False
123
+
124
+ if self.shell.pt_app:
125
+ print_formatted_text(PygmentsTokens(tokens),
126
+ style=self.shell.pt_app.app.style, end='',
127
+ )
128
+ else:
129
+ sys.stdout.write(prompt_txt)
130
+
131
+ def write_format_data(self, format_dict, md_dict=None) -> None:
132
+ assert self.shell is not None
133
+ if self.shell.mime_renderers:
134
+
135
+ for mime, handler in self.shell.mime_renderers.items():
136
+ if mime in format_dict:
137
+ handler(format_dict[mime], None)
138
+ return
139
+
140
+ super().write_format_data(format_dict, md_dict)
141
+
temp_venv/lib/python3.13/site-packages/IPython/terminal/ptutils.py ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """prompt-toolkit utilities
2
+
3
+ Everything in this module is a private API,
4
+ not to be used outside IPython.
5
+ """
6
+
7
+ # Copyright (c) IPython Development Team.
8
+ # Distributed under the terms of the Modified BSD License.
9
+
10
+ import unicodedata
11
+ from wcwidth import wcwidth
12
+
13
+ from IPython.core.completer import (
14
+ provisionalcompleter, cursor_to_position,
15
+ _deduplicate_completions)
16
+ from prompt_toolkit.completion import Completer, Completion
17
+ from prompt_toolkit.lexers import Lexer
18
+ from prompt_toolkit.lexers import PygmentsLexer
19
+ from prompt_toolkit.patch_stdout import patch_stdout
20
+ from IPython.core.getipython import get_ipython
21
+
22
+
23
+ import pygments.lexers as pygments_lexers
24
+ import os
25
+ import sys
26
+ import traceback
27
+
28
+ _completion_sentinel = object()
29
+
30
+
31
+ def _elide_point(string: str, *, min_elide) -> str:
32
+ """
33
+ If a string is long enough, and has at least 3 dots,
34
+ replace the middle part with ellipses.
35
+
36
+ If a string naming a file is long enough, and has at least 3 slashes,
37
+ replace the middle part with ellipses.
38
+
39
+ If three consecutive dots, or two consecutive dots are encountered these are
40
+ replaced by the equivalents HORIZONTAL ELLIPSIS or TWO DOT LEADER unicode
41
+ equivalents
42
+ """
43
+ string = string.replace('...','\N{HORIZONTAL ELLIPSIS}')
44
+ string = string.replace('..','\N{TWO DOT LEADER}')
45
+ if len(string) < min_elide:
46
+ return string
47
+
48
+ object_parts = string.split('.')
49
+ file_parts = string.split(os.sep)
50
+ if file_parts[-1] == '':
51
+ file_parts.pop()
52
+
53
+ if len(object_parts) > 3:
54
+ return "{}.{}\N{HORIZONTAL ELLIPSIS}{}.{}".format(
55
+ object_parts[0],
56
+ object_parts[1][:1],
57
+ object_parts[-2][-1:],
58
+ object_parts[-1],
59
+ )
60
+
61
+ elif len(file_parts) > 3:
62
+ return ("{}" + os.sep + "{}\N{HORIZONTAL ELLIPSIS}{}" + os.sep + "{}").format(
63
+ file_parts[0], file_parts[1][:1], file_parts[-2][-1:], file_parts[-1]
64
+ )
65
+
66
+ return string
67
+
68
+
69
+ def _elide_typed(string: str, typed: str, *, min_elide: int) -> str:
70
+ """
71
+ Elide the middle of a long string if the beginning has already been typed.
72
+ """
73
+
74
+ if len(string) < min_elide:
75
+ return string
76
+ cut_how_much = len(typed)-3
77
+ if cut_how_much < 7:
78
+ return string
79
+ if string.startswith(typed) and len(string)> len(typed):
80
+ return f"{string[:3]}\N{HORIZONTAL ELLIPSIS}{string[cut_how_much:]}"
81
+ return string
82
+
83
+
84
+ def _elide(string: str, typed: str, min_elide) -> str:
85
+ return _elide_typed(
86
+ _elide_point(string, min_elide=min_elide),
87
+ typed, min_elide=min_elide)
88
+
89
+
90
+
91
+ def _adjust_completion_text_based_on_context(text, body, offset):
92
+ if text.endswith('=') and len(body) > offset and body[offset] == '=':
93
+ return text[:-1]
94
+ else:
95
+ return text
96
+
97
+
98
+ class IPythonPTCompleter(Completer):
99
+ """Adaptor to provide IPython completions to prompt_toolkit"""
100
+ def __init__(self, ipy_completer=None, shell=None):
101
+ if shell is None and ipy_completer is None:
102
+ raise TypeError("Please pass shell=an InteractiveShell instance.")
103
+ self._ipy_completer = ipy_completer
104
+ self.shell = shell
105
+
106
+ @property
107
+ def ipy_completer(self):
108
+ if self._ipy_completer:
109
+ return self._ipy_completer
110
+ else:
111
+ return self.shell.Completer
112
+
113
+ def get_completions(self, document, complete_event):
114
+ if not document.current_line.strip():
115
+ return
116
+ # Some bits of our completion system may print stuff (e.g. if a module
117
+ # is imported). This context manager ensures that doesn't interfere with
118
+ # the prompt.
119
+
120
+ with patch_stdout(), provisionalcompleter():
121
+ body = document.text
122
+ cursor_row = document.cursor_position_row
123
+ cursor_col = document.cursor_position_col
124
+ cursor_position = document.cursor_position
125
+ offset = cursor_to_position(body, cursor_row, cursor_col)
126
+ try:
127
+ yield from self._get_completions(body, offset, cursor_position, self.ipy_completer)
128
+ except Exception as e:
129
+ try:
130
+ exc_type, exc_value, exc_tb = sys.exc_info()
131
+ traceback.print_exception(exc_type, exc_value, exc_tb)
132
+ except AttributeError:
133
+ print('Unrecoverable Error in completions')
134
+
135
+ def _get_completions(self, body, offset, cursor_position, ipyc):
136
+ """
137
+ Private equivalent of get_completions() use only for unit_testing.
138
+ """
139
+ debug = getattr(ipyc, 'debug', False)
140
+ completions = _deduplicate_completions(
141
+ body, ipyc.completions(body, offset))
142
+ for c in completions:
143
+ if not c.text:
144
+ # Guard against completion machinery giving us an empty string.
145
+ continue
146
+ text = unicodedata.normalize('NFC', c.text)
147
+ # When the first character of the completion has a zero length,
148
+ # then it's probably a decomposed unicode character. E.g. caused by
149
+ # the "\dot" completion. Try to compose again with the previous
150
+ # character.
151
+ if wcwidth(text[0]) == 0:
152
+ if cursor_position + c.start > 0:
153
+ char_before = body[c.start - 1]
154
+ fixed_text = unicodedata.normalize(
155
+ 'NFC', char_before + text)
156
+
157
+ # Yield the modified completion instead, if this worked.
158
+ if wcwidth(text[0:1]) == 1:
159
+ yield Completion(fixed_text, start_position=c.start - offset - 1)
160
+ continue
161
+
162
+ # TODO: Use Jedi to determine meta_text
163
+ # (Jedi currently has a bug that results in incorrect information.)
164
+ # meta_text = ''
165
+ # yield Completion(m, start_position=start_pos,
166
+ # display_meta=meta_text)
167
+ display_text = c.text
168
+
169
+ adjusted_text = _adjust_completion_text_based_on_context(
170
+ c.text, body, offset
171
+ )
172
+ min_elide = 30 if self.shell is None else self.shell.min_elide
173
+ if c.type == "function":
174
+ yield Completion(
175
+ adjusted_text,
176
+ start_position=c.start - offset,
177
+ display=_elide(
178
+ display_text + "()",
179
+ body[c.start : c.end],
180
+ min_elide=min_elide,
181
+ ),
182
+ display_meta=c.type + c.signature,
183
+ )
184
+ else:
185
+ yield Completion(
186
+ adjusted_text,
187
+ start_position=c.start - offset,
188
+ display=_elide(
189
+ display_text,
190
+ body[c.start : c.end],
191
+ min_elide=min_elide,
192
+ ),
193
+ display_meta=c.type,
194
+ )
195
+
196
+
197
+ class IPythonPTLexer(Lexer):
198
+ """
199
+ Wrapper around PythonLexer and BashLexer.
200
+ """
201
+ def __init__(self):
202
+ l = pygments_lexers
203
+ self.python_lexer = PygmentsLexer(l.Python3Lexer)
204
+ self.shell_lexer = PygmentsLexer(l.BashLexer)
205
+
206
+ self.magic_lexers = {
207
+ 'HTML': PygmentsLexer(l.HtmlLexer),
208
+ 'html': PygmentsLexer(l.HtmlLexer),
209
+ 'javascript': PygmentsLexer(l.JavascriptLexer),
210
+ 'js': PygmentsLexer(l.JavascriptLexer),
211
+ 'perl': PygmentsLexer(l.PerlLexer),
212
+ 'ruby': PygmentsLexer(l.RubyLexer),
213
+ 'latex': PygmentsLexer(l.TexLexer),
214
+ }
215
+
216
+ def lex_document(self, document):
217
+ text = document.text.lstrip()
218
+
219
+ lexer = self.python_lexer
220
+
221
+ if text.startswith('!') or text.startswith('%%bash'):
222
+ lexer = self.shell_lexer
223
+
224
+ elif text.startswith('%%'):
225
+ for magic, l in self.magic_lexers.items():
226
+ if text.startswith('%%' + magic):
227
+ lexer = l
228
+ break
229
+
230
+ return lexer.lex_document(document)
temp_venv/lib/python3.13/site-packages/IPython/utils/__init__.py ADDED
File without changes