Search code examples
pythonpydanticpython-dataclasseslinter

Automatic indent pythoh dataclasses with commentы as Go structs style


Python dataclasses are really great. They allow to define classes in very beautiful way.

from dataclasses import dataclass

@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

Moreover lots of useful tools re-use python annotations the same way and allow to define classes (that are more like structures in other languages) the same way. One of the example is Pydantic.

from pydantic import BaseModel


class User(BaseModel):
    id: int
    name = 'John Doe'
    signup_ts: Optional[datetime] = None
    friends: List[int] = []

I myself use pydantic quite a lot these days. Look at an example from my recent practice:

class G6A(BaseModel):
    transaction_id: items.TransactionReference # Transaction Id
    mpan_core: items.MPAN # MPAN Core
    registration_date: items.CallistoDate # Registration Date
    action_required: G6AAction # Action Required

I parse some very inconvenient api and that's why I want to leave a comment on every line. With that it will be working as self-documentation. The problem is that, at least for me, this looks very ugly. It's hard to look throw lines, 'cause the look like table with broken columns. Let's try to fix this by doing accurate indentations:

class G6A(BaseModel):
    transaction_id:     items.TransactionReference  # Transaction Id
    mpan_core:          items.MPAN                  # MPAN Core
    registration_date:  items.CallistoDate          # Registration Date
    action_required:    G6AAction                   # Action Required

I'm sure that this way it is much more readable. By doing so we define structure, like actual table, where 1 column is attribute name, 2 column is attribute type and the last one is comment. It is actually inspired by Go structs

type T struct {
    name    string // name of the object
    value   int    // its value
}

So, my questions is - are there any automatic tools (linters), that will reformat dataclass/pydantic-models the way I described above? I looked throw autopep8, black linter and find nothing. Also googled and so on and still nothing. Any ideas how to achieve that by existing tools ?


Solution

  • I think yapf has something like that for comments. Check the SPACES_BEFORE_COMMENT "knob":

    The number of spaces required before a trailing comment. This can be a single value (representing the number of spaces before each trailing comment) or list of of values (representing alignment column values; trailing comments within a block will be aligned to the first column value that is greater than the maximum line length within the block)

    .style.yapf:

    [style]
    based_on_style = pep8
    spaces_before_comment = 10,20,30,40,50,60,70,80
    

    Configures alignment columns: 10, 20, ..., 80.

    foo.py:

    class G6A(BaseModel):
        transaction_id: items.TransactionReference # Transaction Id
        mpan_core: items.MPAN # MPAN Core
        registration_date: items.CallistoDate # Registration Date
        action_required: G6AAction # Action Required
    

    Output of yapf foo.py:

    class G6A(BaseModel):
        transaction_id: items.TransactionReference   # Transaction Id
        mpan_core: items.MPAN                        # MPAN Core
        registration_date: items.CallistoDate        # Registration Date
        action_required: G6AAction                   # Action Required