ZhengyangZhang commited on
Commit
4661aaa
·
verified ·
1 Parent(s): 3991a8c

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. lib/python3.12/site-packages/IPython/__pycache__/__init__.cpython-312.pyc +0 -0
  2. lib/python3.12/site-packages/IPython/__pycache__/__main__.cpython-312.pyc +0 -0
  3. lib/python3.12/site-packages/IPython/__pycache__/display.cpython-312.pyc +0 -0
  4. lib/python3.12/site-packages/IPython/__pycache__/paths.cpython-312.pyc +0 -0
  5. lib/python3.12/site-packages/IPython/extensions/__init__.py +2 -0
  6. lib/python3.12/site-packages/IPython/extensions/__pycache__/__init__.cpython-312.pyc +0 -0
  7. lib/python3.12/site-packages/IPython/extensions/__pycache__/autoreload.cpython-312.pyc +0 -0
  8. lib/python3.12/site-packages/IPython/extensions/__pycache__/storemagic.cpython-312.pyc +0 -0
  9. lib/python3.12/site-packages/IPython/extensions/autoreload.py +915 -0
  10. lib/python3.12/site-packages/IPython/extensions/deduperreload/__init__.py +0 -0
  11. lib/python3.12/site-packages/IPython/extensions/deduperreload/__pycache__/__init__.cpython-312.pyc +0 -0
  12. lib/python3.12/site-packages/IPython/extensions/deduperreload/__pycache__/deduperreload.cpython-312.pyc +0 -0
  13. lib/python3.12/site-packages/IPython/extensions/deduperreload/__pycache__/deduperreload_patching.cpython-312.pyc +0 -0
  14. lib/python3.12/site-packages/IPython/extensions/deduperreload/deduperreload.py +637 -0
  15. lib/python3.12/site-packages/IPython/extensions/deduperreload/deduperreload_patching.py +142 -0
  16. lib/python3.12/site-packages/IPython/extensions/storemagic.py +236 -0
  17. lib/python3.12/site-packages/IPython/terminal/__pycache__/__init__.cpython-312.pyc +0 -0
  18. lib/python3.12/site-packages/IPython/terminal/__pycache__/debugger.cpython-312.pyc +0 -0
  19. lib/python3.12/site-packages/IPython/terminal/__pycache__/embed.cpython-312.pyc +0 -0
  20. lib/python3.12/site-packages/IPython/terminal/__pycache__/interactiveshell.cpython-312.pyc +0 -0
  21. lib/python3.12/site-packages/IPython/terminal/__pycache__/ipapp.cpython-312.pyc +0 -0
  22. lib/python3.12/site-packages/IPython/terminal/__pycache__/magics.cpython-312.pyc +0 -0
  23. lib/python3.12/site-packages/IPython/terminal/__pycache__/prompts.cpython-312.pyc +0 -0
  24. lib/python3.12/site-packages/IPython/terminal/__pycache__/ptutils.cpython-312.pyc +0 -0
  25. lib/python3.12/site-packages/IPython/terminal/shortcuts/__pycache__/__init__.cpython-312.pyc +0 -0
  26. lib/python3.12/site-packages/IPython/terminal/shortcuts/__pycache__/auto_match.cpython-312.pyc +0 -0
  27. lib/python3.12/site-packages/IPython/terminal/shortcuts/__pycache__/auto_suggest.cpython-312.pyc +0 -0
  28. lib/python3.12/site-packages/IPython/terminal/shortcuts/__pycache__/filters.cpython-312.pyc +0 -0
  29. lib/python3.12/site-packages/IPython/terminal/shortcuts/auto_suggest.py +658 -0
  30. lib/python3.12/site-packages/attrs/__init__.py +72 -0
  31. lib/python3.12/site-packages/attrs/__init__.pyi +314 -0
  32. lib/python3.12/site-packages/attrs/__pycache__/__init__.cpython-312.pyc +0 -0
  33. lib/python3.12/site-packages/attrs/__pycache__/converters.cpython-312.pyc +0 -0
  34. lib/python3.12/site-packages/attrs/__pycache__/exceptions.cpython-312.pyc +0 -0
  35. lib/python3.12/site-packages/attrs/__pycache__/filters.cpython-312.pyc +0 -0
  36. lib/python3.12/site-packages/attrs/__pycache__/setters.cpython-312.pyc +0 -0
  37. lib/python3.12/site-packages/attrs/__pycache__/validators.cpython-312.pyc +0 -0
  38. lib/python3.12/site-packages/attrs/converters.py +3 -0
  39. lib/python3.12/site-packages/attrs/exceptions.py +3 -0
  40. lib/python3.12/site-packages/attrs/filters.py +3 -0
  41. lib/python3.12/site-packages/attrs/py.typed +0 -0
  42. lib/python3.12/site-packages/attrs/setters.py +3 -0
  43. lib/python3.12/site-packages/attrs/validators.py +3 -0
  44. lib/python3.12/site-packages/torchaudio/__init__.py +53 -0
  45. lib/python3.12/site-packages/torchaudio/_internal/__init__.py +10 -0
  46. lib/python3.12/site-packages/torchaudio/_internal/__pycache__/__init__.cpython-312.pyc +0 -0
  47. lib/python3.12/site-packages/torchaudio/_internal/__pycache__/module_utils.cpython-312.pyc +0 -0
  48. lib/python3.12/site-packages/torchaudio/_internal/module_utils.py +113 -0
  49. lib/python3.12/site-packages/torchaudio/compliance/__init__.py +5 -0
  50. lib/python3.12/site-packages/torchaudio/compliance/__pycache__/__init__.cpython-312.pyc +0 -0
lib/python3.12/site-packages/IPython/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (4.97 kB). View file
 
lib/python3.12/site-packages/IPython/__pycache__/__main__.cpython-312.pyc ADDED
Binary file (310 Bytes). View file
 
lib/python3.12/site-packages/IPython/__pycache__/display.cpython-312.pyc ADDED
Binary file (896 Bytes). View file
 
lib/python3.12/site-packages/IPython/__pycache__/paths.cpython-312.pyc ADDED
Binary file (5.54 kB). View file
 
lib/python3.12/site-packages/IPython/extensions/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """This directory is meant for IPython extensions."""
lib/python3.12/site-packages/IPython/extensions/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (259 Bytes). View file
 
lib/python3.12/site-packages/IPython/extensions/__pycache__/autoreload.cpython-312.pyc ADDED
Binary file (33.9 kB). View file
 
lib/python3.12/site-packages/IPython/extensions/__pycache__/storemagic.cpython-312.pyc ADDED
Binary file (10.1 kB). View file
 
lib/python3.12/site-packages/IPython/extensions/autoreload.py ADDED
@@ -0,0 +1,915 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """IPython extension to reload modules before executing user code.
2
+
3
+ ``autoreload`` reloads modules automatically before entering the execution of
4
+ code typed at the IPython prompt.
5
+
6
+ This makes for example the following workflow possible:
7
+
8
+ .. sourcecode:: ipython
9
+
10
+ In [1]: %load_ext autoreload
11
+
12
+ In [2]: %autoreload 2
13
+
14
+ In [3]: from foo import some_function
15
+
16
+ In [4]: some_function()
17
+ Out[4]: 42
18
+
19
+ In [5]: # open foo.py in an editor and change some_function to return 43
20
+
21
+ In [6]: some_function()
22
+ Out[6]: 43
23
+
24
+ The module was reloaded without reloading it explicitly, and the object
25
+ imported with ``from foo import ...`` was also updated.
26
+
27
+ Usage
28
+ =====
29
+
30
+ The following magic commands are provided:
31
+
32
+ ``%autoreload``, ``%autoreload now``
33
+
34
+ Reload all modules (except those excluded by ``%aimport``)
35
+ automatically now.
36
+
37
+ ``%autoreload 0``, ``%autoreload off``
38
+
39
+ Disable automatic reloading.
40
+
41
+ ``%autoreload 1``, ``%autoreload explicit``
42
+
43
+ Reload all modules imported with ``%aimport`` every time before
44
+ executing the Python code typed.
45
+
46
+ ``%autoreload 2``, ``%autoreload all``
47
+
48
+ Reload all modules (except those excluded by ``%aimport``) every
49
+ time before executing the Python code typed.
50
+
51
+ ``%autoreload 3``, ``%autoreload complete``
52
+
53
+ Same as 2/all, but also adds any new objects in the module. See
54
+ unit test at IPython/extensions/tests/test_autoreload.py::test_autoload_newly_added_objects
55
+
56
+ Adding ``--print`` or ``-p`` to the ``%autoreload`` line will print autoreload activity to
57
+ standard out. ``--log`` or ``-l`` will do it to the log at INFO level; both can be used
58
+ simultaneously.
59
+
60
+ ``%aimport``
61
+
62
+ List modules which are to be automatically imported or not to be imported.
63
+
64
+ ``%aimport foo``
65
+
66
+ Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1``
67
+
68
+ ``%aimport foo, bar``
69
+
70
+ Import modules 'foo', 'bar' and mark them to be autoreloaded for ``%autoreload 1``
71
+
72
+ ``%aimport -foo``
73
+
74
+ Mark module 'foo' to not be autoreloaded.
75
+
76
+ Import Conflict Resolution
77
+ ==========================
78
+
79
+ In ``%autoreload 3`` mode, the extension tracks ``from X import Y`` style imports
80
+ and intelligently resolves conflicts when the same name is imported multiple ways.
81
+
82
+ Import tracking occurs after successful code execution, ensuring that only valid
83
+ imports are tracked. This approach handles edge cases such as:
84
+
85
+ - Importing a name that doesn't initially exist in a module, then adding that name
86
+ to the module and importing it again
87
+ - Conflicts between aliased imports (``from X import Y as Z``) and direct imports
88
+ (``from X import Z``)
89
+
90
+ When conflicts occur:
91
+
92
+ - If you first do ``from X import Y as Z`` then later ``from X import Z``,
93
+ the extension will switch to reloading ``Z`` instead of ``Y`` under the name ``Z``.
94
+
95
+ - Similarly, if you first do ``from X import Z`` then later ``from X import Y as Z``,
96
+ the extension will switch to reloading ``Y`` as ``Z`` instead of the original ``Z``.
97
+
98
+ - The most recent successful import always takes precedence in conflict resolution.
99
+
100
+ Caveats
101
+ =======
102
+
103
+ Reloading Python modules in a reliable way is in general difficult,
104
+ and unexpected things may occur. ``%autoreload`` tries to work around
105
+ common pitfalls by replacing function code objects and parts of
106
+ classes previously in the module with new versions. This makes the
107
+ following things to work:
108
+
109
+ - Functions and classes imported via 'from xxx import foo' are upgraded
110
+ to new versions when 'xxx' is reloaded.
111
+
112
+ - Methods and properties of classes are upgraded on reload, so that
113
+ calling 'c.foo()' on an object 'c' created before the reload causes
114
+ the new code for 'foo' to be executed.
115
+
116
+ Some of the known remaining caveats are:
117
+
118
+ - Replacing code objects does not always succeed: changing a @property
119
+ in a class to an ordinary method or a method to a member variable
120
+ can cause problems (but in old objects only).
121
+
122
+ - Functions that are removed (eg. via monkey-patching) from a module
123
+ before it is reloaded are not upgraded.
124
+
125
+ - C extension modules cannot be reloaded, and so cannot be autoreloaded.
126
+
127
+ - While comparing Enum and Flag, the 'is' Identity Operator is used (even in the case '==' has been used (Similar to the 'None' keyword)).
128
+
129
+ - Reloading a module, or importing the same module by a different name, creates new Enums. These may look the same, but are not.
130
+ """
131
+
132
+ from IPython.core import magic_arguments
133
+ from IPython.core.magic import Magics, magics_class, line_magic
134
+ from IPython.extensions.deduperreload.deduperreload import DeduperReloader
135
+
136
+ __skip_doctest__ = True
137
+
138
+ # -----------------------------------------------------------------------------
139
+ # Copyright (C) 2000 Thomas Heller
140
+ # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi>
141
+ # Copyright (C) 2012 The IPython Development Team
142
+ #
143
+ # Distributed under the terms of the BSD License. The full license is in
144
+ # the file COPYING, distributed as part of this software.
145
+ # -----------------------------------------------------------------------------
146
+ #
147
+ # This IPython module is written by Pauli Virtanen, based on the autoreload
148
+ # code by Thomas Heller.
149
+
150
+ # -----------------------------------------------------------------------------
151
+ # Imports
152
+ # -----------------------------------------------------------------------------
153
+
154
+ import ast
155
+ import os
156
+ import sys
157
+ import traceback
158
+ import types
159
+ import weakref
160
+ import gc
161
+ import logging
162
+ from importlib import import_module, reload
163
+ from importlib.util import source_from_cache
164
+
165
+ # ------------------------------------------------------------------------------
166
+ # Autoreload functionality
167
+ # ------------------------------------------------------------------------------
168
+
169
+
170
+ class ModuleReloader:
171
+ enabled = False
172
+ """Whether this reloader is enabled"""
173
+
174
+ check_all = True
175
+ """Autoreload all modules, not just those listed in 'modules'"""
176
+
177
+ autoload_obj = False
178
+ """Autoreload all modules AND autoload all new objects"""
179
+
180
+ def __init__(self, shell=None):
181
+ # Modules that failed to reload: {module: mtime-on-failed-reload, ...}
182
+ self.failed = {}
183
+ # Modules specially marked as autoreloadable.
184
+ self.modules = {}
185
+ # Modules specially marked as not autoreloadable.
186
+ self.skip_modules = {}
187
+ # (module-name, name) -> weakref, for replacing old code objects
188
+ self.old_objects = {}
189
+ # Module modification timestamps
190
+ self.modules_mtimes = {}
191
+ self.shell = shell
192
+
193
+ # Reporting callable for verbosity
194
+ self._report = lambda msg: None # by default, be quiet.
195
+
196
+ # Deduper reloader
197
+ self.deduper_reloader = DeduperReloader()
198
+
199
+ # Persistent import tracker for from-imports
200
+ self.import_from_tracker = ImportFromTracker({}, {})
201
+
202
+ # Cache module modification times
203
+ self.check(check_all=True, do_reload=False)
204
+
205
+ # To hide autoreload errors
206
+ self.hide_errors = False
207
+
208
+ def mark_module_skipped(self, module_name):
209
+ """Skip reloading the named module in the future"""
210
+ try:
211
+ del self.modules[module_name]
212
+ except KeyError:
213
+ pass
214
+ self.skip_modules[module_name] = True
215
+
216
+ def mark_module_reloadable(self, module_name):
217
+ """Reload the named module in the future (if it is imported)"""
218
+ try:
219
+ del self.skip_modules[module_name]
220
+ except KeyError:
221
+ pass
222
+ self.modules[module_name] = True
223
+
224
+ def clear_import_tracker(self):
225
+ """Clear the persistent import tracker state"""
226
+ self.import_from_tracker = ImportFromTracker({}, {})
227
+
228
+ def aimport_module(self, module_name):
229
+ """Import a module, and mark it reloadable
230
+
231
+ Returns
232
+ -------
233
+ top_module : module
234
+ The imported module if it is top-level, or the top-level
235
+ top_name : module
236
+ Name of top_module
237
+
238
+ """
239
+ self.mark_module_reloadable(module_name)
240
+
241
+ import_module(module_name)
242
+ top_name = module_name.split(".")[0]
243
+ top_module = sys.modules[top_name]
244
+ return top_module, top_name
245
+
246
+ def filename_and_mtime(self, module):
247
+ if not hasattr(module, "__file__") or module.__file__ is None:
248
+ return None, None
249
+
250
+ if getattr(module, "__name__", None) in [None, "__mp_main__", "__main__"]:
251
+ # we cannot reload(__main__) or reload(__mp_main__)
252
+ return None, None
253
+
254
+ filename = module.__file__
255
+ path, ext = os.path.splitext(filename)
256
+
257
+ if ext.lower() == ".py":
258
+ py_filename = filename
259
+ else:
260
+ try:
261
+ py_filename = source_from_cache(filename)
262
+ except ValueError:
263
+ return None, None
264
+
265
+ try:
266
+ pymtime = os.stat(py_filename).st_mtime
267
+ except OSError:
268
+ return None, None
269
+
270
+ return py_filename, pymtime
271
+
272
+ def check(self, check_all=False, do_reload=True, execution_info=None):
273
+ """Check whether some modules need to be reloaded."""
274
+
275
+ if not self.enabled and not check_all:
276
+ return
277
+
278
+ if check_all or self.check_all:
279
+ modules = list(sys.modules.keys())
280
+ else:
281
+ modules = list(self.modules.keys())
282
+
283
+ # Use the persistent import_from_tracker
284
+ import_from_tracker = (
285
+ self.import_from_tracker if self.import_from_tracker.imports_froms else None
286
+ )
287
+ for modname in modules:
288
+ m = sys.modules.get(modname, None)
289
+
290
+ if modname in self.skip_modules:
291
+ continue
292
+
293
+ py_filename, pymtime = self.filename_and_mtime(m)
294
+ if py_filename is None:
295
+ continue
296
+
297
+ try:
298
+ if pymtime <= self.modules_mtimes[modname]:
299
+ continue
300
+ except KeyError:
301
+ self.modules_mtimes[modname] = pymtime
302
+ continue
303
+ else:
304
+ if self.failed.get(py_filename, None) == pymtime:
305
+ continue
306
+
307
+ self.modules_mtimes[modname] = pymtime
308
+
309
+ # If we've reached this point, we should try to reload the module
310
+ if do_reload:
311
+ self._report(f"Reloading '{modname}'.")
312
+ try:
313
+ if self.autoload_obj:
314
+ superreload(
315
+ m,
316
+ reload,
317
+ self.old_objects,
318
+ self.shell,
319
+ import_from_tracker=import_from_tracker,
320
+ )
321
+ # if not using autoload, check if deduperreload is viable for this module
322
+ elif self.deduper_reloader.maybe_reload_module(m):
323
+ pass
324
+ else:
325
+ superreload(m, reload, self.old_objects)
326
+ if py_filename in self.failed:
327
+ del self.failed[py_filename]
328
+ except:
329
+ if not self.hide_errors:
330
+ logger = logging.getLogger("autoreload")
331
+ logger.exception(
332
+ f"Failed to reload module '{modname}' from file '{py_filename}'"
333
+ )
334
+ print(
335
+ "[autoreload of {} failed: {}]".format(
336
+ modname, traceback.format_exc(10)
337
+ ),
338
+ file=sys.stderr,
339
+ )
340
+ self.failed[py_filename] = pymtime
341
+ self.deduper_reloader.update_sources()
342
+
343
+
344
+ # ------------------------------------------------------------------------------
345
+ # superreload
346
+ # ------------------------------------------------------------------------------
347
+
348
+
349
+ func_attrs = [
350
+ "__code__",
351
+ "__defaults__",
352
+ "__doc__",
353
+ "__closure__",
354
+ "__globals__",
355
+ "__dict__",
356
+ ]
357
+
358
+
359
+ def update_function(old, new):
360
+ """Upgrade the code object of a function"""
361
+ for name in func_attrs:
362
+ try:
363
+ setattr(old, name, getattr(new, name))
364
+ except (AttributeError, TypeError):
365
+ pass
366
+
367
+
368
+ def update_instances(old, new):
369
+ """Use garbage collector to find all instances that refer to the old
370
+ class definition and update their __class__ to point to the new class
371
+ definition"""
372
+
373
+ refs = gc.get_referrers(old)
374
+
375
+ for ref in refs:
376
+ if type(ref) is old:
377
+ object.__setattr__(ref, "__class__", new)
378
+
379
+
380
+ def update_class(old, new):
381
+ """Replace stuff in the __dict__ of a class, and upgrade
382
+ method code objects, and add new methods, if any"""
383
+ for key in list(old.__dict__.keys()):
384
+ old_obj = getattr(old, key)
385
+ try:
386
+ new_obj = getattr(new, key)
387
+ # explicitly checking that comparison returns True to handle
388
+ # cases where `==` doesn't return a boolean.
389
+ if (old_obj == new_obj) is True:
390
+ continue
391
+ except AttributeError:
392
+ # obsolete attribute: remove it
393
+ try:
394
+ delattr(old, key)
395
+ except (AttributeError, TypeError):
396
+ pass
397
+ continue
398
+ except ValueError:
399
+ # can't compare nested structures containing
400
+ # numpy arrays using `==`
401
+ pass
402
+
403
+ if update_generic(old_obj, new_obj):
404
+ continue
405
+
406
+ try:
407
+ setattr(old, key, getattr(new, key))
408
+ except (AttributeError, TypeError):
409
+ pass # skip non-writable attributes
410
+
411
+ for key in list(new.__dict__.keys()):
412
+ if key not in list(old.__dict__.keys()):
413
+ try:
414
+ setattr(old, key, getattr(new, key))
415
+ except (AttributeError, TypeError):
416
+ pass # skip non-writable attributes
417
+
418
+ # update all instances of class
419
+ update_instances(old, new)
420
+
421
+
422
+ def update_property(old, new):
423
+ """Replace get/set/del functions of a property"""
424
+ update_generic(old.fdel, new.fdel)
425
+ update_generic(old.fget, new.fget)
426
+ update_generic(old.fset, new.fset)
427
+
428
+
429
+ def isinstance2(a, b, typ):
430
+ return isinstance(a, typ) and isinstance(b, typ)
431
+
432
+
433
+ UPDATE_RULES = [
434
+ (lambda a, b: isinstance2(a, b, type), update_class),
435
+ (lambda a, b: isinstance2(a, b, types.FunctionType), update_function),
436
+ (lambda a, b: isinstance2(a, b, property), update_property),
437
+ ]
438
+ UPDATE_RULES.extend(
439
+ [
440
+ (
441
+ lambda a, b: isinstance2(a, b, types.MethodType),
442
+ lambda a, b: update_function(a.__func__, b.__func__),
443
+ ),
444
+ ]
445
+ )
446
+
447
+
448
+ def update_generic(a, b):
449
+ for type_check, update in UPDATE_RULES:
450
+ if type_check(a, b):
451
+ update(a, b)
452
+ return True
453
+ return False
454
+
455
+
456
+ class StrongRef:
457
+ def __init__(self, obj):
458
+ self.obj = obj
459
+
460
+ def __call__(self):
461
+ return self.obj
462
+
463
+
464
+ mod_attrs = [
465
+ "__name__",
466
+ "__doc__",
467
+ "__package__",
468
+ "__loader__",
469
+ "__spec__",
470
+ "__file__",
471
+ "__cached__",
472
+ "__builtins__",
473
+ ]
474
+
475
+
476
+ class ImportFromTracker:
477
+ def __init__(self, imports_froms: dict, symbol_map: dict):
478
+ self.imports_froms = imports_froms
479
+ # symbol_map maps original_name -> list of resolved_names
480
+ self.symbol_map = {}
481
+ if symbol_map:
482
+ for module_name, mappings in symbol_map.items():
483
+ self.symbol_map[module_name] = {}
484
+ for original_name, resolved_names in mappings.items():
485
+ if isinstance(resolved_names, list):
486
+ self.symbol_map[module_name][original_name] = resolved_names[:]
487
+ else:
488
+ self.symbol_map[module_name][original_name] = [resolved_names]
489
+ else:
490
+ self.symbol_map = symbol_map or {}
491
+
492
+ def add_import(
493
+ self, module_name: str, original_name: str, resolved_name: str
494
+ ) -> None:
495
+ """Add an import, handling conflicts with existing imports.
496
+
497
+ This method is called after successful code execution, so we know the import is valid.
498
+ """
499
+ if module_name not in self.imports_froms:
500
+ self.imports_froms[module_name] = []
501
+ if module_name not in self.symbol_map:
502
+ self.symbol_map[module_name] = {}
503
+
504
+ # Check if there's already a different mapping for the same resolved_name from a different original_name
505
+ # We need to remove any conflicting mappings
506
+ for orig_name, res_names in list(self.symbol_map[module_name].items()):
507
+ if resolved_name in res_names and orig_name != original_name:
508
+ # Remove the conflicting resolved_name from the other original_name's list
509
+ res_names.remove(resolved_name)
510
+ if (
511
+ not res_names
512
+ ): # If the list is now empty, remove the original_name entirely
513
+ if orig_name in self.imports_froms[module_name]:
514
+ self.imports_froms[module_name].remove(orig_name)
515
+ del self.symbol_map[module_name][orig_name]
516
+
517
+ # Add the new mapping
518
+ if original_name not in self.imports_froms[module_name]:
519
+ self.imports_froms[module_name].append(original_name)
520
+
521
+ if original_name not in self.symbol_map[module_name]:
522
+ self.symbol_map[module_name][original_name] = []
523
+
524
+ # Add the resolved_name if it's not already in the list
525
+ if resolved_name not in self.symbol_map[module_name][original_name]:
526
+ self.symbol_map[module_name][original_name].append(resolved_name)
527
+
528
+
529
+ def append_obj(module, d, name, obj, autoload=False):
530
+ in_module = hasattr(obj, "__module__") and obj.__module__ == module.__name__
531
+ if autoload:
532
+ # check needed for module global built-ins
533
+ if not in_module and name in mod_attrs:
534
+ return False
535
+ else:
536
+ if not in_module:
537
+ return False
538
+
539
+ key = (module.__name__, name)
540
+ try:
541
+ d.setdefault(key, []).append(weakref.ref(obj))
542
+ except TypeError:
543
+ pass
544
+ return True
545
+
546
+
547
+ def superreload(
548
+ module, reload=reload, old_objects=None, shell=None, import_from_tracker=None
549
+ ):
550
+ """Enhanced version of the builtin reload function.
551
+
552
+ superreload remembers objects previously in the module, and
553
+
554
+ - upgrades the class dictionary of every old class in the module
555
+ - upgrades the code object of every old function and method
556
+ - clears the module's namespace before reloading
557
+
558
+ """
559
+ if old_objects is None:
560
+ old_objects = {}
561
+
562
+ # collect old objects in the module
563
+ for name, obj in list(module.__dict__.items()):
564
+ if not append_obj(module, old_objects, name, obj):
565
+ continue
566
+ key = (module.__name__, name)
567
+ try:
568
+ old_objects.setdefault(key, []).append(weakref.ref(obj))
569
+ except TypeError:
570
+ pass
571
+
572
+ # reload module
573
+ try:
574
+ # clear namespace first from old cruft
575
+ old_dict = module.__dict__.copy()
576
+ old_name = module.__name__
577
+ module.__dict__.clear()
578
+ module.__dict__["__name__"] = old_name
579
+ module.__dict__["__loader__"] = old_dict["__loader__"]
580
+ except (TypeError, AttributeError, KeyError):
581
+ pass
582
+
583
+ try:
584
+ module = reload(module)
585
+ except:
586
+ # restore module dictionary on failed reload
587
+ module.__dict__.update(old_dict)
588
+ raise
589
+
590
+ for name, new_obj in list(module.__dict__.items()):
591
+ key = (module.__name__, name)
592
+ if key not in old_objects:
593
+ # here 'shell' acts both as a flag and as an output var
594
+ imports_froms = (
595
+ import_from_tracker.imports_froms if import_from_tracker else None
596
+ )
597
+ symbol_map = import_from_tracker.symbol_map if import_from_tracker else None
598
+ if (
599
+ shell is None
600
+ or name == "Enum"
601
+ or not append_obj(module, old_objects, name, new_obj, True)
602
+ or (
603
+ imports_froms
604
+ and module.__name__ in imports_froms
605
+ and "*" not in imports_froms[module.__name__]
606
+ and name not in imports_froms[module.__name__]
607
+ )
608
+ ):
609
+ continue
610
+
611
+ # Handle symbol mapping - now supporting multiple resolved names per original name
612
+ if symbol_map and name in symbol_map.get(module.__name__, {}):
613
+ resolved_names = symbol_map.get(module.__name__, {})[name]
614
+ for resolved_name in resolved_names:
615
+ shell.user_ns[resolved_name] = new_obj
616
+ else:
617
+ shell.user_ns[name] = new_obj
618
+
619
+ new_refs = []
620
+ for old_ref in old_objects[key]:
621
+ old_obj = old_ref()
622
+ if old_obj is None:
623
+ continue
624
+ new_refs.append(old_ref)
625
+ update_generic(old_obj, new_obj)
626
+
627
+ if new_refs:
628
+ old_objects[key] = new_refs
629
+ else:
630
+ del old_objects[key]
631
+
632
+ return module
633
+
634
+
635
+ # ------------------------------------------------------------------------------
636
+ # IPython connectivity
637
+ # ------------------------------------------------------------------------------
638
+
639
+
640
+ @magics_class
641
+ class AutoreloadMagics(Magics):
642
+ def __init__(self, *a, **kw):
643
+ super().__init__(*a, **kw)
644
+ self._reloader = ModuleReloader(self.shell)
645
+ self._reloader.check_all = False
646
+ self._reloader.autoload_obj = False
647
+ self.loaded_modules = set(sys.modules)
648
+
649
+ @line_magic
650
+ @magic_arguments.magic_arguments()
651
+ @magic_arguments.argument(
652
+ "mode",
653
+ type=str,
654
+ default="now",
655
+ nargs="?",
656
+ help="""blank or 'now' - Reload all modules (except those excluded by %%aimport)
657
+ automatically now.
658
+
659
+ '0' or 'off' - Disable automatic reloading.
660
+
661
+ '1' or 'explicit' - Reload only modules imported with %%aimport every
662
+ time before executing the Python code typed.
663
+
664
+ '2' or 'all' - Reload all modules (except those excluded by %%aimport)
665
+ every time before executing the Python code typed.
666
+
667
+ '3' or 'complete' - Same as 2/all, but also adds any new
668
+ objects in the module.
669
+
670
+ By default, a newer autoreload algorithm that diffs the module's source code
671
+ with the previous version and only reloads changed parts is applied for modes
672
+ 2 and below. To use the original algorithm, add the `-` suffix to the mode,
673
+ e.g. '%autoreload 2-', or pass in --full.
674
+ """,
675
+ )
676
+ @magic_arguments.argument(
677
+ "-p",
678
+ "--print",
679
+ action="store_true",
680
+ default=False,
681
+ help="Show autoreload activity using `print` statements",
682
+ )
683
+ @magic_arguments.argument(
684
+ "-l",
685
+ "--log",
686
+ action="store_true",
687
+ default=False,
688
+ help="Show autoreload activity using the logger",
689
+ )
690
+ @magic_arguments.argument(
691
+ "--hide-errors",
692
+ action="store_true",
693
+ default=False,
694
+ help="Hide autoreload errors",
695
+ )
696
+ @magic_arguments.argument(
697
+ "--full",
698
+ action="store_true",
699
+ default=False,
700
+ help="Don't ever use new diffing algorithm",
701
+ )
702
+ def autoreload(self, line=""):
703
+ r"""%autoreload => Reload modules automatically
704
+
705
+ %autoreload or %autoreload now
706
+ Reload all modules (except those excluded by %aimport) automatically
707
+ now.
708
+
709
+ %autoreload 0 or %autoreload off
710
+ Disable automatic reloading.
711
+
712
+ %autoreload 1 or %autoreload explicit
713
+ Reload only modules imported with %aimport every time before executing
714
+ the Python code typed.
715
+
716
+ %autoreload 2 or %autoreload all
717
+ Reload all modules (except those excluded by %aimport) every time
718
+ before executing the Python code typed.
719
+
720
+ %autoreload 3 or %autoreload complete
721
+ Same as 2/all, but also but also adds any new objects in the module. See
722
+ unit test at IPython/extensions/tests/test_autoreload.py::test_autoload_newly_added_objects
723
+
724
+ The optional arguments --print and --log control display of autoreload activity. The default
725
+ is to act silently; --print (or -p) will print out the names of modules that are being
726
+ reloaded, and --log (or -l) outputs them to the log at INFO level.
727
+
728
+ The optional argument --hide-errors hides any errors that can happen when trying to
729
+ reload code.
730
+
731
+ Reloading Python modules in a reliable way is in general
732
+ difficult, and unexpected things may occur. %autoreload tries to
733
+ work around common pitfalls by replacing function code objects and
734
+ parts of classes previously in the module with new versions. This
735
+ makes the following things to work:
736
+
737
+ - Functions and classes imported via 'from xxx import foo' are upgraded
738
+ to new versions when 'xxx' is reloaded.
739
+
740
+ - Methods and properties of classes are upgraded on reload, so that
741
+ calling 'c.foo()' on an object 'c' created before the reload causes
742
+ the new code for 'foo' to be executed.
743
+
744
+ Some of the known remaining caveats are:
745
+
746
+ - Replacing code objects does not always succeed: changing a @property
747
+ in a class to an ordinary method or a method to a member variable
748
+ can cause problems (but in old objects only).
749
+
750
+ - Functions that are removed (eg. via monkey-patching) from a module
751
+ before it is reloaded are not upgraded.
752
+
753
+ - C extension modules cannot be reloaded, and so cannot be
754
+ autoreloaded.
755
+
756
+ """
757
+ args = magic_arguments.parse_argstring(self.autoreload, line)
758
+ mode = args.mode.lower()
759
+
760
+ enable_deduperreload = not args.full
761
+ if mode.endswith("-"):
762
+ enable_deduperreload = False
763
+ mode = mode[:-1]
764
+ self._reloader.deduper_reloader.enabled = enable_deduperreload
765
+
766
+ p = print
767
+
768
+ logger = logging.getLogger("autoreload")
769
+
770
+ l = logger.info
771
+
772
+ def pl(msg):
773
+ p(msg)
774
+ l(msg)
775
+
776
+ if args.print is False and args.log is False:
777
+ self._reloader._report = lambda msg: None
778
+ elif args.print is True:
779
+ if args.log is True:
780
+ self._reloader._report = pl
781
+ else:
782
+ self._reloader._report = p
783
+ elif args.log is True:
784
+ self._reloader._report = l
785
+
786
+ self._reloader.hide_errors = args.hide_errors
787
+
788
+ if mode == "" or mode == "now":
789
+ self._reloader.check(True)
790
+ elif mode == "0" or mode == "off":
791
+ self._reloader.enabled = False
792
+ elif mode == "1" or mode == "explicit":
793
+ self._reloader.enabled = True
794
+ self._reloader.check_all = False
795
+ self._reloader.autoload_obj = False
796
+ elif mode == "2" or mode == "all":
797
+ self._reloader.enabled = True
798
+ self._reloader.check_all = True
799
+ self._reloader.autoload_obj = False
800
+ elif mode == "3" or mode == "complete":
801
+ self._reloader.enabled = True
802
+ self._reloader.check_all = True
803
+ self._reloader.autoload_obj = True
804
+ else:
805
+ raise ValueError(f'Unrecognized autoreload mode "{mode}".')
806
+
807
+ @line_magic
808
+ def aimport(self, parameter_s="", stream=None):
809
+ """%aimport => Import modules for automatic reloading.
810
+
811
+ %aimport
812
+ List modules to automatically import and not to import.
813
+
814
+ %aimport foo
815
+ Import module 'foo' and mark it to be autoreloaded for %autoreload explicit
816
+
817
+ %aimport foo, bar
818
+ Import modules 'foo', 'bar' and mark them to be autoreloaded for %autoreload explicit
819
+
820
+ %aimport -foo, bar
821
+ Mark module 'foo' to not be autoreloaded for %autoreload explicit, all, or complete, and 'bar'
822
+ to be autoreloaded for mode explicit.
823
+ """
824
+ modname = parameter_s
825
+ if not modname:
826
+ to_reload = sorted(self._reloader.modules.keys())
827
+ to_skip = sorted(self._reloader.skip_modules.keys())
828
+ if stream is None:
829
+ stream = sys.stdout
830
+ if self._reloader.check_all:
831
+ stream.write("Modules to reload:\nall-except-skipped\n")
832
+ else:
833
+ stream.write("Modules to reload:\n%s\n" % " ".join(to_reload))
834
+ stream.write("\nModules to skip:\n%s\n" % " ".join(to_skip))
835
+ else:
836
+ for _module in [_.strip() for _ in modname.split(",")]:
837
+ if _module.startswith("-"):
838
+ _module = _module[1:].strip()
839
+ self._reloader.mark_module_skipped(_module)
840
+ else:
841
+ top_module, top_name = self._reloader.aimport_module(_module)
842
+
843
+ # Inject module to user namespace
844
+ self.shell.push({top_name: top_module})
845
+
846
+ def pre_run_cell(self, info):
847
+ # Store the execution info for later use in post_execute_hook
848
+ self._last_execution_info = info
849
+
850
+ if self._reloader.enabled:
851
+ try:
852
+ self._reloader.check()
853
+ except:
854
+ pass
855
+
856
+ def post_execute_hook(self):
857
+ """Cache the modification times of any modules imported in this execution and track imports"""
858
+
859
+ # Track imports from the recently executed code if autoreload 3 is enabled
860
+ if self._reloader.enabled and self._reloader.autoload_obj:
861
+ # Use the stored execution info
862
+ if (
863
+ hasattr(self, "_last_execution_info")
864
+ and self._last_execution_info
865
+ and self._last_execution_info.transformed_cell
866
+ ):
867
+ self._track_imports_from_code(
868
+ self._last_execution_info.transformed_cell
869
+ )
870
+
871
+ newly_loaded_modules = set(sys.modules) - self.loaded_modules
872
+ for modname in newly_loaded_modules:
873
+ _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
874
+ if pymtime is not None:
875
+ self._reloader.modules_mtimes[modname] = pymtime
876
+
877
+ self.loaded_modules.update(newly_loaded_modules)
878
+
879
+ def _track_imports_from_code(self, code: str) -> None:
880
+ """Track import statements from executed code"""
881
+ try:
882
+ tree = ast.parse(code)
883
+
884
+ for node in ast.walk(tree):
885
+ # Handle "from X import Y" style imports
886
+ if isinstance(node, ast.ImportFrom):
887
+ mod = node.module
888
+
889
+ # Skip relative imports that don't have a module name
890
+ if mod is None:
891
+ continue
892
+
893
+ for name in node.names:
894
+ # name.name is going to be actual name that we want to import from module
895
+ # name.asname is Z in the case of from X import Y as Z
896
+ # we should update Z in the shell in this situation, so track it too.
897
+ original_name = name.name
898
+ resolved_name = name.asname if name.asname else name.name
899
+
900
+ # Since the code executed successfully, we know this import is valid
901
+ self._reloader.import_from_tracker.add_import(
902
+ mod, original_name, resolved_name
903
+ )
904
+ except (SyntaxError, ValueError):
905
+ # If there's a syntax error, skip import tracking
906
+ # (though this shouldn't happen since the code already executed successfully)
907
+ pass
908
+
909
+
910
+ def load_ipython_extension(ip):
911
+ """Load the extension in IPython."""
912
+ auto_reload = AutoreloadMagics(ip)
913
+ ip.register_magics(auto_reload)
914
+ ip.events.register("pre_run_cell", auto_reload.pre_run_cell)
915
+ ip.events.register("post_execute", auto_reload.post_execute_hook)
lib/python3.12/site-packages/IPython/extensions/deduperreload/__init__.py ADDED
File without changes
lib/python3.12/site-packages/IPython/extensions/deduperreload/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (211 Bytes). View file
 
lib/python3.12/site-packages/IPython/extensions/deduperreload/__pycache__/deduperreload.cpython-312.pyc ADDED
Binary file (33.2 kB). View file
 
lib/python3.12/site-packages/IPython/extensions/deduperreload/__pycache__/deduperreload_patching.cpython-312.pyc ADDED
Binary file (6.74 kB). View file
 
lib/python3.12/site-packages/IPython/extensions/deduperreload/deduperreload.py ADDED
@@ -0,0 +1,637 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import ast
3
+ import builtins
4
+ import contextlib
5
+ import itertools
6
+ import logging
7
+ import os
8
+ import pickle
9
+ import platform
10
+ import sys
11
+ import textwrap
12
+ from types import ModuleType
13
+ from typing import TYPE_CHECKING, Any, NamedTuple, cast
14
+ from collections.abc import Generator, Iterable
15
+
16
+ from IPython.extensions.deduperreload.deduperreload_patching import (
17
+ DeduperReloaderPatchingMixin,
18
+ )
19
+
20
+ if TYPE_CHECKING:
21
+ TDefinitionAst = (
22
+ ast.FunctionDef
23
+ | ast.AsyncFunctionDef
24
+ | ast.Import
25
+ | ast.ImportFrom
26
+ | ast.Assign
27
+ | ast.AnnAssign
28
+ )
29
+
30
+
31
+ def get_module_file_name(module: ModuleType | str) -> str | None:
32
+ """Returns the module's file path, or the empty string if it's inaccessible"""
33
+ if (mod := sys.modules.get(module) if isinstance(module, str) else module) is None:
34
+ return ""
35
+ return getattr(mod, "__file__", "") or ""
36
+
37
+
38
+ def compare_ast(node1: ast.AST | list[ast.AST], node2: ast.AST | list[ast.AST]) -> bool:
39
+ """Checks if node1 and node2 have identical AST structure/values, apart from some attributes"""
40
+ if type(node1) is not type(node2):
41
+ return False
42
+
43
+ if isinstance(node1, ast.AST):
44
+ for k, v in node1.__dict__.items():
45
+ if k in (
46
+ "lineno",
47
+ "end_lineno",
48
+ "col_offset",
49
+ "end_col_offset",
50
+ "ctx",
51
+ "parent",
52
+ ):
53
+ continue
54
+ if not hasattr(node2, k) or not compare_ast(v, getattr(node2, k)):
55
+ return False
56
+ return True
57
+
58
+ elif isinstance(node1, list) and isinstance( # type:ignore [redundant-expr]
59
+ node2, list
60
+ ):
61
+ return len(node1) == len(node2) and all(
62
+ compare_ast(n1, n2) for n1, n2 in zip(node1, node2)
63
+ )
64
+ else:
65
+ return node1 == node2
66
+
67
+
68
+ class DependencyNode(NamedTuple):
69
+ """
70
+ Each node represents a function.
71
+ qualified_name: string which represents the namespace/name of the function
72
+ abstract_syntax_tree: subtree of the overall module which corresponds to this function
73
+
74
+ qualified_name is of the structure: (namespace1, namespace2, ..., name)
75
+
76
+ For example, foo() in the following would be represented as (A, B, foo):
77
+
78
+ class A:
79
+ class B:
80
+ def foo():
81
+ pass
82
+ """
83
+
84
+ qualified_name: tuple[str, ...]
85
+ abstract_syntax_tree: ast.AST
86
+
87
+
88
+ class GatherResult(NamedTuple):
89
+ import_defs: list[tuple[tuple[str, ...], ast.Import | ast.ImportFrom]] = []
90
+ assign_defs: list[tuple[tuple[str, ...], ast.Assign | ast.AnnAssign]] = []
91
+ function_defs: list[
92
+ tuple[tuple[str, ...], ast.FunctionDef | ast.AsyncFunctionDef]
93
+ ] = []
94
+ classes: dict[str, ast.ClassDef] = {}
95
+ unfixable: list[ast.AST] = []
96
+
97
+ @classmethod
98
+ def create(cls) -> GatherResult:
99
+ return cls([], [], [], {}, [])
100
+
101
+ def all_defs(self) -> Iterable[tuple[tuple[str, ...], TDefinitionAst]]:
102
+ return itertools.chain(self.import_defs, self.assign_defs, self.function_defs)
103
+
104
+ def inplace_merge(self, other: GatherResult) -> None:
105
+ self.import_defs.extend(other.import_defs)
106
+ self.assign_defs.extend(other.assign_defs)
107
+ self.function_defs.extend(other.function_defs)
108
+ self.classes.update(other.classes)
109
+ self.unfixable.extend(other.unfixable)
110
+
111
+
112
+ class ConstexprDetector(ast.NodeVisitor):
113
+ def __init__(self) -> None:
114
+ self.is_constexpr = True
115
+ self._allow_builtins_exceptions = True
116
+
117
+ @contextlib.contextmanager
118
+ def disallow_builtins_exceptions(self) -> Generator[None, None, None]:
119
+ prev_allow = self._allow_builtins_exceptions
120
+ self._allow_builtins_exceptions = False
121
+ try:
122
+ yield
123
+ finally:
124
+ self._allow_builtins_exceptions = prev_allow
125
+
126
+ def visit_Attribute(self, node: ast.Attribute) -> None:
127
+ with self.disallow_builtins_exceptions():
128
+ self.visit(node.value)
129
+
130
+ def visit_Name(self, node: ast.Name) -> None:
131
+ if self._allow_builtins_exceptions and hasattr(builtins, node.id):
132
+ return
133
+ self.is_constexpr = False
134
+
135
+ def visit(self, node: ast.AST) -> None:
136
+ if not self.is_constexpr:
137
+ # can short-circuit if we've already detected that it's not a constexpr
138
+ return
139
+ super().visit(node)
140
+
141
+ def __call__(self, node: ast.AST) -> bool:
142
+ self.is_constexpr = True
143
+ self.visit(node)
144
+ return self.is_constexpr
145
+
146
+
147
+ class AutoreloadTree:
148
+ """
149
+ Recursive data structure to keep track of reloadable functions/methods. Each object corresponds to a specific scope level.
150
+ children: classes inside given scope, maps class name to autoreload tree for that class's scope
151
+ funcs_to_autoreload: list of function names that can be autoreloaded in given scope.
152
+ new_nested_classes: Classes getting added in new autoreload cycle
153
+ """
154
+
155
+ def __init__(self) -> None:
156
+ self.children: dict[str, AutoreloadTree] = {}
157
+ self.defs_to_reload: list[tuple[tuple[str, ...], ast.AST]] = []
158
+ self.defs_to_delete: set[str] = set()
159
+ self.new_nested_classes: dict[str, ast.AST] = {}
160
+
161
+ def traverse_prefixes(self, prefixes: list[str]) -> AutoreloadTree:
162
+ """
163
+ Return ref to the AutoreloadTree at the namespace specified by prefixes
164
+ """
165
+ cur = self
166
+ for prefix in prefixes:
167
+ if prefix not in cur.children:
168
+ cur.children[prefix] = AutoreloadTree()
169
+ cur = cur.children[prefix]
170
+ return cur
171
+
172
+
173
+ class DeduperReloader(DeduperReloaderPatchingMixin):
174
+ """
175
+ This version of autoreload detects when we can leverage targeted recompilation of a subset of a module and patching
176
+ existing function/method objects to reflect these changes.
177
+
178
+ Detects what functions/methods can be reloaded by recursively comparing the old/new AST of module-level classes,
179
+ module-level classes' methods, recursing through nested classes' methods. If other changes are made, original
180
+ autoreload algorithm is called directly.
181
+ """
182
+
183
+ def __init__(self) -> None:
184
+ self._to_autoreload: AutoreloadTree = AutoreloadTree()
185
+ self.source_by_modname: dict[str, str] = {}
186
+ self.dependency_graph: dict[tuple[str, ...], list[DependencyNode]] = {}
187
+ self._enabled = True
188
+
189
+ @property
190
+ def enabled(self) -> bool:
191
+ return self._enabled and platform.python_implementation() == "CPython"
192
+
193
+ @enabled.setter
194
+ def enabled(self, value: bool) -> None:
195
+ self._enabled = value
196
+
197
+ def update_sources(self) -> None:
198
+ """
199
+ Update dictionary source_by_modname with current modules' source codes.
200
+ """
201
+ if not self.enabled:
202
+ return
203
+ for new_modname in sys.modules.keys() - self.source_by_modname.keys():
204
+ new_module = sys.modules[new_modname]
205
+ if (
206
+ (fname := get_module_file_name(new_module)) is None
207
+ or "site-packages" in fname
208
+ or "dist-packages" in fname
209
+ or not os.access(fname, os.R_OK)
210
+ ):
211
+ self.source_by_modname[new_modname] = ""
212
+ continue
213
+ # Skip binary files (e.g., .so, .pyd, .pyo)
214
+ if not fname.endswith(".py"):
215
+ self.source_by_modname[new_modname] = ""
216
+ continue
217
+ try:
218
+ with open(fname, "r") as f:
219
+ self.source_by_modname[new_modname] = f.read()
220
+ except Exception as e:
221
+ logger = logging.getLogger("autoreload")
222
+ logger.exception(
223
+ f"Failed to read module file '{fname}' for module '{new_modname}': {type(e).__name__}"
224
+ )
225
+ self.source_by_modname[new_modname] = ""
226
+
227
+ constexpr_detector = ConstexprDetector()
228
+
229
+ @staticmethod
230
+ def is_enum_subclass(node: ast.Module | ast.ClassDef) -> bool:
231
+ if isinstance(node, ast.Module):
232
+ return False
233
+ for base in node.bases:
234
+ if isinstance(base, ast.Name) and base.id == "Enum":
235
+ return True
236
+ elif (
237
+ isinstance(base, ast.Attribute)
238
+ and base.attr == "Enum"
239
+ and isinstance(base.value, ast.Name)
240
+ and base.value.id == "enum"
241
+ ):
242
+ return True
243
+ return False
244
+
245
+ @classmethod
246
+ def is_constexpr_assign(
247
+ cls, node: ast.AST, parent_node: ast.Module | ast.ClassDef
248
+ ) -> bool:
249
+ if not isinstance(node, (ast.Assign, ast.AnnAssign)) or node.value is None:
250
+ return False
251
+ if cls.is_enum_subclass(parent_node):
252
+ return False
253
+ for target in node.targets if isinstance(node, ast.Assign) else [node.target]:
254
+ if not isinstance(target, ast.Name):
255
+ return False
256
+ return cls.constexpr_detector(node.value)
257
+
258
+ @classmethod
259
+ def _gather_children(
260
+ cls, body: list[ast.stmt], parent_node: ast.Module | ast.ClassDef
261
+ ) -> GatherResult:
262
+ """
263
+ Given list of ast elements, return:
264
+ 1. dict mapping function names to their ASTs.
265
+ 2. dict mapping class names to their ASTs.
266
+ 3. list of any other ASTs.
267
+ """
268
+ result = GatherResult.create()
269
+ for ast_node in body:
270
+ ast_elt: ast.expr | ast.stmt = ast_node
271
+ while isinstance(ast_elt, ast.Expr):
272
+ ast_elt = ast_elt.value
273
+ if isinstance(ast_elt, (ast.FunctionDef, ast.AsyncFunctionDef)):
274
+ result.function_defs.append(((ast_elt.name,), ast_elt))
275
+ elif isinstance(ast_elt, (ast.Import, ast.ImportFrom)):
276
+ result.import_defs.append(
277
+ (tuple(name.asname or name.name for name in ast_elt.names), ast_elt)
278
+ )
279
+ elif isinstance(ast_elt, ast.ClassDef):
280
+ result.classes[ast_elt.name] = ast_elt
281
+ elif isinstance(ast_elt, ast.If):
282
+ result.unfixable.append(ast_elt.test)
283
+ result.inplace_merge(cls._gather_children(ast_elt.body, parent_node))
284
+ result.inplace_merge(cls._gather_children(ast_elt.orelse, parent_node))
285
+ elif isinstance(ast_elt, (ast.AsyncWith, ast.With)):
286
+ result.unfixable.extend(ast_elt.items)
287
+ result.inplace_merge(cls._gather_children(ast_elt.body, parent_node))
288
+ elif isinstance(ast_elt, ast.Try):
289
+ result.inplace_merge(cls._gather_children(ast_elt.body, parent_node))
290
+ result.inplace_merge(cls._gather_children(ast_elt.orelse, parent_node))
291
+ result.inplace_merge(
292
+ cls._gather_children(ast_elt.finalbody, parent_node)
293
+ )
294
+ for handler in ast_elt.handlers:
295
+ if handler.type is not None:
296
+ result.unfixable.append(handler.type)
297
+ result.inplace_merge(
298
+ cls._gather_children(handler.body, parent_node)
299
+ )
300
+ elif not isinstance(ast_elt, (ast.Constant, ast.Pass)):
301
+ if cls.is_constexpr_assign(ast_elt, parent_node):
302
+ assert isinstance(ast_elt, (ast.Assign, ast.AnnAssign))
303
+ targets = (
304
+ ast_elt.targets
305
+ if isinstance(ast_elt, ast.Assign)
306
+ else [ast_elt.target]
307
+ )
308
+ result.assign_defs.append(
309
+ (
310
+ tuple(cast(ast.Name, target).id for target in targets),
311
+ ast_elt,
312
+ )
313
+ )
314
+ else:
315
+ result.unfixable.append(ast_elt)
316
+ return result
317
+
318
+ def detect_autoreload(
319
+ self,
320
+ old_node: ast.Module | ast.ClassDef,
321
+ new_node: ast.Module | ast.ClassDef,
322
+ prefixes: list[str] | None = None,
323
+ ) -> bool:
324
+ """
325
+ Returns
326
+ -------
327
+ `True` if we can run our targeted autoreload algorithm safely.
328
+ `False` if we should instead use IPython's original autoreload implementation.
329
+ """
330
+ if not self.enabled:
331
+ return False
332
+ prefixes = prefixes or []
333
+
334
+ old_result = self._gather_children(old_node.body, old_node)
335
+ new_result = self._gather_children(new_node.body, new_node)
336
+ old_defs_by_name: dict[str, ast.AST] = {
337
+ name: ast_def for names, ast_def in old_result.all_defs() for name in names
338
+ }
339
+ new_defs_by_name: dict[str, ast.AST] = {
340
+ name: ast_def for names, ast_def in new_result.all_defs() for name in names
341
+ }
342
+
343
+ if not compare_ast(old_result.unfixable, new_result.unfixable):
344
+ return False
345
+
346
+ cur = self._to_autoreload.traverse_prefixes(prefixes)
347
+ for names, new_ast_def in new_result.all_defs():
348
+ names_to_reload = []
349
+ for name in names:
350
+ if new_defs_by_name[name] is not new_ast_def:
351
+ continue
352
+ if name not in old_defs_by_name or not compare_ast(
353
+ new_ast_def, old_defs_by_name[name]
354
+ ):
355
+ names_to_reload.append(name)
356
+ if names_to_reload:
357
+ cur.defs_to_reload.append((tuple(names), new_ast_def))
358
+ cur.defs_to_delete |= set(old_defs_by_name.keys()) - set(
359
+ new_defs_by_name.keys()
360
+ )
361
+ for name, new_ast_def_class in new_result.classes.items():
362
+ if name not in old_result.classes:
363
+ cur.new_nested_classes[name] = new_ast_def_class
364
+ elif not compare_ast(
365
+ new_ast_def_class, old_result.classes[name]
366
+ ) and not self.detect_autoreload(
367
+ old_result.classes[name], new_ast_def_class, prefixes + [name]
368
+ ):
369
+ return False
370
+ return True
371
+
372
+ def _check_dependents(self) -> bool:
373
+ """
374
+ If a decorator function is modified, we should similarly reload the functions which are decorated by this
375
+ decorator. Iterate through the Dependency Graph to find such cases in the given AutoreloadTree.
376
+ """
377
+ for node in self._check_dependents_inner():
378
+ self._add_node_to_autoreload_tree(node)
379
+ return True
380
+
381
+ def _add_node_to_autoreload_tree(self, node: DependencyNode) -> None:
382
+ """
383
+ Given a node of the dependency graph, add decorator dependencies to the autoreload tree.
384
+ """
385
+ if len(node.qualified_name) == 0:
386
+ return
387
+ cur = self._to_autoreload.traverse_prefixes(list(node.qualified_name[:-1]))
388
+ if node.abstract_syntax_tree is not None:
389
+ cur.defs_to_reload.append(
390
+ ((node.qualified_name[-1],), node.abstract_syntax_tree)
391
+ )
392
+
393
+ def _check_dependents_inner(
394
+ self, prefixes: list[str] | None = None
395
+ ) -> list[DependencyNode]:
396
+ prefixes = prefixes or []
397
+ cur = self._to_autoreload.traverse_prefixes(prefixes)
398
+ ans = []
399
+ for (func_name, *_), _ in cur.defs_to_reload:
400
+ node = tuple(prefixes + [func_name])
401
+ ans.extend(self._gen_dependents(node))
402
+ for class_name in cur.new_nested_classes:
403
+ ans.extend(self._check_dependents_inner(prefixes + [class_name]))
404
+ return ans
405
+
406
+ def _gen_dependents(self, qualname: tuple[str, ...]) -> list[DependencyNode]:
407
+ ans = []
408
+ if qualname not in self.dependency_graph:
409
+ return []
410
+ for elt in self.dependency_graph[qualname]:
411
+ ans.extend(self._gen_dependents(elt.qualified_name))
412
+ ans.append(elt)
413
+ return ans
414
+
415
+ def _patch_namespace_inner(
416
+ self, ns: ModuleType | type, prefixes: list[str] | None = None
417
+ ) -> bool:
418
+ """
419
+ This function patches module functions and methods. Specifically, only objects with their name in
420
+ self.to_autoreload will be considered for patching. If an object has been marked to be autoreloaded,
421
+ new_source_code gets executed in the old version's global environment. Then, replace the old function's
422
+ attributes with the new function's attributes.
423
+ """
424
+ prefixes = prefixes or []
425
+ cur = self._to_autoreload.traverse_prefixes(prefixes)
426
+ namespace_to_check = ns
427
+ for prefix in prefixes:
428
+ namespace_to_check = namespace_to_check.__dict__[prefix]
429
+ seen_names: set[str] = set()
430
+ for names, new_ast_def in cur.defs_to_reload:
431
+ if len(names) == 1 and names[0] in seen_names:
432
+ continue
433
+ seen_names.update(names)
434
+ local_env: dict[str, Any] = {}
435
+ if (
436
+ isinstance(new_ast_def, (ast.FunctionDef, ast.AsyncFunctionDef))
437
+ and (name := names[0]) in namespace_to_check.__dict__
438
+ ):
439
+ assert len(names) == 1
440
+ to_patch_to = namespace_to_check.__dict__[name]
441
+ if isinstance(to_patch_to, (staticmethod, classmethod)):
442
+ to_patch_to = to_patch_to.__func__
443
+ # exec new source code using old function's (obj) globals environment.
444
+ func_code = textwrap.dedent(ast.unparse(new_ast_def))
445
+ if is_method := (len(prefixes) > 0):
446
+ func_code = "class __autoreload_class__:\n" + textwrap.indent(
447
+ func_code, " "
448
+ )
449
+ global_env = ns.__dict__
450
+ if not isinstance(global_env, dict):
451
+ global_env = dict(global_env)
452
+ # Compile with correct filename to preserve in traceback
453
+ filename = (
454
+ getattr(to_patch_to, "__code__", None)
455
+ and to_patch_to.__code__.co_filename
456
+ or "<string>"
457
+ )
458
+ func_asts = [ast.parse(func_code)]
459
+ if len(cast(ast.FunctionDef, func_asts[0].body[0]).decorator_list) > 0:
460
+ without_decorator_list = pickle.loads(pickle.dumps(func_asts[0]))
461
+ cast(
462
+ ast.FunctionDef, without_decorator_list.body[0]
463
+ ).decorator_list = []
464
+ func_asts.insert(0, without_decorator_list)
465
+ for func_ast in func_asts:
466
+ compiled_code = compile(
467
+ func_ast, filename, mode="exec", dont_inherit=True
468
+ )
469
+ exec(compiled_code, global_env, local_env) # type: ignore[arg-type]
470
+ # local_env contains the function exec'd from new version of function
471
+ if is_method:
472
+ to_patch_from = getattr(local_env["__autoreload_class__"], name)
473
+ else:
474
+ to_patch_from = local_env[name]
475
+ if isinstance(to_patch_from, (staticmethod, classmethod)):
476
+ to_patch_from = to_patch_from.__func__
477
+ if isinstance(to_patch_to, property) and isinstance(
478
+ to_patch_from, property
479
+ ):
480
+ for attr in ("fget", "fset", "fdel"):
481
+ if (
482
+ getattr(to_patch_to, attr) is None
483
+ or getattr(to_patch_from, attr) is None
484
+ ):
485
+ self.try_patch_attr(to_patch_to, to_patch_from, attr)
486
+ else:
487
+ self.patch_function(
488
+ getattr(to_patch_to, attr),
489
+ getattr(to_patch_from, attr),
490
+ is_method,
491
+ )
492
+ elif not isinstance(to_patch_to, property) and not isinstance(
493
+ to_patch_from, property
494
+ ):
495
+ self.patch_function(to_patch_to, to_patch_from, is_method)
496
+ else:
497
+ raise ValueError(
498
+ "adding or removing property decorations not supported"
499
+ )
500
+ else:
501
+ exec(
502
+ ast.unparse(new_ast_def),
503
+ ns.__dict__ | namespace_to_check.__dict__,
504
+ local_env,
505
+ )
506
+ for name in names:
507
+ setattr(namespace_to_check, name, local_env[name])
508
+ cur.defs_to_reload.clear()
509
+ for name in cur.defs_to_delete:
510
+ try:
511
+ delattr(namespace_to_check, name)
512
+ except (AttributeError, TypeError, ValueError):
513
+ # give up on deleting the attribute, let the stale one dangle
514
+ pass
515
+ cur.defs_to_delete.clear()
516
+ for class_name, class_ast_node in cur.new_nested_classes.items():
517
+ local_env_class: dict[str, Any] = {}
518
+ exec(
519
+ ast.unparse(class_ast_node),
520
+ ns.__dict__ | namespace_to_check.__dict__,
521
+ local_env_class,
522
+ )
523
+ setattr(namespace_to_check, class_name, local_env_class[class_name])
524
+ cur.new_nested_classes.clear()
525
+ for class_name in cur.children.keys():
526
+ if not self._patch_namespace(ns, prefixes + [class_name]):
527
+ return False
528
+ cur.children.clear()
529
+ return True
530
+
531
+ def _patch_namespace(
532
+ self, ns: ModuleType | type, prefixes: list[str] | None = None
533
+ ) -> bool:
534
+ """
535
+ Wrapper for patching all elements in a namespace as specified by the to_autoreload member variable.
536
+ Returns `true` if patching was successful, and `false` if unsuccessful.
537
+ """
538
+ try:
539
+ return self._patch_namespace_inner(ns, prefixes=prefixes)
540
+ except Exception:
541
+ return False
542
+
543
+ def maybe_reload_module(self, module: ModuleType) -> bool:
544
+ """
545
+ Uses Deduperreload to try to update a module.
546
+ Returns `true` on success and `false` on failure.
547
+ """
548
+ if not self.enabled:
549
+ return False
550
+ if not (modname := getattr(module, "__name__", None)):
551
+ return False
552
+ if (fname := get_module_file_name(module)) is None:
553
+ return False
554
+ try:
555
+ with open(fname, "r") as f:
556
+ new_source_code = f.read()
557
+ except Exception as e:
558
+ logger = logging.getLogger("autoreload")
559
+ logger.exception(
560
+ f"Failed to read module file '{fname}' for module '{modname}': {type(e).__name__}"
561
+ )
562
+ return False
563
+ patched_flag = False
564
+ if old_source_code := self.source_by_modname.get(modname):
565
+ # get old/new module ast
566
+ try:
567
+ old_module_ast = ast.parse(old_source_code)
568
+ new_module_ast = ast.parse(new_source_code)
569
+ except Exception:
570
+ return False
571
+ # detect if we are able to use our autoreload algorithm
572
+ ctx = contextlib.suppress()
573
+ with ctx:
574
+ self._build_dependency_graph(new_module_ast)
575
+ if (
576
+ self.detect_autoreload(old_module_ast, new_module_ast)
577
+ and self._check_dependents()
578
+ and self._patch_namespace(module)
579
+ ):
580
+ patched_flag = True
581
+
582
+ self.source_by_modname[modname] = new_source_code
583
+ self._to_autoreload = AutoreloadTree()
584
+ return patched_flag
585
+
586
+ def _separate_name(
587
+ self,
588
+ decorator: ast.Attribute | ast.Name | ast.Call | ast.expr,
589
+ accept_calls: bool,
590
+ ) -> list[str] | None:
591
+ """
592
+ Generates a qualified name for a given decorator by finding its relative namespace.
593
+ """
594
+ if isinstance(decorator, ast.Name):
595
+ return [decorator.id]
596
+ elif isinstance(decorator, ast.Call):
597
+ if accept_calls:
598
+ return self._separate_name(decorator.func, False)
599
+ else:
600
+ return None
601
+ if not isinstance(decorator, ast.Attribute):
602
+ return None
603
+ if pref := self._separate_name(decorator.value, False):
604
+ return pref + [decorator.attr]
605
+ else:
606
+ return None
607
+
608
+ def _gather_dependents(
609
+ self, body: list[ast.stmt], body_prefixes: list[str] | None = None
610
+ ) -> bool:
611
+ body_prefixes = body_prefixes or []
612
+ for ast_node in body:
613
+ ast_elt: ast.expr | ast.stmt = ast_node
614
+ if isinstance(ast_elt, ast.ClassDef):
615
+ self._gather_dependents(ast_elt.body, body_prefixes + [ast_elt.name])
616
+ continue
617
+ if not isinstance(ast_elt, (ast.FunctionDef, ast.AsyncFunctionDef)):
618
+ continue
619
+ qualified_name = tuple(body_prefixes + [ast_elt.name])
620
+ cur_dependency_node = DependencyNode(qualified_name, ast_elt)
621
+ for decorator in ast_elt.decorator_list:
622
+ decorator_path = self._separate_name(decorator, True)
623
+ if not decorator_path:
624
+ continue
625
+ decorator_path_tuple = tuple(decorator_path)
626
+ self.dependency_graph.setdefault(decorator_path_tuple, []).append(
627
+ cur_dependency_node
628
+ )
629
+ return True
630
+
631
+ def _build_dependency_graph(self, new_ast: ast.Module | ast.ClassDef) -> bool:
632
+ """
633
+ Wrapper function for generating dependency graph given some AST.
634
+ Returns `true` on success. Returns `false` on failure.
635
+ Currently, only returns `true` as we do not block on failure to build this graph.
636
+ """
637
+ return self._gather_dependents(new_ast.body)
lib/python3.12/site-packages/IPython/extensions/deduperreload/deduperreload_patching.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import ctypes
3
+ import sys
4
+ from typing import Any
5
+
6
+ NOT_FOUND: object = object()
7
+ NULL: object = object()
8
+ _MAX_FIELD_SEARCH_OFFSET = 50
9
+
10
+ POINTER_N_BYTES = ctypes.sizeof(ctypes.c_void_p)
11
+
12
+
13
+ class DeduperReloaderPatchingMixin:
14
+ @staticmethod
15
+ def infer_field_offset(
16
+ obj: object,
17
+ field: str,
18
+ ) -> int:
19
+ field_value = getattr(obj, field, NOT_FOUND)
20
+ if field_value is NOT_FOUND:
21
+ return -1
22
+ obj_addr = ctypes.c_void_p.from_buffer(ctypes.py_object(obj)).value
23
+ field_addr = ctypes.c_void_p.from_buffer(ctypes.py_object(field_value)).value
24
+ if obj_addr is None or field_addr is None:
25
+ return -1
26
+ ret = -1
27
+ for offset in range(1, _MAX_FIELD_SEARCH_OFFSET):
28
+ if (
29
+ ctypes.cast(
30
+ obj_addr + POINTER_N_BYTES * offset, ctypes.POINTER(ctypes.c_void_p)
31
+ ).contents.value
32
+ == field_addr
33
+ ):
34
+ ret = offset
35
+ break
36
+ return ret
37
+
38
+ @classmethod
39
+ def try_write_readonly_attr(
40
+ cls,
41
+ obj: object,
42
+ field: str,
43
+ new_value: object,
44
+ offset: int | None = None,
45
+ ) -> None:
46
+ prev_value = getattr(obj, field, NOT_FOUND)
47
+ if prev_value is NOT_FOUND:
48
+ return
49
+ if offset is None:
50
+ offset = cls.infer_field_offset(obj, field)
51
+ if offset == -1:
52
+ return
53
+ obj_addr = ctypes.c_void_p.from_buffer(ctypes.py_object(obj)).value
54
+ if new_value is NULL:
55
+ new_value_addr: int | None = 0
56
+ else:
57
+ new_value_addr = ctypes.c_void_p.from_buffer(
58
+ ctypes.py_object(new_value)
59
+ ).value
60
+ if obj_addr is None or new_value_addr is None:
61
+ return
62
+ if prev_value is not None:
63
+ ctypes.pythonapi.Py_DecRef(ctypes.py_object(prev_value))
64
+ if new_value not in (None, NULL):
65
+ ctypes.pythonapi.Py_IncRef(ctypes.py_object(new_value))
66
+ ctypes.cast(
67
+ obj_addr + POINTER_N_BYTES * offset, ctypes.POINTER(ctypes.c_void_p)
68
+ ).contents.value = new_value_addr
69
+
70
+ @classmethod
71
+ def try_patch_readonly_attr(
72
+ cls,
73
+ old: object,
74
+ new: object,
75
+ field: str,
76
+ new_is_value: bool = False,
77
+ offset: int = -1,
78
+ ) -> None:
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_closure = []
112
+ for freevar, closure_val in zip(
113
+ to_patch_from.__code__.co_freevars or [], to_patch_from.__closure__ or []
114
+ ):
115
+ if (
116
+ callable(closure_val.cell_contents)
117
+ and freevar in to_patch_to.__code__.co_freevars
118
+ ):
119
+ new_closure.append(
120
+ to_patch_to.__closure__[
121
+ to_patch_to.__code__.co_freevars.index(freevar)
122
+ ]
123
+ )
124
+ else:
125
+ new_closure.append(closure_val)
126
+ # lambdas may complain if there is more than one freevar
127
+ cls.try_patch_attr(to_patch_to, to_patch_from, "__code__")
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
+ if to_patch_to.__closure__ is not None or to_patch_from.__closure__ is not None:
132
+ cls.try_patch_readonly_attr(
133
+ to_patch_to,
134
+ tuple(new_closure) or NULL,
135
+ "__closure__",
136
+ new_is_value=True,
137
+ offset=offset,
138
+ )
139
+ for attr in ("__defaults__", "__kwdefaults__", "__doc__", "__dict__"):
140
+ cls.try_patch_attr(to_patch_to, to_patch_from, attr)
141
+ if is_method:
142
+ cls.try_patch_readonly_attr(to_patch_to, to_patch_from, "__self__")
lib/python3.12/site-packages/IPython/extensions/storemagic.py ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ %store magic for lightweight persistence.
4
+
5
+ Stores variables, aliases and macros in IPython's database.
6
+
7
+ To automatically restore stored variables at startup, add this to your
8
+ :file:`ipython_config.py` file::
9
+
10
+ c.StoreMagics.autorestore = True
11
+ """
12
+
13
+ # Copyright (c) IPython Development Team.
14
+ # Distributed under the terms of the Modified BSD License.
15
+
16
+ import inspect, os, sys, textwrap
17
+
18
+ from IPython.core.error import UsageError
19
+ from IPython.core.magic import Magics, magics_class, line_magic
20
+ from IPython.testing.skipdoctest import skip_doctest
21
+ from traitlets import Bool
22
+
23
+
24
+ def restore_aliases(ip, alias=None):
25
+ staliases = ip.db.get('stored_aliases', {})
26
+ if alias is None:
27
+ for k,v in staliases.items():
28
+ # print("restore alias",k,v) # dbg
29
+ #self.alias_table[k] = v
30
+ ip.alias_manager.define_alias(k,v)
31
+ else:
32
+ ip.alias_manager.define_alias(alias, staliases[alias])
33
+
34
+
35
+ def refresh_variables(ip):
36
+ db = ip.db
37
+ for key in db.keys('autorestore/*'):
38
+ # strip autorestore
39
+ justkey = os.path.basename(key)
40
+ try:
41
+ obj = db[key]
42
+ except KeyError:
43
+ print("Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey)
44
+ print("The error was:", sys.exc_info()[0])
45
+ else:
46
+ # print("restored",justkey,"=",obj) # dbg
47
+ ip.user_ns[justkey] = obj
48
+
49
+
50
+ def restore_dhist(ip):
51
+ ip.user_ns['_dh'] = ip.db.get('dhist',[])
52
+
53
+
54
+ def restore_data(ip):
55
+ refresh_variables(ip)
56
+ restore_aliases(ip)
57
+ restore_dhist(ip)
58
+
59
+
60
+ @magics_class
61
+ class StoreMagics(Magics):
62
+ """Lightweight persistence for python variables.
63
+
64
+ Provides the %store magic."""
65
+
66
+ autorestore = Bool(False, help=
67
+ """If True, any %store-d variables will be automatically restored
68
+ when IPython starts.
69
+ """
70
+ ).tag(config=True)
71
+
72
+ def __init__(self, shell):
73
+ super(StoreMagics, self).__init__(shell=shell)
74
+ self.shell.configurables.append(self)
75
+ if self.autorestore:
76
+ restore_data(self.shell)
77
+
78
+ @skip_doctest
79
+ @line_magic
80
+ def store(self, parameter_s=''):
81
+ """Lightweight persistence for python variables.
82
+
83
+ Example::
84
+
85
+ In [1]: l = ['hello',10,'world']
86
+ In [2]: %store l
87
+ Stored 'l' (list)
88
+ In [3]: exit
89
+
90
+ (IPython session is closed and started again...)
91
+
92
+ ville@badger:~$ ipython
93
+ In [1]: l
94
+ NameError: name 'l' is not defined
95
+ In [2]: %store -r
96
+ In [3]: l
97
+ Out[3]: ['hello', 10, 'world']
98
+
99
+ Usage:
100
+
101
+ * ``%store`` - Show list of all variables and their current
102
+ values
103
+ * ``%store spam bar`` - Store the *current* value of the variables spam
104
+ and bar to disk
105
+ * ``%store -d spam`` - Remove the variable and its value from storage
106
+ * ``%store -z`` - Remove all variables from storage
107
+ * ``%store -r`` - Refresh all variables, aliases and directory history
108
+ from store (overwrite current vals)
109
+ * ``%store -r spam bar`` - Refresh specified variables and aliases from store
110
+ (delete current val)
111
+ * ``%store foo >a.txt`` - Store value of foo to new file a.txt
112
+ * ``%store foo >>a.txt`` - Append value of foo to file a.txt
113
+
114
+ It should be noted that if you change the value of a variable, you
115
+ need to %store it again if you want to persist the new value.
116
+
117
+ Note also that the variables will need to be pickleable; most basic
118
+ python types can be safely %store'd.
119
+
120
+ Also aliases can be %store'd across sessions.
121
+ To remove an alias from the storage, use the %unalias magic.
122
+ """
123
+
124
+ opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
125
+ args = argsl.split()
126
+ ip = self.shell
127
+ db = ip.db
128
+ # delete
129
+ if 'd' in opts:
130
+ try:
131
+ todel = args[0]
132
+ except IndexError as e:
133
+ raise UsageError('You must provide the variable to forget') from e
134
+ else:
135
+ try:
136
+ del db['autorestore/' + todel]
137
+ except BaseException as e:
138
+ raise UsageError("Can't delete variable '%s'" % todel) from e
139
+ # reset
140
+ elif 'z' in opts:
141
+ for k in db.keys('autorestore/*'):
142
+ del db[k]
143
+
144
+ elif 'r' in opts:
145
+ if args:
146
+ for arg in args:
147
+ try:
148
+ obj = db["autorestore/" + arg]
149
+ except KeyError:
150
+ try:
151
+ restore_aliases(ip, alias=arg)
152
+ except KeyError:
153
+ print("no stored variable or alias %s" % arg)
154
+ else:
155
+ ip.user_ns[arg] = obj
156
+ else:
157
+ restore_data(ip)
158
+
159
+ # run without arguments -> list variables & values
160
+ elif not args:
161
+ vars = db.keys('autorestore/*')
162
+ vars.sort()
163
+ if vars:
164
+ size = max(map(len, vars))
165
+ else:
166
+ size = 0
167
+
168
+ print('Stored variables and their in-db values:')
169
+ fmt = '%-'+str(size)+'s -> %s'
170
+ get = db.get
171
+ for var in vars:
172
+ justkey = os.path.basename(var)
173
+ # print 30 first characters from every var
174
+ print(fmt % (justkey, repr(get(var, '<unavailable>'))[:50]))
175
+
176
+ # default action - store the variable
177
+ else:
178
+ # %store foo >file.txt or >>file.txt
179
+ if len(args) > 1 and args[1].startswith(">"):
180
+ fnam = os.path.expanduser(args[1].lstrip(">").lstrip())
181
+ if args[1].startswith(">>"):
182
+ fil = open(fnam, "a", encoding="utf-8")
183
+ else:
184
+ fil = open(fnam, "w", encoding="utf-8")
185
+ with fil:
186
+ obj = ip.ev(args[0])
187
+ print("Writing '%s' (%s) to file '%s'." % (args[0],
188
+ obj.__class__.__name__, fnam))
189
+
190
+ if not isinstance (obj, str):
191
+ from pprint import pprint
192
+ pprint(obj, fil)
193
+ else:
194
+ fil.write(obj)
195
+ if not obj.endswith('\n'):
196
+ fil.write('\n')
197
+
198
+ return
199
+
200
+ # %store foo
201
+ for arg in args:
202
+ try:
203
+ obj = ip.user_ns[arg]
204
+ except KeyError:
205
+ # it might be an alias
206
+ name = arg
207
+ try:
208
+ cmd = ip.alias_manager.retrieve_alias(name)
209
+ except ValueError as e:
210
+ raise UsageError("Unknown variable '%s'" % name) from e
211
+
212
+ staliases = db.get('stored_aliases',{})
213
+ staliases[name] = cmd
214
+ db['stored_aliases'] = staliases
215
+ print("Alias stored: %s (%s)" % (name, cmd))
216
+ return
217
+
218
+ else:
219
+ modname = getattr(inspect.getmodule(obj), '__name__', '')
220
+ if modname == '__main__':
221
+ print(textwrap.dedent("""\
222
+ Warning:%s is %s
223
+ Proper storage of interactively declared classes (or instances
224
+ of those classes) is not possible! Only instances
225
+ of classes in real modules on file system can be %%store'd.
226
+ """ % (arg, obj) ))
227
+ return
228
+ #pickled = pickle.dumps(obj)
229
+ db[ 'autorestore/' + arg ] = obj
230
+ print("Stored '%s' (%s)" % (arg, obj.__class__.__name__))
231
+
232
+
233
+ def load_ipython_extension(ip):
234
+ """Load the extension in IPython."""
235
+ ip.register_magics(StoreMagics)
236
+
lib/python3.12/site-packages/IPython/terminal/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (195 Bytes). View file
 
lib/python3.12/site-packages/IPython/terminal/__pycache__/debugger.cpython-312.pyc ADDED
Binary file (9.07 kB). View file
 
lib/python3.12/site-packages/IPython/terminal/__pycache__/embed.cpython-312.pyc ADDED
Binary file (17.6 kB). View file
 
lib/python3.12/site-packages/IPython/terminal/__pycache__/interactiveshell.cpython-312.pyc ADDED
Binary file (47.3 kB). View file
 
lib/python3.12/site-packages/IPython/terminal/__pycache__/ipapp.cpython-312.pyc ADDED
Binary file (14.3 kB). View file
 
lib/python3.12/site-packages/IPython/terminal/__pycache__/magics.cpython-312.pyc ADDED
Binary file (10.1 kB). View file
 
lib/python3.12/site-packages/IPython/terminal/__pycache__/prompts.cpython-312.pyc ADDED
Binary file (8.14 kB). View file
 
lib/python3.12/site-packages/IPython/terminal/__pycache__/ptutils.cpython-312.pyc ADDED
Binary file (9.76 kB). View file
 
lib/python3.12/site-packages/IPython/terminal/shortcuts/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (22.2 kB). View file
 
lib/python3.12/site-packages/IPython/terminal/shortcuts/__pycache__/auto_match.cpython-312.pyc ADDED
Binary file (4.8 kB). View file
 
lib/python3.12/site-packages/IPython/terminal/shortcuts/__pycache__/auto_suggest.cpython-312.pyc ADDED
Binary file (30.2 kB). View file
 
lib/python3.12/site-packages/IPython/terminal/shortcuts/__pycache__/filters.cpython-312.pyc ADDED
Binary file (13.7 kB). View file
 
lib/python3.12/site-packages/IPython/terminal/shortcuts/auto_suggest.py ADDED
@@ -0,0 +1,658 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import asyncio
3
+ import tokenize
4
+ from io import StringIO
5
+ from typing import List, Optional, Union, Tuple, ClassVar, Any
6
+ from collections.abc import Callable, Generator
7
+ import warnings
8
+
9
+ import prompt_toolkit
10
+ from prompt_toolkit.buffer import Buffer
11
+ from prompt_toolkit.key_binding import KeyPressEvent
12
+ from prompt_toolkit.key_binding.bindings import named_commands as nc
13
+ from prompt_toolkit.auto_suggest import AutoSuggestFromHistory, Suggestion
14
+ from prompt_toolkit.document import Document
15
+ from prompt_toolkit.history import History
16
+ from prompt_toolkit.shortcuts import PromptSession
17
+ from prompt_toolkit.layout.processors import (
18
+ Processor,
19
+ Transformation,
20
+ TransformationInput,
21
+ )
22
+
23
+ from IPython.core.getipython import get_ipython
24
+ from IPython.utils.tokenutil import generate_tokens
25
+
26
+ from .filters import pass_through
27
+
28
+
29
+ def _get_query(document: Document):
30
+ return document.lines[document.cursor_position_row]
31
+
32
+
33
+ class AppendAutoSuggestionInAnyLine(Processor):
34
+ """
35
+ Append the auto suggestion to lines other than the last (appending to the
36
+ last line is natively supported by the prompt toolkit).
37
+
38
+ This has a private `_debug` attribute that can be set to True to display
39
+ debug information as virtual suggestion on the end of any line. You can do
40
+ so with:
41
+
42
+ >>> from IPython.terminal.shortcuts.auto_suggest import AppendAutoSuggestionInAnyLine
43
+ >>> AppendAutoSuggestionInAnyLine._debug = True
44
+
45
+ """
46
+
47
+ _debug: ClassVar[bool] = False
48
+
49
+ def __init__(self, style: str = "class:auto-suggestion") -> None:
50
+ self.style = style
51
+
52
+ def apply_transformation(self, ti: TransformationInput) -> Transformation:
53
+ """
54
+ Apply transformation to the line that is currently being edited.
55
+
56
+ This is a variation of the original implementation in prompt toolkit
57
+ that allows to not only append suggestions to any line, but also to show
58
+ multi-line suggestions.
59
+
60
+ As transformation are applied on a line-by-line basis; we need to trick
61
+ a bit, and elide any line that is after the line we are currently
62
+ editing, until we run out of completions. We cannot shift the existing
63
+ lines
64
+
65
+ There are multiple cases to handle:
66
+
67
+ The completions ends before the end of the buffer:
68
+ We can resume showing the normal line, and say that some code may
69
+ be hidden.
70
+
71
+ The completions ends at the end of the buffer
72
+ We can just say that some code may be hidden.
73
+
74
+ And separately:
75
+
76
+ The completions ends beyond the end of the buffer
77
+ We need to both say that some code may be hidden, and that some
78
+ lines are not shown.
79
+
80
+ """
81
+ last_line_number = ti.document.line_count - 1
82
+ is_last_line = ti.lineno == last_line_number
83
+
84
+ noop = lambda text: Transformation(
85
+ fragments=ti.fragments + [(self.style, " " + text if self._debug else "")]
86
+ )
87
+ if ti.document.line_count == 1:
88
+ return noop("noop:oneline")
89
+ if ti.document.cursor_position_row == last_line_number and is_last_line:
90
+ # prompt toolkit already appends something; just leave it be
91
+ return noop("noop:last line and cursor")
92
+
93
+ # first everything before the current line is unchanged.
94
+ if ti.lineno < ti.document.cursor_position_row:
95
+ return noop("noop:before cursor")
96
+
97
+ buffer = ti.buffer_control.buffer
98
+ if not buffer.suggestion or not ti.document.is_cursor_at_the_end_of_line:
99
+ return noop("noop:not eol")
100
+
101
+ delta = ti.lineno - ti.document.cursor_position_row
102
+ suggestions = buffer.suggestion.text.splitlines()
103
+
104
+ if len(suggestions) == 0:
105
+ return noop("noop: no suggestions")
106
+
107
+ if prompt_toolkit.VERSION < (3, 0, 49):
108
+ if len(suggestions) > 1 and prompt_toolkit.VERSION < (3, 0, 49):
109
+ if ti.lineno == ti.document.cursor_position_row:
110
+ return Transformation(
111
+ fragments=ti.fragments
112
+ + [
113
+ (
114
+ "red",
115
+ "(Cannot show multiline suggestion; requires prompt_toolkit > 3.0.49)",
116
+ )
117
+ ]
118
+ )
119
+ else:
120
+ return Transformation(fragments=ti.fragments)
121
+ elif len(suggestions) == 1:
122
+ if ti.lineno == ti.document.cursor_position_row:
123
+ return Transformation(
124
+ fragments=ti.fragments + [(self.style, suggestions[0])]
125
+ )
126
+ return Transformation(fragments=ti.fragments)
127
+
128
+ if delta == 0:
129
+ suggestion = suggestions[0]
130
+ return Transformation(fragments=ti.fragments + [(self.style, suggestion)])
131
+ if is_last_line:
132
+ if delta < len(suggestions):
133
+ suggestion = f"… rest of suggestion ({len(suggestions) - delta} lines) and code hidden"
134
+ return Transformation([(self.style, suggestion)])
135
+
136
+ n_elided = len(suggestions)
137
+ for i in range(len(suggestions)):
138
+ ll = ti.get_line(last_line_number - i)
139
+ el = "".join(l[1] for l in ll).strip()
140
+ if el:
141
+ break
142
+ else:
143
+ n_elided -= 1
144
+ if n_elided:
145
+ return Transformation([(self.style, f"… {n_elided} line(s) hidden")])
146
+ else:
147
+ return Transformation(
148
+ ti.get_line(last_line_number - len(suggestions) + 1)
149
+ + ([(self.style, "shift-last-line")] if self._debug else [])
150
+ )
151
+
152
+ elif delta < len(suggestions):
153
+ suggestion = suggestions[delta]
154
+ return Transformation([(self.style, suggestion)])
155
+ else:
156
+ shift = ti.lineno - len(suggestions) + 1
157
+ return Transformation(ti.get_line(shift))
158
+
159
+
160
+ class NavigableAutoSuggestFromHistory(AutoSuggestFromHistory):
161
+ """
162
+ A subclass of AutoSuggestFromHistory that allow navigation to next/previous
163
+ suggestion from history. To do so it remembers the current position, but it
164
+ state need to carefully be cleared on the right events.
165
+ """
166
+
167
+ skip_lines: int
168
+ _connected_apps: list[PromptSession]
169
+
170
+ # handle to the currently running llm task that appends suggestions to the
171
+ # current buffer; we keep a handle to it in order to cancel it when there is a cursor movement, or
172
+ # another request.
173
+ _llm_task: asyncio.Task | None = None
174
+
175
+ # This is the constructor of the LLM provider from jupyter-ai
176
+ # to which we forward the request to generate inline completions.
177
+ _init_llm_provider: Callable | None
178
+
179
+ _llm_provider_instance: Any | None
180
+ _llm_prefixer: Callable = lambda self, x: "wrong"
181
+
182
+ def __init__(self):
183
+ super().__init__()
184
+ self.skip_lines = 0
185
+ self._connected_apps = []
186
+ self._llm_provider_instance = None
187
+ self._init_llm_provider = None
188
+ self._request_number = 0
189
+
190
+ def reset_history_position(self, _: Buffer) -> None:
191
+ self.skip_lines = 0
192
+
193
+ def disconnect(self) -> None:
194
+ self._cancel_running_llm_task()
195
+ for pt_app in self._connected_apps:
196
+ text_insert_event = pt_app.default_buffer.on_text_insert
197
+ text_insert_event.remove_handler(self.reset_history_position)
198
+
199
+ def connect(self, pt_app: PromptSession) -> None:
200
+ self._connected_apps.append(pt_app)
201
+ # note: `on_text_changed` could be used for a bit different behaviour
202
+ # on character deletion (i.e. resetting history position on backspace)
203
+ pt_app.default_buffer.on_text_insert.add_handler(self.reset_history_position)
204
+ pt_app.default_buffer.on_cursor_position_changed.add_handler(self._dismiss)
205
+
206
+ def get_suggestion(
207
+ self, buffer: Buffer, document: Document
208
+ ) -> Optional[Suggestion]:
209
+ text = _get_query(document)
210
+
211
+ if text.strip():
212
+ for suggestion, _ in self._find_next_match(
213
+ text, self.skip_lines, buffer.history
214
+ ):
215
+ return Suggestion(suggestion)
216
+
217
+ return None
218
+
219
+ def _dismiss(self, buffer, *args, **kwargs) -> None:
220
+ self._cancel_running_llm_task()
221
+ buffer.suggestion = None
222
+
223
+ def _find_match(
224
+ self, text: str, skip_lines: float, history: History, previous: bool
225
+ ) -> Generator[Tuple[str, float], None, None]:
226
+ """
227
+ text : str
228
+ Text content to find a match for, the user cursor is most of the
229
+ time at the end of this text.
230
+ skip_lines : float
231
+ number of items to skip in the search, this is used to indicate how
232
+ far in the list the user has navigated by pressing up or down.
233
+ The float type is used as the base value is +inf
234
+ history : History
235
+ prompt_toolkit History instance to fetch previous entries from.
236
+ previous : bool
237
+ Direction of the search, whether we are looking previous match
238
+ (True), or next match (False).
239
+
240
+ Yields
241
+ ------
242
+ Tuple with:
243
+ str:
244
+ current suggestion.
245
+ float:
246
+ will actually yield only ints, which is passed back via skip_lines,
247
+ which may be a +inf (float)
248
+
249
+
250
+ """
251
+ line_number = -1
252
+ for string in reversed(list(history.get_strings())):
253
+ for line in reversed(string.splitlines()):
254
+ line_number += 1
255
+ if not previous and line_number < skip_lines:
256
+ continue
257
+ # do not return empty suggestions as these
258
+ # close the auto-suggestion overlay (and are useless)
259
+ if line.startswith(text) and len(line) > len(text):
260
+ yield line[len(text) :], line_number
261
+ if previous and line_number >= skip_lines:
262
+ return
263
+
264
+ def _find_next_match(
265
+ self, text: str, skip_lines: float, history: History
266
+ ) -> Generator[Tuple[str, float], None, None]:
267
+ return self._find_match(text, skip_lines, history, previous=False)
268
+
269
+ def _find_previous_match(self, text: str, skip_lines: float, history: History):
270
+ return reversed(
271
+ list(self._find_match(text, skip_lines, history, previous=True))
272
+ )
273
+
274
+ def up(self, query: str, other_than: str, history: History) -> None:
275
+ self._cancel_running_llm_task()
276
+ for suggestion, line_number in self._find_next_match(
277
+ query, self.skip_lines, history
278
+ ):
279
+ # if user has history ['very.a', 'very', 'very.b'] and typed 'very'
280
+ # we want to switch from 'very.b' to 'very.a' because a) if the
281
+ # suggestion equals current text, prompt-toolkit aborts suggesting
282
+ # b) user likely would not be interested in 'very' anyways (they
283
+ # already typed it).
284
+ if query + suggestion != other_than:
285
+ self.skip_lines = line_number
286
+ break
287
+ else:
288
+ # no matches found, cycle back to beginning
289
+ self.skip_lines = 0
290
+
291
+ def down(self, query: str, other_than: str, history: History) -> None:
292
+ self._cancel_running_llm_task()
293
+ for suggestion, line_number in self._find_previous_match(
294
+ query, self.skip_lines, history
295
+ ):
296
+ if query + suggestion != other_than:
297
+ self.skip_lines = line_number
298
+ break
299
+ else:
300
+ # no matches found, cycle to end
301
+ for suggestion, line_number in self._find_previous_match(
302
+ query, float("Inf"), history
303
+ ):
304
+ if query + suggestion != other_than:
305
+ self.skip_lines = line_number
306
+ break
307
+
308
+ def _cancel_running_llm_task(self) -> None:
309
+ """
310
+ Try to cancel the currently running llm_task if exists, and set it to None.
311
+ """
312
+ if self._llm_task is not None:
313
+ if self._llm_task.done():
314
+ self._llm_task = None
315
+ return
316
+ cancelled = self._llm_task.cancel()
317
+ if cancelled:
318
+ self._llm_task = None
319
+ if not cancelled:
320
+ warnings.warn(
321
+ "LLM task not cancelled, does your provider support cancellation?"
322
+ )
323
+
324
+ @property
325
+ def _llm_provider(self):
326
+ """Lazy-initialized instance of the LLM provider.
327
+
328
+ Do not use in the constructor, as `_init_llm_provider` can trigger slow side-effects.
329
+ """
330
+ if self._llm_provider_instance is None and self._init_llm_provider:
331
+ self._llm_provider_instance = self._init_llm_provider()
332
+ return self._llm_provider_instance
333
+
334
+ async def _trigger_llm(self, buffer) -> None:
335
+ """
336
+ This will ask the current llm provider a suggestion for the current buffer.
337
+
338
+ If there is a currently running llm task, it will cancel it.
339
+ """
340
+ # we likely want to store the current cursor position, and cancel if the cursor has moved.
341
+ try:
342
+ import jupyter_ai_magics
343
+ except ModuleNotFoundError:
344
+ jupyter_ai_magics = None
345
+ if not self._llm_provider:
346
+ warnings.warn("No LLM provider found, cannot trigger LLM completions")
347
+ return
348
+ if jupyter_ai_magics is None:
349
+ warnings.warn("LLM Completion requires `jupyter_ai_magics` to be installed")
350
+
351
+ self._cancel_running_llm_task()
352
+
353
+ async def error_catcher(buffer):
354
+ """
355
+ This catches and log any errors, as otherwise this is just
356
+ lost in the void of the future running task.
357
+ """
358
+ try:
359
+ await self._trigger_llm_core(buffer)
360
+ except Exception as e:
361
+ get_ipython().log.error("error %s", e)
362
+ raise
363
+
364
+ # here we need a cancellable task so we can't just await the error caught
365
+ self._llm_task = asyncio.create_task(error_catcher(buffer))
366
+ await self._llm_task
367
+
368
+ async def _trigger_llm_core(self, buffer: Buffer):
369
+ """
370
+ This is the core of the current llm request.
371
+
372
+ Here we build a compatible `InlineCompletionRequest` and ask the llm
373
+ provider to stream it's response back to us iteratively setting it as
374
+ the suggestion on the current buffer.
375
+
376
+ Unlike with JupyterAi, as we do not have multiple cells, the cell id
377
+ is always set to `None`.
378
+
379
+ We set the prefix to the current cell content, but could also insert the
380
+ rest of the history or even just the non-fail history.
381
+
382
+ In the same way, we do not have cell id.
383
+
384
+ LLM provider may return multiple suggestion stream, but for the time
385
+ being we only support one.
386
+
387
+ Here we make the assumption that the provider will have
388
+ stream_inline_completions, I'm not sure it is the case for all
389
+ providers.
390
+ """
391
+ try:
392
+ import jupyter_ai.completions.models as jai_models
393
+ except ModuleNotFoundError:
394
+ jai_models = None
395
+
396
+ if not jai_models:
397
+ raise ValueError("jupyter-ai is not installed")
398
+
399
+ if not self._llm_provider:
400
+ raise ValueError("No LLM provider found, cannot trigger LLM completions")
401
+
402
+ hm = buffer.history.shell.history_manager
403
+ prefix = self._llm_prefixer(hm)
404
+ get_ipython().log.debug("prefix: %s", prefix)
405
+
406
+ self._request_number += 1
407
+ request_number = self._request_number
408
+
409
+ request = jai_models.InlineCompletionRequest(
410
+ number=request_number,
411
+ prefix=prefix + buffer.document.text_before_cursor,
412
+ suffix=buffer.document.text_after_cursor,
413
+ mime="text/x-python",
414
+ stream=True,
415
+ path=None,
416
+ language="python",
417
+ cell_id=None,
418
+ )
419
+
420
+ async for reply_and_chunks in self._llm_provider.stream_inline_completions(
421
+ request
422
+ ):
423
+ if self._request_number != request_number:
424
+ # If a new suggestion was requested, skip processing this one.
425
+ return
426
+ if isinstance(reply_and_chunks, jai_models.InlineCompletionReply):
427
+ if len(reply_and_chunks.list.items) > 1:
428
+ raise ValueError(
429
+ "Terminal IPython cannot deal with multiple LLM suggestions at once"
430
+ )
431
+ buffer.suggestion = Suggestion(
432
+ reply_and_chunks.list.items[0].insertText
433
+ )
434
+ buffer.on_suggestion_set.fire()
435
+ elif isinstance(reply_and_chunks, jai_models.InlineCompletionStreamChunk):
436
+ buffer.suggestion = Suggestion(reply_and_chunks.response.insertText)
437
+ buffer.on_suggestion_set.fire()
438
+ return
439
+
440
+
441
+ async def llm_autosuggestion(event: KeyPressEvent):
442
+ """
443
+ Ask the AutoSuggester from history to delegate to ask an LLM for completion
444
+
445
+ This will first make sure that the current buffer have _MIN_LINES (7)
446
+ available lines to insert the LLM completion
447
+
448
+ Provisional as of 8.32, may change without warnings
449
+
450
+ """
451
+ _MIN_LINES = 5
452
+ provider = get_ipython().auto_suggest
453
+ if not isinstance(provider, NavigableAutoSuggestFromHistory):
454
+ return
455
+ doc = event.current_buffer.document
456
+ lines_to_insert = max(0, _MIN_LINES - doc.line_count + doc.cursor_position_row)
457
+ for _ in range(lines_to_insert):
458
+ event.current_buffer.insert_text("\n", move_cursor=False, fire_event=False)
459
+
460
+ await provider._trigger_llm(event.current_buffer)
461
+
462
+
463
+ def accept_or_jump_to_end(event: KeyPressEvent):
464
+ """Apply autosuggestion or jump to end of line."""
465
+ buffer = event.current_buffer
466
+ d = buffer.document
467
+ after_cursor = d.text[d.cursor_position :]
468
+ lines = after_cursor.split("\n")
469
+ end_of_current_line = lines[0].strip()
470
+ suggestion = buffer.suggestion
471
+ if (suggestion is not None) and (suggestion.text) and (end_of_current_line == ""):
472
+ buffer.insert_text(suggestion.text)
473
+ else:
474
+ nc.end_of_line(event)
475
+
476
+
477
+ def accept(event: KeyPressEvent):
478
+ """Accept autosuggestion"""
479
+ buffer = event.current_buffer
480
+ suggestion = buffer.suggestion
481
+ if suggestion:
482
+ buffer.insert_text(suggestion.text)
483
+ else:
484
+ nc.forward_char(event)
485
+
486
+
487
+ def discard(event: KeyPressEvent):
488
+ """Discard autosuggestion"""
489
+ buffer = event.current_buffer
490
+ buffer.suggestion = None
491
+
492
+
493
+ def accept_word(event: KeyPressEvent):
494
+ """Fill partial autosuggestion by word"""
495
+ buffer = event.current_buffer
496
+ suggestion = buffer.suggestion
497
+ if suggestion:
498
+ t = re.split(r"(\S+\s+)", suggestion.text)
499
+ buffer.insert_text(next((x for x in t if x), ""))
500
+ else:
501
+ nc.forward_word(event)
502
+
503
+
504
+ def accept_character(event: KeyPressEvent):
505
+ """Fill partial autosuggestion by character"""
506
+ b = event.current_buffer
507
+ suggestion = b.suggestion
508
+ if suggestion and suggestion.text:
509
+ b.insert_text(suggestion.text[0])
510
+
511
+
512
+ def accept_and_keep_cursor(event: KeyPressEvent):
513
+ """Accept autosuggestion and keep cursor in place"""
514
+ buffer = event.current_buffer
515
+ old_position = buffer.cursor_position
516
+ suggestion = buffer.suggestion
517
+ if suggestion:
518
+ buffer.insert_text(suggestion.text)
519
+ buffer.cursor_position = old_position
520
+
521
+
522
+ def accept_and_move_cursor_left(event: KeyPressEvent):
523
+ """Accept autosuggestion and move cursor left in place"""
524
+ accept_and_keep_cursor(event)
525
+ nc.backward_char(event)
526
+
527
+
528
+ def _update_hint(buffer: Buffer):
529
+ if buffer.auto_suggest:
530
+ suggestion = buffer.auto_suggest.get_suggestion(buffer, buffer.document)
531
+ buffer.suggestion = suggestion
532
+
533
+
534
+ def backspace_and_resume_hint(event: KeyPressEvent):
535
+ """Resume autosuggestions after deleting last character"""
536
+ nc.backward_delete_char(event)
537
+ _update_hint(event.current_buffer)
538
+
539
+
540
+ def resume_hinting(event: KeyPressEvent):
541
+ """Resume autosuggestions"""
542
+ pass_through.reply(event)
543
+ # Order matters: if update happened first and event reply second, the
544
+ # suggestion would be auto-accepted if both actions are bound to same key.
545
+ _update_hint(event.current_buffer)
546
+
547
+
548
+ def up_and_update_hint(event: KeyPressEvent):
549
+ """Go up and update hint"""
550
+ current_buffer = event.current_buffer
551
+
552
+ current_buffer.auto_up(count=event.arg)
553
+ _update_hint(current_buffer)
554
+
555
+
556
+ def down_and_update_hint(event: KeyPressEvent):
557
+ """Go down and update hint"""
558
+ current_buffer = event.current_buffer
559
+
560
+ current_buffer.auto_down(count=event.arg)
561
+ _update_hint(current_buffer)
562
+
563
+
564
+ def accept_token(event: KeyPressEvent):
565
+ """Fill partial autosuggestion by token"""
566
+ b = event.current_buffer
567
+ suggestion = b.suggestion
568
+
569
+ if suggestion:
570
+ prefix = _get_query(b.document)
571
+ text = prefix + suggestion.text
572
+
573
+ tokens: List[Optional[str]] = [None, None, None]
574
+ substrings = [""]
575
+ i = 0
576
+
577
+ for token in generate_tokens(StringIO(text).readline):
578
+ if token.type == tokenize.NEWLINE:
579
+ index = len(text)
580
+ else:
581
+ index = text.index(token[1], len(substrings[-1]))
582
+ substrings.append(text[:index])
583
+ tokenized_so_far = substrings[-1]
584
+ if tokenized_so_far.startswith(prefix):
585
+ if i == 0 and len(tokenized_so_far) > len(prefix):
586
+ tokens[0] = tokenized_so_far[len(prefix) :]
587
+ substrings.append(tokenized_so_far)
588
+ i += 1
589
+ tokens[i] = token[1]
590
+ if i == 2:
591
+ break
592
+ i += 1
593
+
594
+ if tokens[0]:
595
+ to_insert: str
596
+ insert_text = substrings[-2]
597
+ if tokens[1] and len(tokens[1]) == 1:
598
+ insert_text = substrings[-1]
599
+ to_insert = insert_text[len(prefix) :]
600
+ b.insert_text(to_insert)
601
+ return
602
+
603
+ nc.forward_word(event)
604
+
605
+
606
+ Provider = Union[AutoSuggestFromHistory, NavigableAutoSuggestFromHistory, None]
607
+
608
+
609
+ def _swap_autosuggestion(
610
+ buffer: Buffer,
611
+ provider: NavigableAutoSuggestFromHistory,
612
+ direction_method: Callable,
613
+ ):
614
+ """
615
+ We skip most recent history entry (in either direction) if it equals the
616
+ current autosuggestion because if user cycles when auto-suggestion is shown
617
+ they most likely want something else than what was suggested (otherwise
618
+ they would have accepted the suggestion).
619
+ """
620
+ suggestion = buffer.suggestion
621
+ if not suggestion:
622
+ return
623
+
624
+ query = _get_query(buffer.document)
625
+ current = query + suggestion.text
626
+
627
+ direction_method(query=query, other_than=current, history=buffer.history)
628
+
629
+ new_suggestion = provider.get_suggestion(buffer, buffer.document)
630
+ buffer.suggestion = new_suggestion
631
+
632
+
633
+ def swap_autosuggestion_up(event: KeyPressEvent):
634
+ """Get next autosuggestion from history."""
635
+ shell = get_ipython()
636
+ provider = shell.auto_suggest
637
+
638
+ if not isinstance(provider, NavigableAutoSuggestFromHistory):
639
+ return
640
+
641
+ return _swap_autosuggestion(
642
+ buffer=event.current_buffer, provider=provider, direction_method=provider.up
643
+ )
644
+
645
+
646
+ def swap_autosuggestion_down(event: KeyPressEvent):
647
+ """Get previous autosuggestion from history."""
648
+ shell = get_ipython()
649
+ provider = shell.auto_suggest
650
+
651
+ if not isinstance(provider, NavigableAutoSuggestFromHistory):
652
+ return
653
+
654
+ return _swap_autosuggestion(
655
+ buffer=event.current_buffer,
656
+ provider=provider,
657
+ direction_method=provider.down,
658
+ )
lib/python3.12/site-packages/attrs/__init__.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ from attr import (
4
+ NOTHING,
5
+ Attribute,
6
+ AttrsInstance,
7
+ Converter,
8
+ Factory,
9
+ NothingType,
10
+ _make_getattr,
11
+ assoc,
12
+ cmp_using,
13
+ define,
14
+ evolve,
15
+ field,
16
+ fields,
17
+ fields_dict,
18
+ frozen,
19
+ has,
20
+ make_class,
21
+ mutable,
22
+ resolve_types,
23
+ validate,
24
+ )
25
+ from attr._make import ClassProps
26
+ from attr._next_gen import asdict, astuple, inspect
27
+
28
+ from . import converters, exceptions, filters, setters, validators
29
+
30
+
31
+ __all__ = [
32
+ "NOTHING",
33
+ "Attribute",
34
+ "AttrsInstance",
35
+ "ClassProps",
36
+ "Converter",
37
+ "Factory",
38
+ "NothingType",
39
+ "__author__",
40
+ "__copyright__",
41
+ "__description__",
42
+ "__doc__",
43
+ "__email__",
44
+ "__license__",
45
+ "__title__",
46
+ "__url__",
47
+ "__version__",
48
+ "__version_info__",
49
+ "asdict",
50
+ "assoc",
51
+ "astuple",
52
+ "cmp_using",
53
+ "converters",
54
+ "define",
55
+ "evolve",
56
+ "exceptions",
57
+ "field",
58
+ "fields",
59
+ "fields_dict",
60
+ "filters",
61
+ "frozen",
62
+ "has",
63
+ "inspect",
64
+ "make_class",
65
+ "mutable",
66
+ "resolve_types",
67
+ "setters",
68
+ "validate",
69
+ "validators",
70
+ ]
71
+
72
+ __getattr__ = _make_getattr(__name__)
lib/python3.12/site-packages/attrs/__init__.pyi ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+
3
+ from typing import (
4
+ Any,
5
+ Callable,
6
+ Mapping,
7
+ Sequence,
8
+ overload,
9
+ TypeVar,
10
+ )
11
+
12
+ # Because we need to type our own stuff, we have to make everything from
13
+ # attr explicitly public too.
14
+ from attr import __author__ as __author__
15
+ from attr import __copyright__ as __copyright__
16
+ from attr import __description__ as __description__
17
+ from attr import __email__ as __email__
18
+ from attr import __license__ as __license__
19
+ from attr import __title__ as __title__
20
+ from attr import __url__ as __url__
21
+ from attr import __version__ as __version__
22
+ from attr import __version_info__ as __version_info__
23
+ from attr import assoc as assoc
24
+ from attr import Attribute as Attribute
25
+ from attr import AttrsInstance as AttrsInstance
26
+ from attr import cmp_using as cmp_using
27
+ from attr import converters as converters
28
+ from attr import Converter as Converter
29
+ from attr import evolve as evolve
30
+ from attr import exceptions as exceptions
31
+ from attr import Factory as Factory
32
+ from attr import fields as fields
33
+ from attr import fields_dict as fields_dict
34
+ from attr import filters as filters
35
+ from attr import has as has
36
+ from attr import make_class as make_class
37
+ from attr import NOTHING as NOTHING
38
+ from attr import resolve_types as resolve_types
39
+ from attr import setters as setters
40
+ from attr import validate as validate
41
+ from attr import validators as validators
42
+ from attr import attrib, asdict as asdict, astuple as astuple
43
+ from attr import NothingType as NothingType
44
+
45
+ if sys.version_info >= (3, 11):
46
+ from typing import dataclass_transform
47
+ else:
48
+ from typing_extensions import dataclass_transform
49
+
50
+ _T = TypeVar("_T")
51
+ _C = TypeVar("_C", bound=type)
52
+
53
+ _EqOrderType = bool | Callable[[Any], Any]
54
+ _ValidatorType = Callable[[Any, "Attribute[_T]", _T], Any]
55
+ _CallableConverterType = Callable[[Any], Any]
56
+ _ConverterType = _CallableConverterType | Converter[Any, Any]
57
+ _ReprType = Callable[[Any], str]
58
+ _ReprArgType = bool | _ReprType
59
+ _OnSetAttrType = Callable[[Any, "Attribute[Any]", Any], Any]
60
+ _OnSetAttrArgType = _OnSetAttrType | list[_OnSetAttrType] | setters._NoOpType
61
+ _FieldTransformer = Callable[
62
+ [type, list["Attribute[Any]"]], list["Attribute[Any]"]
63
+ ]
64
+ # FIXME: in reality, if multiple validators are passed they must be in a list
65
+ # or tuple, but those are invariant and so would prevent subtypes of
66
+ # _ValidatorType from working when passed in a list or tuple.
67
+ _ValidatorArgType = _ValidatorType[_T] | Sequence[_ValidatorType[_T]]
68
+
69
+ @overload
70
+ def field(
71
+ *,
72
+ default: None = ...,
73
+ validator: None = ...,
74
+ repr: _ReprArgType = ...,
75
+ hash: bool | None = ...,
76
+ init: bool = ...,
77
+ metadata: Mapping[Any, Any] | None = ...,
78
+ converter: None = ...,
79
+ factory: None = ...,
80
+ kw_only: bool | None = ...,
81
+ eq: bool | None = ...,
82
+ order: bool | None = ...,
83
+ on_setattr: _OnSetAttrArgType | None = ...,
84
+ alias: str | None = ...,
85
+ type: type | None = ...,
86
+ ) -> Any: ...
87
+
88
+ # This form catches an explicit None or no default and infers the type from the
89
+ # other arguments.
90
+ @overload
91
+ def field(
92
+ *,
93
+ default: None = ...,
94
+ validator: _ValidatorArgType[_T] | None = ...,
95
+ repr: _ReprArgType = ...,
96
+ hash: bool | None = ...,
97
+ init: bool = ...,
98
+ metadata: Mapping[Any, Any] | None = ...,
99
+ converter: _ConverterType
100
+ | list[_ConverterType]
101
+ | tuple[_ConverterType, ...]
102
+ | None = ...,
103
+ factory: Callable[[], _T] | None = ...,
104
+ kw_only: bool | None = ...,
105
+ eq: _EqOrderType | None = ...,
106
+ order: _EqOrderType | None = ...,
107
+ on_setattr: _OnSetAttrArgType | None = ...,
108
+ alias: str | None = ...,
109
+ type: type | None = ...,
110
+ ) -> _T: ...
111
+
112
+ # This form catches an explicit default argument.
113
+ @overload
114
+ def field(
115
+ *,
116
+ default: _T,
117
+ validator: _ValidatorArgType[_T] | None = ...,
118
+ repr: _ReprArgType = ...,
119
+ hash: bool | None = ...,
120
+ init: bool = ...,
121
+ metadata: Mapping[Any, Any] | None = ...,
122
+ converter: _ConverterType
123
+ | list[_ConverterType]
124
+ | tuple[_ConverterType, ...]
125
+ | None = ...,
126
+ factory: Callable[[], _T] | None = ...,
127
+ kw_only: bool | None = ...,
128
+ eq: _EqOrderType | None = ...,
129
+ order: _EqOrderType | None = ...,
130
+ on_setattr: _OnSetAttrArgType | None = ...,
131
+ alias: str | None = ...,
132
+ type: type | None = ...,
133
+ ) -> _T: ...
134
+
135
+ # This form covers type=non-Type: e.g. forward references (str), Any
136
+ @overload
137
+ def field(
138
+ *,
139
+ default: _T | None = ...,
140
+ validator: _ValidatorArgType[_T] | None = ...,
141
+ repr: _ReprArgType = ...,
142
+ hash: bool | None = ...,
143
+ init: bool = ...,
144
+ metadata: Mapping[Any, Any] | None = ...,
145
+ converter: _ConverterType
146
+ | list[_ConverterType]
147
+ | tuple[_ConverterType, ...]
148
+ | None = ...,
149
+ factory: Callable[[], _T] | None = ...,
150
+ kw_only: bool | None = ...,
151
+ eq: _EqOrderType | None = ...,
152
+ order: _EqOrderType | None = ...,
153
+ on_setattr: _OnSetAttrArgType | None = ...,
154
+ alias: str | None = ...,
155
+ type: type | None = ...,
156
+ ) -> Any: ...
157
+ @overload
158
+ @dataclass_transform(field_specifiers=(attrib, field))
159
+ def define(
160
+ maybe_cls: _C,
161
+ *,
162
+ these: dict[str, Any] | None = ...,
163
+ repr: bool = ...,
164
+ unsafe_hash: bool | None = ...,
165
+ hash: bool | None = ...,
166
+ init: bool = ...,
167
+ slots: bool = ...,
168
+ frozen: bool = ...,
169
+ weakref_slot: bool = ...,
170
+ str: bool = ...,
171
+ auto_attribs: bool = ...,
172
+ kw_only: bool = ...,
173
+ cache_hash: bool = ...,
174
+ auto_exc: bool = ...,
175
+ eq: bool | None = ...,
176
+ order: bool | None = ...,
177
+ auto_detect: bool = ...,
178
+ getstate_setstate: bool | None = ...,
179
+ on_setattr: _OnSetAttrArgType | None = ...,
180
+ field_transformer: _FieldTransformer | None = ...,
181
+ match_args: bool = ...,
182
+ ) -> _C: ...
183
+ @overload
184
+ @dataclass_transform(field_specifiers=(attrib, field))
185
+ def define(
186
+ maybe_cls: None = ...,
187
+ *,
188
+ these: dict[str, Any] | None = ...,
189
+ repr: bool = ...,
190
+ unsafe_hash: bool | None = ...,
191
+ hash: bool | None = ...,
192
+ init: bool = ...,
193
+ slots: bool = ...,
194
+ frozen: bool = ...,
195
+ weakref_slot: bool = ...,
196
+ str: bool = ...,
197
+ auto_attribs: bool = ...,
198
+ kw_only: bool = ...,
199
+ cache_hash: bool = ...,
200
+ auto_exc: bool = ...,
201
+ eq: bool | None = ...,
202
+ order: bool | None = ...,
203
+ auto_detect: bool = ...,
204
+ getstate_setstate: bool | None = ...,
205
+ on_setattr: _OnSetAttrArgType | None = ...,
206
+ field_transformer: _FieldTransformer | None = ...,
207
+ match_args: bool = ...,
208
+ ) -> Callable[[_C], _C]: ...
209
+
210
+ mutable = define
211
+
212
+ @overload
213
+ @dataclass_transform(frozen_default=True, field_specifiers=(attrib, field))
214
+ def frozen(
215
+ maybe_cls: _C,
216
+ *,
217
+ these: dict[str, Any] | None = ...,
218
+ repr: bool = ...,
219
+ unsafe_hash: bool | None = ...,
220
+ hash: bool | None = ...,
221
+ init: bool = ...,
222
+ slots: bool = ...,
223
+ frozen: bool = ...,
224
+ weakref_slot: bool = ...,
225
+ str: bool = ...,
226
+ auto_attribs: bool = ...,
227
+ kw_only: bool = ...,
228
+ cache_hash: bool = ...,
229
+ auto_exc: bool = ...,
230
+ eq: bool | None = ...,
231
+ order: bool | None = ...,
232
+ auto_detect: bool = ...,
233
+ getstate_setstate: bool | None = ...,
234
+ on_setattr: _OnSetAttrArgType | None = ...,
235
+ field_transformer: _FieldTransformer | None = ...,
236
+ match_args: bool = ...,
237
+ ) -> _C: ...
238
+ @overload
239
+ @dataclass_transform(frozen_default=True, field_specifiers=(attrib, field))
240
+ def frozen(
241
+ maybe_cls: None = ...,
242
+ *,
243
+ these: dict[str, Any] | None = ...,
244
+ repr: bool = ...,
245
+ unsafe_hash: bool | None = ...,
246
+ hash: bool | None = ...,
247
+ init: bool = ...,
248
+ slots: bool = ...,
249
+ frozen: bool = ...,
250
+ weakref_slot: bool = ...,
251
+ str: bool = ...,
252
+ auto_attribs: bool = ...,
253
+ kw_only: bool = ...,
254
+ cache_hash: bool = ...,
255
+ auto_exc: bool = ...,
256
+ eq: bool | None = ...,
257
+ order: bool | None = ...,
258
+ auto_detect: bool = ...,
259
+ getstate_setstate: bool | None = ...,
260
+ on_setattr: _OnSetAttrArgType | None = ...,
261
+ field_transformer: _FieldTransformer | None = ...,
262
+ match_args: bool = ...,
263
+ ) -> Callable[[_C], _C]: ...
264
+
265
+ class ClassProps:
266
+ # XXX: somehow when defining/using enums Mypy starts looking at our own
267
+ # (untyped) code and causes tons of errors.
268
+ Hashability: Any
269
+ KeywordOnly: Any
270
+
271
+ is_exception: bool
272
+ is_slotted: bool
273
+ has_weakref_slot: bool
274
+ is_frozen: bool
275
+ # kw_only: ClassProps.KeywordOnly
276
+ kw_only: Any
277
+ collected_fields_by_mro: bool
278
+ added_init: bool
279
+ added_repr: bool
280
+ added_eq: bool
281
+ added_ordering: bool
282
+ # hashability: ClassProps.Hashability
283
+ hashability: Any
284
+ added_match_args: bool
285
+ added_str: bool
286
+ added_pickling: bool
287
+ on_setattr_hook: _OnSetAttrType | None
288
+ field_transformer: Callable[[Attribute[Any]], Attribute[Any]] | None
289
+
290
+ def __init__(
291
+ self,
292
+ is_exception: bool,
293
+ is_slotted: bool,
294
+ has_weakref_slot: bool,
295
+ is_frozen: bool,
296
+ # kw_only: ClassProps.KeywordOnly
297
+ kw_only: Any,
298
+ collected_fields_by_mro: bool,
299
+ added_init: bool,
300
+ added_repr: bool,
301
+ added_eq: bool,
302
+ added_ordering: bool,
303
+ # hashability: ClassProps.Hashability
304
+ hashability: Any,
305
+ added_match_args: bool,
306
+ added_str: bool,
307
+ added_pickling: bool,
308
+ on_setattr_hook: _OnSetAttrType,
309
+ field_transformer: Callable[[Attribute[Any]], Attribute[Any]],
310
+ ) -> None: ...
311
+ @property
312
+ def is_hashable(self) -> bool: ...
313
+
314
+ def inspect(cls: type) -> ClassProps: ...
lib/python3.12/site-packages/attrs/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (1.22 kB). View file
 
lib/python3.12/site-packages/attrs/__pycache__/converters.cpython-312.pyc ADDED
Binary file (225 Bytes). View file
 
lib/python3.12/site-packages/attrs/__pycache__/exceptions.cpython-312.pyc ADDED
Binary file (225 Bytes). View file
 
lib/python3.12/site-packages/attrs/__pycache__/filters.cpython-312.pyc ADDED
Binary file (219 Bytes). View file
 
lib/python3.12/site-packages/attrs/__pycache__/setters.cpython-312.pyc ADDED
Binary file (219 Bytes). View file
 
lib/python3.12/site-packages/attrs/__pycache__/validators.cpython-312.pyc ADDED
Binary file (225 Bytes). View file
 
lib/python3.12/site-packages/attrs/converters.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ from attr.converters import * # noqa: F403
lib/python3.12/site-packages/attrs/exceptions.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ from attr.exceptions import * # noqa: F403
lib/python3.12/site-packages/attrs/filters.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ from attr.filters import * # noqa: F403
lib/python3.12/site-packages/attrs/py.typed ADDED
File without changes
lib/python3.12/site-packages/attrs/setters.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ from attr.setters import * # noqa: F403
lib/python3.12/site-packages/attrs/validators.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ from attr.validators import * # noqa: F403
lib/python3.12/site-packages/torchaudio/__init__.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Initialize extension and backend first
2
+ from . import _extension # noqa # usort: skip
3
+ from ._backend import ( # noqa # usort: skip
4
+ AudioMetaData,
5
+ get_audio_backend,
6
+ info,
7
+ list_audio_backends,
8
+ load,
9
+ save,
10
+ set_audio_backend,
11
+ )
12
+
13
+ from . import ( # noqa: F401
14
+ compliance,
15
+ datasets,
16
+ functional,
17
+ io,
18
+ kaldi_io,
19
+ models,
20
+ pipelines,
21
+ sox_effects,
22
+ transforms,
23
+ utils,
24
+ )
25
+
26
+ # For BC
27
+ from . import backend # noqa # usort: skip
28
+
29
+ try:
30
+ from .version import __version__, git_version # noqa: F401
31
+ except ImportError:
32
+ pass
33
+
34
+
35
+ __all__ = [
36
+ "AudioMetaData",
37
+ "load",
38
+ "info",
39
+ "save",
40
+ "io",
41
+ "compliance",
42
+ "datasets",
43
+ "functional",
44
+ "models",
45
+ "pipelines",
46
+ "kaldi_io",
47
+ "utils",
48
+ "sox_effects",
49
+ "transforms",
50
+ "list_audio_backends",
51
+ "get_audio_backend",
52
+ "set_audio_backend",
53
+ ]
lib/python3.12/site-packages/torchaudio/_internal/__init__.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ try:
2
+ from .fb import download_url_to_file, load_state_dict_from_url
3
+ except ImportError:
4
+ from torch.hub import download_url_to_file, load_state_dict_from_url
5
+
6
+
7
+ __all__ = [
8
+ "load_state_dict_from_url",
9
+ "download_url_to_file",
10
+ ]
lib/python3.12/site-packages/torchaudio/_internal/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (438 Bytes). View file
 
lib/python3.12/site-packages/torchaudio/_internal/__pycache__/module_utils.cpython-312.pyc ADDED
Binary file (5.66 kB). View file
 
lib/python3.12/site-packages/torchaudio/_internal/module_utils.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import importlib.util
2
+ import os
3
+ import warnings
4
+ from functools import wraps
5
+ from typing import Optional
6
+
7
+
8
+ def eval_env(var, default):
9
+ """Check if environment varable has True-y value"""
10
+ if var not in os.environ:
11
+ return default
12
+
13
+ val = os.environ.get(var, "0")
14
+ trues = ["1", "true", "TRUE", "on", "ON", "yes", "YES"]
15
+ falses = ["0", "false", "FALSE", "off", "OFF", "no", "NO"]
16
+ if val in trues:
17
+ return True
18
+ if val not in falses:
19
+ # fmt: off
20
+ raise RuntimeError(
21
+ f"Unexpected environment variable value `{var}={val}`. "
22
+ f"Expected one of {trues + falses}")
23
+ # fmt: on
24
+ return False
25
+
26
+
27
+ def is_module_available(*modules: str) -> bool:
28
+ r"""Returns if a top-level module with :attr:`name` exists *without**
29
+ importing it. This is generally safer than try-catch block around a
30
+ `import X`. It avoids third party libraries breaking assumptions of some of
31
+ our tests, e.g., setting multiprocessing start method when imported
32
+ (see librosa/#747, torchvision/#544).
33
+ """
34
+ return all(importlib.util.find_spec(m) is not None for m in modules)
35
+
36
+
37
+ def requires_module(*modules: str):
38
+ """Decorate function to give error message if invoked without required optional modules.
39
+
40
+ This decorator is to give better error message to users rather
41
+ than raising ``NameError: name 'module' is not defined`` at random places.
42
+ """
43
+ missing = [m for m in modules if not is_module_available(m)]
44
+
45
+ if not missing:
46
+ # fall through. If all the modules are available, no need to decorate
47
+ def decorator(func):
48
+ return func
49
+
50
+ else:
51
+ req = f"module: {missing[0]}" if len(missing) == 1 else f"modules: {missing}"
52
+
53
+ def decorator(func):
54
+ @wraps(func)
55
+ def wrapped(*args, **kwargs):
56
+ raise RuntimeError(f"{func.__module__}.{func.__name__} requires {req}")
57
+
58
+ return wrapped
59
+
60
+ return decorator
61
+
62
+
63
+ def deprecated(direction: str, version: Optional[str] = None, remove: bool = False):
64
+ """Decorator to add deprecation message
65
+
66
+ Args:
67
+ direction (str): Migration steps to be given to users.
68
+ version (str or int): The version when the object will be removed
69
+ remove (bool): If enabled, append future removal message.
70
+ """
71
+
72
+ def decorator(func):
73
+ @wraps(func)
74
+ def wrapped(*args, **kwargs):
75
+ message = f"{func.__module__}.{func.__name__} has been deprecated. {direction}"
76
+ if remove:
77
+ message += f' It will be removed from {"future" if version is None else version} release. '
78
+ warnings.warn(message, stacklevel=2)
79
+ return func(*args, **kwargs)
80
+
81
+ message = "This function has been deprecated. "
82
+ if remove:
83
+ message += f'It will be removed from {"future" if version is None else version} release. '
84
+
85
+ wrapped.__doc__ = f"""DEPRECATED: {func.__doc__}
86
+
87
+ .. warning::
88
+
89
+ {message}
90
+ {direction}
91
+ """
92
+
93
+ return wrapped
94
+
95
+ return decorator
96
+
97
+
98
+ def fail_with_message(message):
99
+ """Generate decorator to give users message about missing TorchAudio extension."""
100
+
101
+ def decorator(func):
102
+ @wraps(func)
103
+ def wrapped(*args, **kwargs):
104
+ raise RuntimeError(f"{func.__module__}.{func.__name__} {message}")
105
+
106
+ return wrapped
107
+
108
+ return decorator
109
+
110
+
111
+ def no_op(func):
112
+ """Op-op decorator. Used in place of fail_with_message when a functionality that requires extension works fine."""
113
+ return func
lib/python3.12/site-packages/torchaudio/compliance/__init__.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ from . import kaldi
2
+
3
+ __all__ = [
4
+ "kaldi",
5
+ ]
lib/python3.12/site-packages/torchaudio/compliance/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (265 Bytes). View file