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

Backport Python 3.10 fixes #13412

Merged
merged 5 commits into from Dec 21, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
python-version: ["3.7", "3.8", "3.9"]
python-version: ["3.7", "3.8", "3.9", "3.10"]
# Test all on ubuntu, test ends on macos
include:
- os: macos-latest
Expand All @@ -39,6 +39,7 @@ jobs:
- name: Check manifest
run: check-manifest
- name: iptest
if: matrix.python-version != '3.10'
run: |
cd /tmp && iptest --coverage xml && cd -
cp /tmp/ipy_coverage.xml ./
Expand Down
40 changes: 20 additions & 20 deletions IPython/core/oinspect.py
Expand Up @@ -182,11 +182,12 @@ def getsource(obj, oname='') -> Union[str,None]:
except TypeError:
# The object itself provided no meaningful source, try looking for
# its class definition instead.
if hasattr(obj, '__class__'):
try:
src = inspect.getsource(obj.__class__)
except TypeError:
return None
try:
src = inspect.getsource(obj.__class__)
except (OSError, TypeError):
return None
except OSError:
return None

return src

Expand Down Expand Up @@ -308,14 +309,14 @@ def find_file(obj) -> str:
except TypeError:
# For an instance, the file that matters is where its class was
# declared.
if hasattr(obj, '__class__'):
try:
fname = inspect.getabsfile(obj.__class__)
except TypeError:
# Can happen for builtins
pass
except:
try:
fname = inspect.getabsfile(obj.__class__)
except (OSError, TypeError):
# Can happen for builtins
pass
except OSError:
pass

return cast_unicode(fname)


Expand All @@ -338,15 +339,14 @@ def find_source_lines(obj):
obj = _get_wrapped(obj)

try:
lineno = inspect.getsourcelines(obj)[1]
except TypeError:
# For instances, try the class object like getsource() does
try:
lineno = inspect.getsourcelines(obj)[1]
except TypeError:
# For instances, try the class object like getsource() does
if hasattr(obj, '__class__'):
lineno = inspect.getsourcelines(obj.__class__)[1]
else:
lineno = None
except:
lineno = inspect.getsourcelines(obj.__class__)[1]
except (OSError, TypeError):
return None
except OSError:
return None

return lineno
Expand Down
13 changes: 13 additions & 0 deletions IPython/core/tests/test_completer.py
Expand Up @@ -12,6 +12,7 @@
from contextlib import contextmanager

import nose.tools as nt
import pytest

from traitlets.config.loader import Config
from IPython import get_ipython
Expand All @@ -29,6 +30,15 @@
)
from nose.tools import assert_in, assert_not_in

if sys.version_info >= (3, 10):
import jedi
from pkg_resources import parse_version

# Requires https://github.com/davidhalter/jedi/pull/1795
jedi_issue = parse_version(jedi.__version__) <= parse_version("0.18.0")
else:
jedi_issue = False

# -----------------------------------------------------------------------------
# Test functions
# -----------------------------------------------------------------------------
Expand Down Expand Up @@ -381,6 +391,8 @@ def test_all_completions_dups(self):
matches = c.all_completions("TestCl")
assert matches == ['TestClass'], jedi_status
matches = c.all_completions("TestClass.")
if jedi_status and jedi_issue:
continue
assert len(matches) > 2, jedi_status
matches = c.all_completions("TestClass.a")
assert matches == ['TestClass.a', 'TestClass.a1'], jedi_status
Expand Down Expand Up @@ -435,6 +447,7 @@ def test_completion_have_signature(self):
"encoding" in c.signature
), "Signature of function was not found by completer"

@pytest.mark.xfail(jedi_issue, reason="Known failure on jedi<=0.18.0")
def test_deduplicate_completions(self):
"""
Test that completions are correctly deduplicated (even if ranges are not the same)
Expand Down
18 changes: 12 additions & 6 deletions IPython/core/tests/test_magic_arguments.py
Expand Up @@ -7,6 +7,7 @@
#-----------------------------------------------------------------------------

import argparse
import sys
from nose.tools import assert_equal

from IPython.core.magic_arguments import (argument, argument_group, kwds,
Expand Down Expand Up @@ -74,7 +75,12 @@ def foo(self, args):


def test_magic_arguments():
assert_equal(magic_foo1.__doc__, '::\n\n %foo1 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
# “optional arguments” was replaced with “options” in argparse help
# https://docs.python.org/3/whatsnew/3.10.html#argparse
# https://bugs.python.org/issue9694
options = "optional arguments" if sys.version_info < (3, 10) else "options"

assert_equal(magic_foo1.__doc__, f"::\n\n %foo1 [-f FOO]\n\n A docstring.\n\n{options}:\n -f FOO, --foo FOO an argument\n")
assert_equal(getattr(magic_foo1, 'argcmd_name', None), None)
assert_equal(real_name(magic_foo1), 'foo1')
assert_equal(magic_foo1(None, ''), argparse.Namespace(foo=None))
Expand All @@ -86,32 +92,32 @@ def test_magic_arguments():
assert_equal(magic_foo2(None, ''), argparse.Namespace())
assert hasattr(magic_foo2, 'has_arguments')

assert_equal(magic_foo3.__doc__, '::\n\n %foo3 [-f FOO] [-b BAR] [-z BAZ]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n\nGroup:\n -b BAR, --bar BAR a grouped argument\n\nSecond Group:\n -z BAZ, --baz BAZ another grouped argument\n')
assert_equal(magic_foo3.__doc__, f"::\n\n %foo3 [-f FOO] [-b BAR] [-z BAZ]\n\n A docstring.\n\n{options}:\n -f FOO, --foo FOO an argument\n\nGroup:\n -b BAR, --bar BAR a grouped argument\n\nSecond Group:\n -z BAZ, --baz BAZ another grouped argument\n")
assert_equal(getattr(magic_foo3, 'argcmd_name', None), None)
assert_equal(real_name(magic_foo3), 'foo3')
assert_equal(magic_foo3(None, ''),
argparse.Namespace(bar=None, baz=None, foo=None))
assert hasattr(magic_foo3, 'has_arguments')

assert_equal(magic_foo4.__doc__, '::\n\n %foo4 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
assert_equal(magic_foo4.__doc__, f"::\n\n %foo4 [-f FOO]\n\n A docstring.\n\n{options}:\n -f FOO, --foo FOO an argument\n")
assert_equal(getattr(magic_foo4, 'argcmd_name', None), None)
assert_equal(real_name(magic_foo4), 'foo4')
assert_equal(magic_foo4(None, ''), argparse.Namespace())
assert hasattr(magic_foo4, 'has_arguments')

assert_equal(magic_foo5.__doc__, '::\n\n %frobnicate [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
assert_equal(magic_foo5.__doc__, f"::\n\n %frobnicate [-f FOO]\n\n A docstring.\n\n{options}:\n -f FOO, --foo FOO an argument\n")
assert_equal(getattr(magic_foo5, 'argcmd_name', None), 'frobnicate')
assert_equal(real_name(magic_foo5), 'frobnicate')
assert_equal(magic_foo5(None, ''), argparse.Namespace(foo=None))
assert hasattr(magic_foo5, 'has_arguments')

assert_equal(magic_magic_foo.__doc__, '::\n\n %magic_foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
assert_equal(magic_magic_foo.__doc__, f"::\n\n %magic_foo [-f FOO]\n\n A docstring.\n\n{options}:\n -f FOO, --foo FOO an argument\n")
assert_equal(getattr(magic_magic_foo, 'argcmd_name', None), None)
assert_equal(real_name(magic_magic_foo), 'magic_foo')
assert_equal(magic_magic_foo(None, ''), argparse.Namespace(foo=None))
assert hasattr(magic_magic_foo, 'has_arguments')

assert_equal(foo.__doc__, '::\n\n %foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
assert_equal(foo.__doc__, f"::\n\n %foo [-f FOO]\n\n A docstring.\n\n{options}:\n -f FOO, --foo FOO an argument\n")
assert_equal(getattr(foo, 'argcmd_name', None), None)
assert_equal(real_name(foo), 'foo')
assert_equal(foo(None, ''), argparse.Namespace(foo=None))
Expand Down