diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 842677c2..a1b2d301 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -13,8 +13,12 @@ Unreleased - ``Definition.module_path`` - ``Refactoring.get_renames`` - ``Refactoring.get_changed_files`` +- Functions with ``@property`` now return ``property`` instead of ``function`` + in ``Name().type`` - Started using annotations +This is likely going to be the last minor release before 1.0. + 0.17.2 (2020-07-17) +++++++++++++++++++ diff --git a/conftest.py b/conftest.py index d4aaae42..f174c1e8 100644 --- a/conftest.py +++ b/conftest.py @@ -130,7 +130,11 @@ def goto_or_help(request, Script): @pytest.fixture(scope='session', params=['goto', 'help', 'infer']) def goto_or_help_or_infer(request, Script): - return lambda code, *args, **kwargs: getattr(Script(code), request.param)(*args, **kwargs) + def do(code, *args, **kwargs): + return getattr(Script(code), request.param)(*args, **kwargs) + + do.type = request.param + return do @pytest.fixture(scope='session') diff --git a/jedi/api/classes.py b/jedi/api/classes.py index c55d7b68..679d1e59 100644 --- a/jedi/api/classes.py +++ b/jedi/api/classes.py @@ -174,7 +174,7 @@ class BaseName(object): 'function' Valid values for type are ``module``, ``class``, ``instance``, ``function``, - ``param``, ``path``, ``keyword`` and ``statement``. + ``param``, ``path``, ``keyword``, ``property`` and ``statement``. """ tree_name = self._name.tree_name diff --git a/jedi/inference/value/klass.py b/jedi/inference/value/klass.py index fb3ee0ef..79d070ba 100644 --- a/jedi/inference/value/klass.py +++ b/jedi/inference/value/klass.py @@ -37,7 +37,8 @@ py__doc__() Returns the docstring for a value. """ from jedi import debug -from jedi.parser_utils import get_cached_parent_scope, expr_is_dotted +from jedi.parser_utils import get_cached_parent_scope, expr_is_dotted, \ + function_is_property from jedi.inference.cache import inference_state_method_cache, CachedMetaClass, \ inference_state_method_generator_cache from jedi.inference import compiled @@ -72,6 +73,20 @@ class ClassName(TreeNameDefinition): else: yield result_value + @property + def api_type(self): + if self.tree_name is not None: + definition = self.tree_name.get_definition() + if definition.type == 'funcdef': + if function_is_property(definition): + # This essentially checks if there is an @property before + # the function. @property could be something different, but + # any programmer that redefines property as something that + # is not really a property anymore, should be shot. (i.e. + # this is a heuristic). + return 'property' + return super().api_type + class ClassFilter(ParserTreeFilter): def __init__(self, class_value, node_context=None, until_position=None, diff --git a/jedi/parser_utils.py b/jedi/parser_utils.py index dd86178b..81c8391e 100644 --- a/jedi/parser_utils.py +++ b/jedi/parser_utils.py @@ -316,3 +316,4 @@ def _function_is_x_method(method_name): function_is_staticmethod = _function_is_x_method('staticmethod') function_is_classmethod = _function_is_x_method('classmethod') +function_is_property = _function_is_x_method('property') diff --git a/jedi/plugins/stdlib.py b/jedi/plugins/stdlib.py index 37ed12b2..d9733dff 100644 --- a/jedi/plugins/stdlib.py +++ b/jedi/plugins/stdlib.py @@ -384,6 +384,8 @@ def builtins_classmethod(functions, value, arguments): class PropertyObject(AttributeOverwrite, ValueWrapper): + api_type = 'property' + def __init__(self, property_obj, function): super().__init__(property_obj) self._function = function diff --git a/test/test_api/test_classes.py b/test/test_api/test_classes.py index 27b227c1..6add83f6 100644 --- a/test/test_api/test_classes.py +++ b/test/test_api/test_classes.py @@ -373,6 +373,34 @@ def test_type_II(Script): assert c.type == 'keyword' +@pytest.mark.parametrize( + 'added_code, expected_type, expected_infer_type', [ + ('Foo().x', 'property', 'instance'), + ('Foo.x', 'property', 'property'), + ('Foo().y', 'function', 'function'), + ('Foo.y', 'function', 'function'), + ('Foo().z', 'function', 'function'), + ('Foo.z', 'function', 'function'), + ] +) +def test_class_types(goto_or_help_or_infer, added_code, expected_type, + expected_infer_type): + code = dedent('''\ + class Foo: + @property + def x(self): return 1 + @staticmethod + def y(self): ... + @classmethod + def z(self): ... + ''') + d, = goto_or_help_or_infer(code + added_code) + if goto_or_help_or_infer.type == 'infer': + assert d.type == expected_infer_type + else: + assert d.type == expected_type + + """ This tests the BaseName.goto function, not the jedi function. They are not really different in functionality, but really