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

Fix parametrized generics for dataclasses [GRF-6059] #7

Merged

Conversation

bcalvert-graft
Copy link

Description

Currently, if you have a @dataclass in Sematic where one of the attributes is a parametrized Generic[T] class, Sematic will complain. For example, on graft/sematic/main

from dataclasses import dataclass
from typing import Generic, TypeVar
import sematic

JS = TypeVar("JS")

@dataclass
class A(Generic[JS]):
    val: JS

    def test(self, a: JS) -> JS:
        pass

@dataclass
class B(A[int]):
    def test(self, a: int) -> int:
        return a + self.val


@sematic.func
def gen(inp: B) -> int:
    return inp.test(1)

b = B(val=1)
sematic.SilentRunner().run(gen(b))

TypeError: Invalid input type for argument 'inp' when calling '__main__.gen'. Cannot cast B(val=1) to <class '__main__.B'>: Cannot cast field 'val' of B(val=1) to <class '__main__.B'>: Type 'JS' is not a valid Sematic type: Expected a Sematic-supported type here, but got: ~JS. Please refer to the Sematic docs about supported types: https://docs.sematic.dev/type-support/type-support#what-types-are-supported

The problem is that Sematic is directly pulling field.type which for classes A/B above, returns JS for val. Instead Sematic needs to correctly resolve the JS (or other TypeVars) against the arguments to the Generic class. This PR fixes this issue by introducing a resolve_type method to handle this scenario.

Testing

$ pytest sematic/types/types/tests/ sematic/utils/tests/test_types.py

Manual Testing

In [1]: from dataclasses import dataclass
   ...: from typing import Generic, TypeVar
   ...: import sematic
   ...: 
   ...: JS = TypeVar("JS")
   ...: 
   ...: @dataclass
   ...: class A(Generic[JS]):
   ...:     val: JS
   ...: 
   ...:     def test(self, a: JS) -> JS:
   ...:         pass
   ...: 
   ...: @dataclass
   ...: class B(A[int]):
   ...:     def test(self, a: int) -> int:
   ...:         return a + self.val
   ...: 
   ...: 
   ...: @sematic.func
   ...: def gen(inp: B) -> int:
   ...:     return inp.test(1)
   ...: 
   ...: b = B(val=1)
   ...: sematic.SilentRunner().run(gen(b))
Out[1]: 2

@bcalvert-graft bcalvert-graft added the bug Something isn't working label Jan 19, 2025
Copy link

@mick-graft mick-graft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@bcalvert-graft bcalvert-graft merged commit 082e60e into graft/sematic/main Jan 21, 2025
8 checks passed
@bcalvert-graft bcalvert-graft deleted the bcal/fix_type_annotation_for_generic_types branch January 21, 2025 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants