Pyflyby
Pyflyby is a set of Python programming productivity tools for Python 3.8+.
- For command-line interaction:
py
: command-line multitool
- For IPython interaction:
autoimporter
: automatically imports symbols when needed.
- For editing python source code:
tidy-imports
: adds missing ‘import’s, removes unused ‘import’s, and also reformats import blocks.find-import
: prints to stdout how to import a particular symbol.reformat-imports
: reformatsimport
blockscollect-imports
: prints out all the imports in a given set of files.collect-exports
: prints out definitions in a given set of modules, in the form of import statements.transform-imports
: renames imported modules/functions.
Installation
$ pip install pyflyby
- This creates an alias for your ipython named py which runs the pyflyby plug internally.
pyflyby has a dependency on ipython, if it isn’t already installed do install it with:
$ pip install ipython
Quick start: Autoimporter + IPython
$ py
In [1]: re.search("[a-z]+", "....hello...").group(0)
[PYFLYBY] import re
Out[1]: 'hello'
In [2]: chisqprob(arange(5), 2)
[PYFLYBY] from numpy import arange
[PYFLYBY] from scipy.stats import chisqprob
Out[2]: [ 1. 0.6065 0.3679 0.2231 0.1353]
To load pyflyby into an existing IPython session as a 1-off:
$ ipython
In [1]: %load_ext pyflyby
To configure IPython/Jupyter Notebook to load pyflyby automatically:
$ py pyflyby.install_in_ipython_config_file
or
$ echo 'c.InteractiveShellApp.extensions.append("pyflyby")' \
>> ~/.ipython/profile_default/ipython_config.py
$ ipython
In [1]: b64decode('aGVsbG8=')
[PYFLYBY] from base64 import b64decode
Out[1]: 'hello'
Auto importer lazy variables
It is possible to use the autoimporter to lazily define variables.
To use, put the following in your IPython startup files
(~/.ipython/profile_default/startup/autoimp.py
), or in your IPython
configuration file:
from pyflyby import add_import
add_import("foo", "foo = 1")
add_import(
"df, data as dd",
'''
import pandas as pd
data = [1,2,3]
df = pd.DataFrame(data)
''')
You can add the keyword strict=False
to not fail if not in IPython or of the
pyflyby extensions is not loaded.
Quick start: py
command-line multi-tool
$ py b64decode aGVsbG8=
[PYFLYBY] from base64 import b64decode
[PYFLYBY] b64decode('aGVsbG8=', altchars=None)
'hello'
$ py log2 sys.maxint
[PYFLYBY] from numpy import log2
[PYFLYBY] import sys
[PYFLYBY] log2(9223372036854775807)
63.0
$ py 'plot(cos(arange(30)))'
[PYFLYBY] from numpy import arange
[PYFLYBY] from numpy import cos
[PYFLYBY] from matplotlib.pyplot import plot
[PYFLYBY] plot(cos(arange(30)))
<plot>
$ py 38497631 / 13951446
2.7594007818257693
$ py foo.py
Quick start: tidy-imports
To use tidy-imports
, just specify the filename(s) to tidy.
For example:
$ echo 're.search("[a-z]+", "....hello..."), chisqprob(arange(5), 2)' > foo.py
$ tidy-imports foo.py
--- /tmp/foo.py
+++ /tmp/foo.py
@@ -1 +1,9 @@
+from __future__ import absolute_import, division, with_statement
+
+from numpy import arange
+from scipy.stats import chisqprob
+import re
+
re.search("[a-z]+", "....hello..."), chisqprob(arange(5), 2)
Replace /tmp/foo.py? [y/N]
Quick start: import libraries
Create a file named .pyflyby with lines such as
from mypackage.mymodule import MyClass, my_function
import anotherpackage.anothermodule
You can put this file in your home directory or in the same directory as your
*.py
files.
Details: automatic imports
AUTOMATIC IMPORTS - never type “import” again!
This module allows your “known imports” to work automatically in your IPython interactive session without having to type the ‘import’ statements (and also without having to slow down your Python startup with imports you only use occasionally).
Example:
In [1]: re.search("[a-z]+", "....hello...").group(0)
[PYFLYBY] import re
Out[1]: 'hello'
In [2]: chisqprob(arange(5), 2)
[PYFLYBY] from numpy import arange
[PYFLYBY] from scipy.stats import chisqprob
Out[2]: [ 1. 0.6065 0.3679 0.2231 0.1353]
In [3]: np.sin(arandom(5))
[PYFLYBY] from numpy.random import random as arandom
[PYFLYBY] import numpy as np
Out[3]: [ 0.0282 0.0603 0.4653 0.8371 0.3347]
In [4]: isinstance(42, Number)
[PYFLYBY] from numbers import Number
Out[4]: True
It just works
Tab completion works, even on modules that are not yet imported. In the following example, notice that numpy is imported when we need to know its members, and only then:
$ ipython
In [1]: nump<TAB>
In [1]: numpy
In [1]: numpy.arang<TAB>
[PYFLYBY] import numpy
In [1]: numpy.arange
The IPython “?” magic help (pinfo/pinfo2) automatically imports symbols first if necessary:
$ ipython
In [1]: arange?
[PYFLYBY] from numpy import arange
... Docstring: arange([start,] stop[, step,], dtype=None) ...
Other IPython magic commands work as well:
$ ipython
In [1]: %timeit np.cos(pi)
[PYFLYBY] import numpy as np
[PYFLYBY] from numpy import pi
100000 loops, best of 3: 2.51 us per loop
$ echo 'print arange(4)' > foo.py
$ ipython
In [1]: %run foo.py
[PYFLYBY] from numpy import arange
[0 1 2 3]
Implementation details
The automatic importing happens at parse time, before code is executed. The namespace never contains entries for names that are not yet imported.
This method of importing at parse time contrasts with previous implementations of automatic importing that use proxy objects. Those implementations using proxy objects don’t work as well, because it is impossible to make proxy objects behave perfectly. For example, instance(x, T) will return the wrong answer if either x or T is a proxy object.
Compatibility
- Tested with:
Python 3.8, 3.9, 3.10
IPython 0.10, 0.11, 0.12, 0.13, 1.0, 1.2, 2.0, 2.1, 2.2, 2.3, 2.4, 3.0, 3.1, 3.2, 4.0., 7.11 (latest)
IPython (text console), IPython Notebook, Spyder
Details: import libraries
Pyflyby uses “import libraries” that tell how to import a given symbol.
An import library file is simply a python source file containing ‘import’ (or
‘from … import …’) lines. These can be generated automatically with
collect-imports
and collect-exports
.
Known imports
Find-imports, tidy-imports
, and autoimport consult the database of known
imports to figure out where to get an import. For example, if the
imports database contains:
from numpy import arange, NaN
then when you type the following in IPython:
print(arange(10))
the autoimporter would automatically execute from numpy import arange
.
The database can be one file or multiple files. This makes it easy to have project-specific known_imports along with global and per-user defaults.
The PYFLYBY_PATH
environment variable specifies which files to read.
This is a colon-separated list of filenames or directory names. The default
is:
PYFLYBY_PATH=/etc/pyflyby:~/.pyflyby:.../.pyflyby
If you set:
PYFLYBY_PATH=/foo1/bar1:/foo2/bar2
then this replaces the default.
You can use a hyphen to include the default in the path. If you set:
PYFLYBY_PATH=/foo1/bar1:-:/foo2/bar2
then this reads /foo1/bar1
, then the default locations, then /foo2/bar2
.
In $PYFLYBY_PATH
, .../.pyflyby
(with _three_ dots) means that all ancestor
directories are searched for a member named “.pyflyby”.
For example, suppose the following files exist:
/etc/pyflyby/stuff.py
/u/quarl/.pyflyby/blah1.py
/u/quarl/.pyflyby/more/blah2.py
/proj/share/mypythonstuff/.pyflyby
/proj/share/mypythonstuff/foo/bar/.pyflyby/baz.py
/.pyflyby
Further, suppose:
/proj
is on a separate file system from/
.
$HOME=/u/quarl
Then tidy-imports /proj/share/mypythonstuff/foo/bar/quux/zot.py
will by
default use the following:
/etc/pyflyby/stuff.py
/u/quarl/.pyflyby/blah1.py
/u/quarl/.pyflyby/more/blah2.py
/proj/share/mypythonstuff/foo/bar/.pyflyby/baz.py
/proj/share/mypythonstuff/.pyflyby (a file)
Note
/.pyflyby
is not included, because traversal stops at file system boundaries, and in this example,/proj
is on a different file system than/
..pyflyby
(in$HOME
or near the target file) can be a file or a directory. If it is a directory, then it is recursively searched for*.py
files.The order usually doesn’t matter, but if there are “forget” instructions (see below), then the order matters. In the default
$PYFLYBY_PATH
, …/.pyflyby is placed last so that per-directory configuration can override per-user configuration, which can override systemwide configuration.
Forgetting imports
Occasionally you may have reason to tell pyflyby to “forget” entries from the database of known imports.
You can put the following in any file reachable from $PYFLYBY_PATH
:
__forget_imports__ = ["from numpy import NaN"]
This is useful if you want to use a set of imports maintained by someone else except for a few particular imports.
Entries in $PYFLYBY_PATH
are processed left-to-right in the order specified,
so put the files containing these at the end of your $PYFLYBY_PATH
. By
default, tidy-imports
and friends process /etc/pyflyby
, then ~/.pyflyby
,
then the per-directory .pyflyby
.
Mandatory imports
Within a certain project you may have a policy to always include certain
imports. For example, maybe you always want to do from __future__ import
division
in all files.
You can put the following in any file reachable from $PYFLYBY_PATH
:
__mandatory_imports__ = ["from __future__ import division"]
To undo mandatory imports inherited from other .pyflyby
files, use
__forget_imports__
(see above).
Canonicalize imports
Sometimes you want every run of tidy-imports
to automatically rename an import
to a new name.
You can put the following in any file reachable from $PYFLYBY_PATH
:
__canonical_imports__ = {"oldmodule.oldfunction": "newmodule.newfunction"}
This is equivalent to running:
tidy-imports --transform=oldmodule.oldfunction=newmodule.newfunction
Soapbox: avoid “star” imports
When programming in Python, a good software engineering practice is to avoid
using from foopackage import *
in production code.
This style is a maintenance nightmare:
It becomes difficult to figure out where various symbols (functions/classes/etc) come from.
It’s hard to tell what gets shadowed by what.
When the package changes in trivial ways, your code will be affected. Consider the following example: Suppose
foopackage.py
containsimport sys
, andmyprogram.py
containsfrom foopackage import *; if some_condition: sys.exit(0)
. Iffoopackage.py
changes so thatimport sys
is removed,myprogram.py
is now broken because it’s missingimport sys
.
To fix such code, you can run tidy-imports --replace-star-imports
to
automatically replace star imports with the specific needed imports.
Per-Project configuration of tidy-imports
You can configure Pyflyby on a per-repository basis by using the [tool.pyflyby] section of pyproject.toml files. Pyflyby will look in current working directory and all it’s parent until it find a pyproject.toml file from which it will load the defaults.
Most of the long command line flags default values can be configured in this section. Simply use the long form option name by replacing dashes - by underscore _. For long option that have the form –xxx and –no-xxx, you can assign a boolean to xxx. For example:
[tool.pyflyby]
add_missing=true
from_spaces=7
remove_unused=false
Emacs support
To get a
M-x tidy-imports
command in GNU Emacs, add to your~/.emacs
:(load "/path/to/pyflyby/lib/emacs/pyflyby.el")
Pyflyby.el doesn’t yet work with XEmacs; patches welcome.
saveframe: A utility for debugging / reproducing an issue
PyFlyBy provides a utility named saveframe which can be used to save information for debugging / reproducing an issue.
Usage: If you have a piece of code or a script that is failing due an issue originating from upstream code, and you cannot share your private code as a reproducer, use this utility to save relevant information to a file. Share the generated file with the upstream team, enabling them to reproduce and diagnose the issue independently.
Information saved in the file: This utility captures and saves error stack frames to a file. It includes the values of local variables from each stack frame, as well as metadata about each frame and the exception raised by your code.
This utility comes with 2 interfaces:
A function: For interactive usages such as IPython, Jupyter Notebook, or a debugger (pdb/ipdb), use pyflyby.saveframe function. To know how to use this function, checkout it’s documentation:
In [1]: saveframe?
A script: For cli usages (like a failing script), use pyflyby/bin/saveframe script. To know how to use this script, checkout its documentation:
$ saveframe --help
License
Pyflyby is released under a very permissive license, the MIT/X11 license; see LICENSE.txt.
Release
Check version number in lib/python/pyflyby/_version.py, maybe increase it.
Commit and tag if necessary, and push tags/commits.
Optional: Set SOURCE_DATE_EPOCH for reproducible build:
export SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD)
Build the SDIST:
python setup.py sdist
Optional Repack the Sdist to make sure the ZIP only contain SOURCE_DATE_EPOCH date using IPython tools:
python ~/dev/ipython/tools/retar.py dist/pyflyby-1.7.8.tar.gz shasum -a 256 dist/*
Optional, redo 4 & 5 to verify checksum is unchanged.
Upload using twine:
twine upload dist/*
Check/update https://github.com/conda-forge/pyflyby-feedstock for new pyflyby release on conda-forge
Table of Contents
- pyflyby API
- _autoimp module
LoadSymbolError
ScopeStack
_ClassScope
_IMPORT_FAILED
_MissingImportFinder
_MissingImportFinder._NewScopeCtx()
_MissingImportFinder._UpScopeCtx()
_MissingImportFinder._check_load()
_MissingImportFinder._deferred_load_checks
_MissingImportFinder._finish_deferred_load_checks()
_MissingImportFinder._get_scope_info()
_MissingImportFinder._lineno
_MissingImportFinder._remove_from_missing_imports()
_MissingImportFinder._scan_node()
_MissingImportFinder._scan_unused_imports()
_MissingImportFinder._visit_Load()
_MissingImportFinder._visit_Load_defered()
_MissingImportFinder._visit_Load_defered_global()
_MissingImportFinder._visit_Load_immediate()
_MissingImportFinder._visit_Store()
_MissingImportFinder._visit_StoreImport()
_MissingImportFinder._visit__all__()
_MissingImportFinder._visit_fullname()
_MissingImportFinder._visit_typecomment()
_MissingImportFinder.find_missing_imports()
_MissingImportFinder.generic_visit()
_MissingImportFinder.missing_imports
_MissingImportFinder.parse_docstrings
_MissingImportFinder.scan_for_import_issues()
_MissingImportFinder.scopestack
_MissingImportFinder.unused_imports
_MissingImportFinder.visit()
_MissingImportFinder.visit_Assign()
_MissingImportFinder.visit_AsyncFunctionDef()
_MissingImportFinder.visit_Attribute()
_MissingImportFinder.visit_Call()
_MissingImportFinder.visit_ClassDef()
_MissingImportFinder.visit_Constant()
_MissingImportFinder.visit_Delete()
_MissingImportFinder.visit_Dict()
_MissingImportFinder.visit_DictComp()
_MissingImportFinder.visit_ExceptHandler()
_MissingImportFinder.visit_Expr()
_MissingImportFinder.visit_FunctionDef()
_MissingImportFinder.visit_GeneratorExp()
_MissingImportFinder.visit_ImportFrom()
_MissingImportFinder.visit_Lambda()
_MissingImportFinder.visit_ListComp()
_MissingImportFinder.visit_Match()
_MissingImportFinder.visit_MatchAs()
_MissingImportFinder.visit_MatchMapping()
_MissingImportFinder.visit_Module()
_MissingImportFinder.visit_Name()
_MissingImportFinder.visit_Pass()
_MissingImportFinder.visit_SetComp()
_MissingImportFinder.visit_alias()
_MissingImportFinder.visit_arg()
_MissingImportFinder.visit_arguments()
_MissingImportFinder.visit_comprehension()
_MissingImportFinder.visit_match_case()
_UseChecker
_find_earliest_backjump_label()
_find_loads_without_stores_in_code()
_find_missing_imports_in_ast()
_find_missing_imports_in_code()
_try_import()
auto_import_symbol()
clear_failed_imports_cache()
get_known_import()
load_symbol()
scan_for_import_issues()
symbol_needs_import()
take_arg()
- _cmdline module
AbortActions
Exit1
Modifier
_default_on_error()
_sigpipe_handler()
action_exit1()
action_external_command()
action_ifchanged()
action_print()
action_query()
action_replace()
filename_args()
hfmt()
maindoc()
parse_args()
print_version_and_exit()
process_actions()
symlink_callback()
symlink_error()
symlink_follow()
symlink_replace()
symlink_skip()
syntax()
- _comms module
- _dbg module
DebuggerAttachTimeoutError
Pty
_DebuggerCtx()
_DisplayHookCtx()
_ExceptHookCtx()
_FdCtx()
_NoTtyError
_StdioCtx()
_abbrev_filename()
_debug_code()
_debug_exception()
_dev_null()
_dev_tty_fd()
_escape_for_gdb()
_find_py_commandline()
_get_caller_frame()
_override_excepthook()
_prompt_continue_waiting_for_debugger()
_remote_print_stack_to_file()
_reset_excepthook()
_send_email_with_attach_instructions()
_signal_handler_debugger()
_sigterm_handler()
_sleep_until_debugger_attaches()
enable_sigterm_handler()
get_executable()
inject()
kill_process()
process_exists()
setraw_but_sigint()
syscall_marker()
tty_is_usable()
wait_for_debugger_to_attach()
- _file module
- _flags module
- _format module
- _idents module
- _importclns module
ConflictingImportsError
ImportMap
ImportSet
ImportSet._EMPTY
ImportSet._by_module_name
ImportSet._from_args()
ImportSet._from_imports()
ImportSet._importset
ImportSet.by_import_as
ImportSet.conflicting_imports
ImportSet.flags
ImportSet.get_statements()
ImportSet.imports
ImportSet.member_names
ImportSet.pretty_print()
ImportSet.statements
ImportSet.with_imports()
ImportSet.without_imports()
NoSuchImportError
- _importdb module
- _imports2s module
ImportAlreadyExistsError
ImportPathForRelativeImportsCtx()
LineNumberAmbiguousError
LineNumberNotFoundError
NoImportBlockError
SourceToSourceFileImportsTransformation
SourceToSourceFileImportsTransformation.add_import()
SourceToSourceFileImportsTransformation.find_import_block_by_lineno()
SourceToSourceFileImportsTransformation.insert_new_blocks_after_comments()
SourceToSourceFileImportsTransformation.insert_new_import_block()
SourceToSourceFileImportsTransformation.preprocess()
SourceToSourceFileImportsTransformation.pretty_print()
SourceToSourceFileImportsTransformation.remove_import()
SourceToSourceFileImportsTransformation.select_import_block_by_closest_prefix_match()
SourceToSourceImportBlockTransformation
SourceToSourceTransformation
SourceToSourceTransformationBase
fix_unused_and_missing_imports()
- _importstmt module
- _interactive module
AutoImporter
AutoImporter._advise()
AutoImporter._ast_transformer
AutoImporter._autoimported_this_cell
AutoImporter._construct()
AutoImporter._continue_enable()
AutoImporter._disablers
AutoImporter._enable_ast_hook()
AutoImporter._enable_completer_hooks()
AutoImporter._enable_completion_hook()
AutoImporter._enable_debugger_hook()
AutoImporter._enable_initializer_hooks()
AutoImporter._enable_internal()
AutoImporter._enable_ipython_shell_bugfixes()
AutoImporter._enable_kernel_manager_hook()
AutoImporter._enable_ofind_hook()
AutoImporter._enable_prun_hook()
AutoImporter._enable_reset_hook()
AutoImporter._enable_run_hook()
AutoImporter._enable_shell_hooks()
AutoImporter._enable_start_kernel_hook()
AutoImporter._enable_time_hook()
AutoImporter._enable_timeit_hook()
AutoImporter._errored
AutoImporter._from_app()
AutoImporter._ip
AutoImporter._safe_call()
AutoImporter._state
AutoImporter.app
AutoImporter.auto_import()
AutoImporter.compile_with_autoimport()
AutoImporter.complete_symbol()
AutoImporter.db
AutoImporter.disable()
AutoImporter.enable()
AutoImporter.reset_state_new_cell()
InterceptPrintsDuringPromptCtx()
NoActiveIPythonAppError
NoIPythonPackageError
UpdateIPythonStdioCtx()
_DummyIPythonEmbeddedApp
_EnableState
_app_is_initialized()
_auto_import_in_pdb_frame()
_enable_pdb_hooks()
_enable_terminal_pdb_hooks()
_generate_enabler_code()
_get_IPdb_class()
_get_TerminalPdb_class()
_get_ipython_app()
_get_ipython_color_scheme()
_get_or_create_ipython_kernel_app()
_get_or_create_ipython_terminal_app()
_get_pdb_if_is_in_pdb()
_initialize_and_start_app_with_autoimporter()
_install_in_ipython_config_file_40()
_ipython_in_multiline()
_ipython_namespaces()
_list_members_for_completion()
_python_can_import_pyflyby()
_skip_frames()
complete_symbol()
get_global_namespaces()
get_ipython_terminal_app_with_autoimporter()
new_IPdb_instance()
print_verbose_tb()
run_ipython_line_magic()
start_ipython_kernel_with_autoimporter()
start_ipython_with_autoimporter()
- _livepatch module
- _log module
- _modules module
ErrorDuringImportError
ModuleHandle
ModuleHandle._cls_cache
ModuleHandle._from_filename()
ModuleHandle._from_module()
ModuleHandle._from_modulename()
ModuleHandle._member_from_node()
ModuleHandle.ancestors
ModuleHandle.block
ModuleHandle.containing()
ModuleHandle.exists
ModuleHandle.exports
ModuleHandle.filename
ModuleHandle.list()
ModuleHandle.module
ModuleHandle.name
ModuleHandle.parent
ModuleHandle.submodules
ModuleHandle.text
_my_iter_modules()
import_module()
pyc_to_py()
- _parse module
AnnotatedAst
AnnotatedModule
AstNodeContext
IgnoreOptionsDocTestParser
_DummyAst_Node
_annotate_ast_nodes()
_annotate_ast_startpos()
_ast_str_literal_value()
_flags_to_try()
_flatten_ast_nodes()
_is_ast_bytes()
_is_ast_str()
_is_ast_str_or_byte()
_is_comment_or_blank()
_iter_child_nodes_in_order()
_iter_child_nodes_in_order_internal_1()
_parse_ast_nodes()
_split_code_lines()
_test_parse_string_literal()
_walk_ast_nodes_in_order()
infer_compile_mode()
- _py module
- Invocation summary
- Features
- Options
LoggedList
NotAFunctionError
ParseError
SysArgvCtx()
UnimportableNameError
UserExpr
_Namespace
_ParseInterruptedWantHelp
_ParseInterruptedWantSource
_PyMain
_PyMain._enable_debug_tools()
_PyMain._parse_global_opts()
_PyMain._pre_exit()
_PyMain._pre_exit_interactive_shell()
_PyMain._pre_exit_matplotlib_show()
_PyMain._run_action()
_PyMain._seems_like_runnable_module()
_PyMain.apply()
_PyMain.create_ipython_app()
_PyMain.eval()
_PyMain.exec_stdin()
_PyMain.execfile()
_PyMain.heuristic_cmd()
_PyMain.heuristic_run_module()
_PyMain.print_help()
_PyMain.print_version()
_PyMain.run()
_PyMain.run_module()
_PyMain.start_ipython()
_as_filename_if_seems_like_filename()
_build_function_usage_string()
_format_call()
_format_call_spec()
_get_argspec()
_get_help()
_handle_user_exception()
_has_python_shebang()
_interpret_arg_mode()
_interpret_output_mode()
_parse_auto_apply_args()
_requires_parens_as_function()
auto_apply()
print_result()
py_main()
- _util module
- _autoimp module