ipython 9.0.0b2__py3-none-any.whl → 9.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
IPython/core/debugger.py CHANGED
@@ -228,7 +228,12 @@ class Pdb(OldPdb):
228
228
  }
229
229
 
230
230
  def __init__(
231
- self, completekey=None, stdin=None, stdout=None, context: int = 5, **kwargs
231
+ self,
232
+ completekey=None,
233
+ stdin=None,
234
+ stdout=None,
235
+ context: int | None | str = 5,
236
+ **kwargs,
232
237
  ):
233
238
  """Create a new IPython debugger.
234
239
 
@@ -251,7 +256,11 @@ class Pdb(OldPdb):
251
256
  The possibilities are python version dependent, see the python
252
257
  docs for more info.
253
258
  """
254
-
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)
255
264
  self.context = context
256
265
 
257
266
  # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
@@ -298,7 +307,10 @@ class Pdb(OldPdb):
298
307
  return self._context
299
308
 
300
309
  @context.setter
301
- def context(self, value: int) -> None:
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)
302
314
  assert isinstance(value, int)
303
315
  assert value >= 0
304
316
  self._context = value
@@ -17,6 +17,7 @@ import sys
17
17
  import tokenize
18
18
  from typing import List, Tuple, Optional, Any
19
19
  import warnings
20
+ from textwrap import dedent
20
21
 
21
22
  from IPython.utils import tokenutil
22
23
 
@@ -38,18 +39,11 @@ def leading_empty_lines(lines):
38
39
  def leading_indent(lines):
39
40
  """Remove leading indentation.
40
41
 
41
- If the first line starts with a spaces or tabs, the same whitespace will be
42
- removed from each following line in the cell.
42
+ Removes the minimum common leading indentation from all lines.
43
43
  """
44
44
  if not lines:
45
45
  return lines
46
- m = _indent_re.match(lines[0])
47
- if not m or lines[0].strip().startswith("#"):
48
- return lines
49
- space = m.group(0)
50
- n = len(space)
51
- return [l[n:] if l.startswith(space) else l
52
- for l in lines]
46
+ return dedent("".join(lines)).splitlines(keepends=True)
53
47
 
54
48
  class PromptStripper:
55
49
  """Remove matching input prompts from a block of input.
@@ -329,6 +329,7 @@ class InteractiveShell(SingletonConfigurable):
329
329
 
330
330
  _instance = None
331
331
  _user_ns: dict
332
+ _sys_modules_keys: set[str, AnyType]
332
333
 
333
334
  inspector: oinspect.Inspector
334
335
 
@@ -764,7 +765,9 @@ class InteractiveShell(SingletonConfigurable):
764
765
  if not new == new.lower():
765
766
  warn(
766
767
  f"`TerminalInteractiveShell.colors` is now lowercase: `{new.lower()}`,"
767
- " non lowercase, may invalid in the future."
768
+ " non lowercase, may be invalid in the future.",
769
+ DeprecationWarning,
770
+ stacklevel=2,
768
771
  )
769
772
  return new.lower()
770
773
 
@@ -781,7 +784,8 @@ class InteractiveShell(SingletonConfigurable):
781
784
  )
782
785
 
783
786
  try:
784
- self.inspector.set_theme_name(self.colors)
787
+ # Deprecation in 9.0, colors should always be lower
788
+ self.inspector.set_theme_name(self.colors.lower())
785
789
  except Exception:
786
790
  warn(
787
791
  "Error changing object inspector color schemes.\n%s"
@@ -804,20 +808,19 @@ class InteractiveShell(SingletonConfigurable):
804
808
 
805
809
  self.dir_stack = []
806
810
 
807
- def init_logger(self):
811
+ def init_logger(self) -> None:
808
812
  self.logger = Logger(self.home_dir, logfname='ipython_log.py',
809
813
  logmode='rotate')
810
814
 
811
- def init_logstart(self):
815
+ def init_logstart(self) -> None:
812
816
  """Initialize logging in case it was requested at the command line.
813
817
  """
814
818
  if self.logappend:
815
- self.magic('logstart %s append' % self.logappend)
819
+ self.run_line_magic("logstart", f"{self.logappend} append")
816
820
  elif self.logfile:
817
- self.magic('logstart %s' % self.logfile)
821
+ self.run_line_magic("logstart", self.logfile)
818
822
  elif self.logstart:
819
- self.magic('logstart')
820
-
823
+ self.run_line_magic("logstart", "")
821
824
 
822
825
  def init_builtins(self):
823
826
  # A single, static flag that we set to True. Its presence indicates
IPython/core/magic.py CHANGED
@@ -672,10 +672,11 @@ class Magics(Configurable):
672
672
  argv = arg_split(arg_str, posix, strict)
673
673
  # Do regular option processing
674
674
  try:
675
- opts,args = getopt(argv, opt_str, long_opts)
675
+ opts, args = getopt(argv, opt_str, long_opts)
676
676
  except GetoptError as e:
677
677
  raise UsageError(
678
- '%s ( allowed: "%s" %s)' % (e.msg, opt_str, " ".join(long_opts))
678
+ '%s (allowed: "%s"%s)'
679
+ % (e.msg, opt_str, " ".join(("",) + long_opts) if long_opts else "")
679
680
  ) from e
680
681
  for o, a in opts:
681
682
  if mode == "string" and preserve_non_opts:
@@ -193,7 +193,7 @@ class CodeMagics(Magics):
193
193
 
194
194
  -r: use 'raw' input. By default, the 'processed' history is used,
195
195
  so that magics are loaded in their transformed version to valid
196
- Python. If this option is given, the raw input as typed as the
196
+ Python. If this option is given, the raw input as typed at the
197
197
  command line is used instead.
198
198
 
199
199
  -f: force overwrite. If file exists, %save will prompt for overwrite
IPython/core/oinspect.py CHANGED
@@ -401,7 +401,13 @@ class Inspector(Configurable):
401
401
  parent=None,
402
402
  config=None,
403
403
  ):
404
- assert theme_name == theme_name.lower(), theme_name
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()
405
411
  self._theme_name = theme_name
406
412
  super(Inspector, self).__init__(parent=parent, config=config)
407
413
  self.parser = PyColorize.Parser(out="str", theme_name=theme_name)
IPython/core/release.py CHANGED
@@ -17,10 +17,10 @@
17
17
  # version
18
18
  _version_major = 9
19
19
  _version_minor = 0
20
- _version_patch = 0
20
+ _version_patch = 2
21
21
  _version_extra = ".dev"
22
- _version_extra = "b2"
23
- # _version_extra = "" # Uncomment this for full releases
22
+ # _version_extra = "b2"
23
+ _version_extra = "" # Uncomment this for full releases
24
24
 
25
25
  # Construct full version string from these.
26
26
  _ver = [_version_major, _version_minor, _version_patch]
IPython/core/tbtools.py CHANGED
@@ -401,7 +401,13 @@ class TBTools:
401
401
  stacklevel=2,
402
402
  )
403
403
  theme_name = color_scheme
404
- assert theme_name.lower() == theme_name, theme_name
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()
405
411
  # Whether to call the interactive pdb debugger after printing
406
412
  # tracebacks or not
407
413
  super().__init__()
IPython/core/tips.py CHANGED
@@ -71,12 +71,12 @@ _tips: Any = {
71
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
72
  "You can use `files = !ls *.png`",
73
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.",
74
+ "Put a ';' at the end of a line to suppress the printing of output.",
75
75
  "You can use Ctrl-O to force a new line in terminal IPython",
76
76
  "Use `object?` to see the help on `object`, `object??` to view it's source",
77
77
  "`?` alone on a line will brings up IPython's help",
78
78
  "You can use `%hist` to view history, see the options with `%history?`",
79
- "You can change the edditing mode of IPython to behave more like vi, or emacs.",
79
+ "You can change the editing mode of IPython to behave more like vi, or emacs.",
80
80
  "IPython 9.0+ have hooks to integrate AI/LLM completions.",
81
81
  "Use `%timeit` or `%%timeit`, and the `-r`, `-n`, and `-o` options to easily profile your code.",
82
82
  "Use `ipython --help-all | less` to view all the IPython configurations options.",
@@ -91,11 +91,17 @@ if os.name == "nt":
91
91
  "We can't show you all tips on windows as sometime Unicode characters crash windows console, please help us debug it."
92
92
  ]
93
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
+ }
94
100
  else:
95
101
  _tips["random"].extend(
96
102
  [
97
103
  "You can use latex or unicode completion, `\\alpha<tab>` will insert the α symbol.",
98
- "You can find how to type a latext symbol by back completing it `\\θ<tab>` will expand to `\\theta`.",
104
+ "You can find how to type a latex symbol by back completing it `\\θ<tab>` will expand to `\\theta`.",
99
105
  "You can find how to type a unicode symbol by back completing it `\\Ⅷ<tab>` will expand to `\\ROMAN NUMERAL EIGHT`.",
100
106
  "IPython support combining unicode identifiers, F\\vec<tab> will become F⃗, usefull for physics equations. Play with \\dot \\ddot and others.",
101
107
  ]
File without changes
@@ -0,0 +1,608 @@
1
+ from __future__ import annotations
2
+ import ast
3
+ import builtins
4
+ import contextlib
5
+ import itertools
6
+ import os
7
+ import platform
8
+ import sys
9
+ import textwrap
10
+ from types import ModuleType
11
+ from typing import TYPE_CHECKING, Any, Generator, Iterable, NamedTuple, cast
12
+
13
+ from IPython.extensions.deduperreload.deduperreload_patching import (
14
+ DeduperReloaderPatchingMixin,
15
+ )
16
+
17
+ if TYPE_CHECKING:
18
+ TDefinitionAst = (
19
+ ast.FunctionDef
20
+ | ast.AsyncFunctionDef
21
+ | ast.Import
22
+ | ast.ImportFrom
23
+ | ast.Assign
24
+ | ast.AnnAssign
25
+ )
26
+
27
+
28
+ def get_module_file_name(module: ModuleType | str) -> str:
29
+ """Returns the module's file path, or the empty string if it's inaccessible"""
30
+ if (mod := sys.modules.get(module) if isinstance(module, str) else module) is None:
31
+ return ""
32
+ return getattr(mod, "__file__", "") or ""
33
+
34
+
35
+ def compare_ast(node1: ast.AST | list[ast.AST], node2: ast.AST | list[ast.AST]) -> bool:
36
+ """Checks if node1 and node2 have identical AST structure/values, apart from some attributes"""
37
+ if type(node1) is not type(node2):
38
+ return False
39
+
40
+ if isinstance(node1, ast.AST):
41
+ for k, v in node1.__dict__.items():
42
+ if k in (
43
+ "lineno",
44
+ "end_lineno",
45
+ "col_offset",
46
+ "end_col_offset",
47
+ "ctx",
48
+ "parent",
49
+ ):
50
+ continue
51
+ if not hasattr(node2, k) or not compare_ast(v, getattr(node2, k)):
52
+ return False
53
+ return True
54
+
55
+ elif isinstance(node1, list) and isinstance( # type:ignore [redundant-expr]
56
+ node2, list
57
+ ):
58
+ return len(node1) == len(node2) and all(
59
+ compare_ast(n1, n2) for n1, n2 in zip(node1, node2)
60
+ )
61
+ else:
62
+ return node1 == node2
63
+
64
+
65
+ class DependencyNode(NamedTuple):
66
+ """
67
+ Each node represents a function.
68
+ qualified_name: string which represents the namespace/name of the function
69
+ abstract_syntax_tree: subtree of the overall module which corresponds to this function
70
+
71
+ qualified_name is of the structure: (namespace1, namespace2, ..., name)
72
+
73
+ For example, foo() in the following would be represented as (A, B, foo):
74
+
75
+ class A:
76
+ class B:
77
+ def foo():
78
+ pass
79
+ """
80
+
81
+ qualified_name: tuple[str, ...]
82
+ abstract_syntax_tree: ast.AST
83
+
84
+
85
+ class GatherResult(NamedTuple):
86
+ import_defs: list[tuple[tuple[str, ...], ast.Import | ast.ImportFrom]] = []
87
+ assign_defs: list[tuple[tuple[str, ...], ast.Assign | ast.AnnAssign]] = []
88
+ function_defs: list[
89
+ tuple[tuple[str, ...], ast.FunctionDef | ast.AsyncFunctionDef]
90
+ ] = []
91
+ classes: dict[str, ast.ClassDef] = {}
92
+ unfixable: list[ast.AST] = []
93
+
94
+ @classmethod
95
+ def create(cls) -> GatherResult:
96
+ return cls([], [], [], {}, [])
97
+
98
+ def all_defs(self) -> Iterable[tuple[tuple[str, ...], TDefinitionAst]]:
99
+ return itertools.chain(self.import_defs, self.assign_defs, self.function_defs)
100
+
101
+ def inplace_merge(self, other: GatherResult) -> None:
102
+ self.import_defs.extend(other.import_defs)
103
+ self.assign_defs.extend(other.assign_defs)
104
+ self.function_defs.extend(other.function_defs)
105
+ self.classes.update(other.classes)
106
+ self.unfixable.extend(other.unfixable)
107
+
108
+
109
+ class ConstexprDetector(ast.NodeVisitor):
110
+ def __init__(self) -> None:
111
+ self.is_constexpr = True
112
+ self._allow_builtins_exceptions = True
113
+
114
+ @contextlib.contextmanager
115
+ def disallow_builtins_exceptions(self) -> Generator[None, None, None]:
116
+ prev_allow = self._allow_builtins_exceptions
117
+ self._allow_builtins_exceptions = False
118
+ try:
119
+ yield
120
+ finally:
121
+ self._allow_builtins_exceptions = prev_allow
122
+
123
+ def visit_Attribute(self, node: ast.Attribute) -> None:
124
+ with self.disallow_builtins_exceptions():
125
+ self.visit(node.value)
126
+
127
+ def visit_Name(self, node: ast.Name) -> None:
128
+ if self._allow_builtins_exceptions and hasattr(builtins, node.id):
129
+ return
130
+ self.is_constexpr = False
131
+
132
+ def visit(self, node: ast.AST) -> None:
133
+ if not self.is_constexpr:
134
+ # can short-circuit if we've already detected that it's not a constexpr
135
+ return
136
+ super().visit(node)
137
+
138
+ def __call__(self, node: ast.AST) -> bool:
139
+ self.is_constexpr = True
140
+ self.visit(node)
141
+ return self.is_constexpr
142
+
143
+
144
+ class AutoreloadTree:
145
+ """
146
+ Recursive data structure to keep track of reloadable functions/methods. Each object corresponds to a specific scope level.
147
+ children: classes inside given scope, maps class name to autoreload tree for that class's scope
148
+ funcs_to_autoreload: list of function names that can be autoreloaded in given scope.
149
+ new_nested_classes: Classes getting added in new autoreload cycle
150
+ """
151
+
152
+ def __init__(self) -> None:
153
+ self.children: dict[str, AutoreloadTree] = {}
154
+ self.defs_to_reload: list[tuple[tuple[str, ...], ast.AST]] = []
155
+ self.defs_to_delete: set[str] = set()
156
+ self.new_nested_classes: dict[str, ast.AST] = {}
157
+
158
+ def traverse_prefixes(self, prefixes: list[str]) -> AutoreloadTree:
159
+ """
160
+ Return ref to the AutoreloadTree at the namespace specified by prefixes
161
+ """
162
+ cur = self
163
+ for prefix in prefixes:
164
+ if prefix not in cur.children:
165
+ cur.children[prefix] = AutoreloadTree()
166
+ cur = cur.children[prefix]
167
+ return cur
168
+
169
+
170
+ class DeduperReloader(DeduperReloaderPatchingMixin):
171
+ """
172
+ This version of autoreload detects when we can leverage targeted recompilation of a subset of a module and patching
173
+ existing function/method objects to reflect these changes.
174
+
175
+ Detects what functions/methods can be reloaded by recursively comparing the old/new AST of module-level classes,
176
+ module-level classes' methods, recursing through nested classes' methods. If other changes are made, original
177
+ autoreload algorithm is called directly.
178
+ """
179
+
180
+ def __init__(self) -> None:
181
+ self._to_autoreload: AutoreloadTree = AutoreloadTree()
182
+ self.source_by_modname: dict[str, str] = {}
183
+ self.dependency_graph: dict[tuple[str, ...], list[DependencyNode]] = {}
184
+ self._enabled = True
185
+
186
+ @property
187
+ def enabled(self) -> bool:
188
+ return self._enabled and platform.python_implementation() == "CPython"
189
+
190
+ @enabled.setter
191
+ def enabled(self, value: bool) -> None:
192
+ self._enabled = value
193
+
194
+ def update_sources(self) -> None:
195
+ """
196
+ Update dictionary source_by_modname with current modules' source codes.
197
+ """
198
+ if not self.enabled:
199
+ return
200
+ for new_modname in sys.modules.keys() - self.source_by_modname.keys():
201
+ new_module = sys.modules[new_modname]
202
+ if (
203
+ (fname := get_module_file_name(new_module))
204
+ is None # type:ignore [redundant-expr]
205
+ or "site-packages" in fname
206
+ or "dist-packages" in fname
207
+ or not os.access(fname, os.R_OK)
208
+ ):
209
+ self.source_by_modname[new_modname] = ""
210
+ continue
211
+ with open(fname, "r") as f:
212
+ try:
213
+ self.source_by_modname[new_modname] = f.read()
214
+ except Exception:
215
+ self.source_by_modname[new_modname] = ""
216
+
217
+ constexpr_detector = ConstexprDetector()
218
+
219
+ @staticmethod
220
+ def is_enum_subclass(node: ast.Module | ast.ClassDef) -> bool:
221
+ if isinstance(node, ast.Module):
222
+ return False
223
+ for base in node.bases:
224
+ if isinstance(base, ast.Name) and base.id == "Enum":
225
+ return True
226
+ elif (
227
+ isinstance(base, ast.Attribute)
228
+ and base.attr == "Enum"
229
+ and isinstance(base.value, ast.Name)
230
+ and base.value.id == "enum"
231
+ ):
232
+ return True
233
+ return False
234
+
235
+ @classmethod
236
+ def is_constexpr_assign(
237
+ cls, node: ast.AST, parent_node: ast.Module | ast.ClassDef
238
+ ) -> bool:
239
+ if not isinstance(node, (ast.Assign, ast.AnnAssign)) or node.value is None:
240
+ return False
241
+ if cls.is_enum_subclass(parent_node):
242
+ return False
243
+ for target in node.targets if isinstance(node, ast.Assign) else [node.target]:
244
+ if not isinstance(target, ast.Name):
245
+ return False
246
+ return cls.constexpr_detector(node.value)
247
+
248
+ @classmethod
249
+ def _gather_children(
250
+ cls, body: list[ast.stmt], parent_node: ast.Module | ast.ClassDef
251
+ ) -> GatherResult:
252
+ """
253
+ Given list of ast elements, return:
254
+ 1. dict mapping function names to their ASTs.
255
+ 2. dict mapping class names to their ASTs.
256
+ 3. list of any other ASTs.
257
+ """
258
+ result = GatherResult.create()
259
+ for ast_node in body:
260
+ ast_elt: ast.expr | ast.stmt = ast_node
261
+ while isinstance(ast_elt, ast.Expr):
262
+ ast_elt = ast_elt.value
263
+ if isinstance(ast_elt, (ast.FunctionDef, ast.AsyncFunctionDef)):
264
+ result.function_defs.append(((ast_elt.name,), ast_elt))
265
+ elif isinstance(ast_elt, (ast.Import, ast.ImportFrom)):
266
+ result.import_defs.append(
267
+ (tuple(name.asname or name.name for name in ast_elt.names), ast_elt)
268
+ )
269
+ elif isinstance(ast_elt, ast.ClassDef):
270
+ result.classes[ast_elt.name] = ast_elt
271
+ elif isinstance(ast_elt, ast.If):
272
+ result.unfixable.append(ast_elt.test)
273
+ result.inplace_merge(cls._gather_children(ast_elt.body, parent_node))
274
+ result.inplace_merge(cls._gather_children(ast_elt.orelse, parent_node))
275
+ elif isinstance(ast_elt, (ast.AsyncWith, ast.With)):
276
+ result.unfixable.extend(ast_elt.items)
277
+ result.inplace_merge(cls._gather_children(ast_elt.body, parent_node))
278
+ elif isinstance(ast_elt, ast.Try):
279
+ result.inplace_merge(cls._gather_children(ast_elt.body, parent_node))
280
+ result.inplace_merge(cls._gather_children(ast_elt.orelse, parent_node))
281
+ result.inplace_merge(
282
+ cls._gather_children(ast_elt.finalbody, parent_node)
283
+ )
284
+ for handler in ast_elt.handlers:
285
+ if handler.type is not None:
286
+ result.unfixable.append(handler.type)
287
+ result.inplace_merge(
288
+ cls._gather_children(handler.body, parent_node)
289
+ )
290
+ elif not isinstance(ast_elt, (ast.Ellipsis, ast.Pass)):
291
+ if cls.is_constexpr_assign(ast_elt, parent_node):
292
+ assert isinstance(ast_elt, (ast.Assign, ast.AnnAssign))
293
+ targets = (
294
+ ast_elt.targets
295
+ if isinstance(ast_elt, ast.Assign)
296
+ else [ast_elt.target]
297
+ )
298
+ result.assign_defs.append(
299
+ (
300
+ tuple(cast(ast.Name, target).id for target in targets),
301
+ ast_elt,
302
+ )
303
+ )
304
+ else:
305
+ result.unfixable.append(ast_elt)
306
+ return result
307
+
308
+ def detect_autoreload(
309
+ self,
310
+ old_node: ast.Module | ast.ClassDef,
311
+ new_node: ast.Module | ast.ClassDef,
312
+ prefixes: list[str] | None = None,
313
+ ) -> bool:
314
+ """
315
+ Returns
316
+ -------
317
+ `True` if we can run our targeted autoreload algorithm safely.
318
+ `False` if we should instead use IPython's original autoreload implementation.
319
+ """
320
+ if not self.enabled:
321
+ return False
322
+ prefixes = prefixes or []
323
+
324
+ old_result = self._gather_children(old_node.body, old_node)
325
+ new_result = self._gather_children(new_node.body, new_node)
326
+ old_defs_by_name: dict[str, ast.AST] = {
327
+ name: ast_def for names, ast_def in old_result.all_defs() for name in names
328
+ }
329
+ new_defs_by_name: dict[str, ast.AST] = {
330
+ name: ast_def for names, ast_def in new_result.all_defs() for name in names
331
+ }
332
+
333
+ if not compare_ast(old_result.unfixable, new_result.unfixable):
334
+ return False
335
+
336
+ cur = self._to_autoreload.traverse_prefixes(prefixes)
337
+ for names, new_ast_def in new_result.all_defs():
338
+ names_to_reload = []
339
+ for name in names:
340
+ if new_defs_by_name[name] is not new_ast_def:
341
+ continue
342
+ if name not in old_defs_by_name or not compare_ast(
343
+ new_ast_def, old_defs_by_name[name]
344
+ ):
345
+ names_to_reload.append(name)
346
+ if names_to_reload:
347
+ cur.defs_to_reload.append((tuple(names), new_ast_def))
348
+ cur.defs_to_delete |= set(old_defs_by_name.keys()) - set(
349
+ new_defs_by_name.keys()
350
+ )
351
+ for name, new_ast_def_class in new_result.classes.items():
352
+ if name not in old_result.classes:
353
+ cur.new_nested_classes[name] = new_ast_def_class
354
+ elif not compare_ast(
355
+ new_ast_def_class, old_result.classes[name]
356
+ ) and not self.detect_autoreload(
357
+ old_result.classes[name], new_ast_def_class, prefixes + [name]
358
+ ):
359
+ return False
360
+ return True
361
+
362
+ def _check_dependents(self) -> bool:
363
+ """
364
+ If a decorator function is modified, we should similarly reload the functions which are decorated by this
365
+ decorator. Iterate through the Dependency Graph to find such cases in the given AutoreloadTree.
366
+ """
367
+ for node in self._check_dependents_inner():
368
+ self._add_node_to_autoreload_tree(node)
369
+ return True
370
+
371
+ def _add_node_to_autoreload_tree(self, node: DependencyNode) -> None:
372
+ """
373
+ Given a node of the dependency graph, add decorator dependencies to the autoreload tree.
374
+ """
375
+ if len(node.qualified_name) == 0:
376
+ return
377
+ cur = self._to_autoreload.traverse_prefixes(list(node.qualified_name[:-1]))
378
+ if node.abstract_syntax_tree is not None:
379
+ cur.defs_to_reload.append(
380
+ ((node.qualified_name[-1],), node.abstract_syntax_tree)
381
+ )
382
+
383
+ def _check_dependents_inner(
384
+ self, prefixes: list[str] | None = None
385
+ ) -> list[DependencyNode]:
386
+ prefixes = prefixes or []
387
+ cur = self._to_autoreload.traverse_prefixes(prefixes)
388
+ ans = []
389
+ for (func_name, *_), _ in cur.defs_to_reload:
390
+ node = tuple(prefixes + [func_name])
391
+ ans.extend(self._gen_dependents(node))
392
+ for class_name in cur.new_nested_classes:
393
+ ans.extend(self._check_dependents_inner(prefixes + [class_name]))
394
+ return ans
395
+
396
+ def _gen_dependents(self, qualname: tuple[str, ...]) -> list[DependencyNode]:
397
+ ans = []
398
+ if qualname not in self.dependency_graph:
399
+ return []
400
+ for elt in self.dependency_graph[qualname]:
401
+ ans.extend(self._gen_dependents(elt.qualified_name))
402
+ ans.append(elt)
403
+ return ans
404
+
405
+ def _patch_namespace_inner(
406
+ self, ns: ModuleType | type, prefixes: list[str] | None = None
407
+ ) -> bool:
408
+ """
409
+ This function patches module functions and methods. Specifically, only objects with their name in
410
+ self.to_autoreload will be considered for patching. If an object has been marked to be autoreloaded,
411
+ new_source_code gets executed in the old version's global environment. Then, replace the old function's
412
+ attributes with the new function's attributes.
413
+ """
414
+ prefixes = prefixes or []
415
+ cur = self._to_autoreload.traverse_prefixes(prefixes)
416
+ namespace_to_check = ns
417
+ for prefix in prefixes:
418
+ namespace_to_check = namespace_to_check.__dict__[prefix]
419
+ for names, new_ast_def in cur.defs_to_reload:
420
+ local_env: dict[str, Any] = {}
421
+ if (
422
+ isinstance(new_ast_def, (ast.FunctionDef, ast.AsyncFunctionDef))
423
+ and (name := names[0]) in namespace_to_check.__dict__
424
+ ):
425
+ assert len(names) == 1
426
+ to_patch_to = namespace_to_check.__dict__[name]
427
+ if isinstance(to_patch_to, (staticmethod, classmethod)):
428
+ to_patch_to = to_patch_to.__func__
429
+ # exec new source code using old function's (obj) globals environment.
430
+ func_code = textwrap.dedent(ast.unparse(new_ast_def))
431
+ if is_method := (len(prefixes) > 0):
432
+ func_code = "class __autoreload_class__:\n" + textwrap.indent(
433
+ func_code, " "
434
+ )
435
+ global_env = namespace_to_check.__dict__
436
+ if hasattr(to_patch_to, "__globals__"):
437
+ global_env = to_patch_to.__globals__
438
+ elif isinstance(to_patch_to, property):
439
+ if to_patch_to.fget is not None:
440
+ global_env = to_patch_to.fget.__globals__
441
+ elif to_patch_to.fset is not None:
442
+ global_env = to_patch_to.fset.__globals__
443
+ elif to_patch_to.fdel is not None:
444
+ global_env = to_patch_to.fdel.__globals__
445
+ if not isinstance(global_env, dict):
446
+ global_env = dict(global_env)
447
+ exec(func_code, global_env, local_env) # type: ignore[arg-type]
448
+ # local_env contains the function exec'd from new version of function
449
+ if is_method:
450
+ to_patch_from = getattr(local_env["__autoreload_class__"], name)
451
+ else:
452
+ to_patch_from = local_env[name]
453
+ if isinstance(to_patch_from, (staticmethod, classmethod)):
454
+ to_patch_from = to_patch_from.__func__
455
+ if isinstance(to_patch_to, property) and isinstance(
456
+ to_patch_from, property
457
+ ):
458
+ for attr in ("fget", "fset", "fdel"):
459
+ if (
460
+ getattr(to_patch_to, attr) is None
461
+ or getattr(to_patch_from, attr) is None
462
+ ):
463
+ self.try_patch_attr(to_patch_to, to_patch_from, attr)
464
+ else:
465
+ self.patch_function(
466
+ getattr(to_patch_to, attr),
467
+ getattr(to_patch_from, attr),
468
+ is_method,
469
+ )
470
+ elif not isinstance(to_patch_to, property) and not isinstance(
471
+ to_patch_from, property
472
+ ):
473
+ self.patch_function(to_patch_to, to_patch_from, is_method)
474
+ else:
475
+ raise ValueError(
476
+ "adding or removing property decorations not supported"
477
+ )
478
+ else:
479
+ exec(
480
+ ast.unparse(new_ast_def),
481
+ ns.__dict__ | namespace_to_check.__dict__,
482
+ local_env,
483
+ )
484
+ for name in names:
485
+ setattr(namespace_to_check, name, local_env[name])
486
+ cur.defs_to_reload.clear()
487
+ for name in cur.defs_to_delete:
488
+ try:
489
+ delattr(namespace_to_check, name)
490
+ except (AttributeError, TypeError, ValueError):
491
+ # give up on deleting the attribute, let the stale one dangle
492
+ pass
493
+ cur.defs_to_delete.clear()
494
+ for class_name, class_ast_node in cur.new_nested_classes.items():
495
+ local_env_class: dict[str, Any] = {}
496
+ exec(
497
+ ast.unparse(class_ast_node),
498
+ ns.__dict__ | namespace_to_check.__dict__,
499
+ local_env_class,
500
+ )
501
+ setattr(namespace_to_check, class_name, local_env_class[class_name])
502
+ cur.new_nested_classes.clear()
503
+ for class_name in cur.children.keys():
504
+ if not self._patch_namespace(ns, prefixes + [class_name]):
505
+ return False
506
+ cur.children.clear()
507
+ return True
508
+
509
+ def _patch_namespace(
510
+ self, ns: ModuleType | type, prefixes: list[str] | None = None
511
+ ) -> bool:
512
+ """
513
+ Wrapper for patching all elements in a namespace as specified by the to_autoreload member variable.
514
+ Returns `true` if patching was successful, and `false` if unsuccessful.
515
+ """
516
+ try:
517
+ return self._patch_namespace_inner(ns, prefixes=prefixes)
518
+ except Exception:
519
+ return False
520
+
521
+ def maybe_reload_module(self, module: ModuleType) -> bool:
522
+ """
523
+ Uses Deduperreload to try to update a module.
524
+ Returns `true` on success and `false` on failure.
525
+ """
526
+ if not self.enabled:
527
+ return False
528
+ if not (modname := getattr(module, "__name__", None)):
529
+ return False
530
+ if (fname := get_module_file_name(module)) is None:
531
+ return False
532
+ with open(fname, "r") as f:
533
+ new_source_code = f.read()
534
+ patched_flag = False
535
+ if old_source_code := self.source_by_modname.get(modname):
536
+ # get old/new module ast
537
+ try:
538
+ old_module_ast = ast.parse(old_source_code)
539
+ new_module_ast = ast.parse(new_source_code)
540
+ except Exception:
541
+ return False
542
+ # detect if we are able to use our autoreload algorithm
543
+ ctx = contextlib.suppress()
544
+ with ctx:
545
+ self._build_dependency_graph(new_module_ast)
546
+ if (
547
+ self.detect_autoreload(old_module_ast, new_module_ast)
548
+ and self._check_dependents()
549
+ and self._patch_namespace(module)
550
+ ):
551
+ patched_flag = True
552
+
553
+ self.source_by_modname[modname] = new_source_code
554
+ self._to_autoreload = AutoreloadTree()
555
+ return patched_flag
556
+
557
+ def _separate_name(
558
+ self,
559
+ decorator: ast.Attribute | ast.Name | ast.Call | ast.expr,
560
+ accept_calls: bool,
561
+ ) -> list[str] | None:
562
+ """
563
+ Generates a qualified name for a given decorator by finding its relative namespace.
564
+ """
565
+ if isinstance(decorator, ast.Name):
566
+ return [decorator.id]
567
+ elif isinstance(decorator, ast.Call):
568
+ if accept_calls:
569
+ return self._separate_name(decorator.func, False)
570
+ else:
571
+ return None
572
+ if not isinstance(decorator, ast.Attribute):
573
+ return None
574
+ if pref := self._separate_name(decorator.value, False):
575
+ return pref + [decorator.attr]
576
+ else:
577
+ return None
578
+
579
+ def _gather_dependents(
580
+ self, body: list[ast.stmt], body_prefixes: list[str] | None = None
581
+ ) -> bool:
582
+ body_prefixes = body_prefixes or []
583
+ for ast_node in body:
584
+ ast_elt: ast.expr | ast.stmt = ast_node
585
+ if isinstance(ast_elt, ast.ClassDef):
586
+ self._gather_dependents(ast_elt.body, body_prefixes + [ast_elt.name])
587
+ continue
588
+ if not isinstance(ast_elt, (ast.FunctionDef, ast.AsyncFunctionDef)):
589
+ continue
590
+ qualified_name = tuple(body_prefixes + [ast_elt.name])
591
+ cur_dependency_node = DependencyNode(qualified_name, ast_elt)
592
+ for decorator in ast_elt.decorator_list:
593
+ decorator_path = self._separate_name(decorator, True)
594
+ if not decorator_path:
595
+ continue
596
+ decorator_path_tuple = tuple(decorator_path)
597
+ self.dependency_graph.setdefault(decorator_path_tuple, []).append(
598
+ cur_dependency_node
599
+ )
600
+ return True
601
+
602
+ def _build_dependency_graph(self, new_ast: ast.Module | ast.ClassDef) -> bool:
603
+ """
604
+ Wrapper function for generating dependency graph given some AST.
605
+ Returns `true` on success. Returns `false` on failure.
606
+ Currently, only returns `true` as we do not block on failure to build this graph.
607
+ """
608
+ return self._gather_dependents(new_ast.body)
@@ -0,0 +1,141 @@
1
+ from __future__ import annotations
2
+ import ctypes
3
+ import sys
4
+ from typing import Any
5
+
6
+ NOT_FOUND: object = object()
7
+ _MAX_FIELD_SEARCH_OFFSET = 50
8
+
9
+ if sys.maxsize > 2**32:
10
+ WORD_TYPE: type[ctypes.c_int32] | type[ctypes.c_int64] = ctypes.c_int64
11
+ WORD_N_BYTES = 8
12
+ else:
13
+ WORD_TYPE = ctypes.c_int32
14
+ WORD_N_BYTES = 4
15
+
16
+
17
+ class DeduperReloaderPatchingMixin:
18
+ @staticmethod
19
+ def infer_field_offset(
20
+ obj: object,
21
+ field: str,
22
+ ) -> int:
23
+ field_value = getattr(obj, field, NOT_FOUND)
24
+ if field_value is NOT_FOUND:
25
+ return -1
26
+ obj_addr = ctypes.c_void_p.from_buffer(ctypes.py_object(obj)).value
27
+ field_addr = ctypes.c_void_p.from_buffer(ctypes.py_object(field_value)).value
28
+ if obj_addr is None or field_addr is None:
29
+ return -1
30
+ ret = -1
31
+ for offset in range(1, _MAX_FIELD_SEARCH_OFFSET):
32
+ if (
33
+ ctypes.cast(
34
+ obj_addr + WORD_N_BYTES * offset, ctypes.POINTER(WORD_TYPE)
35
+ ).contents.value
36
+ == field_addr
37
+ ):
38
+ ret = offset
39
+ break
40
+ return ret
41
+
42
+ @classmethod
43
+ def try_write_readonly_attr(
44
+ cls,
45
+ obj: object,
46
+ field: str,
47
+ new_value: object,
48
+ offset: int | None = None,
49
+ ) -> None:
50
+ prev_value = getattr(obj, field, NOT_FOUND)
51
+ if prev_value is NOT_FOUND:
52
+ return
53
+ if offset is None:
54
+ offset = cls.infer_field_offset(obj, field)
55
+ if offset == -1:
56
+ return
57
+ obj_addr = ctypes.c_void_p.from_buffer(ctypes.py_object(obj)).value
58
+ new_value_addr = ctypes.c_void_p.from_buffer(ctypes.py_object(new_value)).value
59
+ if obj_addr is None or new_value_addr is None:
60
+ return
61
+ if prev_value is not None:
62
+ ctypes.pythonapi.Py_DecRef(ctypes.py_object(prev_value))
63
+ if new_value is not None:
64
+ ctypes.pythonapi.Py_IncRef(ctypes.py_object(new_value))
65
+ ctypes.cast(
66
+ obj_addr + WORD_N_BYTES * offset, ctypes.POINTER(WORD_TYPE)
67
+ ).contents.value = new_value_addr
68
+
69
+ @classmethod
70
+ def try_patch_readonly_attr(
71
+ cls,
72
+ old: object,
73
+ new: object,
74
+ field: str,
75
+ new_is_value: bool = False,
76
+ offset: int = -1,
77
+ ) -> None:
78
+
79
+ old_value = getattr(old, field, NOT_FOUND)
80
+ new_value = new if new_is_value else getattr(new, field, NOT_FOUND)
81
+ if old_value is NOT_FOUND or new_value is NOT_FOUND:
82
+ return
83
+ elif old_value is new_value:
84
+ return
85
+ elif old_value is not None and offset < 0:
86
+ offset = cls.infer_field_offset(old, field)
87
+ elif offset < 0:
88
+ assert not new_is_value
89
+ assert new_value is not None
90
+ offset = cls.infer_field_offset(new, field)
91
+ cls.try_write_readonly_attr(old, field, new_value, offset=offset)
92
+
93
+ @classmethod
94
+ def try_patch_attr(
95
+ cls,
96
+ old: object,
97
+ new: object,
98
+ field: str,
99
+ new_is_value: bool = False,
100
+ offset: int = -1,
101
+ ) -> None:
102
+ try:
103
+ setattr(old, field, new if new_is_value else getattr(new, field))
104
+ except (AttributeError, TypeError, ValueError):
105
+ cls.try_patch_readonly_attr(old, new, field, new_is_value, offset)
106
+
107
+ @classmethod
108
+ def patch_function(
109
+ cls, to_patch_to: Any, to_patch_from: Any, is_method: bool
110
+ ) -> None:
111
+ new_freevars = []
112
+ new_closure = []
113
+ for i, v in enumerate(to_patch_to.__code__.co_freevars):
114
+ if v not in to_patch_from.__code__.co_freevars or v == "__class__":
115
+ new_freevars.append(v)
116
+ new_closure.append(to_patch_to.__closure__[i])
117
+ for i, v in enumerate(to_patch_from.__code__.co_freevars):
118
+ if v not in new_freevars:
119
+ new_freevars.append(v)
120
+ new_closure.append(to_patch_from.__closure__[i])
121
+ code_with_new_freevars = to_patch_from.__code__.replace(
122
+ co_freevars=tuple(new_freevars)
123
+ )
124
+ # lambdas may complain if there is more than one freevar
125
+ cls.try_patch_attr(
126
+ to_patch_to, code_with_new_freevars, "__code__", new_is_value=True
127
+ )
128
+ offset = -1
129
+ if to_patch_to.__closure__ is None and to_patch_from.__closure__ is not None:
130
+ offset = cls.infer_field_offset(to_patch_from, "__closure__")
131
+ cls.try_patch_readonly_attr(
132
+ to_patch_to,
133
+ tuple(new_closure) or None,
134
+ "__closure__",
135
+ new_is_value=True,
136
+ offset=offset,
137
+ )
138
+ for attr in ("__defaults__", "__kwdefaults__", "__doc__", "__dict__"):
139
+ cls.try_patch_attr(to_patch_to, to_patch_from, attr)
140
+ if is_method:
141
+ cls.try_patch_readonly_attr(to_patch_to, to_patch_from, "__self__")
@@ -1030,6 +1030,12 @@ class TerminalInteractiveShell(InteractiveShell):
1030
1030
 
1031
1031
  _inputhook = None
1032
1032
  def inputhook(self, context):
1033
+ warn(
1034
+ "inputkook seem unused, and marked for deprecation/Removal as of IPython 9.0. "
1035
+ "Please open an issue if you are using it.",
1036
+ category=PendingDeprecationWarning,
1037
+ stacklevel=2,
1038
+ )
1033
1039
  if self._inputhook is not None:
1034
1040
  self._inputhook(context)
1035
1041
 
@@ -50,7 +50,6 @@ def inputhook(context):
50
50
  except AttributeError: # Only for Qt>=5.14.
51
51
  pass
52
52
  _appref = app = QtGui.QApplication([" "])
53
- _eventloop = QtCore.QEventLoop(app)
54
53
 
55
54
  # "reclaim" IPython sys.excepthook after event loop starts
56
55
  # without this, it defaults back to BaseIPythonApplication.excepthook
@@ -58,6 +57,9 @@ def inputhook(context):
58
57
  # formatting and look like "bug in IPython".
59
58
  QtCore.QTimer.singleShot(0, _reclaim_excepthook)
60
59
 
60
+ if _eventloop is None:
61
+ _eventloop = QtCore.QEventLoop(app)
62
+
61
63
  if sys.platform == 'win32':
62
64
  # The QSocketNotifier method doesn't appear to work on Windows.
63
65
  # Use polling instead.
@@ -169,6 +169,7 @@ class IPythonPTCompleter(Completer):
169
169
  adjusted_text = _adjust_completion_text_based_on_context(
170
170
  c.text, body, offset
171
171
  )
172
+ min_elide = 30 if self.shell is None else self.shell.min_elide
172
173
  if c.type == "function":
173
174
  yield Completion(
174
175
  adjusted_text,
@@ -176,7 +177,7 @@ class IPythonPTCompleter(Completer):
176
177
  display=_elide(
177
178
  display_text + "()",
178
179
  body[c.start : c.end],
179
- min_elide=self.shell.min_elide,
180
+ min_elide=min_elide,
180
181
  ),
181
182
  display_meta=c.type + c.signature,
182
183
  )
@@ -187,7 +188,7 @@ class IPythonPTCompleter(Completer):
187
188
  display=_elide(
188
189
  display_text,
189
190
  body[c.start : c.end],
190
- min_elide=self.shell.min_elide,
191
+ min_elide=min_elide,
191
192
  ),
192
193
  display_meta=c.type,
193
194
  )
@@ -203,7 +203,7 @@ AUTO_SUGGEST_BINDINGS = [
203
203
  Binding(
204
204
  auto_suggest.accept,
205
205
  ["right"],
206
- "has_suggestion & default_buffer_focused & emacs_like_insert_mode",
206
+ "has_suggestion & default_buffer_focused & emacs_like_insert_mode & is_cursor_at_the_end_of_line",
207
207
  ),
208
208
  Binding(
209
209
  auto_suggest.accept_word,
@@ -3,6 +3,7 @@ import os
3
3
  import sys
4
4
  import token
5
5
  import tokenize
6
+ import warnings
6
7
  from io import StringIO
7
8
  from typing import TypeAlias
8
9
 
@@ -353,7 +354,6 @@ class Parser:
353
354
  Call format() to process code.
354
355
  """
355
356
 
356
-
357
357
  assert theme_name is not None
358
358
 
359
359
  self.out = out
@@ -361,7 +361,13 @@ class Parser:
361
361
  self.lines = None
362
362
  self.raw = None
363
363
  if theme_name is not None:
364
- assert theme_name == theme_name.lower()
364
+ if theme_name in ["Linux", "LightBG", "Neutral", "NoColor"]:
365
+ warnings.warn(
366
+ f"Theme names and color schemes are lowercase in IPython 9.0 use {theme_name.lower()} instead",
367
+ DeprecationWarning,
368
+ stacklevel=2,
369
+ )
370
+ theme_name = theme_name.lower()
365
371
  if not theme_name:
366
372
  self.theme_name = "nocolor"
367
373
  else:
IPython/utils/_sysinfo.py CHANGED
@@ -1,2 +1,2 @@
1
1
  # GENERATED BY setup.py
2
- commit = "20b9e8efe"
2
+ commit = "9970f5e4b"
@@ -0,0 +1,9 @@
1
+ # Deprecated/should be removed, but we break older version of ipyparallel
2
+ # https://github.com/ipython/ipyparallel/pull/924
3
+
4
+
5
+ # minimal subset of TermColors, removed from IPython
6
+ # not for public consumption, beyond ipyparallel.
7
+ class TermColors:
8
+ Normal = "\033[0m"
9
+ Red = "\033[0;31m"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ipython
3
- Version: 9.0.0b2
3
+ Version: 9.0.2
4
4
  Summary: IPython: Productive Interactive Computing
5
5
  Author: The IPython Development Team
6
6
  Author-email: ipython-dev@python.org
@@ -13,7 +13,7 @@ IPython/core/compilerop.py,sha256=tA8xHh10gp85brI2OYmvl7kW0TgDghdKbzmZE7nS4sw,69
13
13
  IPython/core/completer.py,sha256=grT260S45DxAthhOeZZfp1YmZRhCKwvXstvxRnxMytQ,120548
14
14
  IPython/core/completerlib.py,sha256=C_1uFwR4eiqIsemMRbluMQV1WJ3qSfnGxO01PsGSpr8,12641
15
15
  IPython/core/crashhandler.py,sha256=8-kyI6aNkqbaB_lBlbNKAOFv34HDBCpLggMfiu4oIDg,8747
16
- IPython/core/debugger.py,sha256=JikRcF51mtHHH0oqDADBVrvnOi4wwiEAnFKq1oe8p00,41406
16
+ IPython/core/debugger.py,sha256=I3LA4HQFEP5cy-kbbRvJqlLlSgVHSWBbp0cbPs3nU8M,41806
17
17
  IPython/core/display.py,sha256=wQgVFY_U1O-a-jJSLb00nJ9m2w9-NsBhEBGnQUcWUd0,41129
18
18
  IPython/core/display_functions.py,sha256=hlj1gXXrcIQU_ita03dHFesltOCViP1N3RcoLtLuyFI,12407
19
19
  IPython/core/display_trap.py,sha256=31jM26B3PhNSLJhpyRHuTWscObwVMOlH9PDC2v8HmsU,2098
@@ -29,14 +29,14 @@ IPython/core/guarded_eval.py,sha256=dQXW-e3wM6z9WGcszdJLhqiK0UCii303GOOXd3z3ze8,
29
29
  IPython/core/history.py,sha256=aTBJxCtZ_gPPJbzhAj6bOqII8AJClVWTH7xZX44m5jk,40602
30
30
  IPython/core/historyapp.py,sha256=xVVF2UmWH7UUYg6L4bmpMnSgC1C7QXwNEDbQ1Waedkc,5871
31
31
  IPython/core/hooks.py,sha256=xBWTZqycxZi97yj01IFc-SoJBzV5B73IoDHbAAlKUpQ,5193
32
- IPython/core/inputtransformer2.py,sha256=90eWEZMKJdPTnRHCENGGp5BLDg4jn1a2PP-WCJzgUmg,29108
33
- IPython/core/interactiveshell.py,sha256=aqyzXD2VXYsTs9_OmcNCRkw3lClPKL4eBPU1Nohl8y8,153561
32
+ IPython/core/inputtransformer2.py,sha256=7sRleytrcAbp5PZMOrDw59MjUAGXg5BbaRbPkSW83-I,28909
33
+ IPython/core/interactiveshell.py,sha256=uAGkI0JslwYzc2WEhl3R38gqLG2w9xQpAJjhLuCVZ0g,153787
34
34
  IPython/core/latex_symbols.py,sha256=DzFecvqWVSsdN7vWAsp0mlYAHRDQKfZGAmvuDUh0M-s,30127
35
35
  IPython/core/logger.py,sha256=Iwe4xKMmxEdvSwHYPMfsTWkmdaqVCgvZT3R3I3qTmrU,8436
36
36
  IPython/core/macro.py,sha256=OhvXWNhLe393rI2wTpMgbUVHWSnmC_ycHiYqzqSHXZU,1726
37
- IPython/core/magic.py,sha256=QWXILCerTOrO5VD8Qr4GoUt_0l-oNXz3ZCLKuCpx8aU,28919
37
+ IPython/core/magic.py,sha256=sUPVeVSk70aGVaPXncrSJxYJU8u_6YGCnR3folx8wys,28967
38
38
  IPython/core/magic_arguments.py,sha256=utkhQzGmn9Uw-ye33VO9P8ryP1L6ghkPcCdmhjri8K0,9701
39
- IPython/core/oinspect.py,sha256=h7Q-2gcB-UomOa0RG2q03DvWQ-5y4-2oLBC2GCKOmVU,40008
39
+ IPython/core/oinspect.py,sha256=_C6tUncGVHmaU89_chHRknQJ264mDYxzsNxeHNYFOao,40280
40
40
  IPython/core/page.py,sha256=P8Dmo0MjUq71DEnIPM_kaSGZoKTaPkrk4sUuM4I6HDA,11788
41
41
  IPython/core/payload.py,sha256=uHcwG5Ahm3fnz2dsIKbzYK_lHOilqfen0IhQffOUQbE,1763
42
42
  IPython/core/payloadpage.py,sha256=xGz4Ov82-0lmhKSIlM-kyIa50COk-ojB7tXLkGIRnPw,1177
@@ -44,18 +44,18 @@ IPython/core/prefilter.py,sha256=JHQ3feaD4bhoBDqZcEgmlDjQ2sfRXC1DNjgJhpaMU7E,257
44
44
  IPython/core/profileapp.py,sha256=bFMFIyehxeF9pDUtxw_6D3b0nxeqsupKTe7XhH7GMkc,10711
45
45
  IPython/core/profiledir.py,sha256=-vjOa1I_UajMZJblJRYXh16Y0RaAUn5a2swQBsw2qEU,8459
46
46
  IPython/core/pylabtools.py,sha256=LfNV9xCJ3flCfJXmv1NaCRYj9jZDtHAQ5oSEHWo3Gmg,17376
47
- IPython/core/release.py,sha256=0CxBJbsL5-EJCviE2KKYKeQNnbHDFmymlO-I7ccgIwQ,1505
47
+ IPython/core/release.py,sha256=Xh10_c3N-D5TcMiswCtqiR5brz6KpxliDhScbJLcRuI,1505
48
48
  IPython/core/shellapp.py,sha256=oZIzj_sqIXrN3qyyhinZ1gLXvFviKYHkmS4H3wVEb74,19307
49
49
  IPython/core/splitinput.py,sha256=bAX1puQjvYB-otJyqiqeOhWj6dooWuQeNVx2YdaKQs8,5006
50
- IPython/core/tbtools.py,sha256=Ykn8800kc2B2Z3UX91mDXUvR_px_PzmTo3LuSfJwre8,16608
51
- IPython/core/tips.py,sha256=E-M8aYtcQfARpyyjqTuuoXaKdUEH4dKD_YZYf3jWHVA,5939
50
+ IPython/core/tbtools.py,sha256=X4iB5zKAT2y4TK1R9l3d3kiW5htrzKn3qxalFFe2xzI,16880
51
+ IPython/core/tips.py,sha256=EBvLHsSwa5EwXjIJDotgm_t5r_dWWvnkJy-e1WvphoU,6164
52
52
  IPython/core/ultratb.py,sha256=ItaUt56wPnptv6f5ecbcBR3FtHljTDoIRHKsU-ZWGqI,46265
53
53
  IPython/core/usage.py,sha256=agrZE5eZIvJnXoqI8VV9e-oWZx5LbLxUq9MdQpEyts4,13542
54
54
  IPython/core/magics/__init__.py,sha256=pkd-UfzjDGp5UHuFKjw192vZnigpTP9ftXzG3oLdiS8,1619
55
55
  IPython/core/magics/ast_mod.py,sha256=06OoRO7Z7Jzfc-cflf8Z3wyqF17fkYv6fJ_Nw4d7eQE,10295
56
56
  IPython/core/magics/auto.py,sha256=yEouIjsQ6LmfSEfNvkZbNmNDFl19KLRnaJciYdR7a1A,4816
57
57
  IPython/core/magics/basic.py,sha256=hvqMBRbn4rwvjDhjuZkZotMV__oSRpmKc08zQy81QhM,22356
58
- IPython/core/magics/code.py,sha256=iPjF0eW3A2VkQj2bOLA6qgptDpYsGpnnQ14U-jxPGrU,28144
58
+ IPython/core/magics/code.py,sha256=h_dho9niPvtf_IpoOZf5GAD6CYbT0EQGsfLfutyX-7I,28144
59
59
  IPython/core/magics/config.py,sha256=QBL5uY7m-Q7C46mO3q1Yio9s73w1TnI9y__j5E-j44Y,4881
60
60
  IPython/core/magics/display.py,sha256=STRq66GlZwcvFyBxbkqslclpP_s9LnqD0ew9Z3S4-Jo,3130
61
61
  IPython/core/magics/execution.py,sha256=KHggud3yvtJ-w7PX7qXRF7Y9IsBqS8sZmzgzdmoP1YI,61879
@@ -71,6 +71,9 @@ IPython/core/profile/README_STARTUP,sha256=Y47GtYQkWy6pdzkqylUNL6eBSZMUIRGwTxXPU
71
71
  IPython/extensions/__init__.py,sha256=V4vSllFd18CVUvDTbXLYqfwTFmPXUiQZGCs5LX2IliE,78
72
72
  IPython/extensions/autoreload.py,sha256=_qU6PNtAm93J2UqtD3TMA68rj9wUWJPXfqb_VidrOCM,24571
73
73
  IPython/extensions/storemagic.py,sha256=ths8PLtGmYZAYaibRuS1QetLm1Xu1soyGwehBjayByg,8168
74
+ IPython/extensions/deduperreload/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
+ IPython/extensions/deduperreload/deduperreload.py,sha256=jh4sa6b9w2zO4PQRUtPwAFtVUlSJwPKRAQg4q_csh9s,24680
76
+ IPython/extensions/deduperreload/deduperreload_patching.py,sha256=xOaws3UV5KmaAK8yZ3qtQO45w_5Ntkt_qZTcZ1psszY,4934
74
77
  IPython/external/__init__.py,sha256=-EQHbuUnBe1RS1_CwaLGzNSZQsCJsrxHW_r15smvVW0,126
75
78
  IPython/external/pickleshare.py,sha256=Zs0Hq8IXbf51RNFCr1AFxZKtlhfPJMM3b1WD4sNB670,9974
76
79
  IPython/external/qt_for_kernel.py,sha256=ROLfN-NcA_h-ZgZsm9pBW1y57I_gxnJmOdpRwvpw8DA,3442
@@ -93,11 +96,11 @@ IPython/sphinxext/ipython_directive.py,sha256=hOQCdWx2N7WzBJVHMw7qVvAwLJxBZJxFCE
93
96
  IPython/terminal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
94
97
  IPython/terminal/debugger.py,sha256=0LdBmCKVHKNGpmBKdb4bBgKlyuTVyfz2Dg2Y8wE4FTM,6937
95
98
  IPython/terminal/embed.py,sha256=9PnL6nL6mOklg77Zrn6bqw9TUswjH2d7VNRtcuAi0FI,16122
96
- IPython/terminal/interactiveshell.py,sha256=Tqvby5MaqG2HYY829UCYHHHv5r5jomeKMoxEaaei09Y,39684
99
+ IPython/terminal/interactiveshell.py,sha256=yd-33C6tvN0Oc-_ftLbhPMmq6HufyNUo7fygO753p5k,39931
97
100
  IPython/terminal/ipapp.py,sha256=d2Rog4DRkVv0_fReqZOuorKMXAtEqsCskwPejtjj0Lg,12512
98
101
  IPython/terminal/magics.py,sha256=49ZVJzbAUkG_EFpebxIBEqm3tEClvqefoeM6QnxGrrk,7705
99
102
  IPython/terminal/prompts.py,sha256=5IoXb-pXA4MWu3gAfEuyIwZUbDg6mxxJuMkOqRBmYa0,4555
100
- IPython/terminal/ptutils.py,sha256=oyQrcrctdHIyaNsKgiHYd6IeigSrYna9P8AGv9psw2Q,8014
103
+ IPython/terminal/ptutils.py,sha256=6PWDVPuBzpMxLdl_jqK7QtdnYVoVIjUP4Vl1JpFZFoM,8067
101
104
  IPython/terminal/pt_inputhooks/__init__.py,sha256=jB7MOn9ZtC5qcq9RnCu9kbxjysP5YrN9KbXeYIx79Q4,3606
102
105
  IPython/terminal/pt_inputhooks/asyncio.py,sha256=ffxVjt0RIsh0yYzcPDpSTrhWv5JDNRBTof0OlJgksFU,1271
103
106
  IPython/terminal/pt_inputhooks/glut.py,sha256=CchyzpkfAqVytLzuKv_XWsU4rmAJXCgxBksACM2bk2s,4999
@@ -106,10 +109,10 @@ IPython/terminal/pt_inputhooks/gtk3.py,sha256=_MuZIjZuLsW2i4vvUjNQh9u4xIU8kcITyJ
106
109
  IPython/terminal/pt_inputhooks/gtk4.py,sha256=r_MxCT7a0nTHZtqyuZpPgCW2Cl7iuomC0PjgFteSL9c,557
107
110
  IPython/terminal/pt_inputhooks/osx.py,sha256=TnndyR_SPbmWC_A--0ORB06rhH7IS2_7kjphfPcRqXo,4448
108
111
  IPython/terminal/pt_inputhooks/pyglet.py,sha256=wDDQDgilvK9uzwGB3XyAqcf8LfKr-Lc_NMAzunZMu_Y,2371
109
- IPython/terminal/pt_inputhooks/qt.py,sha256=mdPInyP_AXl3OiKsuCnwaaPHCQuBcny7YiULMqB5wyo,3451
112
+ IPython/terminal/pt_inputhooks/qt.py,sha256=ivVQu3UzzfGGeuowh6lNeVtuOJOL7g_VTKcgMG8JHpM,3479
110
113
  IPython/terminal/pt_inputhooks/tk.py,sha256=FjejvtwbvpeBZLoBCci1RDo_jWD5qElMy7PP-atd8j4,3651
111
114
  IPython/terminal/pt_inputhooks/wx.py,sha256=9yI52lDSZ3O_5Gww_3IeenEk_3PepLIME3Onh4X3kW0,7126
112
- IPython/terminal/shortcuts/__init__.py,sha256=QlXJBs_B6Sb_rla9-m2Mtn5b69LjUPGXFBI9On8cmDY,18532
115
+ IPython/terminal/shortcuts/__init__.py,sha256=irSX9mwHzeLDQ95fXRGdZxduRknwATtESvm2NIov7KU,18563
113
116
  IPython/terminal/shortcuts/auto_match.py,sha256=9uT1fDb-c4Ew7TSIs_zET1jSxDlbfWGluxfW_pj39tk,3066
114
117
  IPython/terminal/shortcuts/auto_suggest.py,sha256=dJGZGymi8xEmCurPYsqYeWHMP5kfqcnCcezDdBlv9wk,23177
115
118
  IPython/terminal/shortcuts/filters.py,sha256=MgRTQWq8YfIyWvMASuQ9BGKq5RQwiEY5trSyMnMtJAo,10998
@@ -131,7 +134,7 @@ IPython/testing/plugin/test_example.txt,sha256=CGM8aZIYHlePDdAnR1yX3MfDGu0OceZpU
131
134
  IPython/testing/plugin/test_exampleip.txt,sha256=5gLcj8iCk-WCOGz0ObpQpuZMhGwS1jUMyH3mouGxQJI,814
132
135
  IPython/testing/plugin/test_ipdoctest.py,sha256=Lc3qQdZ3amXf9EKA7JlXf30b3BzP8RwdNS9-SMRe2P0,1907
133
136
  IPython/testing/plugin/test_refs.py,sha256=y-Y2Q8niRIbaanbwpIzvEwwaHkJfAq10HYfb4bAXHBc,715
134
- IPython/utils/PyColorize.py,sha256=kdxL1H7O3xKF7VUt22Sqr_GllCpemZ8458ekNCksiX0,15230
137
+ IPython/utils/PyColorize.py,sha256=VyNAE6pgw8sCrS2-1v4p30TZIHe6SFz_0YqLUdsOc4k,15553
135
138
  IPython/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
136
139
  IPython/utils/_process_cli.py,sha256=tJWYMEgNYgeMx9v-n3YLbKW0tPbFtpczfDH28RC3n4A,2020
137
140
  IPython/utils/_process_common.py,sha256=hMFRGOJh-n-uBcSAOnr2qetQXAthMVpS8mwRcZuQqlo,7306
@@ -139,8 +142,9 @@ IPython/utils/_process_emscripten.py,sha256=lGLQb2IgmanNtb502KflfuKIhgOF119Ji3cw
139
142
  IPython/utils/_process_posix.py,sha256=aOEtguhS3vdWngBpws1XQURO8Ozqd5gRiCk9VLky6tA,7502
140
143
  IPython/utils/_process_win32.py,sha256=Pcf6ZiqMbqDT79edzegE_AX3D367UtE8bbhT41no54A,6775
141
144
  IPython/utils/_process_win32_controller.py,sha256=hi2eR7mLbl3TTMCVbgps85GppxdtYbhOYK_l13WvYaM,21343
142
- IPython/utils/_sysinfo.py,sha256=lRLokemEP_hVMNVbW8zAXf6B8ZCegFZl7rwfHJs5o1U,45
145
+ IPython/utils/_sysinfo.py,sha256=k_tFtBwenx5P8zcV-6FTx5UbGvbQ9A6h5D7WevWmHoI,45
143
146
  IPython/utils/capture.py,sha256=h5yL5Lxq8bgO1SFpoNDYjEi6mh1IW_2X9CE7vOsUxE4,5137
147
+ IPython/utils/coloransi.py,sha256=CML-SkzLa7oaIK1qypb3uwcfPXDeKHxZQiMJ0IWvUY0,293
144
148
  IPython/utils/contexts.py,sha256=w5_uXc0WTU3KKV1kcCW9A0_Mz5mGRoeGWMq_P_eo-Dg,1610
145
149
  IPython/utils/data.py,sha256=36VVGY1b0JG7_zSdbVSy8IzLqM0uT-uB12TBYWgd1lI,1015
146
150
  IPython/utils/decorators.py,sha256=qsYLskFlT2bB_Q-87ttBA56lAf-knWLOe5WiOwPdXFE,2680
@@ -170,11 +174,11 @@ IPython/utils/text.py,sha256=6s-y4KvDmnJxLs0urf5D-1auZGSnj2xmXgQ-9jVu0N8,18788
170
174
  IPython/utils/timing.py,sha256=nND-ZUBkHWfYevvbRG-YfOSIFczz_epzMqWK5PH6nqA,4275
171
175
  IPython/utils/tokenutil.py,sha256=x6KQ6ZCGOY7j5GQcr7byJRZSBFgyBcfkTiLtjxkl9f8,6552
172
176
  IPython/utils/wildcard.py,sha256=6EEc3OEYp-IuSoidL6nwpaHg--GxnzbAJTmFiz77CNE,4612
173
- ipython-9.0.0b2.data/data/share/man/man1/ipython.1,sha256=PVdQP2hHmHyUEwzLOPcgavnCe9jTDVrM1jKZt4cnF_Q,2058
174
- ipython-9.0.0b2.dist-info/COPYING.rst,sha256=NBr8vXKYh7cEb-e5j8T07f867Y048G7v2bMGcPBD3xc,1639
175
- ipython-9.0.0b2.dist-info/LICENSE,sha256=4OOQdI7UQKuJPKHxNaiKkgqvVAnbuQpbQnx1xeUSaPs,1720
176
- ipython-9.0.0b2.dist-info/METADATA,sha256=RfZOkmtXPv5oBzosTeWtFtms_bj5UV-5XVGfw369YU8,4256
177
- ipython-9.0.0b2.dist-info/WHEEL,sha256=OD3CZ31UWfp_hGz-1fPUpTR7Tv5ndR05pV1l1CfXGfg,92
178
- ipython-9.0.0b2.dist-info/entry_points.txt,sha256=z5BEEohWgg0SHdgdeNABf4T3fu-lr9W6F_bWOQHLdVs,83
179
- ipython-9.0.0b2.dist-info/top_level.txt,sha256=PKjvHtNCBZ9EHTmd2mwJ1J_k3j0F6D1lTFzIcJFFPEU,8
180
- ipython-9.0.0b2.dist-info/RECORD,,
177
+ ipython-9.0.2.data/data/share/man/man1/ipython.1,sha256=PVdQP2hHmHyUEwzLOPcgavnCe9jTDVrM1jKZt4cnF_Q,2058
178
+ ipython-9.0.2.dist-info/COPYING.rst,sha256=NBr8vXKYh7cEb-e5j8T07f867Y048G7v2bMGcPBD3xc,1639
179
+ ipython-9.0.2.dist-info/LICENSE,sha256=4OOQdI7UQKuJPKHxNaiKkgqvVAnbuQpbQnx1xeUSaPs,1720
180
+ ipython-9.0.2.dist-info/METADATA,sha256=7S5uSfcEcaNZ9JJsCMTJ_lBitQ-QnvsIZjPLk6DZc6Q,4254
181
+ ipython-9.0.2.dist-info/WHEEL,sha256=RnOlZk4WRTvz4xhLM2LyBNUiU6tTosLZam4r642KGVM,90
182
+ ipython-9.0.2.dist-info/entry_points.txt,sha256=z5BEEohWgg0SHdgdeNABf4T3fu-lr9W6F_bWOQHLdVs,83
183
+ ipython-9.0.2.dist-info/top_level.txt,sha256=PKjvHtNCBZ9EHTmd2mwJ1J_k3j0F6D1lTFzIcJFFPEU,8
184
+ ipython-9.0.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (9.0.0b2)
2
+ Generator: setuptools (9.0.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5