Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling invalid kwarg names in __init__ #1411

Open
redruin1 opened this issue Feb 24, 2025 · 0 comments
Open

Handling invalid kwarg names in __init__ #1411

redruin1 opened this issue Feb 24, 2025 · 0 comments

Comments

@redruin1
Copy link

Hi. I'm seriously considering using attrs as a replacement for validation in my own library. However, currently (and historically) I've used the kwarg expansion format to init my classes:

@define
class Example:
    blah: str

some_dict = {"blah": "blah"}
e = Example(**some_dict)

For almost all cases, attrs works perfectly out of the box. However, since my input data is typically in JSON format, their keys can sometimes contain hyphens - which cannot be resolved to python variable/attribute names. Trying to use alias like so:

@define
class Example:
    annoying_attribute: str = field(alias="annoying-attribute")
    # Meaning, populate `annoying_attribute` from `annoying-attribute`

results in attrs attempting to populate the signature of the init method with the hyphenated name, which obviously results in a syntax error.

I'm already aware that the best option (and officially sanctioned by the maintainers) is to instead use a factory method with cattrs, a la:

my_converter = cattrs.Converter()
# register proper conversion hooks on my_converter...

@define
class Example:
    annoying_attribute: str
    
    @classmethod
    def from_dict(cls, dict):
        return my_converter.structure(cls, dict)

e = Example.from_dict(some_dict)

But I am still somewhat interested in preserving the legacy behavior using init, especially since this syntax is convenient and most keywords that I'm handling are not unrepresentable in this manner. Is there any way to modify the generated init nicely without reinventing the wheel? I could simply handle the substitution myself with a custom init:

@define
class Example
    annoying_attribute: str

    def __init__(self, **kwargs):
        if "annoying-attribute" in kwargs:
            kwargs["annoying_attribute"] = kwargs.pop("annoying-attribute")
        self.__attrs_init__(**kwargs)

but this would of course lose the annotations which would have to be redone manually (unless there's some way to copy them from __attrs_init__?). Further, I do actually think using a cattrs converter is the right direction (since my conversion from raw JSON dict to Python object is bidirectional), but I have no idea how you would call a structure equivalent from the __init__ method...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant