How to improve PyCharm code completion (auto-complete)?

  • 31
  • 82

When you are working with a huge project, especially when it wasn't developed by you, code completion is an essential productivity tool, one that can save you many hours of searching inside source files or documentation.

As we probably all know, code completion cannot perfectly work with dynamic code but this doesn't mean that it has to work only in few cases, and more important the user should be able to write the code and the comments in such way that it will help the IDE to detect the expected type of the variable.

Now, the question is how we can help PyCharm to help us with code completion?

One first issue would be code completion for XMLRPC calls : PY-5313 - or to rephrase it: how can I write the code in such way that I will have code completion available.

The best thing to do is to pick a docstring style (epydoc or sphinx) and to specify types of parameters and function return values according to that style.


I totally agree with you, the only problem being convincing PyCharm to use the type from the comments.

Take a look at this example https://gist.github.com/1486122


How about if PyCharm allowed us to force a type, with a comment.  Similar thi

ngs are done in Java to disable certain inspections:

        from openpyxl.reader.excel import load_workbook

        book  = load_workbook('corpus.xlsx')

        sheet = book.get_sheet_by_name('Sheet1')

        # @PyCharm:(Sheet)book

        sheet.whatever()

The decorator could be 100% PyCharm, without needing to mess with the language.

I love autocompletion... when it works.  If we could force declare a type in PyCharm it would unlock a lot of productivity.

See also: http://www.python.org/dev/peps/pep-3107/


As described in this very thread, you can use docstrings to specify types for parameters and instance variables. I see no point in making it possible to specify types for local variables, because you'll spend far more time describing the type than you'll save thanks to the autocompletion which will use the type declaration.


Hi,

I cannot figure out how to specify type of a local variable?

I tried like this #:type s: data.MyClass, but this does not work, ie I still do not have the autocompletion.

thanks

--

Oleksandr Huziy


The very message that you're replying to says that it is not possible to specifty types for local variables, and explains why.


Hi,

yes, sorry about that, I've only read the first line of your post...

--

Oleksandr Huziy


Hi Oleksandr,

actually it's possible to specify type for local variables.

You can see syntax here -- http://youtrack.jetbrains.net/issue/PY-4083

Note: when you declare type using docstrings -- you should write it after variable declaration.

If you use #: syntax -- write it before variable declaration.


Actually this work only for local variables. That is ok, but the same problem with autocompletion is for dynamic code. By example framework SQLAlchemy - declare relation with backreference:

class Group(DeclarativeBase):

    """

    Group definition for :mod:`repoze.what`.


    Only the ``group_name`` column is required by :mod:`repoze.what`.


    """


    __tablename__ = 'tg_group'


    #{ Columns

    group_id = Column(Integer, autoincrement=True, primary_key=True)

    group_name = Column(Unicode(50), unique=True, nullable=False)

    display_name = Column(Unicode(255))

    created = Column(DateTime, default=datetime.now)

    #}


    #{ Relations

    users = relation('User', secondary=user_group_table, backref='groups')


class User(DeclarativeBase):

    """

    User definition.


    This is the user definition used by :mod:`repoze.who`, which requires at

    least the ``user_name`` column.


    """

    __tablename__ = 'tg_user'


    #{ Columns

.........

    @property

    def permissions(self):

        """Return a set of strings for the permissions granted."""

        perms = set()

        for g in self.groups:   <- PROBLEM BEGIN - autocompletion failed because class atribute groups will be created on runtime by SQLAlchemy

            perms = perms | set(g.permissions)

        return perms

I will be very happy :-) if docstring hint will work also for this cases.


"I see no point in making it possible to specify types for local variables, because you'll spend far more time describing the type than you'll save thanks to the autocompletion which will use the type declaration"

IMO, you are off on a wrong track here. For the following reasons:

1. I fail to see the huge difference between function parameters and function local variables. It is very useful to be able to specify the type for a function parameter. What makes you think it is not useful to do the same thing for local variables?

2. We (the PyCharm users) want this. Please add it to your product. It will make PyCharm even better.

Is there an issue in the bug tracker I can vote on?


Type annotations on function parameters can be used to validate that parameters in function calls have the right type. They are also part of the documentation describing how the function is used. Type annotations on local variables have none of these benefits.

As described elsewhere in this thread, PyCharm does in fact support type annotations on local variables.


OK, from your initial reply I was under the impression that PyCharm did not support this.

This really needs to go in the documentation.

And validation/documentation is the least useful feature of type annotations, IMO. The code completion feature is more important/useful.


Found a few bugs with the parsing...

    -- overloaded calls, all return the same type

    """

    getCurrency(self, char currency) -> my_Currency

    getCurrency(self, int index) -> my_Currency

    """


    -- does not parse because it is on one line...

    """getAsOfDate(self) -> my_Date"""

       

Is there anything I can do to force this to parse?


I too would find it useful to be able to specify code completion hints on-the-fly inside code. This is especially true for attributes where PyCharm's inspection doesn't know ahead of time what type the attributes are. Your SQLAlchemy example is a perfect one:

for x in object_set:

    # x is an instance of class 'Foo'

    x.this_should_auto_complete

FWIW, I'm a former Wing user and I found the following system they have to be valuable:

for x in object_set:

    assert isinstance(x, Foo)

    assert isinstance(x.some_attribute, Bar)

    x.this_now_completes = 1

    x.some_attribute.this_now_completes_too = 2

I like this because it both tells the editor what to do, and also keeps bugs from creeping into your code. Although admittedly, it isn't ideal for duck typing.


I'd love the combination type checking/type hintnig proposed by @bouncing, but I'd want it to be a true compile time/debugging option, only optionally an assert:

for x in object_set:
    # @PyCharm defaultType(x, Foo)
    # @PyCharm assertType(t, Foo)     x.this_now_completes = 1

There are plenty of places where I don't care if it's a duck: it better not be a duck: it better be the type I specified.


A quick Wing tip: To get the benifit of type hinting and still be able to use duck typing, you can skip the assert.

You simply write:

for x in object_set:
    isinstance(x, Foo)
    ...

PyCharm also recognizes the "assert isinstance" pattern for specifying types of variables.


Does PyCharm also support the style without the assert, i.e. simply

isinstance(f, Foo)

?


Looks like pycharm does not support that:

        if form.is_valid():

            question                = form.save(commit=False)

            assert isinstance(question, models.CategoryQuestion)  # works

            isinstance(question, models.CategoryQuestion)            # no effect

            question.categ<tab>


Is there any way to apply similar type hints within a django template?


There is currently no way to apply type hints in a Django template. You can file a feature request, but frankly speaking I don't see any good way to support them. Putting a {% comment %} / {% endcomment %} block for the sake of a single type hint looks extremely cumbersome to me, and there doesn't seem to be any other way to put meta-information in a Django template that would not affect the output.


I feel the type hints should go in the matching view.

I know that's not without pitfalls: but it also matches the situation where a different class of programmer or web designer works with the templates.

The template author then knows what they've got to work with, if a type hint is provided on all the parameters passed.


Actually if you specify type hints for things you put into the template context (using the standard syntax you use for local variable type hints), this should work in the current version.


You wrote 'Actually if you specify type hints for things you put into the template context (using the standard syntax you use for local variable type hints), this should work in the current version.'

I upgraded to pycharm 2.5.2 specifically in hopes this would work, but I get nothing at all in the templates:

{% for entry in entries %}
entry.entry.subtitle
{% endfor %}

Could you please show a sample of code from the view function that calls the template?

def xxx_test(request, pk):
category = models.Category.objects.select_related().get(pk=pk)
return render_to_response('xxx_test.html',
dict(category=category),
context_instance=RequestContext(request))

xxx_test1.html
{% block content %}
Curator={ { category.curator.email } }
{% for foo in category.entry.all %}
Title={ { foo.title } } ID={ { foo.id } }
{% endfor %}
{% include 'xxx_test2.html' with test=category %}
{% endblock %}

xxx_test2.html
Curator={ { test.curator } }




All the stuff in strikethrough does not complete. Beyond that, sometimes pycharm lets me complete things with an underscore, which the template system blocks with 'Variables and attributes may not begin with underscores'.

It looks like %include does not pass type hints ever.
And the for loop does not recognize the type of the temporary "foo" variable.

At some point pycharm can't do this all via magic: what about duck typing on a template included from two places?

I've filed a couple of issues for things that we should do but currently don't:
http://youtrack.jetbrains.com/issue/PY-7446
http://youtrack.jetbrains.com/issue/PY-7447

The remaining stuff (including tracking the types of variables used for iterating in the {% for %} tag) is within PyCharm's currently implemented capabilities to analyze and is supposed to work; we'll investigate why this doesn't work in your example.

Sometimes it is just easier to give a type hint, rather than follow through pycharm's twisted paths and get the automatic type hints working. Unfortunately I can't do that in a template.

Also I might call my xxx_test2.html snippet from various includes in various places: I don't see how pycharm could ever properly work out the type without a hint.

Please understand that I don't have any fundamental objection to the idea of type hints (and actually a custom tag distributed as a separate open-source package is probably the best way that this could be supported). The fact is that most users won't have type hints in their templates, so we really want PyCharm's code insight to work as good as possible without any type hints.