Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pycharm plugin for attrs?

Tags:

python

pycharm

attrs is a useful package for reducing boilerplate. Example:

class SomeClass(object):
    a_number = attr.ib(default=42)
    list2_of_numbers = attr.ib(default=attr.Factory(list))

PyCharm does not offer code completion for the generated __init__ method, is there a plugin that can do this? Or some other work around?

like image 915
Morgoth Avatar asked Jun 21 '17 09:06

Morgoth


2 Answers

Update Aug 2018 - As per Hynek's Answer, attrs support is in PyCharm 2018.2. I'm a month late to the party as I haven't been working in Python much lately...

My original answer is as follows, although it only applies if you are on PyCharm 2018.1 or lower...


The only workaround I'm aware of (as of PyCharm 2017.2.4) is to define a redundant __init__ in the class declaration. PyCharm's code completion picks up this method ok, but at runtime the attrs-generated __init__ will override it. For example (with type hints):

import attr
from typing import List, Optional


@attr.s
class SomeClass:

    def __init__(self, a_number: int = 42,
                 list2_of_numbers : Optional[List[int]] = None) -> None:
        ...

    a_number: int = attr.ib(default=42)
    list2_of_numbers: List[int] = attr.ib(default=attr.Factory(list))

PyCharm code completion screen shot

I also tried using a stub file to remove the dummy __init__ out of the class declaration, with a view to deleting the .pyi if this is ever supported, but it didn't work.

Of course, this kind of defeats the purpose of using attrs in the first place (i.e. reducing class declaration boilerplate, aiding maintainability etc.)

If you add, remove or amend an attr.ib(), you'll need to remember to manually update the __init__ signature which is antithetical to attrs' philosophy.

FYI, Jonas Obrist tweeted one of the Jetbrains Devs about attrs support earlier in the year regarding the 2017.3 release, so fingers crossed...

EDIT

Actually, there is a slightly more palatable workaround. Just pass init=False to attr.s and define your __init__ method as you normally would. You're still not leveraging the full magic of attrs, but hey... it's better than nothing.

Take care to intialize your default values in your manual __init__ rather than the attr.ib declaration against the class. As per the docs, the attr.ib's default value will only be initialised inside an attrs-generated __init__.

Aside from that, you still get your other dunder methods, validation, and all the other goodness as far as I can tell.

import attr
from typing import List, Optional


@attr.s(init=False)
class SomeClass:

    a_number: int = attr.ib()
    list2_of_numbers: List[int] = attr.ib()

    def __init__(self, a_number: int = 42,
                 list2_of_numbers : Optional[List[int]] = None) -> None:
        self.a_number: int = a_number
        self.list2_of_numbers: List[int] = list2_of_numbers or []
        # Replicate behaviour of attrs-generated __init__
        attr.validate(self)
like image 109
chrisimcevoy Avatar answered Oct 11 '22 19:10

chrisimcevoy


Good news: PyCharm 2018.2 has added support for attrs.

like image 39
hynek Avatar answered Oct 11 '22 18:10

hynek