Skip to content

Commit 5bbbc53

Browse files
authored
Merge pull request #2097 from davidhalter/py314
Python 3.14; fixes #2093, fixes #2070, will fix #2064 after release.
2 parents 4cca2ed + 6e17c85 commit 5bbbc53

14 files changed

Lines changed: 68 additions & 83 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ jobs:
77
strategy:
88
matrix:
99
os: [ubuntu-24.04, windows-2022]
10-
python-version: ["3.13", "3.12", "3.11", "3.10"]
11-
environment: ['3.13', '3.12', '3.11', '3.10', 'interpreter']
10+
python-version: ["3.14", "3.13", "3.12", "3.11", "3.10"]
11+
environment: ['3.14', '3.13', '3.12', '3.11', '3.10', 'interpreter']
1212
steps:
1313
- name: Checkout code
1414
uses: actions/checkout@v4

jedi/api/environment.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
_VersionInfo = namedtuple('VersionInfo', 'major minor micro') # type: ignore[name-match]
2424

25-
_SUPPORTED_PYTHONS = ['3.13', '3.12', '3.11', '3.10']
25+
_SUPPORTED_PYTHONS = ['3.14', '3.13', '3.12', '3.11', '3.10']
2626
_SAFE_PATHS = ['/usr/bin', '/usr/local/bin']
2727
_CONDA_VAR = 'CONDA_PREFIX'
2828
_CURRENT_VERSION = '%s.%s' % (sys.version_info.major, sys.version_info.minor)

jedi/inference/compiled/access.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,18 +471,23 @@ def execute_operation(self, other_access_handle, operator):
471471
op = _OPERATORS[operator]
472472
return self._create_access_path(op(self._obj, other_access._obj))
473473

474-
def get_annotation_name_and_args(self):
474+
def get_annotation_name_and_args(self) -> tuple[str | None, tuple[AccessPath, ...]]:
475475
"""
476476
Returns Tuple[Optional[str], Tuple[AccessPath, ...]]
477477
"""
478478
name = None
479479
args = ()
480-
if safe_getattr(self._obj, '__module__', default='') == 'typing':
480+
if type(self._obj) is typing.Union: # zuban: ignore[comparison-overlap] # TODO zuban
481+
# This is mostly formatted like `int | str` and we therefor need to
482+
# check the type.
483+
args = typing.get_args(self._obj)
484+
name = "Union"
485+
elif safe_getattr(self._obj, '__module__', default='') == 'typing':
486+
# Try regex first (works for most types)
481487
m = re.match(r'typing.(\w+)\[', repr(self._obj))
482488
if m is not None:
483489
name = m.group(1)
484490

485-
import typing
486491
if sys.version_info >= (3, 8):
487492
args = typing.get_args(self._obj)
488493
else:
@@ -493,6 +498,18 @@ def needs_type_completions(self):
493498
return inspect.isclass(self._obj) and self._obj != type
494499

495500
def _annotation_to_str(self, annotation):
501+
# In Python 3.14+, Union types are displayed as X | Y instead of Union[X, Y]
502+
# We normalize to that for consistency
503+
import typing
504+
origin = typing.get_origin(annotation)
505+
if origin is typing.Union:
506+
# Get the args and format them as Union[...]
507+
args = typing.get_args(annotation)
508+
return ' | '.join(
509+
self._annotation_to_str(arg) if hasattr(arg, '__origin__')
510+
else getattr(arg, '__name__', str(arg))
511+
for arg in args
512+
)
496513
return inspect.formatannotation(annotation)
497514

498515
def get_signature_params(self):

jedi/inference/compiled/getattr_static.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,10 @@ def getattr_static(obj, attr, default=_sentinel):
9090
if not _is_type(obj):
9191
klass = type(obj)
9292
dict_attr = _shadowed_dict(klass)
93-
if (dict_attr is _sentinel or type(dict_attr) is types.MemberDescriptorType):
93+
# In Python 3.15+, __dict__ is a GetSetDescriptorType instead of being _sentinel
94+
if (dict_attr is _sentinel
95+
or type(dict_attr) is types.MemberDescriptorType
96+
or type(dict_attr) is types.GetSetDescriptorType):
9497
instance_result = _check_instance(obj, attr)
9598
else:
9699
klass = obj

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
'Programming Language :: Python :: 3.11',
100100
'Programming Language :: Python :: 3.12',
101101
'Programming Language :: Python :: 3.13',
102+
'Programming Language :: Python :: 3.14',
102103
'Topic :: Software Development :: Libraries :: Python Modules',
103104
'Topic :: Text Editors :: Integrated Development Environments (IDE)',
104105
'Topic :: Utilities',

test/run.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,7 @@
104104
import re
105105
import sys
106106
import operator
107-
if sys.version_info < (3, 8):
108-
literal_eval = eval
109-
else:
110-
from ast import literal_eval
107+
from ast import literal_eval
111108
from io import StringIO
112109
from functools import reduce
113110
from unittest.mock import ANY

test/test_api/test_call_signatures.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ def get(source, column=None):
420420
(code1, 'f(a,b,xy', 4),
421421
(code1, 'f(a,b,xyz=', 4),
422422
(code1, 'f(a,b,xy=', None),
423-
(code1, 'f(u=', (0, None)),
423+
(code1, 'f(u=', None),
424424
(code1, 'f(v=', 1),
425425

426426
# **kwargs
@@ -438,7 +438,7 @@ def get(source, column=None):
438438
(code2, 'g(a,b,abc=1,abd=4,abd=', 5),
439439
(code2, 'g(a,b,kw', 5),
440440
(code2, 'g(a,b,kwargs=', 5),
441-
(code2, 'g(u=', (0, 5)),
441+
(code2, 'g(u=', 5),
442442
(code2, 'g(v=', 1),
443443

444444
# *args
@@ -450,7 +450,7 @@ def get(source, column=None):
450450
(code3, 'h(a,b,c,(3,)', 2),
451451
(code3, 'h(a,b,args=', None),
452452
(code3, 'h(u,v=', 1),
453-
(code3, 'h(u=', (0, None)),
453+
(code3, 'h(u=', None),
454454
(code3, 'h(u,*xxx', 1),
455455
(code3, 'h(u,*xxx,*yyy', 1),
456456
(code3, 'h(u,*[]', 1),
@@ -483,7 +483,7 @@ def get(source, column=None):
483483
(code4, 'i(1, [a?b,*', 2),
484484
(code4, 'i(?b,*r,c', 1),
485485
(code4, 'i(?*', 0),
486-
(code4, 'i(?**', (0, 1)),
486+
(code4, 'i(?**', 1),
487487

488488
# Random
489489
(code4, 'i(()', 0),
@@ -497,11 +497,6 @@ def get(source, column=None):
497497
@pytest.mark.parametrize('ending', ['', ')'])
498498
@pytest.mark.parametrize('code, call, expected_index', _calls)
499499
def test_signature_index(Script, environment, code, call, expected_index, ending):
500-
if isinstance(expected_index, tuple):
501-
expected_index = expected_index[environment.version_info > (3, 8)]
502-
if environment.version_info < (3, 8):
503-
code = code.replace('/,', '')
504-
505500
sig, = Script(code + '\n' + call + ending).get_signatures(column=len(call))
506501
index = sig.index
507502
assert expected_index == index

test/test_api/test_environment.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def test_find_system_environments():
2626

2727
@pytest.mark.parametrize(
2828
'version',
29-
['3.10', '3.11', '3.12', '3.13']
29+
jedi.api.environment._SUPPORTED_PYTHONS,
3030
)
3131
def test_versions(version):
3232
try:

test/test_api/test_interpreter.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -661,17 +661,15 @@ def bar():
661661
662662
# typing is available via globals.
663663
({'return': 'typing.Union[str, int]'}, ['int', 'str'], ''),
664-
({'return': 'typing.Union["str", int]'},
665-
['int', 'str'] if sys.version_info >= (3, 9) else ['int'], ''),
664+
({'return': 'typing.Union["str", int]'}, ['int', 'str'], ''),
666665
({'return': 'typing.Union["str", 1]'},
667-
['str'] if sys.version_info >= (3, 11) else [], ''),
666+
(['str'] if (3, 14) > sys.version_info >= (3, 11) else []), ''),
668667
({'return': 'typing.Optional[str]'}, ['NoneType', 'str'], ''),
669668
({'return': 'typing.Optional[str, int]'}, [], ''), # Takes only one arg
670669
({'return': 'typing.Any'},
671670
['_AnyMeta'] if sys.version_info >= (3, 11) else [], ''),
672671
673-
({'return': 'typing.Tuple[int, str]'},
674-
['Tuple' if sys.version_info[:2] == (3, 6) else 'tuple'], ''),
672+
({'return': 'typing.Tuple[int, str]'}, ['tuple'], ''),
675673
({'return': 'typing.Tuple[int, str]'}, ['int'], 'x()[0]'),
676674
({'return': 'typing.Tuple[int, str]'}, ['str'], 'x()[1]'),
677675
({'return': 'typing.Tuple[int, str]'}, [], 'x()[2]'),
@@ -746,7 +744,8 @@ class TestClass():
746744
def test_param_infer_default():
747745
abs_sig, = jedi.Interpreter('abs(', [{'abs': abs}]).get_signatures()
748746
param, = abs_sig.params
749-
assert param.name == 'x'
747+
# Parameter name changed from 'x' to 'number' in Python 3.15
748+
assert param.name in ('x', 'number')
750749
assert param.infer_default() == []
751750

752751

test/test_inference/test_gradual/test_stubs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@
4343
])
4444
def test_infer_and_goto(Script, code, full_name, has_stub, has_python, way,
4545
kwargs, type_, options, environment):
46-
if type_ == 'infer' and full_name == 'typing.Sequence' and environment.version_info >= (3, 7):
47-
# In Python 3.7+ there's not really a sequence definition, there's just
46+
if type_ == 'infer' and full_name == 'typing.Sequence':
47+
# Since Python 3.7+ there's not really a sequence definition, there's just
4848
# a name that leads nowhere.
4949
has_python = False
5050

0 commit comments

Comments
 (0)