Skip to content

Commit

Permalink
Some cleanup while looking into local imports
Browse files Browse the repository at this point in the history
  • Loading branch information
Carreau committed Oct 29, 2024
1 parent 55cb843 commit 719eb2a
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 18 deletions.
18 changes: 9 additions & 9 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ configuration file:

.. code:: python
from pyflyby import add_import
add_import("foo", "foo = 1")
Expand Down Expand Up @@ -463,29 +463,29 @@ Emacs support
saveframe: A utility for debugging / reproducing an issue
=========================================================

PyFlyBy provides a utility named **saveframe** which can be used to save
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
**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
**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:

1. **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
debugger (pdb/ipdb), use **pyflyby.saveframe** function. To know how to use this
function, checkout it's documentation:

.. code::
In [1]: saveframe?
2. **A script**: For cli usages (like a failing script), use **pyflyby/bin/saveframe**
2. **A script**: For cli usages (like a failing script), use **pyflyby/bin/saveframe**
script. To know how to use this script, checkout its documentation:

.. code::
Expand Down
38 changes: 29 additions & 9 deletions lib/python/pyflyby/_autoimp.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from six import reraise
import sys
import types
from typing import Any, Set
from typing import Any, Set, Optional, List, Tuple

if sys.version_info >= (3, 12):
ATTRIBUTE_NAME = "value"
Expand Down Expand Up @@ -353,7 +353,7 @@ def __init__(self, name, source, lineno):
self.lineno = lineno


class _MissingImportFinder(object):
class _MissingImportFinder:
"""
A helper class to be used only by `_find_missing_imports_in_ast`.
Expand All @@ -371,8 +371,12 @@ class _MissingImportFinder(object):
"""

def __init__(self, scopestack, find_unused_imports=False,
parse_docstrings=False):
scopestack: ScopeStack
_lineno: Optional[int]
missing_imports: List[Tuple[Optional[int], DottedIdentifier]]
parse_docstrings: bool

def __init__(self, scopestack, *, find_unused_imports:bool, parse_docstrings:bool):
"""
Construct the AST visitor.
Expand All @@ -385,19 +389,26 @@ def __init__(self, scopestack, find_unused_imports=False,
# includes the globals dictionary. ScopeStack() will make sure this
# includes builtins.
scopestack = ScopeStack(scopestack)

# Add an empty namespace to the stack. This facilitates adding stuff
# to scopestack[-1] without ever modifying user globals.
scopestack = scopestack.with_new_scope()

self.scopestack = scopestack

# Create data structure to hold the result.
# missing_imports is a list of (lineno, DottedIdentifier) tuples.
self.missing_imports = []

# unused_imports is a list of (lineno, Import) tuples, if enabled.
self.unused_imports = [] if find_unused_imports else None

self.parse_docstrings = parse_docstrings

# Function bodies that we need to check after defining names in this
# function scope.
self._deferred_load_checks = []

# Whether we're currently in a FunctionDef.
self._in_FunctionDef = False
# Current lineno.
Expand All @@ -419,7 +430,8 @@ def _scan_node(self, node):
finally:
self.scopestack = oldscopestack

def scan_for_import_issues(self, codeblock):
def scan_for_import_issues(self, codeblock: PythonBlock):
assert isinstance(codeblock, PythonBlock)
# See global `scan_for_import_issues`
if not isinstance(codeblock, PythonBlock):
codeblock = PythonBlock(codeblock)
Expand Down Expand Up @@ -998,10 +1010,11 @@ def visit_Delete(self, node):
# Don't call generic_visit(node) here. Reason: We already visit the
# parts above, if relevant.

def _visit_Load_defered_global(self, fullname):
def _visit_Load_defered_global(self, fullname:str):
"""
Some things will be resolved in global scope later.
"""
assert isinstance(fullname, str), fullname
logger.debug("_visit_Load_defered_global(%r)", fullname)
if symbol_needs_import(fullname, self.scopestack):
data = (fullname, self.scopestack, self._lineno)
Expand Down Expand Up @@ -1090,7 +1103,11 @@ def _scan_unused_imports(self):
unused_imports.sort()


def scan_for_import_issues(codeblock, find_unused_imports=True, parse_docstrings=False):
def scan_for_import_issues(
codeblock: PythonBlock,
find_unused_imports: bool = True,
parse_docstrings: bool = False,
):
"""
Find missing and unused imports, by lineno.
Expand All @@ -1117,7 +1134,7 @@ def scan_for_import_issues(codeblock, find_unused_imports=True, parse_docstrings
([], [(1, Import('import baz'))])
"""
logger.debug("scan_for_import_issues()")
logger.debug("global scan_for_import_issues()")
if not isinstance(codeblock, PythonBlock):
codeblock = PythonBlock(codeblock)
namespaces = ScopeStack([{}])
Expand Down Expand Up @@ -1148,7 +1165,10 @@ def _find_missing_imports_in_ast(node, namespaces):
# Traverse the abstract syntax tree.
if logger.debug_enabled:
logger.debug("ast=%s", ast.dump(node))
return _MissingImportFinder(namespaces).find_missing_imports(node)
return _MissingImportFinder(
namespaces,
find_unused_imports=False,
parse_docstrings=False).find_missing_imports(node)

# TODO: maybe we should replace _find_missing_imports_in_ast with
# _find_missing_imports_in_code(compile(node)). The method of parsing opcodes
Expand Down

0 comments on commit 719eb2a

Please sign in to comment.