Incorrect detection of types?
Describe the Bug
I am testing out Pyrefly, and so I have a class like this:
class Tenant(TenantBase, table=True):
registrationDate: datetime | None = Field(
default_factory=lambda: datetime.now(timezone.utc),
sa_type=DateTime(timezone=True),
nullable=False,
)
lastModifiedDate: datetime | None = Field(
default=None,
sa_type=DateTime(timezone=True),
nullable=True,
)
sid: int | None = Field(
default=None, sa_column=Column(Integer, primary_key=True, autoincrement=True)
)
__tablename__ = "tenant"
I get error on sa_type=DateTime(timezone=True), saying:
Argument
DateTimeis not assignable to parametersa_typewith typePydanticUndefinedType | type[Any]in functionsqlmodel.main.FieldPyrefly(bad-argument-type)
However if I go into the Field function of the SQLModel, it is defined as follows:
# include sa_type, sa_column_args, sa_column_kwargs
@overload
def Field(
default: Any = Undefined,
*,
default_factory: Optional[NoArgAnyCallable] = None,
alias: Optional[str] = None,
title: Optional[str] = None,
description: Optional[str] = None,
exclude: Union[
AbstractSet[Union[int, str]], Mapping[Union[int, str], Any], Any
] = None,
include: Union[
AbstractSet[Union[int, str]], Mapping[Union[int, str], Any], Any
] = None,
const: Optional[bool] = None,
gt: Optional[float] = None,
ge: Optional[float] = None,
lt: Optional[float] = None,
le: Optional[float] = None,
multiple_of: Optional[float] = None,
max_digits: Optional[int] = None,
decimal_places: Optional[int] = None,
min_items: Optional[int] = None,
max_items: Optional[int] = None,
unique_items: Optional[bool] = None,
min_length: Optional[int] = None,
max_length: Optional[int] = None,
allow_mutation: bool = True,
regex: Optional[str] = None,
discriminator: Optional[str] = None,
repr: bool = True,
primary_key: Union[bool, UndefinedType] = Undefined,
foreign_key: Any = Undefined,
unique: Union[bool, UndefinedType] = Undefined,
nullable: Union[bool, UndefinedType] = Undefined,
index: Union[bool, UndefinedType] = Undefined,
sa_type: Union[Type[Any], UndefinedType] = Undefined,
sa_column_args: Union[Sequence[Any], UndefinedType] = Undefined,
sa_column_kwargs: Union[Mapping[str, Any], UndefinedType] = Undefined,
schema_extra: Optional[Dict[str, Any]] = None,
) -> Any: ...
Which to me seems to indicate it is defined correctly:
sa_type: Union[Type[Any], UndefinedType] = Undefined,
Meaning it is taking any type, including DateTime.
On the Field itself, there is also error:
No matching overload found for function `sqlmodel.main.Field`, reporting errors for closest overload: `(default: Any = ..., *, default_factory: (() -> Any) | None, alias: str | None, title: str | None, description: str | None, exclude: AbstractSet[int | str] | Mapping[int | str, Any] | Any, include: AbstractSet[int | str] | Mapping[int | str, Any] | Any, const: bool | None, gt: float | None, ge: float | None, lt: float | None, le: float | None, multiple_of: float | None, max_digits: int | None, decimal_places: int | None, min_items: int | None, max_items: int | None, unique_items: bool | None, min_length: int | None, max_length: int | None, allow_mutation: bool, regex: str | None, discriminator: str | None, repr: bool, primary_key: PydanticUndefinedType | bool, foreign_key: Any, unique: PydanticUndefinedType | bool, nullable: PydanticUndefinedType | bool, index: PydanticUndefinedType | bool, sa_type: PydanticUndefinedType | type[Any], sa_column_args: PydanticUndefinedType | Sequence[Any], sa_column_kwargs: Mapping[str, Any] | PydanticUndefinedType, schema_extra: dict[str, Any] | None) -> Any`Pyrefly(no-matching-overload)
I am not sure what to make of all of that, so apologies if my title of the issue is not the most descriptive.
Sandbox Link
No response
(Only applicable for extension issues) IDE Information
Using Cursor IDE, output of Pyrefly language server is:
starting generic LSP server Reading messages Purged 0 dropped configs Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Purged 0 dropped configs Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Prepare to check 6484 files. Populated all files in the project path. Prepare to check 6484 files. Populated all files in the project path. Prepare to check 6484 files. Populated all files in the project path. Prepare to check 6484 files. Populated all files in the project path. Prepare to check 6484 files. Populated all files in the project path. Prepare to check 6484 files. Populated all files in the project path. Prepare to check 6484 files. Populated all files in the project path. Prepare to check 6484 files. Populated all files in the project path. Prepare to check 6484 files. Populated all files in the project path. Prepare to check 6484 files. Populated all files in the project path. Prepare to check 6484 files. Populated all files in the project path. Prepare to check 6484 files. Purged 0 dropped configs Populated all files in the project path. We should cancel request RequestId(I32(2)) Handling non-canceled request textDocument/documentSymbol (1) Request 2 is canceled Handling non-canceled request textDocument/documentSymbol (3) Handling non-canceled request textDocument/inlayHint (4) Handling non-canceled request textDocument/inlayHint (5) Handling non-canceled request textDocument/hover (6) Handling non-canceled request textDocument/hover (7) Purged 0 dropped configs Purged 0 dropped configs We should cancel request RequestId(I32(10)) We should cancel request RequestId(I32(11)) We should cancel request RequestId(I32(12)) We should cancel request RequestId(I32(13)) We should cancel request RequestId(I32(14)) We should cancel request RequestId(I32(15)) We should cancel request RequestId(I32(16)) We should cancel request RequestId(I32(17)) We should cancel request RequestId(I32(19)) We should cancel request RequestId(I32(20)) We should cancel request RequestId(I32(8)) We should cancel request RequestId(I32(21)) We should cancel request RequestId(I32(22)) We should cancel request RequestId(I32(23)) We should cancel request RequestId(I32(25)) Request 8 is canceled Handling non-canceled request textDocument/inlayHint (9) Request 10 is canceled Request 11 is canceled Request 12 is canceled Request 13 is canceled Request 14 is canceled Request 15 is canceled Request 16 is canceled Request 17 is canceled Handling non-canceled request textDocument/definition (18) Request 19 is canceled Request 20 is canceled Request 21 is canceled Request 22 is canceled Request 23 is canceled Handling non-canceled request textDocument/documentHighlight (24) Request 25 is canceled Handling non-canceled request textDocument/hover (26) Handling non-canceled request textDocument/hover (27) Handling non-canceled request textDocument/documentHighlight (28) Handling non-canceled request textDocument/hover (29) Handling non-canceled request textDocument/documentHighlight (30) Handling non-canceled request textDocument/hover (31) Handling non-canceled request textDocument/documentHighlight (32) Handling non-canceled request textDocument/definition (33) Handling non-canceled request textDocument/definition (34) Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Handling non-canceled request textDocument/definition (35) Handling non-canceled request textDocument/definition (36) Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Handling non-canceled request textDocument/documentHighlight (37) Handling non-canceled request textDocument/definition (38) Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Handling non-canceled request textDocument/documentSymbol (39) Handling non-canceled request textDocument/documentSymbol (40) Handling non-canceled request textDocument/inlayHint (41) Prepare to check 7171 files. Handling non-canceled request textDocument/documentHighlight (42) Handling non-canceled request textDocument/documentSymbol (43) Handling non-canceled request textDocument/inlayHint (44) Handling non-canceled request textDocument/hover (45) Handling non-canceled request textDocument/hover (46) Handling non-canceled request textDocument/hover (47) Handling non-canceled request textDocument/documentHighlight (48) Handling non-canceled request textDocument/hover (49) Handling non-canceled request textDocument/hover (50) Handling non-canceled request textDocument/hover (51) Populated all files in the project path. Prepare to check 7171 files. Populated all files in the project path. Prepare to check 7171 files. Populated all files in the project path. Handling non-canceled request textDocument/definition (52) Handling non-canceled request textDocument/definition (53) Handling non-canceled request textDocument/definition (54) Handling non-canceled request textDocument/definition (55) Populating all files in the config (ConfigBase { errors: Some(ErrorDisplayConfig({})), replace_imports_with_any: Some([]), untyped_def_behavior: Some(CheckAndInferReturnType), ignore_errors_in_generated_code: Some(false), extras: ExtraConfigs({}) }). Handling non-canceled request textDocument/definition (56) Handling non-canceled request textDocument/definition (57) Handling non-canceled request textDocument/definition (58) Handling non-canceled request textDocument/definition (59) Handling non-canceled request textDocument/definition (60) Handling non-canceled request textDocument/documentHighlight (61) Handling non-canceled request textDocument/definition (62) Handling non-canceled request textDocument/inlayHint (63) Handling non-canceled request textDocument/documentSymbol (64) Prepare to check 7171 files. Populated all files in the project path. Handling non-canceled request textDocument/inlayHint (65) Handling non-canceled request textDocument/hover (66) Handling non-canceled request textDocument/documentSymbol (67) Handling non-canceled request textDocument/inlayHint (68) Handling non-canceled request textDocument/hover (69) Handling non-canceled request textDocument/hover (70) Handling non-canceled request textDocument/hover (71) Handling non-canceled request textDocument/hover (72) Handling non-canceled request textDocument/hover (73) Handling non-canceled request textDocument/hover (74) Handling non-canceled request textDocument/hover (75) Handling non-canceled request textDocument/documentSymbol (76) Handling non-canceled request textDocument/inlayHint (77) Handling non-canceled request textDocument/inlayHint (78) Handling non-canceled request textDocument/documentSymbol (79) Handling non-canceled request textDocument/documentSymbol (80) Handling non-canceled request textDocument/inlayHint (81) Handling non-canceled request textDocument/inlayHint (82) Handling non-canceled request textDocument/inlayHint (83) Handling non-canceled request textDocument/documentSymbol (84) Handling non-canceled request textDocument/inlayHint (85) Handling non-canceled request textDocument/inlayHint (86) Handling non-canceled request textDocument/documentSymbol (87) Handling non-canceled request textDocument/inlayHint (88) Handling non-canceled request textDocument/documentSymbol (89) Handling non-canceled request textDocument/inlayHint (90) Handling non-canceled request textDocument/completion (91) Handling non-canceled request textDocument/inlayHint (92) Handling non-canceled request textDocument/documentSymbol (93) Handling non-canceled request textDocument/inlayHint (94) Handling non-canceled request textDocument/inlayHint (95) Handling non-canceled request textDocument/documentSymbol (96) Handling non-canceled request textDocument/inlayHint (97) Handling non-canceled request textDocument/definition (98) Handling non-canceled request textDocument/inlayHint (99) Handling non-canceled request textDocument/documentSymbol (100) Handling non-canceled request textDocument/definition (101) Handling non-canceled request textDocument/definition (102) Handling non-canceled request textDocument/definition (103) Handling non-canceled request textDocument/definition (104) Handling non-canceled request textDocument/hover (105) Handling non-canceled request textDocument/documentHighlight (106) Handling non-canceled request textDocument/definition (107) Handling non-canceled request textDocument/inlayHint (108) Handling non-canceled request textDocument/documentSymbol (109) Handling non-canceled request textDocument/inlayHint (110) Handling non-canceled request textDocument/inlayHint (111) Handling non-canceled request textDocument/inlayHint (112) Handling non-canceled request textDocument/inlayHint (113) Handling non-canceled request textDocument/inlayHint (114) Handling non-canceled request textDocument/documentHighlight (115) Handling non-canceled request textDocument/inlayHint (116) Handling non-canceled request textDocument/inlayHint (117) Handling non-canceled request textDocument/inlayHint (118) Handling non-canceled request textDocument/inlayHint (119) Handling non-canceled request textDocument/documentSymbol (120) Handling non-canceled request textDocument/documentHighlight (121) Handling non-canceled request textDocument/hover (122) Handling non-canceled request textDocument/hover (123) Handling non-canceled request textDocument/hover (124) Handling non-canceled request textDocument/hover (125) Handling non-canceled request textDocument/documentHighlight (126)
I don't have all the imports available, so can't precisely reproduce this, but with some plausible guesses:
-
sa_type: Union[Type[Any], UndefinedType] -
sa_type=DateTime(timezone=True)
Assuming that UndefinedType can't represent anything, and that DateTime constructor creates a DateTime then I think this is probably a type bug. It seems that sa_type wants a type, but DateTime is a value of type DateTime. It might be the case that sa_type = DateTime would be correct?
Although I'm somewhat guessing as I don't have all the imports available. Is it possible to recreate a small example with the Sandbox at https://pyrefly.org/sandbox/
I think I managed to get a reproducible snippet based on the way Pydantic uses Sentinel value.
What do you think of this?
@ndmitchell following up
Hi, sorry for the delay here.
I entered your snippet into the mypy and pyright playgrounds and they both give the same error. I also don't quite understand what the expected behavior is here.
From reading the code, an instance of DateTime is neither a type[Any] nor a subtype of UndefinedType, so the error seems legit.
Does Pydantic have special semantics here that I'm not aware of?
I have done some further reading on the meaning of Union[Type[Any], UndefinedType - and it looks like you are correct, I misunderstood it's meaning!
It does not take instantiated objects as the type.
Looks like I am not the only one to make this mistake: https://github.com/fastapi/sqlmodel/discussions/955
But I think this issue can be closed, I am sorry @yangdanny97 and @ndmitchell !
No worries! If you encounter any more possible bugs in Pyrefly, please continue to report them :)