1
0
forked from VimPlug/jedi

Compare commits

...

78 Commits

Author SHA1 Message Date
Dave Halter
4e175ca82b Prepare for release 0.19.1 2023-10-02 10:55:17 +02:00
Dave Halter
30e9b7b5be Merge pull request #1961 from PeterJCLaw/docs-link
Include a link to the docs in the package metadata
2023-09-23 23:56:11 +00:00
Peter Law
83545bc9ec Include a link to the docs in the package metadata 2023-09-22 21:33:52 +01:00
Peter Law
57e7b83455 Add missing import
I suspect this got lost in a merge somewhere, probably the
combination of 7e533ca7e1 and the
Python 3.12 work in a60fdba1d4.
2023-09-22 21:29:31 +01:00
Dave Halter
0770372857 Merge pull request #1956 from PeterJCLaw/python-3.12
Support Python 3.12
2023-09-17 19:25:45 +00:00
Peter Law
7e533ca7e1 Drop redundant conditional skips for unsupported Python versions 2023-09-17 18:38:12 +01:00
Peter Law
a60fdba1d4 Adjust for change to documention change of next in Python 3.12
The signature of the builtin isn't actually changing in Python 3.12,
however its documentation has changed.
2023-09-17 18:27:53 +01:00
Dave Halter
9d399a9229 Merge pull request #1959 from davidhalter/unpin-test-django
Unpin Django in tests
2023-09-17 12:37:00 +00:00
Peter Law
770cdade00 Claim support for Python 3.12 2023-09-16 21:41:06 +01:00
Peter Law
29890c1f29 Ignore linux-only os.CLONE_* constants in Python 3.12 in import test 2023-09-16 21:41:06 +01:00
Peter Law
159566e1a0 Add Python 3.12 as a tested platform 2023-09-16 21:41:06 +01:00
Peter Law
a80618a2df Unpin Django in tests
It's not completely clear why this was pinned originally, though
at the time Jedi supported Python 2.7 as well as 3.5-3.8, so that
may have had something to do with it.

Removing this pin now seems to work in CI and unblocks some issues
we're seeing around Python 3.12 (specifically that Django<3.1
implicitly relies on distutils, which is no longer available by
default, and possibly other issues).
2023-09-16 21:40:34 +01:00
Peter Law
4bc1b6ef99 Bump use of actions/checkout to avoid Node JS deprecations 2023-09-16 18:12:28 +01:00
Peter Law
d655d65d3a Fix typo in comment 2023-09-16 18:03:56 +01:00
Dave Halter
51f4a99a1e Bump version to 0.19.0 2023-07-29 00:57:34 +02:00
Dave Halter
93c14d2e6e Add release notes for 0.19.0 2023-07-29 00:46:38 +02:00
Dave Halter
57aefed6ea Allow unsafe custom __getitem__ executions when allow unsafe executions is on 2023-07-29 00:33:09 +02:00
Dave Halter
8a4b079d0f allow_descriptor_getattr -> allow_unsafe_interpreter_executions 2023-07-29 00:06:55 +02:00
Dave Halter
62cbcb0844 Make nested dict completions possible.
See also https://github.com/ipython/ipython/issues/13866
2023-07-28 23:50:38 +02:00
Dave Halter
d8420d0f72 Add a note to the changelog 2023-07-28 22:59:03 +02:00
Dave Halter
886279fb6d Try to use the return annotations of properties, if available, fixes #1933 2023-07-28 22:35:15 +02:00
Dave Halter
ff3a7f367f Avoid evaluating properties just for the api type, improves #1933 2023-07-28 22:11:15 +02:00
Dave Halter
1f70e3301e Revert "Avoid one layer of caching that is probably useless"
This reverts commit a34c348a55.
2023-07-28 16:10:30 +02:00
Dave Halter
a34c348a55 Avoid one layer of caching that is probably useless 2023-07-28 16:05:56 +02:00
Dave Halter
972123c9c9 Introduce the property return annotation 2023-07-28 15:54:54 +02:00
Dave Halter
6455a14841 Avoid multiple getattrs instead of a single one, see also #1933 2023-07-28 15:10:37 +02:00
Dave Halter
8d9e3ab3a7 Simplify 2023-07-28 13:10:26 +02:00
Dave Halter
048173e467 Remove a piece of unimportant code, see discussion in #1933 2023-07-28 10:15:28 +02:00
Dave Halter
1947e7dd56 Avoid dynamic params search for Interpreter, fixes #1899 2023-07-27 13:49:27 +02:00
Dave Halter
01d8da8f73 Reset the recursion limitations at the start of the main Script calls, fixes #1796 2023-07-27 13:14:24 +02:00
Dave Halter
6ea5ad7b19 Fix issue around completions with multiple with with_items, fixes 1931 2023-07-27 11:54:39 +02:00
Dave Halter
cd4ca74d7a Satisfy flake8 2023-07-27 11:36:16 +02:00
Dave Halter
67d6262f45 Skip the namespace package test correctly 2023-07-27 10:07:16 +02:00
Dave Halter
5f19237a3e Fix renaming of namespace packages, fixes #1779 2023-07-27 03:09:25 +02:00
Dave Halter
f2444b4be5 Merge pull request #1943 from diegorodriguezv/patch-1
Fix language servers reference
2023-06-22 08:44:15 +00:00
diegorodriguezv
7028bbb5d5 Fix language servers reference 2023-06-21 19:06:53 -05:00
Dave Halter
3699ba0aa7 Merge pull request #1942 from lkh42t/inference-annotated
Support typing.Annotated inference
2023-06-19 00:28:27 +00:00
Luc Khai Hai
72d34f3d7d Support typing.Annotated inference 2023-06-17 20:46:03 +09:00
Peter Law
a28bd24bef Merge branch 'importlib-metadata-entry-points' 2023-05-28 12:08:09 +01:00
Peter Law
54cb64292c Support importlib.metadata entry points for newer python
pkg_resources is deprecated and liable to be dropped at some point.
2023-05-28 11:48:19 +01:00
Dave Halter
d421b920fa Merge pull request #1937 from PeterJCLaw/update-importlib-usage
Modernise importlib usage
2023-05-27 22:54:45 +00:00
Peter Law
c137eb6918 Modernise importlib usage
`find_module` is deprecated in all supported version of Python and
is slated for removal in the upcoming 3.12. Happily it seems we
can move to the related `find_spec` and just hoist the loader from
the spec which that returns. (This is mostly what current `find_module`
implementations do anyway).
2023-05-27 22:03:51 +01:00
Peter Law
d67facc922 Merge branch 'update-github-actions' 2023-05-27 22:03:03 +01:00
Dave Halter
7023b645b1 Merge pull request #1935 from PeterJCLaw/fix-attrs-as-dataclass
Teach Jedi that `attrs`' `frozen` decorator also acts like a dataclass
2023-05-27 21:01:21 +00:00
Peter Law
b5120cc90b Update GitHub Actions for Node 16 support 2023-05-27 00:18:38 +01:00
Peter Law
483e78993d attrs' frozen decorator also acts like a dataclass
In 23.1.0 (specifically in 46053d703d)
the definition of the `frozen` decorator was tweaked slightly, such
that its type stub is separate from that for `define`. This means
that Jedi needs to be told about it as a separate member.

I've manually checked that this still works with the prior version
of `attrs`.

Fixes https://github.com/davidhalter/jedi/issues/1929
2023-05-27 00:13:55 +01:00
Peter Law
3dbcd2c6de Whitespace 2023-05-27 00:10:00 +01:00
Peter Law
ca36fcfa4b Fix typo in comment 2023-05-26 21:38:42 +01:00
Dave Halter
825c6b93bf Merge pull request #1930 from tachikoma-li/doc-fix
docs: Fix typo in acknowledgements
2023-04-21 21:55:14 +00:00
Li Li
c22585c6f2 small doc fix 2023-04-19 15:12:39 +10:00
Dave Halter
431d1e104d Merge pull request #1926 from dijonkitchen/patch-1
Update usage.rst to match Readme language servers
2023-04-12 15:50:21 +00:00
Dave Halter
adcd6ade8b Merge pull request #1927 from dijonkitchen/patch-2
docs: fix spelling
2023-04-12 15:49:40 +00:00
JC (Jonathan Chen)
32a1dd33a6 docs: fix spelling 2023-04-12 11:01:10 -04:00
JC (Jonathan Chen)
9ea01bcc69 Update usage.rst to match Readme language servers 2023-04-12 10:58:57 -04:00
Dave Halter
77cfefc1cc Add a security policy 2023-03-22 00:47:58 +01:00
Dave Halter
ff7d6c6e4c Merge pull request #1922 from zerocewl/add_pylsp_link
Added link to the python-lsp-server
2023-03-21 23:24:04 +00:00
Dave Halter
6ee33bd385 Merge pull request #1923 from dimbleby/help-after-newline
fix help when in column zero
2023-03-13 20:17:13 +00:00
David Hotham
0fbc2aafa3 fix help when in column zero 2023-03-12 14:21:09 +00:00
zerocewl
fe7e350051 Added link to the python-lsp-server 2023-03-01 16:27:11 +01:00
Dave Halter
b814ca2951 Merge pull request #1917 from PeterJCLaw/python-3.11
Support Python 3.11
2023-02-14 00:48:04 +00:00
Peter Law
aae2a8e3ed Cope with Windows virtualenvs different casing 2023-02-13 20:25:31 +00:00
Peter Law
67e0bec597 Support Python 3.11
This adds support for targetting Python 3.11 via picking up the
latest grammar from parso while also validating support for running
on 3.11 by adding it to the CI matrix.
2023-02-13 19:58:35 +00:00
Peter Law
c71e06fcb3 Clarify that this is also the latest flake8 version which supports 3.6 2023-02-13 19:57:38 +00:00
Peter Law
bbd5bcf3ca Merge branch 'update-mypy' 2023-02-13 19:57:20 +00:00
Dave Halter
d888c1b266 Merge pull request #1915 from PeterJCLaw/update-flake8
Update flake8 and fix issue found
2023-02-13 19:49:36 +00:00
Peter Law
83d0e23800 Type check setup.py too now we can 2023-02-13 19:40:16 +00:00
Peter Law
dc4e48d7c7 Be stricter about mypy needing error codes
These make it clearer what's being ignored and harder to
accidentally ignore more than expected.
2023-02-13 19:40:16 +00:00
Peter Law
664b10a5c6 Update mypy to the latest
This includes updating the ignore comments for things which mypy
now knows about or now complains about, as well as pulling in some
typeshed packages for things outside the standard library.
2023-02-13 19:40:16 +00:00
Peter Law
36a4b7d48c Update flake8 and fix issue found 2023-02-13 19:15:35 +00:00
Dave Halter
b0025ee6ba Merge pull request #1911 from krpatter-intc/allow_descriptor_getattr_official_support
Make allow_descriptor_getattr a non-private variable for more official
2023-02-10 22:30:33 +00:00
Patterson, Kevin R
fac0b7f068 instance_allow_descriptor_getattr as public setting 2023-02-10 05:43:21 -06:00
Dave Halter
aeadba7cad Merge pull request #1910 from ghrist8p/1909-fix-sys-path-is-tuple
Replaced tuple passed as sys_path actual argument with list
2023-02-07 23:21:16 +00:00
Georgi Hristov
fd0e6aed96 Replaced tuple passed as sys_path actual argument with list
Fixes davidhalter#1909
2023-02-05 15:46:23 -08:00
Dave Halter
c89fa8e927 Merge pull request #1903 from s-t-e-v-e-n-k/python-311-string-typing
Support Python 3.11 typing changes
2023-01-10 19:57:58 +00:00
Steve Kowalik
00e23ddcee Support Python 3.11 typing changes
Python 3.11 has changed typing so that unions now  return forward
refrences instead of erroring, and typing.Any is now an _AnyMeta class.
Correct the parameters for both of those.

Fixes #1858
2023-01-10 14:52:24 +11:00
Dave Halter
66e97e5b93 Jedi is now a fixed part of the Eric IDE 2022-12-16 15:37:22 +01:00
Dave Halter
0f5ea3de5f Revert "Removed all usages of Eric IDE, because apparently it's not using Jedi anymore"
This reverts commit e47bbbb851.
2022-12-16 15:36:31 +01:00
Dave Halter
e47bbbb851 Removed all usages of Eric IDE, because apparently it's not using Jedi anymore 2022-12-16 15:33:07 +01:00
44 changed files with 474 additions and 155 deletions

View File

@@ -7,22 +7,24 @@ jobs:
strategy:
matrix:
os: [ubuntu-20.04, windows-2019]
python-version: ["3.10", "3.9", "3.8", "3.7", "3.6"]
environment: ['3.8', '3.10', '3.9', '3.7', '3.6', 'interpreter']
python-version: ["3.12", "3.11", "3.10", "3.9", "3.8", "3.7", "3.6"]
environment: ['3.8', '3.12', '3.11', '3.10', '3.9', '3.7', '3.6', 'interpreter']
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-python@v2
- uses: actions/setup-python@v4
if: ${{ matrix.environment != 'interpreter' }}
with:
python-version: ${{ matrix.environment }}
allow-prereleases: true
- uses: actions/setup-python@v2
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Install dependencies
run: 'pip install .[testing]'
@@ -36,7 +38,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
submodules: recursive
@@ -46,14 +48,14 @@ jobs:
- name: Run tests
run: |
python -m flake8 jedi setup.py
python -m mypy jedi sith.py
python -m mypy jedi sith.py setup.py
coverage:
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
submodules: recursive

View File

@@ -6,6 +6,20 @@ Changelog
Unreleased
++++++++++
0.19.1 (2023-10-02)
+++++++++++++++++++
- Python 3.12 support (Thanks Peter!)
0.19.0 (2023-07-29)
+++++++++++++++++++
- Python 3.11 support
- Massive improvements in performance for ``Interpreter`` (e.g. IPython) users.
This especially affects ``pandas`` users with large datasets.
- Add ``jedi.settings.allow_unsafe_interpreter_executions`` to make it easier
for IPython users to avoid unsafe executions.
0.18.2 (2022-11-21)
+++++++++++++++++++

View File

@@ -42,7 +42,7 @@ Jedi can currently be used with the following editors/projects:
- `GNOME Builder`_ (with support for GObject Introspection)
- Gedit (gedi_)
- wdb_ - Web Debugger
- `Eric IDE`_ (Available as a plugin)
- `Eric IDE`_
- `IPython 6.0.0+ <https://ipython.readthedocs.io/en/stable/whatsnew/version6.html>`_
- `xonsh shell <https://xon.sh/contents.html>`_ has `jedi extension <https://xon.sh/xontribs.html#jedi>`_
@@ -51,7 +51,8 @@ and many more!
There are a few language servers that use Jedi:
- `jedi-language-server <https://github.com/pappasam/jedi-language-server>`_
- `python-language-server <https://github.com/palantir/python-language-server>`_
- `python-language-server <https://github.com/palantir/python-language-server>`_ (currently unmaintained)
- `python-lsp-server <https://github.com/python-lsp/python-lsp-server>`_ (fork from python-language-server)
- `anakin-language-server <https://github.com/muffinmad/anakin-language-server>`_
Here are some pictures taken from jedi-vim_:

9
SECURITY.md Normal file
View File

@@ -0,0 +1,9 @@
# Security Policy
If security issues arise, we will try to fix those as soon as possible.
Due to Jedi's nature, Security Issues will probably be extremely rare, but we will neverless treat them seriously.
## Reporting Security Problems
If you need to report a security vulnerability, please send an email to davidhalter88@gmail.com. Typically, I will respond in the next few business days.

View File

@@ -35,7 +35,7 @@ to write my own version of a completion engine.
The first idea was to execute non-dangerous code. But I soon realized, that
this would not work. So I started to build a static analysis tool.
The biggest problem that I had at the time was that I did not know a thing
about parsers.I did not did not even know the word static analysis. It turns
about parsers. I did not even know the word static analysis. It turns
out they are the foundation of a good static analysis tool. I of course did not
know that and tried to write my own poor version of a parser that I ended up
throwing away two years later.
@@ -53,7 +53,7 @@ quick and is pretty much feature complete.
--------
I will leave you with a small annectote that happend in 2012, if I remember
I will leave you with a small anecdote that happened in 2012, if I remember
correctly. After I explained Guido van Rossum, how some parts of my
auto-completion work, he said:

View File

@@ -77,7 +77,7 @@ Performance Issues
Importing ``numpy`` can be quite slow sometimes, as well as loading the
builtins the first time. If you want to speed things up, you could preload
libriaries in |jedi|, with :func:`.preload_module`. However, once loaded, this
libraries in |jedi|, with :func:`.preload_module`. However, once loaded, this
should not be a problem anymore. The same is true for huge modules like
``PySide``, ``wx``, ``tensorflow``, ``pandas``, etc.

View File

@@ -4,7 +4,7 @@ Using Jedi
==========
|jedi| is can be used with a variety of :ref:`plugins <editor-plugins>`,
`language servers <language-servers>` and other software.
:ref:`language servers <language-servers>` and other software.
It is also possible to use |jedi| in the :ref:`Python shell or with IPython
<repl-completion>`.
@@ -16,7 +16,8 @@ Language Servers
--------------
- `jedi-language-server <https://github.com/pappasam/jedi-language-server>`_
- `python-language-server <https://github.com/palantir/python-language-server>`_
- `python-language-server <https://github.com/palantir/python-language-server>`_ (currently unmaintained)
- `python-lsp-server <https://github.com/python-lsp/python-lsp-server>`_ (fork from python-language-server)
- `anakin-language-server <https://github.com/muffinmad/anakin-language-server>`_
.. _editor-plugins:
@@ -86,7 +87,7 @@ Gedit
Eric IDE
~~~~~~~~
- `Eric IDE`_ (Available as a plugin)
- `Eric IDE`_
Web Debugger
~~~~~~~~~~~~

View File

@@ -27,7 +27,7 @@ ad
load
"""
__version__ = '0.18.2'
__version__ = '0.19.1'
from jedi.api import Script, Interpreter, set_debug_function, preload_module
from jedi import settings

View File

@@ -206,6 +206,7 @@ class Script:
before magic methods and name mangled names that start with ``__``.
:rtype: list of :class:`.Completion`
"""
self._inference_state.reset_recursion_limitations()
with debug.increase_indent_cm('complete'):
completion = Completion(
self._inference_state, self._get_module_context(), self._code_lines,
@@ -215,6 +216,7 @@ class Script:
@validate_line_column
def infer(self, line=None, column=None, *, only_stubs=False, prefer_stubs=False):
self._inference_state.reset_recursion_limitations()
"""
Return the definitions of under the cursor. It is basically a wrapper
around Jedi's type inference.
@@ -260,6 +262,7 @@ class Script:
@validate_line_column
def goto(self, line=None, column=None, *, follow_imports=False, follow_builtin_imports=False,
only_stubs=False, prefer_stubs=False):
self._inference_state.reset_recursion_limitations()
"""
Goes to the name that defined the object under the cursor. Optionally
you can follow imports.
@@ -365,10 +368,17 @@ class Script:
:rtype: list of :class:`.Name`
"""
self._inference_state.reset_recursion_limitations()
definitions = self.goto(line, column, follow_imports=True)
if definitions:
return definitions
leaf = self._module_node.get_leaf_for_position((line, column))
if leaf is not None and leaf.end_pos == (line, column) and leaf.type == 'newline':
next_ = leaf.get_next_leaf()
if next_ is not None and next_.start_pos == leaf.end_pos:
leaf = next_
if leaf is not None and leaf.type in ('keyword', 'operator', 'error_leaf'):
def need_pydoc():
if leaf.value in ('(', ')', '[', ']'):
@@ -400,6 +410,7 @@ class Script:
the current module only.
:rtype: list of :class:`.Name`
"""
self._inference_state.reset_recursion_limitations()
def _references(include_builtins=True, scope='project'):
if scope not in ('project', 'file'):
@@ -434,6 +445,7 @@ class Script:
:rtype: list of :class:`.Signature`
"""
self._inference_state.reset_recursion_limitations()
pos = line, column
call_details = helpers.get_signature_details(self._module_node, pos)
if call_details is None:
@@ -553,6 +565,7 @@ class Script:
return parso_to_jedi_errors(self._inference_state.grammar, self._module_node)
def _names(self, all_scopes=False, definitions=True, references=False):
self._inference_state.reset_recursion_limitations()
# Set line/column to a random position, because they don't matter.
module_context = self._get_module_context()
defs = [
@@ -708,7 +721,6 @@ class Interpreter(Script):
:param namespaces: A list of namespace dictionaries such as the one
returned by :func:`globals` and :func:`locals`.
"""
_allow_descriptor_getattr_default = True
def __init__(self, code, namespaces, *, project=None, **kwds):
try:
@@ -729,7 +741,16 @@ class Interpreter(Script):
super().__init__(code, environment=environment, project=project, **kwds)
self.namespaces = namespaces
self._inference_state.allow_descriptor_getattr = self._allow_descriptor_getattr_default
self._inference_state.allow_unsafe_executions = \
settings.allow_unsafe_interpreter_executions
# Dynamic params search is important when we work on functions that are
# called by other pieces of code. However for interpreter completions
# this is not important at all, because the current code is always new
# and will never be called by something.
# Also sometimes this logic goes a bit too far like in
# https://github.com/ipython/ipython/issues/13866, where it takes
# seconds to do a simple completion.
self._inference_state.do_dynamic_params_search = False
@cache.memoize_method
def _get_module_context(self):

View File

@@ -105,8 +105,7 @@ class BaseName:
# Compiled modules should not return a module path even if they
# have one.
path: Optional[Path] = self._get_module_context().py__file__()
if path is not None:
return path
return path
return None

View File

@@ -15,9 +15,9 @@ from jedi.inference.compiled.subprocess import CompiledSubprocess, \
import parso
_VersionInfo = namedtuple('VersionInfo', 'major minor micro')
_VersionInfo = namedtuple('VersionInfo', 'major minor micro') # type: ignore[name-match]
_SUPPORTED_PYTHONS = ['3.10', '3.9', '3.8', '3.7', '3.6']
_SUPPORTED_PYTHONS = ['3.12', '3.11', '3.10', '3.9', '3.8', '3.7', '3.6']
_SAFE_PATHS = ['/usr/bin', '/usr/local/bin']
_CONDA_VAR = 'CONDA_PREFIX'
_CURRENT_VERSION = '%s.%s' % (sys.version_info.major, sys.version_info.minor)
@@ -384,8 +384,7 @@ def _get_executable_path(path, safe=True):
def _get_executables_from_windows_registry(version):
# https://github.com/python/typeshed/pull/3794 adds winreg
import winreg # type: ignore[import]
import winreg
# TODO: support Python Anaconda.
sub_keys = [

View File

@@ -5,8 +5,7 @@ from typing import Dict, Optional
from jedi.inference.names import AbstractArbitraryName
try:
# https://github.com/python/typeshed/pull/4351 adds pydoc_data
from pydoc_data import topics # type: ignore[import]
from pydoc_data import topics
pydoc_topics: Optional[Dict[str, str]] = topics.topics
except ImportError:
# Python 3.6.8 embeddable does not have pydoc_data.

View File

@@ -5,6 +5,7 @@ from typing import Dict, Iterable, Tuple
from parso import split_lines
from jedi.api.exceptions import RefactoringError
from jedi.inference.value.namespace import ImplicitNSName
EXPRESSION_PARTS = (
'or_test and_test not_test comparison '
@@ -102,7 +103,12 @@ class Refactoring:
to_path=calculate_to_path(path),
module_node=next(iter(map_)).get_root_node(),
node_to_str_map=map_
) for path, map_ in sorted(self._file_to_node_changes.items())
)
# We need to use `or`, because the path can be None
for path, map_ in sorted(
self._file_to_node_changes.items(),
key=lambda x: x[0] or Path("")
)
}
def get_renames(self) -> Iterable[Tuple[Path, Path]]:
@@ -116,7 +122,7 @@ class Refactoring:
project_path = self._inference_state.project.path
for from_, to in self.get_renames():
text += 'rename from %s\nrename to %s\n' \
% (from_.relative_to(project_path), to.relative_to(project_path))
% (_try_relative_to(from_, project_path), _try_relative_to(to, project_path))
return text + ''.join(f.get_diff() for f in self.get_changed_files().values())
@@ -146,13 +152,16 @@ def rename(inference_state, definitions, new_name):
raise RefactoringError("There is no name under the cursor")
for d in definitions:
# This private access is ok in a way. It's not public to
# protect Jedi users from seeing it.
tree_name = d._name.tree_name
if d.type == 'module' and tree_name is None:
p = None if d.module_path is None else Path(d.module_path)
if d.type == 'module' and tree_name is None and d.module_path is not None:
p = Path(d.module_path)
file_renames.add(_calculate_rename(p, new_name))
elif isinstance(d._name, ImplicitNSName):
for p in d._name._value.py__path__():
file_renames.add(_calculate_rename(Path(p), new_name))
else:
# This private access is ok in a way. It's not public to
# protect Jedi users from seeing it.
if tree_name is not None:
fmap = file_tree_name_map.setdefault(d.module_path, {})
fmap[tree_name] = tree_name.prefix + new_name
@@ -246,3 +255,10 @@ def _remove_indent_of_prefix(prefix):
Removes the last indentation of a prefix, e.g. " \n \n " becomes " \n \n".
"""
return ''.join(split_lines(prefix, keepends=True)[:-1])
def _try_relative_to(path: Path, base: Path) -> Path:
try:
return path.relative_to(base)
except ValueError:
return path

View File

@@ -36,8 +36,11 @@ def complete_dict(module_context, code_lines, leaf, position, string, fuzzy):
string = cut_value_at_position(leaf, position)
context = module_context.create_context(bracket_leaf)
before_bracket_leaf = bracket_leaf.get_previous_leaf()
if before_bracket_leaf.type in ('atom', 'trailer', 'name'):
before_node = before_bracket_leaf = bracket_leaf.get_previous_leaf()
if before_node in (')', ']', '}'):
before_node = before_node.parent
if before_node.type in ('atom', 'trailer', 'name'):
values = infer_call_of_leaf(context, before_bracket_leaf)
return list(_completions_for_dicts(
module_context.inference_state,

View File

@@ -90,7 +90,7 @@ class InferenceState:
self.compiled_subprocess = environment.get_inference_state_subprocess(self)
self.grammar = environment.get_grammar()
self.latest_grammar = parso.load_grammar(version='3.10')
self.latest_grammar = parso.load_grammar(version='3.12')
self.memoize_cache = {} # for memoize decorators
self.module_cache = imports.ModuleCache() # does the job of `sys.modules`.
self.stub_module_cache = {} # Dict[Tuple[str, ...], Optional[ModuleValue]]
@@ -99,10 +99,11 @@ class InferenceState:
self.mixed_cache = {} # see `inference.compiled.mixed._create()`
self.analysis = []
self.dynamic_params_depth = 0
self.do_dynamic_params_search = settings.dynamic_params
self.is_analysis = False
self.project = project
self.access_cache = {}
self.allow_descriptor_getattr = False
self.allow_unsafe_executions = False
self.flow_analysis_enabled = True
self.reset_recursion_limitations()
@@ -125,7 +126,7 @@ class InferenceState:
@inference_state_function_cache()
def builtins_module(self):
module_name = 'builtins'
builtins_module, = self.import_module((module_name,), sys_path=())
builtins_module, = self.import_module((module_name,), sys_path=[])
return builtins_module
@property # type: ignore[misc]

View File

@@ -9,7 +9,7 @@ import re
import builtins
import typing
from pathlib import Path
from typing import Optional
from typing import Optional, Tuple
from jedi.inference.compiled.getattr_static import getattr_static
@@ -40,7 +40,7 @@ NOT_CLASS_TYPES = (
MethodDescriptorType = type(str.replace)
WrapperDescriptorType = type(set.__iter__)
# `object.__subclasshook__` is an already executed descriptor.
object_class_dict = type.__dict__["__dict__"].__get__(object)
object_class_dict = type.__dict__["__dict__"].__get__(object) # type: ignore[index]
ClassMethodDescriptorType = type(object_class_dict['__subclasshook__'])
_sentinel = object()
@@ -147,7 +147,7 @@ class AccessPath:
self.accesses = accesses
def create_access_path(inference_state, obj):
def create_access_path(inference_state, obj) -> AccessPath:
access = create_access(inference_state, obj)
return AccessPath(access.get_access_path_tuples())
@@ -175,7 +175,7 @@ class DirectObjectAccess:
def _create_access(self, obj):
return create_access(self._inference_state, obj)
def _create_access_path(self, obj):
def _create_access_path(self, obj) -> AccessPath:
return create_access_path(self._inference_state, obj)
def py__bool__(self):
@@ -230,8 +230,8 @@ class DirectObjectAccess:
return [annotation]
return None
def py__simple_getitem__(self, index):
if type(self._obj) not in ALLOWED_GETITEM_TYPES:
def py__simple_getitem__(self, index, *, safe=True):
if safe and type(self._obj) not in ALLOWED_GETITEM_TYPES:
# Get rid of side effects, we won't call custom `__getitem__`s.
return None
@@ -329,33 +329,37 @@ class DirectObjectAccess:
except TypeError:
return False
def is_allowed_getattr(self, name, safe=True):
def is_allowed_getattr(self, name, safe=True) -> Tuple[bool, bool, Optional[AccessPath]]:
# TODO this API is ugly.
if not safe:
# Unsafe is mostly used to check for __getattr__/__getattribute__.
# getattr_static works for properties, but the underscore methods
# are just ignored (because it's safer and avoids more code
# execution). See also GH #1378.
# Avoid warnings, see comment in the next function.
with warnings.catch_warnings(record=True):
warnings.simplefilter("always")
try:
return hasattr(self._obj, name), False
except Exception:
# Obviously has an attribute (propably a property) that
# gets executed, so just avoid all exceptions here.
return False, False
try:
attr, is_get_descriptor = getattr_static(self._obj, name)
except AttributeError:
return False, False
if not safe:
# Unsafe is mostly used to check for __getattr__/__getattribute__.
# getattr_static works for properties, but the underscore methods
# are just ignored (because it's safer and avoids more code
# execution). See also GH #1378.
# Avoid warnings, see comment in the next function.
with warnings.catch_warnings(record=True):
warnings.simplefilter("always")
try:
return hasattr(self._obj, name), False, None
except Exception:
# Obviously has an attribute (probably a property) that
# gets executed, so just avoid all exceptions here.
pass
return False, False, None
else:
if is_get_descriptor and type(attr) not in ALLOWED_DESCRIPTOR_ACCESS:
if isinstance(attr, property):
if hasattr(attr.fget, '__annotations__'):
a = DirectObjectAccess(self._inference_state, attr.fget)
return True, True, a.get_return_annotation()
# In case of descriptors that have get methods we cannot return
# it's value, because that would mean code execution.
return True, True
return True, False
return True, True, None
return True, False, None
def getattr_paths(self, name, default=_sentinel):
try:
@@ -515,7 +519,7 @@ class DirectObjectAccess:
# the signature. In that case we just want a simple escape for now.
raise ValueError
def get_return_annotation(self):
def get_return_annotation(self) -> Optional[AccessPath]:
try:
o = self._obj.__annotations__.get('return')
except AttributeError:

View File

@@ -142,9 +142,9 @@ class MixedObjectFilter(compiled.CompiledValueFilter):
super().__init__(inference_state, compiled_value)
self._tree_value = tree_value
def _create_name(self, name):
def _create_name(self, *args, **kwargs):
return MixedName(
super()._create_name(name),
super()._create_name(*args, **kwargs),
self._tree_value,
)

View File

@@ -21,11 +21,11 @@ class _ExactImporter(MetaPathFinder):
def __init__(self, path_dct):
self._path_dct = path_dct
def find_module(self, fullname, path=None):
def find_spec(self, fullname, path=None, target=None):
if path is None and fullname in self._path_dct:
p = self._path_dct[fullname]
loader = PathFinder.find_module(fullname, path=[p])
return loader
spec = PathFinder.find_spec(fullname, path=[p], target=target)
return spec
return None

View File

@@ -2,7 +2,6 @@ import sys
import os
import inspect
import importlib
import warnings
from pathlib import Path
from zipfile import ZipFile
from zipimport import zipimporter, ZipImportError
@@ -167,17 +166,16 @@ def _find_module(string, path=None, full_name=None, is_global_search=True):
def _find_module_py33(string, path=None, loader=None, full_name=None, is_global_search=True):
loader = loader or importlib.machinery.PathFinder.find_module(string, path)
if not loader:
spec = importlib.machinery.PathFinder.find_spec(string, path)
if spec is not None:
loader = spec.loader
if loader is None and path is None: # Fallback to find builtins
try:
with warnings.catch_warnings(record=True):
# Mute "DeprecationWarning: Use importlib.util.find_spec()
# instead." While we should replace that in the future, it's
# probably good to wait until we deprecate Python 3.3, since
# it was added in Python 3.4 and find_loader hasn't been
# removed in 3.6.
loader = importlib.find_loader(string)
spec = importlib.util.find_spec(string)
if spec is not None:
loader = spec.loader
except ValueError as e:
# See #491. Importlib might raise a ValueError, to avoid this, we
# just raise an ImportError to fix the issue.

View File

@@ -51,7 +51,6 @@ class CompiledValue(Value):
def py__call__(self, arguments):
return_annotation = self.access_handle.get_return_annotation()
if return_annotation is not None:
# TODO the return annotation may also be a string.
return create_from_access_path(
self.inference_state,
return_annotation
@@ -163,7 +162,10 @@ class CompiledValue(Value):
def py__simple_getitem__(self, index):
with reraise_getitem_errors(IndexError, KeyError, TypeError):
try:
access = self.access_handle.py__simple_getitem__(index)
access = self.access_handle.py__simple_getitem__(
index,
safe=not self.inference_state.allow_unsafe_executions
)
except AttributeError:
return super().py__simple_getitem__(index)
if access is None:
@@ -311,11 +313,12 @@ class CompiledModule(CompiledValue):
class CompiledName(AbstractNameDefinition):
def __init__(self, inference_state, parent_value, name):
def __init__(self, inference_state, parent_value, name, is_descriptor):
self._inference_state = inference_state
self.parent_context = parent_value.as_context()
self._parent_value = parent_value
self.string_name = name
self.is_descriptor = is_descriptor
def py__doc__(self):
return self.infer_compiled_value().py__doc__()
@@ -342,6 +345,11 @@ class CompiledName(AbstractNameDefinition):
@property
def api_type(self):
if self.is_descriptor:
# In case of properties we want to avoid executions as much as
# possible. Since the api_type can be wrong for other reasons
# anyway, we just return instance here.
return "instance"
return self.infer_compiled_value().api_type
def infer(self):
@@ -432,9 +440,10 @@ class CompiledValueFilter(AbstractFilter):
def get(self, name):
access_handle = self.compiled_value.access_handle
safe = not self._inference_state.allow_unsafe_executions
return self._get(
name,
lambda name, safe: access_handle.is_allowed_getattr(name, safe=safe),
lambda name: access_handle.is_allowed_getattr(name, safe=safe),
lambda name: name in access_handle.dir(),
check_has_attribute=True
)
@@ -443,30 +452,34 @@ class CompiledValueFilter(AbstractFilter):
"""
To remove quite a few access calls we introduced the callback here.
"""
if self._inference_state.allow_descriptor_getattr:
pass
has_attribute, is_descriptor = allowed_getattr_callback(
has_attribute, is_descriptor, property_return_annotation = allowed_getattr_callback(
name,
safe=not self._inference_state.allow_descriptor_getattr
)
if property_return_annotation is not None:
values = create_from_access_path(
self._inference_state,
property_return_annotation
).execute_annotation()
if values:
return [CompiledValueName(v, name) for v in values]
if check_has_attribute and not has_attribute:
return []
if (is_descriptor or not has_attribute) \
and not self._inference_state.allow_descriptor_getattr:
and not self._inference_state.allow_unsafe_executions:
return [self._get_cached_name(name, is_empty=True)]
if self.is_instance and not in_dir_callback(name):
return []
return [self._get_cached_name(name)]
return [self._get_cached_name(name, is_descriptor=is_descriptor)]
@memoize_method
def _get_cached_name(self, name, is_empty=False):
def _get_cached_name(self, name, is_empty=False, *, is_descriptor=False):
if is_empty:
return EmptyCompiledName(self._inference_state, name)
else:
return self._create_name(name)
return self._create_name(name, is_descriptor=is_descriptor)
def values(self):
from jedi.inference.compiled import builtin_from_name
@@ -480,7 +493,7 @@ class CompiledValueFilter(AbstractFilter):
for name in dir_infos:
names += self._get(
name,
lambda name, safe: dir_infos[name],
lambda name: dir_infos[name],
lambda name: name in dir_infos,
)
@@ -490,11 +503,12 @@ class CompiledValueFilter(AbstractFilter):
names += filter.values()
return names
def _create_name(self, name):
def _create_name(self, name, is_descriptor):
return CompiledName(
self._inference_state,
self.compiled_value,
name
name,
is_descriptor,
)
def __repr__(self):

View File

@@ -66,11 +66,11 @@ def dynamic_param_lookup(function_value, param_index):
have to look for all calls to ``func`` to find out what ``foo`` possibly
is.
"""
funcdef = function_value.tree_node
if not settings.dynamic_params:
if not function_value.inference_state.do_dynamic_params_search:
return NO_VALUES
funcdef = function_value.tree_node
path = function_value.get_root_context().py__file__()
if path is not None and is_stdlib_path(path):
# We don't want to search for references in the stdlib. Usually people

View File

@@ -402,6 +402,10 @@ def find_type_from_comment_hint_for(context, node, name):
def find_type_from_comment_hint_with(context, node, name):
if len(node.children) > 4:
# In case there are multiple with_items, we do not want a type hint for
# now.
return []
assert len(node.children[1].children) == 3, \
"Can only be here when children[1] is 'foo() as f'"
varlist = node.children[1].children[2]

View File

@@ -32,7 +32,7 @@ _TYPE_ALIAS_TYPES = {
'DefaultDict': 'collections.defaultdict',
'Deque': 'collections.deque',
}
_PROXY_TYPES = 'Optional Union ClassVar'.split()
_PROXY_TYPES = 'Optional Union ClassVar Annotated'.split()
class TypingModuleName(NameWrapper):
@@ -113,7 +113,7 @@ class ProxyWithGenerics(BaseTypingClassWithGenerics):
elif string_name == 'Type':
# The type is actually already given in the index_value
return self._generics_manager[0]
elif string_name == 'ClassVar':
elif string_name in ['ClassVar', 'Annotated']:
# For now don't do anything here, ClassVars are always used.
return self._generics_manager[0].execute_annotation()

View File

@@ -329,8 +329,8 @@ def infer_atom(context, atom):
c = atom.children
# Parentheses without commas are not tuples.
if c[0] == '(' and not len(c) == 2 \
and not(c[1].type == 'testlist_comp'
and len(c[1].children) > 1):
and not (c[1].type == 'testlist_comp'
and len(c[1].children) > 1):
return context.infer_node(c[1])
try:

View File

@@ -1,3 +1,5 @@
import sys
from typing import List
from pathlib import Path
from parso.tree import search_ancestor
@@ -131,15 +133,34 @@ def _is_pytest_func(func_name, decorator_nodes):
or any('fixture' in n.get_code() for n in decorator_nodes)
def _find_pytest_plugin_modules():
def _find_pytest_plugin_modules() -> List[List[str]]:
"""
Finds pytest plugin modules hooked by setuptools entry points
See https://docs.pytest.org/en/stable/how-to/writing_plugins.html#setuptools-entry-points
"""
from pkg_resources import iter_entry_points
if sys.version_info >= (3, 8):
from importlib.metadata import entry_points
return [ep.module_name.split(".") for ep in iter_entry_points(group="pytest11")]
if sys.version_info >= (3, 10):
pytest_entry_points = entry_points(group="pytest11")
else:
pytest_entry_points = entry_points().get("pytest11", ())
if sys.version_info >= (3, 9):
return [ep.module.split(".") for ep in pytest_entry_points]
else:
# Python 3.8 doesn't have `EntryPoint.module`. Implement equivalent
# to what Python 3.9 does (with additional None check to placate `mypy`)
matches = [
ep.pattern.match(ep.value)
for ep in pytest_entry_points
]
return [x.group('module').split(".") for x in matches if x]
else:
from pkg_resources import iter_entry_points
return [ep.module_name.split(".") for ep in iter_entry_points(group="pytest11")]
@inference_state_method_cache()

View File

@@ -808,9 +808,11 @@ _implemented = {
# https://www.attrs.org/en/stable/names.html
'attr': {
'define': _dataclass,
'frozen': _dataclass,
},
'attrs': {
'define': _dataclass,
'frozen': _dataclass,
},
'os.path': {
'dirname': _create_string_input_function(os.path.dirname),

View File

@@ -143,6 +143,15 @@ This improves autocompletion for libraries that use ``setattr`` or
``globals()`` modifications a lot.
"""
allow_unsafe_interpreter_executions = True
"""
Controls whether descriptors are evaluated when using an Interpreter. This is
something you might want to control when using Jedi from a Repl (e.g. IPython)
Generally this setting allows Jedi to execute __getitem__ and descriptors like
`property`.
"""
# ----------------
# Caching Validity
# ----------------

View File

@@ -2,7 +2,7 @@
Utilities for end-users.
"""
import __main__ # type: ignore[import]
import __main__
from collections import namedtuple
import logging
import traceback

View File

@@ -28,6 +28,12 @@ max-line-length = 100
[mypy]
# Exclude our copies of external stubs
exclude = ^jedi/third_party
show_error_codes = true
enable_error_code = ignore-without-code
# Ensure generics are explicit about what they are (e.g: `List[str]` rather than
# just `List`)
disallow_any_generics = True

View File

@@ -27,12 +27,16 @@ setup(name='jedi',
maintainer=__AUTHOR__,
maintainer_email=__AUTHOR_EMAIL__,
url='https://github.com/davidhalter/jedi',
project_urls={
"Documentation": 'https://jedi.readthedocs.io/en/latest/',
},
license='MIT',
keywords='python completion refactoring vim',
long_description=readme,
packages=find_packages(exclude=['test', 'test.*']),
python_requires='>=3.6',
install_requires=['parso>=0.8.0,<0.9.0'],
# Python 3.11 & 3.12 grammars are added to parso in 0.8.3
install_requires=['parso>=0.8.3,<0.9.0'],
extras_require={
'testing': [
'pytest<7.0.0',
@@ -40,12 +44,16 @@ setup(name='jedi',
'docopt',
# coloroma for colored debug output
'colorama',
'Django<3.1', # For now pin this.
'Django',
'attrs',
],
'qa': [
'flake8==3.8.3',
'mypy==0.782',
# latest version supporting Python 3.6
'flake8==5.0.4',
# latest version supporting Python 3.6
'mypy==0.971',
# Arbitrary pins, latest at the time of pinning
'types-setuptools==67.2.0.1',
],
'docs': [
# Just pin all of these.
@@ -91,6 +99,8 @@ setup(name='jedi',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Text Editors :: Integrated Development Environments (IDE)',
'Topic :: Utilities',

View File

@@ -413,6 +413,10 @@ with Foo() as f3:
with Foo() as f3:
f3
with open("a"), open("b") as bfile:
#? ['flush']
bfile.flush
# -----------------
# Avoiding multiple definitions
# -----------------

View File

@@ -76,7 +76,7 @@ from import_tree.pkg.mod1 import not_existant,
#? 22 ['mod1', 'base']
from import_tree.pkg. import mod1
#? 17 ['mod1', 'mod2', 'random', 'pkg', 'references', 'rename1', 'rename2', 'classes', 'globals', 'recurse_class1', 'recurse_class2', 'invisible_pkg', 'flow_import']
from import_tree. import pkg
from import_tree. import new_pkg
#? 18 ['pkg']
from import_tree.p import pkg

View File

@@ -0,0 +1,26 @@
# python >= 3.9
from typing import Annotated
# This is just a dummy and very meaningless thing to use with to the Annotated
# type hint
class Foo:
pass
class A:
pass
def annotated_function_params(
basic: Annotated[str, Foo()],
obj: A,
annotated_obj: Annotated[A, Foo()],
):
#? str()
basic
#? A()
obj
#? A()
annotated_obj

View File

@@ -188,10 +188,7 @@ def test_functions_should_have_params(Script):
assert c.get_signatures()
def test_hashlib_params(Script, environment):
if environment.version_info < (3,):
pytest.skip()
def test_hashlib_params(Script):
script = Script('from hashlib import sha256')
c, = script.complete()
sig, = c.get_signatures()

View File

@@ -28,6 +28,11 @@ def test_import_keyword(Script):
# unrelated to #44
def test_import_keyword_after_newline(Script):
d, = Script("import x\nimport y").help(line=2, column=0)
assert d.docstring().startswith('The "import" statement')
def test_import_keyword_with_gotos(goto_or_infer):
assert not goto_or_infer("import x", column=0)

View File

@@ -8,6 +8,7 @@ import typing
import pytest
import jedi
import jedi.settings
from jedi.inference.compiled import mixed
from importlib import import_module
@@ -101,11 +102,13 @@ def test_side_effect_completion():
assert foo.name == 'foo'
def _assert_interpreter_complete(source, namespace, completions,
**kwds):
def _assert_interpreter_complete(source, namespace, completions, *, check_type=False, **kwds):
script = jedi.Interpreter(source, [namespace], **kwds)
cs = script.complete()
actual = [c.name for c in cs]
if check_type:
for c in cs:
c.type
assert sorted(actual) == sorted(completions)
@@ -219,7 +222,7 @@ def test__getattr__completions(allow_unsafe_getattr, class_is_findable):
@pytest.fixture(params=[False, True])
def allow_unsafe_getattr(request, monkeypatch):
monkeypatch.setattr(jedi.Interpreter, '_allow_descriptor_getattr_default', request.param)
monkeypatch.setattr(jedi.settings, 'allow_unsafe_interpreter_executions', request.param)
return request.param
@@ -610,12 +613,12 @@ def test_dict_getitem(code, types):
#('for x in dunder: x', 'str'),
]
)
def test_dunders(class_is_findable, code, expected):
def test_dunders(class_is_findable, code, expected, allow_unsafe_getattr):
from typing import Iterator
class DunderCls:
def __getitem__(self, key) -> int:
pass
return 1
def __iter__(self, key) -> Iterator[str]:
pass
@@ -656,10 +659,12 @@ def bar():
({'return': 'typing.Union[str, int]'}, ['int', 'str'], ''),
({'return': 'typing.Union["str", int]'},
['int', 'str'] if sys.version_info >= (3, 9) else ['int'], ''),
({'return': 'typing.Union["str", 1]'}, [], ''),
({'return': 'typing.Union["str", 1]'},
['str'] if sys.version_info >= (3, 11) else [], ''),
({'return': 'typing.Optional[str]'}, ['NoneType', 'str'], ''),
({'return': 'typing.Optional[str, int]'}, [], ''), # Takes only one arg
({'return': 'typing.Any'}, [], ''),
({'return': 'typing.Any'},
['_AnyMeta'] if sys.version_info >= (3, 11) else [], ''),
({'return': 'typing.Tuple[int, str]'},
['Tuple' if sys.version_info[:2] == (3, 6) else 'tuple'], ''),
@@ -754,3 +759,99 @@ def test_keyword_param_completion(code, expected):
import random
completions = jedi.Interpreter(code, [locals()]).complete()
assert expected == [c.name for c in completions if c.name.endswith('=')]
@pytest.mark.parametrize('class_is_findable', [False, True])
def test_avoid_descriptor_executions_if_not_necessary(class_is_findable):
counter = 0
class AvoidDescriptor(object):
@property
def prop(self):
nonlocal counter
counter += 1
return self
if not class_is_findable:
AvoidDescriptor.__name__ = "something_somewhere"
namespace = {'b': AvoidDescriptor()}
expected = ['prop']
_assert_interpreter_complete('b.pro', namespace, expected, check_type=True)
assert counter == 0
_assert_interpreter_complete('b.prop.pro', namespace, expected, check_type=True)
assert counter == 1
class Hello:
its_me = 1
@pytest.mark.parametrize('class_is_findable', [False, True])
def test_try_to_use_return_annotation_for_property(class_is_findable):
class WithProperties(object):
@property
def with_annotation1(self) -> str:
raise BaseException
@property
def with_annotation2(self) -> 'str':
raise BaseException
@property
def with_annotation3(self) -> Hello:
raise BaseException
@property
def with_annotation4(self) -> 'Hello':
raise BaseException
@property
def with_annotation_garbage1(self) -> 'asldjflksjdfljdslkjfsl': # noqa
return Hello()
@property
def with_annotation_garbage2(self) -> 'sdf$@@$5*+8': # noqa
return Hello()
@property
def without_annotation(self):
return ""
if not class_is_findable:
WithProperties.__name__ = "something_somewhere"
Hello.__name__ = "something_somewhere_else"
namespace = {'p': WithProperties()}
_assert_interpreter_complete('p.without_annotation.upp', namespace, ['upper'])
_assert_interpreter_complete('p.with_annotation1.upp', namespace, ['upper'])
_assert_interpreter_complete('p.with_annotation2.upp', namespace, ['upper'])
_assert_interpreter_complete('p.with_annotation3.its', namespace, ['its_me'])
_assert_interpreter_complete('p.with_annotation4.its', namespace, ['its_me'])
# This is a fallback, if the annotations don't help
_assert_interpreter_complete('p.with_annotation_garbage1.its', namespace, ['its_me'])
_assert_interpreter_complete('p.with_annotation_garbage2.its', namespace, ['its_me'])
def test_nested__getitem__():
d = {'foo': {'bar': 1}}
_assert_interpreter_complete('d["fo', locals(), ['"foo"'])
_assert_interpreter_complete('d["foo"]["ba', locals(), ['"bar"'])
_assert_interpreter_complete('(d["foo"])["ba', locals(), ['"bar"'])
_assert_interpreter_complete('((d["foo"]))["ba', locals(), ['"bar"'])
@pytest.mark.parametrize('class_is_findable', [False, True])
def test_custom__getitem__(class_is_findable, allow_unsafe_getattr):
class CustomGetItem:
def __getitem__(self, x: int):
return "asdf"
if not class_is_findable:
CustomGetItem.__name__ = "something_somewhere"
namespace = {'c': CustomGetItem()}
if not class_is_findable and not allow_unsafe_getattr:
expected = []
else:
expected = ['upper']
_assert_interpreter_complete('c["a"].up', namespace, expected)

View File

@@ -1,4 +1,5 @@
import os
import shutil
from textwrap import dedent
from pathlib import Path
import platform
@@ -6,6 +7,7 @@ import platform
import pytest
import jedi
from test.helpers import get_example_dir
@pytest.fixture()
@@ -52,6 +54,47 @@ def test_rename_mod(Script, dir_with_content):
''').format(dir=dir_with_content)
@pytest.mark.skipif('sys.version_info[:2] < (3, 8)', message="Python 3.8 introduces dirs_exist_ok")
def test_namespace_package(Script, tmpdir):
origin = get_example_dir('implicit_namespace_package')
shutil.copytree(origin, tmpdir.strpath, dirs_exist_ok=True)
sys_path = [
os.path.join(tmpdir.strpath, 'ns1'),
os.path.join(tmpdir.strpath, 'ns2')
]
script_path = os.path.join(tmpdir.strpath, 'script.py')
script = Script(
'import pkg\n',
path=script_path,
project=jedi.Project(os.path.join(tmpdir.strpath, 'does-not-exist'), sys_path=sys_path),
)
refactoring = script.rename(line=1, new_name='new_pkg')
refactoring.apply()
old1 = os.path.join(sys_path[0], "pkg")
new1 = os.path.join(sys_path[0], "new_pkg")
old2 = os.path.join(sys_path[1], "pkg")
new2 = os.path.join(sys_path[1], "new_pkg")
assert not os.path.exists(old1)
assert os.path.exists(new1)
assert not os.path.exists(old2)
assert os.path.exists(new2)
changed, = iter(refactoring.get_changed_files().values())
assert changed.get_new_code() == "import new_pkg\n"
assert refactoring.get_diff() == dedent(f'''\
rename from {old1}
rename to {new1}
rename from {old2}
rename to {new2}
--- {script_path}
+++ {script_path}
@@ -1,2 +1,2 @@
-import pkg
+import new_pkg
''').format(dir=dir_with_content)
def test_rename_none_path(Script):
refactoring = Script('foo', path=None).rename(new_name='bar')
with pytest.raises(jedi.RefactoringError, match='on a Script with path=None'):

View File

@@ -1,4 +1,5 @@
from textwrap import dedent
import sys
import math
from collections import Counter
from datetime import datetime
@@ -26,7 +27,10 @@ def test_builtin_loading(inference_state):
assert not from_name.py__doc__() # It's a stub
def test_next_docstr(inference_state):
def test_next_docstr(inference_state, environment):
if environment.version_info[:2] != sys.version_info[:2]:
pytest.skip()
next_ = compiled.builtin_from_name(inference_state, 'next')
assert next_.tree_node is not None
assert next_.py__doc__() == '' # It's a stub

View File

@@ -1,13 +1,6 @@
import pytest
from textwrap import dedent
@pytest.fixture(autouse=True)
def skip_not_supported(environment):
if environment.version_info < (3, 6):
pytest.skip()
def test_fstring_multiline(Script):
code = dedent("""\
'' f'''s{

View File

@@ -12,29 +12,23 @@ def _infer_literal(Script, code, is_fstring=False):
return def_._name._value.get_safe_value()
def test_f_strings(Script, environment):
def test_f_strings(Script):
"""
f literals are not really supported in Jedi. They just get ignored and an
empty string is returned.
"""
if environment.version_info < (3, 6):
pytest.skip()
assert _infer_literal(Script, 'f"asdf"', is_fstring=True) == ''
assert _infer_literal(Script, 'f"{asdf} "', is_fstring=True) == ''
assert _infer_literal(Script, 'F"{asdf} "', is_fstring=True) == ''
assert _infer_literal(Script, 'rF"{asdf} "', is_fstring=True) == ''
def test_rb_strings(Script, environment):
def test_rb_strings(Script):
assert _infer_literal(Script, 'x = br"asdf"; x') == b'asdf'
assert _infer_literal(Script, 'x = rb"asdf"; x') == b'asdf'
def test_thousand_separators(Script, environment):
if environment.version_info < (3, 6):
pytest.skip()
def test_thousand_separators(Script):
assert _infer_literal(Script, '1_2_3') == 123
assert _infer_literal(Script, '123_456_789') == 123456789
assert _infer_literal(Script, '0x3_4') == 52

View File

@@ -13,18 +13,15 @@ from ..helpers import get_example_dir
'code, sig, names, op, version', [
('import math; math.cos', 'cos(x, /)', ['x'], ge, (3, 6)),
('next', 'next(iterator, default=None, /)', ['iterator', 'default'], ge, (3, 6)),
('next', 'next(iterator, default=None, /)', ['iterator', 'default'], lt, (3, 12)),
('next', 'next()', [], ge, (3, 12)),
('str', "str(object='', /) -> str", ['object'], ge, (3, 6)),
('pow', 'pow(x, y, z=None, /) -> number', ['x', 'y', 'z'], lt, (3, 6)),
('pow', 'pow(base, exp, mod=None)', ['base', 'exp', 'mod'], ge, (3, 8)),
('bytes.partition', 'partition(self, sep, /) -> (head, sep, tail)',
['self', 'sep'], lt, (3, 6)),
('bytes.partition', 'partition(self, sep, /)', ['self', 'sep'], ge, (3, 6)),
('bytes().partition', 'partition(sep, /) -> (head, sep, tail)', ['sep'], lt, (3, 6)),
('bytes().partition', 'partition(sep, /)', ['sep'], ge, (3, 6)),
]
)
@@ -355,6 +352,7 @@ def test_dataclass_signature(Script, skip_pre_python37, start, start_params):
price, = sig.params[-2].infer()
assert price.name == 'float'
@pytest.mark.parametrize(
'start, start_params', [
['@define\nclass X:', []],

View File

@@ -30,14 +30,16 @@ def test_paths_from_assignment(Script):
assert paths('sys.path, other = ["a"], 2') == set()
def test_venv_and_pths(venv_path):
def test_venv_and_pths(venv_path, environment):
pjoin = os.path.join
site_pkg_path = pjoin(venv_path, 'lib')
if os.name == 'nt':
site_pkg_path = pjoin(site_pkg_path, 'site-packages')
if environment.version_info < (3, 11):
site_pkg_path = pjoin(venv_path, 'lib', 'site-packages')
else:
site_pkg_path = pjoin(venv_path, 'Lib', 'site-packages')
else:
site_pkg_path = glob(pjoin(site_pkg_path, 'python*', 'site-packages'))[0]
site_pkg_path = glob(pjoin(venv_path, 'lib', 'python*', 'site-packages'))[0]
shutil.rmtree(site_pkg_path)
shutil.copytree(get_example_dir('sample_venvs', 'pth_directory'), site_pkg_path)

View File

@@ -1,4 +1,5 @@
import os
import sys
from collections import namedtuple
import pytest
@@ -50,14 +51,32 @@ def test_completion(case, monkeypatch, environment, has_django):
pytest_plugin_dir = str(helpers.get_example_dir("pytest_plugin_package"))
case._project.added_sys_path = [pytest_plugin_dir]
# ... and mock setuptools entry points to include it
# ... and mock the entry points to include it
# see https://docs.pytest.org/en/stable/how-to/writing_plugins.html#setuptools-entry-points
def mock_iter_entry_points(group):
assert group == "pytest11"
EntryPoint = namedtuple("EntryPoint", ["module_name"])
return [EntryPoint("pytest_plugin.plugin")]
if sys.version_info >= (3, 8):
def mock_entry_points(*, group=None):
import importlib.metadata
entries = [importlib.metadata.EntryPoint(
name=None,
value="pytest_plugin.plugin",
group="pytest11",
)]
monkeypatch.setattr("pkg_resources.iter_entry_points", mock_iter_entry_points)
if sys.version_info >= (3, 10):
assert group == "pytest11"
return entries
else:
assert group is None
return {"pytest11": entries}
monkeypatch.setattr("importlib.metadata.entry_points", mock_entry_points)
else:
def mock_iter_entry_points(group):
assert group == "pytest11"
EntryPoint = namedtuple("EntryPoint", ["module_name"])
return [EntryPoint("pytest_plugin.plugin")]
monkeypatch.setattr("pkg_resources.iter_entry_points", mock_iter_entry_points)
repo_root = helpers.root_dir
monkeypatch.chdir(os.path.join(repo_root, 'jedi'))

View File

@@ -81,10 +81,10 @@ class TestSetupReadline(unittest.TestCase):
if all(not x.startswith('from os import ' + s)
for s in ['_', 'O_', 'EX_', 'MFD_', 'SF_', 'ST_',
'CLD_', 'POSIX_SPAWN_', 'P_', 'RWF_',
'SCHED_'])
'CLONE_', 'SCHED_'])
}
# There are quite a few differences, because both Windows and Linux
# (posix and nt) librariesare included.
# (posix and nt) libraries are included.
assert len(difference) < 30
def test_local_import(self):