mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-16 10:37:52 +08:00
Merge branch 'master' into fix-nested-tuple-argument
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +1,6 @@
|
||||
[submodule "jedi/third_party/typeshed"]
|
||||
path = jedi/third_party/typeshed
|
||||
url = https://github.com/davidhalter/typeshed.git
|
||||
[submodule "jedi/third_party/django-stubs"]
|
||||
path = jedi/third_party/django-stubs
|
||||
url = https://github.com/typeddjango/django-stubs
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
Changelog
|
||||
---------
|
||||
|
||||
Unreleased: 0.17.1 (2020-04-)
|
||||
+++++++++++++++++++
|
||||
|
||||
- Django ``Model`` meta class support
|
||||
- Added Django Stubs to Jedi, thanks to all contributors of the
|
||||
`Django Stubs <https://github.com/typeddjango/django-stubs>`_ project
|
||||
- A few bugfixes
|
||||
|
||||
0.17.0 (2020-04-14)
|
||||
+++++++++++++++++++
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ include requirements.txt
|
||||
include jedi/parser/python/grammar*.txt
|
||||
recursive-include jedi/third_party *.pyi
|
||||
include jedi/third_party/typeshed/LICENSE
|
||||
include jedi/third_party/django-stubs/LICENSE.txt
|
||||
include jedi/third_party/typeshed/README
|
||||
recursive-include test *
|
||||
recursive-include docs *
|
||||
|
||||
@@ -13,7 +13,7 @@ from test.helpers import test_dir
|
||||
|
||||
collect_ignore = [
|
||||
'setup.py',
|
||||
'__main__.py',
|
||||
'jedi/__main__.py',
|
||||
'jedi/inference/compiled/subprocess/__main__.py',
|
||||
'build/',
|
||||
'test/examples',
|
||||
@@ -147,6 +147,12 @@ def has_typing(environment):
|
||||
return bool(script.infer())
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def has_django(environment):
|
||||
script = jedi.Script('import django', environment=environment)
|
||||
return bool(script.infer())
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def jedi_path():
|
||||
return os.path.dirname(__file__)
|
||||
|
||||
@@ -27,7 +27,7 @@ ad
|
||||
load
|
||||
"""
|
||||
|
||||
__version__ = '0.17.0'
|
||||
__version__ = '0.17.1'
|
||||
|
||||
from jedi.api import Script, Interpreter, set_debug_function, \
|
||||
preload_module, names
|
||||
|
||||
@@ -44,20 +44,29 @@ def _complete():
|
||||
import jedi
|
||||
import pdb
|
||||
|
||||
if '-d' in sys.argv:
|
||||
sys.argv.remove('-d')
|
||||
jedi.set_debug_function()
|
||||
|
||||
try:
|
||||
for c in jedi.Script(sys.argv[2]).complete():
|
||||
completions = jedi.Script(sys.argv[2]).complete()
|
||||
for c in completions:
|
||||
c.docstring()
|
||||
c.type
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(repr(e))
|
||||
pdb.post_mortem()
|
||||
else:
|
||||
print(completions)
|
||||
|
||||
|
||||
if len(sys.argv) == 2 and sys.argv[1] == 'repl':
|
||||
# don't want to use __main__ only for repl yet, maybe we want to use it for
|
||||
# something else. So just use the keyword ``repl`` for now.
|
||||
print(join(dirname(abspath(__file__)), 'api', 'replstartup.py'))
|
||||
elif len(sys.argv) > 1 and sys.argv[1] == 'linter':
|
||||
elif len(sys.argv) > 1 and sys.argv[1] == '_linter':
|
||||
_start_linter()
|
||||
elif len(sys.argv) > 1 and sys.argv[1] == '_complete':
|
||||
_complete()
|
||||
else:
|
||||
print('Command not implemented: %s' % sys.argv[1])
|
||||
|
||||
@@ -339,6 +339,13 @@ try:
|
||||
except NameError:
|
||||
PermissionError = IOError
|
||||
|
||||
try:
|
||||
NotADirectoryError = NotADirectoryError
|
||||
except NameError:
|
||||
class NotADirectoryError(Exception):
|
||||
# Don't implement this for Python 2 anymore.
|
||||
pass
|
||||
|
||||
|
||||
def no_unicode_pprint(dct):
|
||||
"""
|
||||
|
||||
@@ -472,9 +472,20 @@ class Script(object):
|
||||
if definitions:
|
||||
return definitions
|
||||
leaf = self._module_node.get_leaf_for_position((line, column))
|
||||
if leaf.type in ('keyword', 'operator', 'error_leaf'):
|
||||
reserved = self._inference_state.grammar._pgen_grammar.reserved_syntax_strings.keys()
|
||||
if leaf.value in reserved:
|
||||
if leaf is not None and leaf.type in ('keyword', 'operator', 'error_leaf'):
|
||||
def need_pydoc():
|
||||
if leaf.value in ('(', ')', '[', ']'):
|
||||
if leaf.parent.type == 'trailer':
|
||||
return False
|
||||
if leaf.parent.type == 'atom':
|
||||
return False
|
||||
grammar = self._inference_state.grammar
|
||||
# This parso stuff is not public, but since I control it, this
|
||||
# is fine :-) ~dave
|
||||
reserved = grammar._pgen_grammar.reserved_syntax_strings.keys()
|
||||
return leaf.value in reserved
|
||||
|
||||
if need_pydoc():
|
||||
name = KeywordName(self._inference_state, leaf.value)
|
||||
return [classes.Name(self._inference_state, name)]
|
||||
return []
|
||||
|
||||
@@ -94,7 +94,11 @@ class BaseName(object):
|
||||
|
||||
@property
|
||||
def module_path(self):
|
||||
"""Shows the file path of a module. e.g. ``/usr/lib/python2.7/os.py``"""
|
||||
"""
|
||||
Shows the file path of a module. e.g. ``/usr/lib/python2.7/os.py``
|
||||
|
||||
:rtype: str or None
|
||||
"""
|
||||
module = self._get_module_context()
|
||||
if module.is_stub() or not module.is_compiled():
|
||||
# Compiled modules should not return a module path even if they
|
||||
@@ -168,7 +172,7 @@ class BaseName(object):
|
||||
>>> defs[3]
|
||||
'function'
|
||||
|
||||
Valid values for are ``module``, ``class``, ``instance``, ``function``,
|
||||
Valid values for type are ``module``, ``class``, ``instance``, ``function``,
|
||||
``param``, ``path``, ``keyword`` and ``statement``.
|
||||
|
||||
"""
|
||||
@@ -245,8 +249,8 @@ class BaseName(object):
|
||||
Document for function f.
|
||||
|
||||
Notice that useful extra information is added to the actual
|
||||
docstring. For function, it is signature. If you need
|
||||
actual docstring, use ``raw=True`` instead.
|
||||
docstring, e.g. function signatures are prepended to their docstrings.
|
||||
If you need the actual docstring, use ``raw=True`` instead.
|
||||
|
||||
>>> print(script.infer(1, len('def f'))[0].docstring(raw=True))
|
||||
Document for function f.
|
||||
@@ -665,7 +669,7 @@ class Completion(BaseName):
|
||||
|
||||
def docstring(self, raw=False, fast=True):
|
||||
"""
|
||||
Documentated under :meth:`BaseName.docstring`.
|
||||
Documented under :meth:`BaseName.docstring`.
|
||||
"""
|
||||
if self._like_name_length >= 3:
|
||||
# In this case we can just resolve the like name, because we
|
||||
@@ -703,7 +707,7 @@ class Completion(BaseName):
|
||||
@property
|
||||
def type(self):
|
||||
"""
|
||||
Documentated under :meth:`BaseName.type`.
|
||||
Documented under :meth:`BaseName.type`.
|
||||
"""
|
||||
# Purely a speed optimization.
|
||||
if self._cached_name is not None:
|
||||
@@ -734,8 +738,7 @@ class Name(BaseName):
|
||||
DeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
position = '' if self.in_builtin_module else '@%s' % self.line
|
||||
return "%s:%s%s" % (self.module_name, self.description, position)
|
||||
return "%s:%s" % (self.module_name, self.description)
|
||||
|
||||
@memoize_method
|
||||
def defined_names(self):
|
||||
@@ -798,7 +801,7 @@ class BaseSignature(Name):
|
||||
Returns a text representation of the signature. This could for example
|
||||
look like ``foo(bar, baz: int, **kwargs)``.
|
||||
|
||||
:return str
|
||||
:rtype: str
|
||||
"""
|
||||
return self._signature.to_string()
|
||||
|
||||
@@ -865,7 +868,7 @@ class ParamName(Name):
|
||||
Returns a simple representation of a param, like
|
||||
``f: Callable[..., Any]``.
|
||||
|
||||
:rtype: :class:`str`
|
||||
:rtype: str
|
||||
"""
|
||||
return self._name.to_string()
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import json
|
||||
import sys
|
||||
|
||||
from jedi._compatibility import FileNotFoundError, PermissionError, \
|
||||
IsADirectoryError
|
||||
IsADirectoryError, NotADirectoryError
|
||||
from jedi import debug
|
||||
from jedi.api.environment import get_cached_default_environment, create_environment
|
||||
from jedi.api.exceptions import WrongVersion
|
||||
@@ -383,6 +383,8 @@ def get_default_project(path=None):
|
||||
return Project.load(dir)
|
||||
except (FileNotFoundError, IsADirectoryError, PermissionError):
|
||||
pass
|
||||
except NotADirectoryError:
|
||||
continue
|
||||
|
||||
if first_no_init_file is None:
|
||||
if os.path.exists(os.path.join(dir, '__init__.py')):
|
||||
|
||||
@@ -99,7 +99,7 @@ class DefineGenericBase(LazyValueWrapper):
|
||||
for generic_set in self.get_generics():
|
||||
values = NO_VALUES
|
||||
for generic in generic_set:
|
||||
if isinstance(generic, (GenericClass, TypeVar)):
|
||||
if isinstance(generic, (DefineGenericBase, TypeVar)):
|
||||
result = generic.define_generics(type_var_dict)
|
||||
values |= result
|
||||
if result != ValueSet({generic}):
|
||||
|
||||
@@ -12,6 +12,8 @@ from jedi.inference.value import ModuleValue
|
||||
|
||||
_jedi_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
TYPESHED_PATH = os.path.join(_jedi_path, 'third_party', 'typeshed')
|
||||
DJANGO_INIT_PATH = os.path.join(_jedi_path, 'third_party', 'django-stubs',
|
||||
'django-stubs', '__init__.pyi')
|
||||
|
||||
_IMPORT_MAP = dict(
|
||||
_collections='collections',
|
||||
@@ -173,6 +175,13 @@ def _try_to_load_stub(inference_state, import_names, python_value_set,
|
||||
)
|
||||
if m is not None:
|
||||
return m
|
||||
if import_names[0] == 'django':
|
||||
return _try_to_load_stub_from_file(
|
||||
inference_state,
|
||||
python_value_set,
|
||||
file_io=FileIO(DJANGO_INIT_PATH),
|
||||
import_names=import_names,
|
||||
)
|
||||
|
||||
# 2. Try to load pyi files next to py files.
|
||||
for c in python_value_set:
|
||||
|
||||
@@ -226,6 +226,21 @@ class TypingClassValueWithIndex(_TypingClassMixin, TypingValueWithIndex):
|
||||
|
||||
elif annotation_name == 'Callable':
|
||||
if len(annotation_generics) == 2:
|
||||
if is_class_value:
|
||||
# This only applies if we are comparing something like
|
||||
# List[Callable[..., T]] with Iterable[Callable[..., T]].
|
||||
# First, Jedi tries to match List/Iterable. After that we
|
||||
# will land here, because is_class_value will be True at
|
||||
# that point. Obviously we also compare below that both
|
||||
# sides are `Callable`.
|
||||
for element in value_set:
|
||||
element_name = element.py__name__()
|
||||
if element_name == 'Callable':
|
||||
merge_type_var_dicts(
|
||||
type_var_dict,
|
||||
merge_pairwise_generics(self, element),
|
||||
)
|
||||
else:
|
||||
return annotation_generics[1].infer_type_vars(
|
||||
value_set.execute_annotation(),
|
||||
)
|
||||
@@ -421,6 +436,10 @@ class NewType(Value):
|
||||
self._type_value_set = type_value_set
|
||||
self.tree_node = tree_node
|
||||
|
||||
def py__class__(self):
|
||||
c, = self._type_value_set.py__class__()
|
||||
return c
|
||||
|
||||
def py__call__(self, arguments):
|
||||
return self._type_value_set.execute_annotation()
|
||||
|
||||
|
||||
@@ -192,13 +192,17 @@ class Sequence(LazyAttributeOverwrite, IterableMixin):
|
||||
def _get_generics(self):
|
||||
return (self.merge_types_of_iterate().py__class__(),)
|
||||
|
||||
@inference_state_method_cache(default=())
|
||||
def _cached_generics(self):
|
||||
return self._get_generics()
|
||||
|
||||
def _get_wrapped_value(self):
|
||||
from jedi.inference.gradual.base import GenericClass
|
||||
from jedi.inference.gradual.generics import TupleGenericManager
|
||||
klass = compiled.builtin_from_name(self.inference_state, self.array_type)
|
||||
c, = GenericClass(
|
||||
klass,
|
||||
TupleGenericManager(self._get_generics())
|
||||
TupleGenericManager(self._cached_generics())
|
||||
).execute_annotation()
|
||||
return c
|
||||
|
||||
|
||||
@@ -186,7 +186,8 @@ class ClassMixin(object):
|
||||
mro.append(cls_new)
|
||||
yield cls_new
|
||||
|
||||
def get_filters(self, origin_scope=None, is_instance=False):
|
||||
def get_filters(self, origin_scope=None, is_instance=False, include_metaclasses=True):
|
||||
if include_metaclasses:
|
||||
metaclasses = self.get_metaclasses()
|
||||
if metaclasses:
|
||||
for f in self.get_metaclass_filters(metaclasses):
|
||||
|
||||
130
jedi/plugins/django.py
Normal file
130
jedi/plugins/django.py
Normal file
@@ -0,0 +1,130 @@
|
||||
"""
|
||||
Module is used to infer Django model fields.
|
||||
"""
|
||||
from jedi import debug
|
||||
from jedi.inference.base_value import ValueSet, iterator_to_value_set
|
||||
from jedi.inference.filters import ParserTreeFilter, DictFilter
|
||||
from jedi.inference.names import NameWrapper
|
||||
from jedi.inference.value.instance import TreeInstance
|
||||
from jedi.inference.gradual.base import GenericClass
|
||||
from jedi.inference.gradual.generics import TupleGenericManager
|
||||
|
||||
|
||||
mapping = {
|
||||
'IntegerField': (None, 'int'),
|
||||
'BigIntegerField': (None, 'int'),
|
||||
'PositiveIntegerField': (None, 'int'),
|
||||
'SmallIntegerField': (None, 'int'),
|
||||
'CharField': (None, 'str'),
|
||||
'TextField': (None, 'str'),
|
||||
'EmailField': (None, 'str'),
|
||||
'FloatField': (None, 'float'),
|
||||
'BinaryField': (None, 'bytes'),
|
||||
'BooleanField': (None, 'bool'),
|
||||
'DecimalField': ('decimal', 'Decimal'),
|
||||
'TimeField': ('datetime', 'time'),
|
||||
'DurationField': ('datetime', 'timedelta'),
|
||||
'DateField': ('datetime', 'date'),
|
||||
'DateTimeField': ('datetime', 'datetime'),
|
||||
}
|
||||
|
||||
|
||||
def _infer_scalar_field(inference_state, field_name, field_tree_instance):
|
||||
try:
|
||||
module_name, attribute_name = mapping[field_tree_instance.py__name__()]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
if module_name is None:
|
||||
module = inference_state.builtins_module
|
||||
else:
|
||||
module = inference_state.import_module((module_name,))
|
||||
|
||||
for attribute in module.py__getattribute__(attribute_name):
|
||||
return attribute.execute_with_values()
|
||||
|
||||
|
||||
@iterator_to_value_set
|
||||
def _get_foreign_key_values(cls, field_tree_instance):
|
||||
if isinstance(field_tree_instance, TreeInstance):
|
||||
# TODO private access..
|
||||
argument_iterator = field_tree_instance._arguments.unpack()
|
||||
key, lazy_values = next(argument_iterator, (None, None))
|
||||
if key is None and lazy_values is not None:
|
||||
for value in lazy_values.infer():
|
||||
if value.py__name__() == 'str':
|
||||
foreign_key_class_name = value.get_safe_value()
|
||||
module = cls.get_root_context()
|
||||
for v in module.py__getattribute__(foreign_key_class_name):
|
||||
if v.is_class():
|
||||
yield v
|
||||
elif value.is_class():
|
||||
yield value
|
||||
|
||||
|
||||
def _infer_field(cls, field_name):
|
||||
inference_state = cls.inference_state
|
||||
for field_tree_instance in field_name.infer():
|
||||
scalar_field = _infer_scalar_field(inference_state, field_name, field_tree_instance)
|
||||
if scalar_field is not None:
|
||||
return scalar_field
|
||||
|
||||
name = field_tree_instance.py__name__()
|
||||
is_many_to_many = name == 'ManyToManyField'
|
||||
if name == 'ForeignKey' or is_many_to_many:
|
||||
values = _get_foreign_key_values(cls, field_tree_instance)
|
||||
if is_many_to_many:
|
||||
return ValueSet(filter(None, [
|
||||
_create_manager_for(v, 'RelatedManager') for v in values
|
||||
]))
|
||||
else:
|
||||
return values.execute_with_values()
|
||||
|
||||
debug.dbg('django plugin: fail to infer `%s` from class `%s`',
|
||||
field_name.string_name, cls.py__name__())
|
||||
return field_name.infer()
|
||||
|
||||
|
||||
class DjangoModelName(NameWrapper):
|
||||
def __init__(self, cls, name):
|
||||
super(DjangoModelName, self).__init__(name)
|
||||
self._cls = cls
|
||||
|
||||
def infer(self):
|
||||
return _infer_field(self._cls, self._wrapped_name)
|
||||
|
||||
|
||||
def _create_manager_for(cls, manager_cls='BaseManager'):
|
||||
managers = cls.inference_state.import_module(
|
||||
('django', 'db', 'models', 'manager')
|
||||
).py__getattribute__(manager_cls)
|
||||
for m in managers:
|
||||
if m.is_class() and not m.is_compiled():
|
||||
generics_manager = TupleGenericManager((ValueSet([cls]),))
|
||||
for c in GenericClass(m, generics_manager).execute_annotation():
|
||||
return c
|
||||
return None
|
||||
|
||||
|
||||
def _new_dict_filter(cls):
|
||||
filters = cls.get_filters(is_instance=True, include_metaclasses=False)
|
||||
dct = {
|
||||
name.string_name: DjangoModelName(cls, name)
|
||||
for filter_ in reversed(list(filters))
|
||||
for name in filter_.values()
|
||||
}
|
||||
manager = _create_manager_for(cls)
|
||||
if manager:
|
||||
dct['objects'] = manager.name
|
||||
return DictFilter(dct)
|
||||
|
||||
|
||||
def get_metaclass_filters(func):
|
||||
def wrapper(cls, metaclasses):
|
||||
for metaclass in metaclasses:
|
||||
if metaclass.py__name__() == 'ModelBase' \
|
||||
and metaclass.get_root_context().py__name__() == 'django.db.models.base':
|
||||
return [_new_dict_filter(cls)]
|
||||
|
||||
return func(cls, metaclasses)
|
||||
return wrapper
|
||||
@@ -5,7 +5,8 @@ This is not a plugin, this is just the place were plugins are registered.
|
||||
from jedi.plugins import stdlib
|
||||
from jedi.plugins import flask
|
||||
from jedi.plugins import pytest
|
||||
from jedi.plugins import django
|
||||
from jedi.plugins import plugin_manager
|
||||
|
||||
|
||||
plugin_manager.register(stdlib, flask, pytest)
|
||||
plugin_manager.register(stdlib, flask, pytest, django)
|
||||
|
||||
1
jedi/third_party/django-stubs
vendored
Submodule
1
jedi/third_party/django-stubs
vendored
Submodule
Submodule jedi/third_party/django-stubs added at 92c8dfc93f
3
setup.py
3
setup.py
@@ -19,6 +19,8 @@ with open('requirements.txt') as f:
|
||||
|
||||
assert os.path.isfile("jedi/third_party/typeshed/LICENSE"), \
|
||||
"Please download the typeshed submodule first (Hint: git submodule update --init)"
|
||||
assert os.path.isfile("jedi/third_party/django-stubs/LICENSE.txt"), \
|
||||
"Please download the django-stubs submodule first (Hint: git submodule update --init)"
|
||||
|
||||
setup(name='jedi',
|
||||
version=version,
|
||||
@@ -43,6 +45,7 @@ setup(name='jedi',
|
||||
'docopt',
|
||||
# coloroma for colored debug output
|
||||
'colorama',
|
||||
'Django<3.1', # For now pin this.
|
||||
],
|
||||
'qa': [
|
||||
'flake8==3.7.9',
|
||||
|
||||
2
sith.py
2
sith.py
@@ -123,7 +123,7 @@ class TestCase(object):
|
||||
with open(self.path) as f:
|
||||
self.script = jedi.Script(f.read(), path=self.path)
|
||||
kwargs = {}
|
||||
if self.operation == 'goto_assignments':
|
||||
if self.operation == 'goto':
|
||||
kwargs['follow_imports'] = random.choice([False, True])
|
||||
|
||||
self.objects = getattr(self.script, self.operation)(self.line, self.column, **kwargs)
|
||||
|
||||
168
test/completion/django.py
Normal file
168
test/completion/django.py
Normal file
@@ -0,0 +1,168 @@
|
||||
import datetime
|
||||
import decimal
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
class Tag(models.Model):
|
||||
tag_name = models.CharField()
|
||||
|
||||
|
||||
class Category(models.Model):
|
||||
category_name = models.CharField()
|
||||
|
||||
|
||||
class BusinessModel(models.Model):
|
||||
category_fk = models.ForeignKey(Category)
|
||||
category_fk2 = models.ForeignKey('Category')
|
||||
category_fk3 = models.ForeignKey(1)
|
||||
category_fk4 = models.ForeignKey('models')
|
||||
category_fk5 = models.ForeignKey()
|
||||
|
||||
integer_field = models.IntegerField()
|
||||
big_integer_field = models.BigIntegerField()
|
||||
positive_integer_field = models.PositiveIntegerField()
|
||||
small_integer_field = models.SmallIntegerField()
|
||||
char_field = models.CharField()
|
||||
text_field = models.TextField()
|
||||
email_field = models.EmailField()
|
||||
float_field = models.FloatField()
|
||||
binary_field = models.BinaryField()
|
||||
boolean_field = models.BooleanField()
|
||||
decimal_field = models.DecimalField()
|
||||
time_field = models.TimeField()
|
||||
duration_field = models.DurationField()
|
||||
date_field = models.DateField()
|
||||
date_time_field = models.DateTimeField()
|
||||
tags_m2m = models.ManyToManyField(Tag)
|
||||
|
||||
unidentifiable = NOT_FOUND
|
||||
|
||||
# -----------------
|
||||
# Model attribute inference
|
||||
# -----------------
|
||||
|
||||
model_instance = BusinessModel()
|
||||
|
||||
#? int()
|
||||
model_instance.integer_field
|
||||
#? int()
|
||||
model_instance.big_integer_field
|
||||
#? int()
|
||||
model_instance.positive_integer_field
|
||||
#? int()
|
||||
model_instance.small_integer_field
|
||||
#? str()
|
||||
model_instance.char_field
|
||||
#? str()
|
||||
model_instance.text_field
|
||||
#? str()
|
||||
model_instance.email_field
|
||||
#? float()
|
||||
model_instance.float_field
|
||||
#? bytes()
|
||||
model_instance.binary_field
|
||||
#? bool()
|
||||
model_instance.boolean_field
|
||||
#? decimal.Decimal()
|
||||
model_instance.decimal_field
|
||||
#? datetime.time()
|
||||
model_instance.time_field
|
||||
#? datetime.timedelta()
|
||||
model_instance.duration_field
|
||||
#? datetime.date()
|
||||
model_instance.date_field
|
||||
#? datetime.datetime()
|
||||
model_instance.date_time_field
|
||||
|
||||
#! ['category_fk = models.ForeignKey(Category)']
|
||||
model_instance.category_fk
|
||||
#! ['category_name = models.CharField()']
|
||||
model_instance.category_fk.category_name
|
||||
#? Category()
|
||||
model_instance.category_fk
|
||||
#? str()
|
||||
model_instance.category_fk.category_name
|
||||
#? Category()
|
||||
model_instance.category_fk2
|
||||
#? str()
|
||||
model_instance.category_fk2.category_name
|
||||
#?
|
||||
model_instance.category_fk3
|
||||
#?
|
||||
model_instance.category_fk4
|
||||
#?
|
||||
model_instance.category_fk5
|
||||
|
||||
#? models.manager.RelatedManager()
|
||||
model_instance.tags_m2m
|
||||
#? Tag()
|
||||
model_instance.tags_m2m.get()
|
||||
#? ['add']
|
||||
model_instance.tags_m2m.add
|
||||
|
||||
#?
|
||||
model_instance.unidentifiable
|
||||
#! ['unidentifiable = NOT_FOUND']
|
||||
model_instance.unidentifiable
|
||||
|
||||
# -----------------
|
||||
# Queries
|
||||
# -----------------
|
||||
|
||||
#? models.query.QuerySet.filter
|
||||
model_instance.objects.filter
|
||||
#? BusinessModel() None
|
||||
model_instance.objects.filter().first()
|
||||
#? str()
|
||||
model_instance.objects.get().char_field
|
||||
#? int()
|
||||
model_instance.objects.update(x='')
|
||||
#? BusinessModel()
|
||||
model_instance.objects.create()
|
||||
|
||||
# -----------------
|
||||
# Inheritance
|
||||
# -----------------
|
||||
|
||||
class Inherited(BusinessModel):
|
||||
text_field = models.IntegerField()
|
||||
new_field = models.FloatField()
|
||||
|
||||
inherited = Inherited()
|
||||
#? int()
|
||||
inherited.text_field
|
||||
#? str()
|
||||
inherited.char_field
|
||||
#? float()
|
||||
inherited.new_field
|
||||
|
||||
#? str()
|
||||
inherited.category_fk2.category_name
|
||||
#? str()
|
||||
inherited.objects.get().char_field
|
||||
#? int()
|
||||
inherited.objects.get().text_field
|
||||
#? float()
|
||||
inherited.objects.get().new_field
|
||||
|
||||
# -----------------
|
||||
# Django Auth
|
||||
# -----------------
|
||||
|
||||
#? str()
|
||||
User().email
|
||||
#? str()
|
||||
User.objects.get().email
|
||||
|
||||
# -----------------
|
||||
# values & values_list (dave is too lazy to implement it)
|
||||
# -----------------
|
||||
|
||||
#?
|
||||
model_instance.objects.values_list('char_field')[0]
|
||||
#? dict()
|
||||
model_instance.objects.values('char_field')[0]
|
||||
#?
|
||||
model_instance.objects.values('char_field')[0]['char_field']
|
||||
@@ -207,40 +207,36 @@ for a in list_func_t_to_list_t(12):
|
||||
a
|
||||
|
||||
|
||||
# The following are all actually wrong, however we're mainly testing here that
|
||||
# we don't error when processing invalid values, rather than that we get the
|
||||
# right output.
|
||||
|
||||
x0 = list_func_t_to_list_t(["abc"])[0]
|
||||
#? str()
|
||||
#?
|
||||
x0
|
||||
|
||||
x2 = list_func_t_to_list_t([tpl])[0]
|
||||
#? tuple()
|
||||
#?
|
||||
x2
|
||||
|
||||
x3 = list_func_t_to_list_t([tpl_typed])[0]
|
||||
#? tuple()
|
||||
#?
|
||||
x3
|
||||
|
||||
x4 = list_func_t_to_list_t([collection])[0]
|
||||
#? dict()
|
||||
#?
|
||||
x4
|
||||
|
||||
x5 = list_func_t_to_list_t([collection_typed])[0]
|
||||
#? dict()
|
||||
#?
|
||||
x5
|
||||
|
||||
x6 = list_func_t_to_list_t([custom_generic])[0]
|
||||
#? CustomGeneric()
|
||||
#?
|
||||
x6
|
||||
|
||||
x7 = list_func_t_to_list_t([plain_instance])[0]
|
||||
#? PlainClass()
|
||||
#?
|
||||
x7
|
||||
|
||||
for a in list_func_t_to_list_t([12]):
|
||||
#? int()
|
||||
#?
|
||||
a
|
||||
|
||||
|
||||
|
||||
@@ -60,6 +60,27 @@ for b in list_type_t_to_list_t(list_of_int_type):
|
||||
b
|
||||
|
||||
|
||||
# Test construction of nested generic tuple return parameters
|
||||
def list_t_to_list_tuple_t(the_list: List[T]) -> List[Tuple[T]]:
|
||||
return [(x,) for x in the_list]
|
||||
|
||||
|
||||
x1t = list_t_to_list_tuple_t(list_of_ints)[0][0]
|
||||
#? int()
|
||||
x1t
|
||||
|
||||
|
||||
for c1 in list_t_to_list_tuple_t(list_of_ints):
|
||||
#? int()
|
||||
c1[0]
|
||||
|
||||
|
||||
for c2, in list_t_to_list_tuple_t(list_of_ints):
|
||||
#? int()
|
||||
c2
|
||||
|
||||
|
||||
# Test handling of nested tuple input parameters
|
||||
def list_tuple_t_to_tuple_list_t(the_list: List[Tuple[T]]) -> Tuple[List[T], ...]:
|
||||
return tuple(list(x) for x in the_list)
|
||||
|
||||
@@ -82,11 +103,12 @@ for b in list_tuple_t_elipsis_to_tuple_list_t(list_of_int_tuple_elipsis):
|
||||
b[0]
|
||||
|
||||
|
||||
def foo(x: T) -> T:
|
||||
# Test handling of nested callables
|
||||
def foo(x: int) -> int:
|
||||
return x
|
||||
|
||||
|
||||
list_of_funcs = [foo] # type: List[Callable[[T], T]]
|
||||
list_of_funcs = [foo] # type: List[Callable[[int], int]]
|
||||
|
||||
def list_func_t_to_list_func_type_t(the_list: List[Callable[[T], T]]) -> List[Callable[[Type[T]], T]]:
|
||||
def adapt(func: Callable[[T], T]) -> Callable[[Type[T]], T]:
|
||||
@@ -101,6 +123,21 @@ for b in list_func_t_to_list_func_type_t(list_of_funcs):
|
||||
b(int)
|
||||
|
||||
|
||||
def bar(*a, **k) -> int:
|
||||
return len(a) + len(k)
|
||||
|
||||
|
||||
list_of_funcs_2 = [bar] # type: List[Callable[..., int]]
|
||||
|
||||
def list_func_t_passthrough(the_list: List[Callable[..., T]]) -> List[Callable[..., T]]:
|
||||
return the_list
|
||||
|
||||
|
||||
for b in list_func_t_passthrough(list_of_funcs_2):
|
||||
#? int()
|
||||
b(None, x="x")
|
||||
|
||||
|
||||
mapping_int_str = {42: 'a'} # type: Dict[int, str]
|
||||
|
||||
# Test that mappings (that have more than one parameter) are handled
|
||||
|
||||
@@ -283,6 +283,18 @@ def testnewtype2(y):
|
||||
y
|
||||
#? []
|
||||
y.
|
||||
|
||||
# The type of a NewType is equivalent to the type of its underlying type.
|
||||
MyInt = typing.NewType('MyInt', int)
|
||||
x = type(MyInt)
|
||||
#? type.mro
|
||||
x.mro
|
||||
|
||||
PlainInt = int
|
||||
y = type(PlainInt)
|
||||
#? type.mro
|
||||
y.mro
|
||||
|
||||
# python > 2.7
|
||||
|
||||
class TestDefaultDict(typing.DefaultDict[str, int]):
|
||||
|
||||
@@ -103,3 +103,15 @@ while True:
|
||||
bar = bar # type: bar
|
||||
#? int()
|
||||
bar
|
||||
|
||||
|
||||
class Comprehension:
|
||||
def __init__(self, foo):
|
||||
self.foo = foo
|
||||
|
||||
def update(self):
|
||||
self.foo = (self.foo,)
|
||||
|
||||
|
||||
#? int() tuple()
|
||||
Comprehension(1).foo[0]
|
||||
|
||||
11
test/completion/thirdparty/django_.py
vendored
11
test/completion/thirdparty/django_.py
vendored
@@ -1,11 +0,0 @@
|
||||
#! ['class ObjectDoesNotExist']
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
import django
|
||||
|
||||
#? ['get_version']
|
||||
django.get_version
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
#? ['configured']
|
||||
settings.configured
|
||||
@@ -115,3 +115,18 @@ def test_docstring_decorator(goto_or_help_or_infer, skip_python2):
|
||||
|
||||
doc = d.docstring()
|
||||
assert doc == 'FunctionType(*args: Any, **kwargs: Any) -> Any\n\nhello'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('code', ['', '\n', ' '])
|
||||
def test_empty(Script, code):
|
||||
assert not Script(code).help(1, 0)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('code', ['f()', '(bar or baz)', 'f[3]'])
|
||||
def test_no_help_for_operator(Script, code):
|
||||
assert not Script(code).help()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('code', ['()', '(1,)', '[]', '[1]', 'f[]'])
|
||||
def test_help_for_operator(Script, code):
|
||||
assert Script(code).help()
|
||||
|
||||
@@ -20,6 +20,12 @@ def test_django_default_project(Script):
|
||||
assert script._inference_state.project._django is True
|
||||
|
||||
|
||||
def test_django_default_project_of_file(Script):
|
||||
project = get_default_project(__file__)
|
||||
d = os.path.dirname
|
||||
assert project._path == d(d(d(__file__)))
|
||||
|
||||
|
||||
def test_interpreter_project_path():
|
||||
# Run from anywhere it should be the cwd.
|
||||
dir = os.path.join(root_dir, 'test')
|
||||
|
||||
@@ -36,7 +36,7 @@ unspecified = %s
|
||||
""" % (case, sorted(d - a), sorted(a - d))
|
||||
|
||||
|
||||
def test_completion(case, monkeypatch, environment, has_typing):
|
||||
def test_completion(case, monkeypatch, environment, has_typing, has_django):
|
||||
skip_reason = case.get_skip_reason(environment)
|
||||
if skip_reason is not None:
|
||||
pytest.skip(skip_reason)
|
||||
@@ -47,6 +47,8 @@ def test_completion(case, monkeypatch, environment, has_typing):
|
||||
_CONTAINS_TYPING = ('pep0484_typing', 'pep0484_comments', 'pep0526_variables')
|
||||
if not has_typing and any(x in case.path for x in _CONTAINS_TYPING):
|
||||
pytest.skip('Needs the typing module installed to run this test.')
|
||||
if (not has_django or environment.version_info.major == 2) and case.path.endswith('django.py'):
|
||||
pytest.skip('Needs django to be installed to run this test.')
|
||||
repo_root = helpers.root_dir
|
||||
monkeypatch.chdir(os.path.join(repo_root, 'jedi'))
|
||||
case.run(assert_case_equal, environment)
|
||||
|
||||
Reference in New Issue
Block a user