pyflyby 1.9.6__tar.gz → 1.9.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyflyby might be problematic. Click here for more details.
- {pyflyby-1.9.6/lib/python/pyflyby.egg-info → pyflyby-1.9.8}/PKG-INFO +35 -3
- {pyflyby-1.9.6 → pyflyby-1.9.8}/README.rst +33 -1
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/__init__.py +1 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_autoimp.py +45 -17
- {pyflyby-1.9.6/lib/python → pyflyby-1.9.8/bin}/pyflyby/_importdb.py +6 -4
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_importstmt.py +1 -1
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_interactive.py +13 -6
- {pyflyby-1.9.6/lib/python → pyflyby-1.9.8/bin}/pyflyby/_parse.py +27 -18
- {pyflyby-1.9.6/lib/python → pyflyby-1.9.8/bin}/pyflyby/_py.py +18 -18
- pyflyby-1.9.8/bin/pyflyby/_saveframe.py +1074 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_util.py +1 -2
- {pyflyby-1.9.6/lib/python → pyflyby-1.9.8/bin}/pyflyby/_version.py +1 -1
- pyflyby-1.9.8/bin/saveframe +298 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/etc/pyflyby/common.py +1 -1
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/__init__.py +1 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_autoimp.py +45 -17
- {pyflyby-1.9.6/bin → pyflyby-1.9.8/lib/python}/pyflyby/_importdb.py +6 -4
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_importstmt.py +1 -1
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_interactive.py +13 -6
- {pyflyby-1.9.6/bin → pyflyby-1.9.8/lib/python}/pyflyby/_parse.py +27 -18
- {pyflyby-1.9.6/bin → pyflyby-1.9.8/lib/python}/pyflyby/_py.py +18 -18
- pyflyby-1.9.8/lib/python/pyflyby/_saveframe.py +1074 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_util.py +1 -2
- {pyflyby-1.9.6/bin → pyflyby-1.9.8/lib/python}/pyflyby/_version.py +1 -1
- {pyflyby-1.9.6 → pyflyby-1.9.8/lib/python/pyflyby.egg-info}/PKG-INFO +35 -3
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby.egg-info/SOURCES.txt +4 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/setup.py +2 -1
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_autoimp.py +11 -1
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_importdb.py +24 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_modules.py +3 -1
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_parse.py +36 -0
- pyflyby-1.9.8/tests/test_saveframe.py +964 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/.pyflyby +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/LICENSE.txt +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/MANIFEST.in +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/autoipython +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/autopython +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/collect-exports +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/collect-imports +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/create-imports +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/find-import +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/list-bad-xrefs +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/prune-broken-imports +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/__main__.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_cmdline.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_comms.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_dbg.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_docxref.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_dynimp.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_file.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_flags.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_format.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_idents.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_import_sorting.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_importclns.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_imports2s.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_livepatch.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_log.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/_modules.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/autoimport.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby/importdb.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/pyflyby-diff +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/reformat-imports +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/replace-star-imports +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/tidy-imports +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/bin/transform-imports +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/LICENSE.txt +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/Makefile +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/TODO.txt +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/__init__.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/api.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/autoimp.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/cmdline.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/comms.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/dbg.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/file.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/flags.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/format.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/idents.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/importclns.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/importdb.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/imports2s.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/importstmt.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/interactive.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/livepatch.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/log.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/modules.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/parse.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/py.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/api/util.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/cli/autoipython.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/cli/cli.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/cli/collect_exports.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/cli/collect_imports.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/cli/find_import.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/cli/prune_broken_imports.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/cli/py.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/cli/pyflyby_diff.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/cli/reformat_imports.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/cli/replace_star_imports.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/cli/tidy_imports.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/cli/transform_imports.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/conf.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/index.rst +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/make.bat +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/doc/testing.txt +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/etc/pyflyby/canonical.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/etc/pyflyby/forget.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/etc/pyflyby/mandatory.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/etc/pyflyby/numpy.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/etc/pyflyby/std.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/emacs/pyflyby.el +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/__main__.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_cmdline.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_comms.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_dbg.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_docxref.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_dynimp.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_file.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_flags.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_format.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_idents.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_import_sorting.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_importclns.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_imports2s.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_livepatch.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_log.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/_modules.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/autoimport.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby/importdb.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby.egg-info/dependency_links.txt +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby.egg-info/entry_points.txt +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby.egg-info/requires.txt +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/lib/python/pyflyby.egg-info/top_level.txt +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/libexec/pyflyby/colordiff +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/libexec/pyflyby/diff-colorize +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/pyproject.toml +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/setup.cfg +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/__init__.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_0testconfig.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_cmdline.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_docxref.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_file.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_flags.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_format.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_idents.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_importclns.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_imports2s.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_importstmt.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_interactive.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_jupyterlab_pyflyby.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_livepatch.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_py.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/test_util.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/tests_sorts.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tests/xrefs.py +0 -0
- {pyflyby-1.9.6 → pyflyby-1.9.8}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyflyby
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.8
|
|
4
4
|
Summary: pyflyby - Python development productivity tools, in particular automatic import management
|
|
5
5
|
Home-page: https://pypi.org/project/pyflyby/
|
|
6
6
|
Author: Karl Chen
|
|
@@ -16,7 +16,7 @@ Classifier: Topic :: Software Development :: Interpreters
|
|
|
16
16
|
Classifier: Intended Audience :: Developers
|
|
17
17
|
Classifier: License :: OSI Approved :: MIT License
|
|
18
18
|
Classifier: Programming Language :: Python
|
|
19
|
-
Requires-Python: >3.
|
|
19
|
+
Requires-Python: >3.9, <4
|
|
20
20
|
License-File: LICENSE.txt
|
|
21
21
|
|
|
22
22
|
#########
|
|
@@ -113,7 +113,7 @@ configuration file:
|
|
|
113
113
|
|
|
114
114
|
.. code:: python
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
from pyflyby import add_import
|
|
118
118
|
|
|
119
119
|
add_import("foo", "foo = 1")
|
|
@@ -481,6 +481,38 @@ Emacs support
|
|
|
481
481
|
- Pyflyby.el doesn't yet work with XEmacs; patches welcome.
|
|
482
482
|
|
|
483
483
|
|
|
484
|
+
saveframe: A utility for debugging / reproducing an issue
|
|
485
|
+
=========================================================
|
|
486
|
+
|
|
487
|
+
PyFlyBy provides a utility named **saveframe** which can be used to save
|
|
488
|
+
information for debugging / reproducing an issue.
|
|
489
|
+
|
|
490
|
+
**Usage**: If you have a piece of code or a script that is failing due an issue
|
|
491
|
+
originating from upstream code, and you cannot share your private code as a reproducer,
|
|
492
|
+
use this utility to save relevant information to a file. Share the generated file with
|
|
493
|
+
the upstream team, enabling them to reproduce and diagnose the issue independently.
|
|
494
|
+
|
|
495
|
+
**Information saved in the file**: This utility captures and saves *error stack frames*
|
|
496
|
+
to a file. It includes the values of local variables from each stack frame, as well
|
|
497
|
+
as metadata about each frame and the exception raised by your code.
|
|
498
|
+
|
|
499
|
+
This utility comes with 2 interfaces:
|
|
500
|
+
|
|
501
|
+
1. **A function**: For interactive usages such as IPython, Jupyter Notebook, or a
|
|
502
|
+
debugger (pdb/ipdb), use **pyflyby.saveframe** function. To know how to use this
|
|
503
|
+
function, checkout it's documentation:
|
|
504
|
+
|
|
505
|
+
.. code::
|
|
506
|
+
|
|
507
|
+
In [1]: saveframe?
|
|
508
|
+
|
|
509
|
+
2. **A script**: For cli usages (like a failing script), use **pyflyby/bin/saveframe**
|
|
510
|
+
script. To know how to use this script, checkout its documentation:
|
|
511
|
+
|
|
512
|
+
.. code::
|
|
513
|
+
|
|
514
|
+
$ saveframe --help
|
|
515
|
+
|
|
484
516
|
Authorship
|
|
485
517
|
==========
|
|
486
518
|
|
|
@@ -92,7 +92,7 @@ configuration file:
|
|
|
92
92
|
|
|
93
93
|
.. code:: python
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
from pyflyby import add_import
|
|
97
97
|
|
|
98
98
|
add_import("foo", "foo = 1")
|
|
@@ -460,6 +460,38 @@ Emacs support
|
|
|
460
460
|
- Pyflyby.el doesn't yet work with XEmacs; patches welcome.
|
|
461
461
|
|
|
462
462
|
|
|
463
|
+
saveframe: A utility for debugging / reproducing an issue
|
|
464
|
+
=========================================================
|
|
465
|
+
|
|
466
|
+
PyFlyBy provides a utility named **saveframe** which can be used to save
|
|
467
|
+
information for debugging / reproducing an issue.
|
|
468
|
+
|
|
469
|
+
**Usage**: If you have a piece of code or a script that is failing due an issue
|
|
470
|
+
originating from upstream code, and you cannot share your private code as a reproducer,
|
|
471
|
+
use this utility to save relevant information to a file. Share the generated file with
|
|
472
|
+
the upstream team, enabling them to reproduce and diagnose the issue independently.
|
|
473
|
+
|
|
474
|
+
**Information saved in the file**: This utility captures and saves *error stack frames*
|
|
475
|
+
to a file. It includes the values of local variables from each stack frame, as well
|
|
476
|
+
as metadata about each frame and the exception raised by your code.
|
|
477
|
+
|
|
478
|
+
This utility comes with 2 interfaces:
|
|
479
|
+
|
|
480
|
+
1. **A function**: For interactive usages such as IPython, Jupyter Notebook, or a
|
|
481
|
+
debugger (pdb/ipdb), use **pyflyby.saveframe** function. To know how to use this
|
|
482
|
+
function, checkout it's documentation:
|
|
483
|
+
|
|
484
|
+
.. code::
|
|
485
|
+
|
|
486
|
+
In [1]: saveframe?
|
|
487
|
+
|
|
488
|
+
2. **A script**: For cli usages (like a failing script), use **pyflyby/bin/saveframe**
|
|
489
|
+
script. To know how to use this script, checkout its documentation:
|
|
490
|
+
|
|
491
|
+
.. code::
|
|
492
|
+
|
|
493
|
+
$ saveframe --help
|
|
494
|
+
|
|
463
495
|
Authorship
|
|
464
496
|
==========
|
|
465
497
|
|
|
@@ -32,6 +32,7 @@ from pyflyby._interactive import (disable_auto_importer,
|
|
|
32
32
|
from pyflyby._livepatch import livepatch, xreload
|
|
33
33
|
from pyflyby._log import logger
|
|
34
34
|
from pyflyby._parse import PythonBlock, PythonStatement
|
|
35
|
+
from pyflyby._saveframe import saveframe
|
|
35
36
|
from pyflyby._version import __version__
|
|
36
37
|
|
|
37
38
|
# Deprecated:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# pyflyby/_autoimp.py.
|
|
2
|
-
# Copyright (C) 2011, 2012, 2013, 2014, 2015, 2018, 2019 Karl Chen.
|
|
2
|
+
# Copyright (C) 2011, 2012, 2013, 2014, 2015, 2018, 2019, 2024 Karl Chen.
|
|
3
3
|
# License: MIT http://opensource.org/licenses/MIT
|
|
4
4
|
|
|
5
5
|
|
|
@@ -24,7 +24,7 @@ from pyflyby._parse import (PythonBlock, _is_ast_str,
|
|
|
24
24
|
from six import reraise
|
|
25
25
|
import sys
|
|
26
26
|
import types
|
|
27
|
-
from typing import Any, Set
|
|
27
|
+
from typing import Any, Set, Optional, List, Tuple
|
|
28
28
|
|
|
29
29
|
if sys.version_info >= (3, 12):
|
|
30
30
|
ATTRIBUTE_NAME = "value"
|
|
@@ -341,19 +341,26 @@ def symbol_needs_import(fullname, namespaces):
|
|
|
341
341
|
return True
|
|
342
342
|
|
|
343
343
|
|
|
344
|
-
class _UseChecker
|
|
344
|
+
class _UseChecker:
|
|
345
345
|
"""
|
|
346
346
|
An object that can check whether it was used.
|
|
347
347
|
"""
|
|
348
|
-
used = False
|
|
349
348
|
|
|
350
|
-
|
|
349
|
+
used: bool = False
|
|
350
|
+
name: str
|
|
351
|
+
source: str
|
|
352
|
+
lineno: int
|
|
353
|
+
|
|
354
|
+
def __init__(self, name: str, source: str, lineno: int):
|
|
351
355
|
self.name = name
|
|
352
356
|
self.source = source # generally an Import
|
|
353
357
|
self.lineno = lineno
|
|
354
358
|
|
|
359
|
+
def __repr__(self):
|
|
360
|
+
return f"<{type(self).__name__}: name:{self.name} source:{self.source!r} lineno:{self.lineno} used:{self.used}>"
|
|
361
|
+
|
|
355
362
|
|
|
356
|
-
class _MissingImportFinder
|
|
363
|
+
class _MissingImportFinder:
|
|
357
364
|
"""
|
|
358
365
|
A helper class to be used only by `_find_missing_imports_in_ast`.
|
|
359
366
|
|
|
@@ -371,8 +378,14 @@ class _MissingImportFinder(object):
|
|
|
371
378
|
|
|
372
379
|
"""
|
|
373
380
|
|
|
374
|
-
|
|
375
|
-
|
|
381
|
+
scopestack: ScopeStack
|
|
382
|
+
_lineno: Optional[int]
|
|
383
|
+
missing_imports: List[Tuple[Optional[int], DottedIdentifier]]
|
|
384
|
+
parse_docstrings: bool
|
|
385
|
+
unused_imports: Optional[List[Tuple[int, str]]]
|
|
386
|
+
_deferred_load_checks: list[tuple[str, ScopeStack, Optional[int]]]
|
|
387
|
+
|
|
388
|
+
def __init__(self, scopestack, *, find_unused_imports:bool, parse_docstrings:bool):
|
|
376
389
|
"""
|
|
377
390
|
Construct the AST visitor.
|
|
378
391
|
|
|
@@ -385,19 +398,26 @@ class _MissingImportFinder(object):
|
|
|
385
398
|
# includes the globals dictionary. ScopeStack() will make sure this
|
|
386
399
|
# includes builtins.
|
|
387
400
|
scopestack = ScopeStack(scopestack)
|
|
401
|
+
|
|
388
402
|
# Add an empty namespace to the stack. This facilitates adding stuff
|
|
389
403
|
# to scopestack[-1] without ever modifying user globals.
|
|
390
404
|
scopestack = scopestack.with_new_scope()
|
|
405
|
+
|
|
391
406
|
self.scopestack = scopestack
|
|
407
|
+
|
|
392
408
|
# Create data structure to hold the result.
|
|
393
409
|
# missing_imports is a list of (lineno, DottedIdentifier) tuples.
|
|
394
410
|
self.missing_imports = []
|
|
411
|
+
|
|
395
412
|
# unused_imports is a list of (lineno, Import) tuples, if enabled.
|
|
396
413
|
self.unused_imports = [] if find_unused_imports else None
|
|
414
|
+
|
|
397
415
|
self.parse_docstrings = parse_docstrings
|
|
416
|
+
|
|
398
417
|
# Function bodies that we need to check after defining names in this
|
|
399
418
|
# function scope.
|
|
400
419
|
self._deferred_load_checks = []
|
|
420
|
+
|
|
401
421
|
# Whether we're currently in a FunctionDef.
|
|
402
422
|
self._in_FunctionDef = False
|
|
403
423
|
# Current lineno.
|
|
@@ -419,7 +439,8 @@ class _MissingImportFinder(object):
|
|
|
419
439
|
finally:
|
|
420
440
|
self.scopestack = oldscopestack
|
|
421
441
|
|
|
422
|
-
def scan_for_import_issues(self, codeblock):
|
|
442
|
+
def scan_for_import_issues(self, codeblock: PythonBlock):
|
|
443
|
+
assert isinstance(codeblock, PythonBlock)
|
|
423
444
|
# See global `scan_for_import_issues`
|
|
424
445
|
if not isinstance(codeblock, PythonBlock):
|
|
425
446
|
codeblock = PythonBlock(codeblock)
|
|
@@ -588,7 +609,7 @@ class _MissingImportFinder(object):
|
|
|
588
609
|
return
|
|
589
610
|
if (len(node.targets) == 1 and isinstance(node.targets[0], ast.Name)
|
|
590
611
|
and node.targets[0].id == '__all__'):
|
|
591
|
-
if not isinstance(node.value, ast.List):
|
|
612
|
+
if not isinstance(node.value, (ast.List, ast.Tuple)):
|
|
592
613
|
logger.warning("Don't know how to handle __all__ as (%s)" % node.value)
|
|
593
614
|
return
|
|
594
615
|
if not all(_is_ast_str(e) for e in node.value.elts):
|
|
@@ -909,9 +930,8 @@ class _MissingImportFinder(object):
|
|
|
909
930
|
|
|
910
931
|
def _visit_StoreImport(self, node, modulename):
|
|
911
932
|
name = node.asname or node.name
|
|
912
|
-
logger.debug("_visit_StoreImport(asname=%r,name=%r)",
|
|
913
|
-
|
|
914
|
-
is_star = node.name == '*'
|
|
933
|
+
logger.debug("_visit_StoreImport(asname=%r, name=%r)", node.asname, node.name)
|
|
934
|
+
is_star = node.name == "*"
|
|
915
935
|
if is_star:
|
|
916
936
|
logger.debug("Got star import: line %s: 'from %s import *'",
|
|
917
937
|
self._lineno, modulename)
|
|
@@ -998,10 +1018,11 @@ class _MissingImportFinder(object):
|
|
|
998
1018
|
# Don't call generic_visit(node) here. Reason: We already visit the
|
|
999
1019
|
# parts above, if relevant.
|
|
1000
1020
|
|
|
1001
|
-
def _visit_Load_defered_global(self, fullname):
|
|
1021
|
+
def _visit_Load_defered_global(self, fullname:str):
|
|
1002
1022
|
"""
|
|
1003
1023
|
Some things will be resolved in global scope later.
|
|
1004
1024
|
"""
|
|
1025
|
+
assert isinstance(fullname, str), fullname
|
|
1005
1026
|
logger.debug("_visit_Load_defered_global(%r)", fullname)
|
|
1006
1027
|
if symbol_needs_import(fullname, self.scopestack):
|
|
1007
1028
|
data = (fullname, self.scopestack, self._lineno)
|
|
@@ -1090,7 +1111,11 @@ class _MissingImportFinder(object):
|
|
|
1090
1111
|
unused_imports.sort()
|
|
1091
1112
|
|
|
1092
1113
|
|
|
1093
|
-
def scan_for_import_issues(
|
|
1114
|
+
def scan_for_import_issues(
|
|
1115
|
+
codeblock: PythonBlock,
|
|
1116
|
+
find_unused_imports: bool = True,
|
|
1117
|
+
parse_docstrings: bool = False,
|
|
1118
|
+
):
|
|
1094
1119
|
"""
|
|
1095
1120
|
Find missing and unused imports, by lineno.
|
|
1096
1121
|
|
|
@@ -1117,7 +1142,7 @@ def scan_for_import_issues(codeblock, find_unused_imports=True, parse_docstrings
|
|
|
1117
1142
|
([], [(1, Import('import baz'))])
|
|
1118
1143
|
|
|
1119
1144
|
"""
|
|
1120
|
-
logger.debug("scan_for_import_issues()")
|
|
1145
|
+
logger.debug("global scan_for_import_issues()")
|
|
1121
1146
|
if not isinstance(codeblock, PythonBlock):
|
|
1122
1147
|
codeblock = PythonBlock(codeblock)
|
|
1123
1148
|
namespaces = ScopeStack([{}])
|
|
@@ -1148,7 +1173,10 @@ def _find_missing_imports_in_ast(node, namespaces):
|
|
|
1148
1173
|
# Traverse the abstract syntax tree.
|
|
1149
1174
|
if logger.debug_enabled:
|
|
1150
1175
|
logger.debug("ast=%s", ast.dump(node))
|
|
1151
|
-
return _MissingImportFinder(
|
|
1176
|
+
return _MissingImportFinder(
|
|
1177
|
+
namespaces,
|
|
1178
|
+
find_unused_imports=False,
|
|
1179
|
+
parse_docstrings=False).find_missing_imports(node)
|
|
1152
1180
|
|
|
1153
1181
|
# TODO: maybe we should replace _find_missing_imports_in_ast with
|
|
1154
1182
|
# _find_missing_imports_in_code(compile(node)). The method of parsing opcodes
|
|
@@ -284,7 +284,7 @@ class ImportDB:
|
|
|
284
284
|
target_path = Path(target_filename).resolve()
|
|
285
285
|
|
|
286
286
|
parents: List[Path]
|
|
287
|
-
if
|
|
287
|
+
if target_path.is_dir():
|
|
288
288
|
parents = [target_path]
|
|
289
289
|
else:
|
|
290
290
|
parents = []
|
|
@@ -560,9 +560,11 @@ class ImportDB:
|
|
|
560
560
|
filenames = [filenames]
|
|
561
561
|
for f in filenames:
|
|
562
562
|
assert isinstance(f, Filename)
|
|
563
|
-
logger.debug(
|
|
564
|
-
|
|
565
|
-
|
|
563
|
+
logger.debug(
|
|
564
|
+
"ImportDB: loading %r, mandatory=%r",
|
|
565
|
+
[str(f) for f in filenames],
|
|
566
|
+
[str(f) for f in _mandatory_filenames_deprecated],
|
|
567
|
+
)
|
|
566
568
|
if SUPPORT_DEPRECATED_BEHAVIOR:
|
|
567
569
|
# Before 2014-10, pyflyby read the following:
|
|
568
570
|
# * known_imports from $PYFLYBY_PATH/known_imports/**/*.py or
|
|
@@ -15,7 +15,7 @@ import re
|
|
|
15
15
|
import subprocess
|
|
16
16
|
import sys
|
|
17
17
|
|
|
18
|
-
from typing import List, Any, Dict
|
|
18
|
+
from typing import List, Any, Dict, Union, Literal
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
from pyflyby._autoimp import (LoadSymbolError, ScopeStack, auto_eval,
|
|
@@ -2269,7 +2269,9 @@ class AutoImporter:
|
|
|
2269
2269
|
|
|
2270
2270
|
def _safe_call(self, function, *args, **kwargs):
|
|
2271
2271
|
on_error = kwargs.pop("on_error", None)
|
|
2272
|
-
raise_on_error = kwargs.pop(
|
|
2272
|
+
raise_on_error: Union[bool, Literal["if_debug"]] = kwargs.pop(
|
|
2273
|
+
"raise_on_error", "if_debug"
|
|
2274
|
+
)
|
|
2273
2275
|
if self._errored:
|
|
2274
2276
|
# If we previously errored, then we should already have
|
|
2275
2277
|
# unregistered the hook that led to here. However, in some corner
|
|
@@ -2295,7 +2297,7 @@ class AutoImporter:
|
|
|
2295
2297
|
logger.error("Error trying to disable: %s: %s",
|
|
2296
2298
|
type(e2).__name__, e2)
|
|
2297
2299
|
# Raise or print traceback in debug mode.
|
|
2298
|
-
if raise_on_error
|
|
2300
|
+
if raise_on_error is True:
|
|
2299
2301
|
raise
|
|
2300
2302
|
elif raise_on_error == 'if_debug':
|
|
2301
2303
|
if logger.debug_enabled:
|
|
@@ -2305,7 +2307,7 @@ class AutoImporter:
|
|
|
2305
2307
|
import traceback
|
|
2306
2308
|
traceback.print_exc()
|
|
2307
2309
|
raise
|
|
2308
|
-
elif raise_on_error
|
|
2310
|
+
elif raise_on_error is False:
|
|
2309
2311
|
if logger.debug_enabled:
|
|
2310
2312
|
import traceback
|
|
2311
2313
|
traceback.print_exc()
|
|
@@ -2328,8 +2330,13 @@ class AutoImporter:
|
|
|
2328
2330
|
sorted([k for k,v in autoimported.items() if not v]))
|
|
2329
2331
|
self._autoimported_this_cell = {}
|
|
2330
2332
|
|
|
2331
|
-
def auto_import(
|
|
2332
|
-
|
|
2333
|
+
def auto_import(
|
|
2334
|
+
self,
|
|
2335
|
+
arg,
|
|
2336
|
+
namespaces=None,
|
|
2337
|
+
raise_on_error: Union[bool, Literal["if_debug"]] = "if_debug",
|
|
2338
|
+
on_error=None,
|
|
2339
|
+
):
|
|
2333
2340
|
if namespaces is None:
|
|
2334
2341
|
namespaces = get_global_namespaces(self._ip)
|
|
2335
2342
|
|
|
@@ -20,7 +20,7 @@ import re
|
|
|
20
20
|
import sys
|
|
21
21
|
from textwrap import dedent
|
|
22
22
|
import types
|
|
23
|
-
from typing import Any, List, Optional, Tuple, Union, cast
|
|
23
|
+
from typing import Any, List, Optional, Tuple, Union, cast, Literal
|
|
24
24
|
import warnings
|
|
25
25
|
|
|
26
26
|
|
|
@@ -353,7 +353,7 @@ def _annotate_ast_nodes(ast_node: ast.AST) -> AnnotatedAst:
|
|
|
353
353
|
|
|
354
354
|
|
|
355
355
|
def _annotate_ast_startpos(
|
|
356
|
-
ast_node: ast.AST, parent_ast_node, minpos, text, flags
|
|
356
|
+
ast_node: ast.AST, parent_ast_node, minpos: FilePos, text: FileText, flags
|
|
357
357
|
) -> bool:
|
|
358
358
|
r"""
|
|
359
359
|
Annotate ``ast_node``. Set ``ast_node.startpos`` to the starting position
|
|
@@ -410,8 +410,8 @@ def _annotate_ast_startpos(
|
|
|
410
410
|
# Walk all nodes/fields of the AST. We implement this as a custom
|
|
411
411
|
# depth-first search instead of using ast.walk() or ast.NodeVisitor
|
|
412
412
|
# so that we can easily keep track of the preceding node's lineno.
|
|
413
|
-
child_minpos = minpos
|
|
414
|
-
is_first_child = True
|
|
413
|
+
child_minpos: FilePos = minpos
|
|
414
|
+
is_first_child: bool = True
|
|
415
415
|
leftstr_node = None
|
|
416
416
|
for child_node in _iter_child_nodes_in_order(aast_node):
|
|
417
417
|
leftstr = _annotate_ast_startpos(
|
|
@@ -469,7 +469,16 @@ def _annotate_ast_startpos(
|
|
|
469
469
|
# Not a multiline string literal. (I.e., it could be a non-string or
|
|
470
470
|
# a single-line string.)
|
|
471
471
|
# Easy.
|
|
472
|
-
|
|
472
|
+
if sys.version_info < (3, 12):
|
|
473
|
+
"""There is an issue for f-strings at the begining of a file in 3.11 and
|
|
474
|
+
before
|
|
475
|
+
|
|
476
|
+
https://github.com/deshaw/pyflyby/issues/361,
|
|
477
|
+
here we ensure a child node min pos, can't be before it's parent.
|
|
478
|
+
"""
|
|
479
|
+
startpos = max(text.startpos + delta, minpos)
|
|
480
|
+
else:
|
|
481
|
+
startpos = text.startpos + delta
|
|
473
482
|
|
|
474
483
|
# Special case for 'with' statements. Consider the code:
|
|
475
484
|
# with X: pass
|
|
@@ -668,7 +677,7 @@ def _ast_node_is_in_docstring_position(ast_node):
|
|
|
668
677
|
return False
|
|
669
678
|
|
|
670
679
|
|
|
671
|
-
def infer_compile_mode(arg):
|
|
680
|
+
def infer_compile_mode(arg:ast.AST) -> Literal['exec','eval','single']:
|
|
672
681
|
"""
|
|
673
682
|
Infer the mode needed to compile ``arg``.
|
|
674
683
|
|
|
@@ -679,19 +688,18 @@ def infer_compile_mode(arg):
|
|
|
679
688
|
"""
|
|
680
689
|
# Infer mode from ast object.
|
|
681
690
|
if isinstance(arg, ast.Module):
|
|
682
|
-
|
|
691
|
+
return "exec"
|
|
683
692
|
elif isinstance(arg, ast.Expression):
|
|
684
|
-
|
|
693
|
+
return "eval"
|
|
685
694
|
elif isinstance(arg, ast.Interactive):
|
|
686
|
-
|
|
695
|
+
return "single"
|
|
687
696
|
else:
|
|
688
697
|
raise TypeError(
|
|
689
698
|
"Expected Module/Expression/Interactive ast node; got %s"
|
|
690
699
|
% (type(arg).__name__))
|
|
691
|
-
return mode
|
|
692
700
|
|
|
693
701
|
|
|
694
|
-
class _DummyAst_Node
|
|
702
|
+
class _DummyAst_Node:
|
|
695
703
|
pass
|
|
696
704
|
|
|
697
705
|
|
|
@@ -945,8 +953,9 @@ class PythonBlock:
|
|
|
945
953
|
return cls.from_text(Filename(filename))
|
|
946
954
|
|
|
947
955
|
@classmethod
|
|
948
|
-
def from_text(
|
|
949
|
-
|
|
956
|
+
def from_text(
|
|
957
|
+
cls, text, filename=None, startpos=None, flags=None, auto_flags: bool = False
|
|
958
|
+
):
|
|
950
959
|
"""
|
|
951
960
|
:type text:
|
|
952
961
|
`FileText` or convertible
|
|
@@ -1144,7 +1153,7 @@ class PythonBlock:
|
|
|
1144
1153
|
else:
|
|
1145
1154
|
return None
|
|
1146
1155
|
|
|
1147
|
-
def parse(self, mode=None) -> Union[ast.Expression, ast.Module]:
|
|
1156
|
+
def parse(self, mode: Optional[str] = None) -> Union[ast.Expression, ast.Module]:
|
|
1148
1157
|
"""
|
|
1149
1158
|
Parse the source text into an AST.
|
|
1150
1159
|
|
|
@@ -1165,7 +1174,7 @@ class PythonBlock:
|
|
|
1165
1174
|
return self.expression_ast_node
|
|
1166
1175
|
else:
|
|
1167
1176
|
raise SyntaxError
|
|
1168
|
-
elif mode
|
|
1177
|
+
elif mode is None:
|
|
1169
1178
|
if self.expression_ast_node:
|
|
1170
1179
|
return self.expression_ast_node
|
|
1171
1180
|
else:
|
|
@@ -1176,7 +1185,7 @@ class PythonBlock:
|
|
|
1176
1185
|
else:
|
|
1177
1186
|
raise ValueError("parse(): invalid mode=%r" % (mode,))
|
|
1178
1187
|
|
|
1179
|
-
def compile(self, mode=None):
|
|
1188
|
+
def compile(self, mode: Optional[str] = None):
|
|
1180
1189
|
"""
|
|
1181
1190
|
Parse into AST and compile AST into code.
|
|
1182
1191
|
|
|
@@ -1184,9 +1193,9 @@ class PythonBlock:
|
|
|
1184
1193
|
``CodeType``
|
|
1185
1194
|
"""
|
|
1186
1195
|
ast_node = self.parse(mode=mode)
|
|
1187
|
-
|
|
1196
|
+
c_mode = infer_compile_mode(ast_node)
|
|
1188
1197
|
filename = str(self.filename or "<unknown>")
|
|
1189
|
-
return compile(ast_node, filename,
|
|
1198
|
+
return compile(ast_node, filename, c_mode)
|
|
1190
1199
|
|
|
1191
1200
|
@cached_property
|
|
1192
1201
|
def statements(self) -> Tuple[PythonStatement, ...]:
|
|
@@ -286,19 +286,6 @@ from typing import Any
|
|
|
286
286
|
from pyflyby._util import cmp
|
|
287
287
|
from shlex import quote as shquote
|
|
288
288
|
|
|
289
|
-
usage = """
|
|
290
|
-
py --- command-line python multitool with automatic importing
|
|
291
|
-
|
|
292
|
-
$ py [--file] filename.py arg1 arg2 Execute file
|
|
293
|
-
$ py [--apply] function arg1 arg2 Call function
|
|
294
|
-
$ py [--eval] 'function(arg1, arg2)' Evaluate code
|
|
295
|
-
$ py [--module] modname arg1 arg2 Run a module
|
|
296
|
-
|
|
297
|
-
$ py --debug file/code... args... Debug code
|
|
298
|
-
$ py --debug PID Attach debugger to PID
|
|
299
|
-
|
|
300
|
-
$ py IPython shell
|
|
301
|
-
""".strip()
|
|
302
289
|
|
|
303
290
|
# TODO: add --tidy-imports, etc
|
|
304
291
|
|
|
@@ -377,6 +364,19 @@ from pyflyby._modules import ModuleHandle
|
|
|
377
364
|
from pyflyby._parse import PythonBlock
|
|
378
365
|
from pyflyby._util import indent, prefixes
|
|
379
366
|
|
|
367
|
+
usage = """
|
|
368
|
+
py --- command-line python multitool with automatic importing
|
|
369
|
+
|
|
370
|
+
$ py [--file] filename.py arg1 arg2 Execute file
|
|
371
|
+
$ py [--apply] function arg1 arg2 Call function
|
|
372
|
+
$ py [--eval] 'function(arg1, arg2)' Evaluate code
|
|
373
|
+
$ py [--module] modname arg1 arg2 Run a module
|
|
374
|
+
|
|
375
|
+
$ py --debug file/code... args... Debug code
|
|
376
|
+
$ py --debug PID Attach debugger to PID
|
|
377
|
+
|
|
378
|
+
$ py IPython shell
|
|
379
|
+
""".strip()
|
|
380
380
|
|
|
381
381
|
# Default compiler flags (feature flags) used for all user code. We include
|
|
382
382
|
# "print_function" here, but we also use auto_flags=True, which means
|
|
@@ -409,7 +409,7 @@ def _get_argspec(arg:Any) -> inspect.FullArgSpec:
|
|
|
409
409
|
"_get_argspec: unexpected %s" % (type(arg).__name__,))
|
|
410
410
|
|
|
411
411
|
|
|
412
|
-
def _requires_parens_as_function(function_name:str):
|
|
412
|
+
def _requires_parens_as_function(function_name:str) -> bool:
|
|
413
413
|
"""
|
|
414
414
|
Returns whether the given string of a callable would require parentheses
|
|
415
415
|
around it to call it.
|
|
@@ -552,7 +552,7 @@ class _ParseInterruptedWantSource(Exception):
|
|
|
552
552
|
pass
|
|
553
553
|
|
|
554
554
|
|
|
555
|
-
class UserExpr
|
|
555
|
+
class UserExpr:
|
|
556
556
|
"""
|
|
557
557
|
An expression from user input, and its evaluated value.
|
|
558
558
|
|
|
@@ -647,7 +647,7 @@ class UserExpr(object):
|
|
|
647
647
|
else:
|
|
648
648
|
raise ValueError("UserExpr(): bad arg_mode=%r" % (arg_mode,))
|
|
649
649
|
|
|
650
|
-
def _infer_and_evaluate(self):
|
|
650
|
+
def _infer_and_evaluate(self) -> None:
|
|
651
651
|
if self._original_arg_mode == "raw_value":
|
|
652
652
|
pass
|
|
653
653
|
elif self._original_arg_mode == "eval":
|
|
@@ -891,7 +891,7 @@ class NotAFunctionError(Exception):
|
|
|
891
891
|
pass
|
|
892
892
|
|
|
893
893
|
|
|
894
|
-
def _get_help(expr, verbosity=1):
|
|
894
|
+
def _get_help(expr:UserExpr, verbosity:int=1) -> str:
|
|
895
895
|
"""
|
|
896
896
|
Construct a help string.
|
|
897
897
|
|
|
@@ -1053,7 +1053,7 @@ def auto_apply(function, commandline_args, namespace, arg_mode=None,
|
|
|
1053
1053
|
|
|
1054
1054
|
|
|
1055
1055
|
@total_ordering
|
|
1056
|
-
class LoggedList
|
|
1056
|
+
class LoggedList:
|
|
1057
1057
|
"""
|
|
1058
1058
|
List that logs which members have not yet been accessed (nor removed).
|
|
1059
1059
|
"""
|