From 9fa052d52c4603f26d670533fb32be441fcf95e0 Mon Sep 17 00:00:00 2001 From: Gopar Date: Sat, 16 May 2026 22:36:56 -0700 Subject: [PATCH 1/4] Add Python versions 3.11, 3.13, and 3.14 in CI --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1b81f5ee..3fe9d310 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,10 @@ jobs: - 30.2 # - snapshot python_version: + - 3.11 - 3.12 + - 3.13 + - 3.14 steps: # Checkout - uses: actions/checkout@v4 From 3cc78b192abfc2781294259a4e2ad6da7de057a0 Mon Sep 17 00:00:00 2001 From: Gopar Date: Sat, 16 May 2026 22:37:29 -0700 Subject: [PATCH 2/4] Update tests for multiple python versions --- elpy/tests/support.py | 23 +++++++++++++++++------ elpy/tests/test_jedibackend.py | 34 ++++++++++++++++++---------------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/elpy/tests/support.py b/elpy/tests/support.py index ff76d477..7446d01b 100644 --- a/elpy/tests/support.py +++ b/elpy/tests/support.py @@ -618,20 +618,31 @@ def test_should_raise_fault(self): class RPCGetCalltipTests(GenericRPCTests): METHOD = "rpc_get_calltip" + def _assert_thread_calltip(self, actual): + """Assert that a Thread calltip has the expected structure. + + We check name and index exactly, and verify that all known-stable + params are present. Extra params (added in newer Python versions) + are allowed. + """ + self.assertEqual(actual['name'], 'Thread') + self.assertEqual(actual['index'], 0) + for param in self.THREAD_CALLTIP_REQUIRED_PARAMS: + self.assertIn(param, actual['params']) + def test_should_get_calltip(self): - expected = self.THREAD_CALLTIP source, offset = source_and_offset( "import threading\nthreading.Thread(_|_") filename = self.project_file("test.py", source) calltip = self.backend.rpc_get_calltip(filename, source, offset) - self.assertEqual(calltip, expected) + self._assert_thread_calltip(calltip) calltip = self.backend.rpc_get_calltip_or_oneline_docstring(filename, source, offset) calltip.pop('kind') - self.assertEqual(calltip, expected) + self._assert_thread_calltip(calltip) def test_should_get_calltip_even_after_parens(self): source, offset = source_and_offset( @@ -640,12 +651,12 @@ def test_should_get_calltip_even_after_parens(self): actual = self.backend.rpc_get_calltip(filename, source, offset) - self.assertEqual(self.THREAD_CALLTIP, actual) + self._assert_thread_calltip(actual) actual = self.backend.rpc_get_calltip_or_oneline_docstring(filename, source, offset) actual.pop('kind') - self.assertEqual(self.THREAD_CALLTIP, actual) + self._assert_thread_calltip(actual) def test_should_get_calltip_at_closing_paren(self): source, offset = source_and_offset( @@ -654,7 +665,7 @@ def test_should_get_calltip_at_closing_paren(self): actual = self.backend.rpc_get_calltip(filename, source, offset) - self.assertEqual(self.THREAD_CALLTIP, actual) + self._assert_thread_calltip(actual) actual = self.backend.rpc_get_calltip_or_oneline_docstring(filename, source, offset) diff --git a/elpy/tests/test_jedibackend.py b/elpy/tests/test_jedibackend.py index 12a5b8a8..709d91c6 100644 --- a/elpy/tests/test_jedibackend.py +++ b/elpy/tests/test_jedibackend.py @@ -171,23 +171,25 @@ class TestRPCGetCalltip(RPCGetCalltipTests, 'params': [u'a', u'b'], 'name': u'add'} if jedibackend.JEDISUP19: - THREAD_CALLTIP = {'name': 'Thread', - 'index': 0, - 'params': ['group: None=None', - 'target: Callable[..., object] | None=None', - 'name: str | None=None', - 'args: Iterable[Any]=()', - 'kwargs: Mapping[str, Any] | None=None', - 'daemon: bool | None=None']} + # Thread's calltip params vary by Python version (e.g., 3.14 added + # `context`) and by the typeshed stubs bundled with jedi. We only + # assert on the stable subset of params that have been present since + # Python 3.0 to avoid brittleness. + THREAD_CALLTIP_REQUIRED_PARAMS = [ + 'group: None=None', + 'target: Callable[..., object] | None=None', + 'name: str | None=None', + 'args: Iterable[Any]=()', + 'kwargs: Mapping[str, Any] | None=None', + 'daemon: bool | None=None'] else: - THREAD_CALLTIP = {'name': 'Thread', - 'index': 0, - 'params': ['group: None=...', - 'target: Optional[Callable[..., Any]]=...', - 'name: Optional[str]=...', - 'args: Iterable[Any]=...', - 'kwargs: Mapping[str, Any]=...', - 'daemon: Optional[bool]=...']} + THREAD_CALLTIP_REQUIRED_PARAMS = [ + 'group: None=...', + 'target: Optional[Callable[..., Any]]=...', + 'name: Optional[str]=...', + 'args: Iterable[Any]=...', + 'kwargs: Mapping[str, Any]=...', + 'daemon: Optional[bool]=...'] def test_should_not_fail_with_get_subscope_by_name(self): # Bug #677 / jedi#628 From 0efcc0e329f4dc45ad88057d772797074452ab9c Mon Sep 17 00:00:00 2001 From: Gopar Date: Sat, 16 May 2026 22:37:47 -0700 Subject: [PATCH 3/4] Add python version file for `uv` creation --- .python-version | 1 + 1 file changed, 1 insertion(+) create mode 100644 .python-version diff --git a/.python-version b/.python-version new file mode 100644 index 00000000..e4fba218 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 From 2886a8f2fe7ad0b8b01fe18c3f2ec6a5beab3045 Mon Sep 17 00:00:00 2001 From: Gopar Date: Sat, 16 May 2026 23:22:47 -0700 Subject: [PATCH 4/4] Fix failing test --- elpy/tests/test_jedibackend.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/elpy/tests/test_jedibackend.py b/elpy/tests/test_jedibackend.py index 709d91c6..75cb0c0c 100644 --- a/elpy/tests/test_jedibackend.py +++ b/elpy/tests/test_jedibackend.py @@ -95,16 +95,17 @@ def __init__(self, *args, **kwargs): ' ``bytearray`` instance containing a JSON' ' document) to a Python object.' ) - if sys.version_info >= (3, 12): - self.JSON_DOCSTRING = ( - "JSON (JavaScript Object Notation) " - " is a subset of JavaScript syntax (ECMA-262" - " 3rd edition) used as a lightweight data interchange format.") - else: - self.JSON_DOCSTRING = ( - "JSON (JavaScript Object Notation) " - " is a subset of JavaScript syntax (ECMA-262" - " 3rd edition) used as a lightweight data interchange format.") + # The json module docstring URL changed from http to https in + # CPython 3.12, but jedi uses bundled typeshed stubs which may + # have either version regardless of runtime. We don't set + # JSON_DOCSTRING here; instead we override check_module_docstring. + + def check_module_docstring(self, docstring): + # Accept either http or https since jedi's typeshed stubs may differ + self.assertIn("JSON (JavaScript Object Notation)