typing icon indicating copy to clipboard operation
typing copied to clipboard

Inferring keyword arguments from attributes

Open GideonBear opened this issue 2 years ago • 5 comments

I'm making an alternative python AST. Just like the ast module, I want attributes to be set on a node using keyword arguments. The only problem with using

class Node:
    attr: int

    def __init__(self, **kwargs: object):
        for name, value in kwargs.items():  # alternative: self.__dict__.update(kwargs)
            setattr(self, name, value)

is that mypy doesn't catch it when I supply invalid types for the attributes, e.g. Node(attr='invalid'), or when assigning to a non-existing attribute, e.g. Node(nonexistant=1). The ast module has this problem too:

from ast import For

For(name=1)     # no error
For(body=1)     # no error
For().name = 1  # [attr-defined]
For().body = 1  # [assignment]

One approach to solving this would be to add a class decorator to typing (or mypy_extensions, I don't know what the best place is) that implements that __init__ method, and lets type checkers know that it accepts the same keyword arguments as its attributes. This would also help with type-checking code that's using the ast.AST constructor.

GideonBear avatar Jan 05 '23 08:01 GideonBear

The dataclass_transform mechanism described in PEP 681 is probably what you're looking for here.

Or perhaps dataclass with kw_only would meet your needs.

erictraut avatar Jan 05 '23 16:01 erictraut

PEP 692 may also be useful to you

hauntsaninja avatar Jan 05 '23 17:01 hauntsaninja

Hello @erictraut, dataclass_transform seems to be what I'm looking for, but it doesn't seem to work?

from typing_extensions import dataclass_transform

@dataclass_transform(eq_default=False, kw_only_default=True)
class AttrKwargs:
    def __init__(self, **kwargs: object):
        for name, value in kwargs.items():
            setattr(self, name, value)

class N1(AttrKwargs):
    a: str

class N2(N1):
    b: int


N1(a=1)     # no error
N1(b=1)     # no error

N2(a=1)     # no error
N2(b=1)     # no error

Using a decorator instead of a superclass also doesn't work... Can you give an example of how to use it in my situation? I would ideally not change N2 at all.

GideonBear avatar Jan 05 '23 18:01 GideonBear

Mypy doesn't support dataclass_transform yet.

JelleZijlstra avatar Jan 05 '23 19:01 JelleZijlstra

Mypy doesn't support dataclass_transform yet.

Ah, that explains it. Maybe there should be a warning when using it, instead of just ignoring it? dataclass requires adding the decorator to each class, and there doesn't seem to be an option to leave attributes empty if they are not given. I'm assuming that dataclass_transform essentially does the same as dataclass here, just not functional, so that also won't really work if that's the case... Is there some other way to do this?

GideonBear avatar Jan 06 '23 06:01 GideonBear

Now that mypy supports dataclass_transform, I think this can be closed.

erictraut avatar May 17 '24 04:05 erictraut