Skip to content

Commit

Permalink
Merge pull request #364 from Carreau/clean-x
Browse files Browse the repository at this point in the history
Some cleanup while looking into local imports
  • Loading branch information
Carreau authored Oct 29, 2024
2 parents 55cb843 + d466592 commit 7d1390e
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 24 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ jobs:
matrix:
os: [ubuntu-latest]
python-version:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
include:
- os: macos-latest
python-version: "3.10"
python-version: "3.12"

env:
DEBUG_TEST_PYFLYBY: 1
Expand Down
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
40 changes: 31 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,14 @@ 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
unused_imports: Optional[List[Tuple[int, str]]]
_deferred_load_checks: list[tuple[str, ScopeStack, Optional[int]]]

def __init__(self, scopestack, *, find_unused_imports:bool, parse_docstrings:bool):
"""
Construct the AST visitor.
Expand All @@ -385,19 +391,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 +432,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 +1012,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 +1105,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 +1136,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 +1167,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
8 changes: 5 additions & 3 deletions lib/python/pyflyby/_importdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,9 +560,11 @@ def _from_filenames(cls, filenames, _mandatory_filenames_deprecated=[]):
filenames = [filenames]
for f in filenames:
assert isinstance(f, Filename)
logger.debug("ImportDB: loading [%s], mandatory=[%s]",
', '.join(map(str, filenames)),
', '.join(map(str, _mandatory_filenames_deprecated)))
logger.debug(
"ImportDB: loading %r, mandatory=%r",
[str(f) for f in filenames],
[str(f) for f in _mandatory_filenames_deprecated],
)
if SUPPORT_DEPRECATED_BEHAVIOR:
# Before 2014-10, pyflyby read the following:
# * known_imports from $PYFLYBY_PATH/known_imports/**/*.py or
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ def make_distribution(self):
"black",
"typing_extensions>=4.6; python_version<'3.12'"
],
python_requires=">3.8, <4",
python_requires=">3.9, <4",
tests_require=['pexpect>=3.3', 'pytest', 'epydoc', 'rlipython', 'requests'],
cmdclass = {
'test' : PyTest,
Expand Down

0 comments on commit 7d1390e

Please sign in to comment.