Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test on more recent Python versions. #279

Merged
merged 1 commit into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
include:
- os: macos-latest
python-version: "3.10"
Expand All @@ -31,7 +32,7 @@ jobs:
- name: Install and update Python dependencies on Python 3
run: |
python -m pip install --upgrade pip setuptools wheel
python -m pip install --upgrade "pexpect>=3.3" 'pytest<7' rlipython 'ipykernel>=5.4.3' requests jupyter flaky 'notebook<6.1' 'prompt_toolkit<3.0.15' wheel 'jupyter_console>=6.2' 'pytest-cov<3' ipython 'coverage<6.3' pytest-json-report
python -m pip install --upgrade "pexpect>=3.3" 'pytest<=8' rlipython 'ipykernel>=5.4.3' requests jupyter flaky 'notebook<6.1' wheel 'jupyter_console>=6.2' pytest-cov ipython coverage pytest-json-report
pip install -e .
- name: test release build
run: |
Expand Down Expand Up @@ -61,7 +62,7 @@ jobs:
./report-*.json
- uses: codecov/codecov-action@v2
- name: Build docs
if: ${{ matrix.python-version == '3.10'}}
if: ${{ matrix.python-version == '3.11'}}
run: |
pip install sphinx sphinx_rtd_theme sphinx-autodoc-typehints
cd doc
Expand Down
21 changes: 17 additions & 4 deletions lib/python/pyflyby/_autoimp.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from pyflyby._importstmt import Import
from pyflyby._log import logger
from pyflyby._modules import ModuleHandle
from pyflyby._parse import PythonBlock, infer_compile_mode
from pyflyby._parse import PythonBlock, infer_compile_mode, _is_ast_str

if sys.version_info >= (3, 12):
ATTRIBUTE_NAME = "value"
Expand All @@ -34,6 +34,11 @@
else:
LOAD_SHIFT = 0

if sys.version_info > (3, 11):
LOAD_SHIFT = 1
else:
LOAD_SHIFT = 0

NoneType = type(None)
EllipsisType = type(Ellipsis)

Expand Down Expand Up @@ -580,15 +585,19 @@ def _visit__all__(self, node):
if not isinstance(node.value, ast.List):
logger.warning("Don't know how to handle __all__ as (%s)" % node.value)
return
if not all(isinstance(e, ast.Str) for e in node.value.elts):
if not all(_is_ast_str(e) for e in node.value.elts):
logger.warning("Don't know how to handle __all__ with list elements other than str")
return
for e in node.value.elts:
self._visit_Load_defered_global(e.s)

def visit_ClassDef(self, node):
logger.debug("visit_ClassDef(%r)", node)
assert node._fields == ('name', 'bases', 'keywords', 'body', 'decorator_list')
if sys.version_info > (3,12):
# we don't visit type_params, so autoimport won't work yet for type annotations
assert node._fields == ('name', 'bases', 'keywords', 'body', 'decorator_list', 'type_params'), node._fields
else:
assert node._fields == ('name', 'bases', 'keywords', 'body', 'decorator_list'), node._fields
self.visit(node.bases)
self.visit(node.decorator_list)
# The class's name is only visible to others (not to the body to the
Expand Down Expand Up @@ -619,7 +628,11 @@ def visit_FunctionDef(self, node):
# scope.
# - Store the name in the current scope (but not visibly to
# args/decorator_list).
assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment'), node._fields
if sys.version_info > (3, 12):
# we don't visit type_params, so autoimport won't work yet for type annotations
assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment', 'type_params'), node._fields
else:
assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment'), node._fields
with self._NewScopeCtx(include_class_scopes=True):
self.visit(node.decorator_list)
self.visit(node.args)
Expand Down
5 changes: 4 additions & 1 deletion lib/python/pyflyby/_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,10 @@ def _test_parse_string_literal(text, flags):
body = module_node.body
if not _is_ast_str_or_byte(body):
return None
return body.s
if sys.version_info < (3 ,9):
return body.s
else:
return body.value


AstNodeContext = namedtuple("AstNodeContext", "parent field index")
Expand Down
28 changes: 18 additions & 10 deletions lib/python/pyflyby/_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@


from functools import total_ordering
from typing import Any

from pyflyby._util import cmp

Expand Down Expand Up @@ -377,7 +378,7 @@
"print_function"])


def _get_argspec(arg, _recurse=False):
def _get_argspec(arg:Any) -> inspect.FullArgSpec:
from inspect import getfullargspec as getargspec, FullArgSpec as ArgSpec
if isinstance(arg, FunctionType):
return getargspec(arg)
Expand All @@ -394,8 +395,6 @@ def _get_argspec(arg, _recurse=False):
else:
argspec = _get_argspec(arg.__init__)
return ArgSpec(argspec.args[1:], *argspec[1:])
elif _recurse and hasattr(arg, '__call__'):
return _get_argspec(arg.__call__, _recurse=False)
elif callable(arg):
# Unknown, probably a built-in method.
return ArgSpec([], "args", "kwargs", None, [], None, {})
Expand Down Expand Up @@ -458,8 +457,16 @@ def _requires_parens_as_function(function_name:str):
return True


def _format_call_spec(function_name, argspec):
callspec = inspect.formatargspec(*argspec)
def _format_call_spec(function_name:str, obj:Any)-> str:
# using signature() is not strictly identical
# as it might look at __text_signature__ and/or respect possitional only
# forward slash:
# >>> def foo(a, /, b)
# whcih formatargspec did not do.
# argspec = _get_argspec(obj)
# old_callspect = inspect.formatargspec(*argspec)
# assert old_callspect == callspec , f"{old_callspect} !={callspec}"
callspec = str(inspect.signature(obj))
if _requires_parens_as_function(function_name):
return "(%s)%s" % (function_name, callspec)
else:
Expand All @@ -483,11 +490,13 @@ def shquote(s):
return "'" + s.replace("'", "'\"'\"'") + "'"


def _build_function_usage_string(function_name, argspec, prefix):
def _build_function_usage_string(function_name:str, obj:Any, prefix:str):
argspec = _get_argspec(obj)

usage = []
# TODO: colorize
usage.append("Python signature:")
usage.append(" >"+">> " + _format_call_spec(function_name, argspec))
usage.append(" >"+">> " + _format_call_spec(function_name, obj))
usage.append("")
usage.append("Command-line signature:")
keywords = argspec.varkw
Expand Down Expand Up @@ -902,11 +911,10 @@ def _get_help(expr, verbosity=1):
# TODO: colorize headers
result = ""
obj = expr.value
name = str(expr.source)
name:str = str(expr.source)
if callable(obj):
argspec = _get_argspec(obj)
prefix = os.path.basename(sys.orig_argv[0]) + " "
result += _build_function_usage_string(name, argspec, prefix)
result += _build_function_usage_string(name, obj, prefix)
if verbosity == 0:
include_filename = False
include_doc = False
Expand Down
7 changes: 3 additions & 4 deletions tests/test_interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -3854,7 +3854,7 @@ def test_debug_tab_completion_db_1(frontend):
""", frontend=frontend)


@retry
@pytest.mark.xfail(IPython.version_info >= (8, 14), reason='not working on tab completion since https://github.com/ipython/ipython/pull/13889')
def test_debug_tab_completion_module_1(frontend, tmp):
# Verify that tab completion on module names works.
writetext(tmp.dir/"thornton60097181.py", """
Expand Down Expand Up @@ -3900,7 +3900,7 @@ def test_debug_tab_completion_multiple_1(frontend, tmp):
""", PYTHONPATH=tmp.dir, frontend=frontend)


@retry
@pytest.mark.xfail(IPython.version_info >= (8, 14), reason='not working on tab completion since https://github.com/ipython/ipython/pull/13889')
def test_debug_postmortem_tab_completion_1(frontend):
# Verify that tab completion in %debug postmortem mode works.
ipython("""
Expand All @@ -3922,7 +3922,7 @@ def test_debug_postmortem_tab_completion_1(frontend):
ipdb> q
""", frontend=frontend)


@pytest.mark.xfail(IPython.version_info >= (8, 14), reason='not working on tab completion since https://github.com/ipython/ipython/pull/13889')
def test_debug_namespace_1_py3(frontend):
# Verify that autoimporting and tab completion happen in the local
# namespace.
Expand Down Expand Up @@ -3953,7 +3953,6 @@ def test_debug_namespace_1_py3(frontend):
""", frontend=frontend)


@retry
def test_debug_second_1(frontend):
# Verify that a second postmortem debug of the same function behaves as
# expected.
Expand Down
Loading