microsoft / pyright

Static Type Checker for Python
Other
13.12k stars 1.4k forks source link

dataclass_json Decorator Not Being Correctly Parsed #8718

Closed mahyarmirrashed closed 1 month ago

mahyarmirrashed commented 1 month ago

Describe the bug The decorator shows that it expects the letter_case kwarg. Why isn't Pyright picking this up?

Code or Screenshots

from dataclasses import dataclass
from typing import Optional

from dataclasses_json import LetterCase, dataclass_json

@dataclass_json(letter_case=LetterCase.PASCAL)
@dataclass
class ReprogrammerModel:
    Status: int
    Message: Optional[str]
    ControllerIndex: int
    Success: bool
def dataclass_json(_cls=None, *, letter_case=None,
                   undefined: Optional[Union[str, Undefined]] = None):
    """
    Based on the code in the `dataclasses` module to handle optional-parens
    decorators. See example below:

    @dataclass_json
    @dataclass_json(letter_case=LetterCase.CAMEL)
    class Example:
        ...
    """

    def wrap(cls):
        return _process_class(cls, letter_case, undefined)

    if _cls is None:
        return wrap
    return wrap(_cls)

If your code relies on symbols that are imported from a third-party library, include the associated import statements and specify which versions of those libraries you have installed.

VS Code extension or command-line I am running it in Neovim.

basedpyright 1.14.0
based on pyright 1.1.372
erictraut commented 1 month ago

I think pyright's behavior here is correct, so I don't consider this a bug.

This appears to be a bug in the dataclass_json class. It's defining an enum that looks like this:

class LetterCase(Enum):
    CAMEL = camelcase
    KEBAB = spinalcase
    SNAKE = snakecase
    PASCAL = pascalcase

The symbols camelcase, spinalcase, etc. are functions. The EnumMeta metaclass treats callables as non-members, so these are regular attributes in the class. Their types are callable. You can see this if you run this small script:

from dataclasses_json import LetterCase
print(type(LetterCase.PASCAL))

This prints <class 'function'>. If this were a real enum member, it would print LetterCase.

If the maintainers of dataclass_json intended for these to be enum members (as I suspect they did), they can use the Python 3.12 enum.member call to designate them as such.

class LetterCase(Enum):
    CAMEL = member(camelcase)
    KEBAB = member(spinalcase)
    SNAKE = member(snakecase)
    PASCAL = member(pascalcase)

When I make that modification, pyright no longer reports an error.

mahyarmirrashed commented 1 month ago

@erictraut , I don't agree with your assessment. The error is saying that it is expecting zero positional arguments, not that something is wrong with the enum definition.

pyright: Expected 0 positional arguments [reportCallIssue]

erictraut commented 1 month ago

The error message is correct given the type information provided to pyright by the library. However, the type information provided by the library is incorrect because of the bad enum definition. There's nothing I can do in pyright to fix this. The root cause of the problem is in the library, so if you want it fixed, you'll need to work with the maintainer of the library.

mahyarmirrashed commented 1 month ago

Ignoring the enum definition part of it, the issue seems to be that Pyright isn't able to pick up on the positional argument.

def dataclass_json(_cls=None, *, letter_case=None,
                   undefined: Optional[Union[str, Undefined]] = None)

Correct me if I am wrong, but this signature indicates that letter_case is a positional argument. Therefore, it stands to reason that the error that Expected 0 positional arguments [reportCallIssue] is incorrect.

erictraut commented 1 month ago

Which version of dataclasses_json are you using? I suspect we're using different versions. I'm using the latest version (0.6.7).

I also just noticed that you are using basedpyright, which contains modifications to pyright and is often several versions behind. In the future, please verify any potential bugs using the latest version of pyright before reporting an issue.

mahyarmirrashed commented 1 month ago

Hi @erictraut , I am using dataclasses_json v0.5.9. I should have mentioned that in my original issue description, sorry. Could you see again if that still comes up in Pyright?

erictraut commented 1 month ago

That version of dataclasses_json has no type annotations, so pyright does its best to infer types from the library implementation. The decorator function dataclass_json returns two values of different types, so the inferred return type is a union of these two types. Both of these types are callable, but one of them does not accept any positional arguments, which is why you're seeing the error message that you are.

I recommend updating to the latest version of the library which has type annotations.

mahyarmirrashed commented 1 month ago

Thanks, that makes more sense.