I am using Python 3.11.4, and Pydantic 2.10.0. The following is a toy example of a real-world problem.
I have defined two Pydantic Basemodels, and created a list containing instances of both, as follows.
import pydantic
class Foo(pydantic.BaseModel):
a: int
b: int
class Bar(pydantic.BaseModel):
c: int
d: int
non_homogeneous_container = [
Foo(a=1, b=5),
Foo(a=7, b=8),
Bar(c=5, d=3),
Bar(c=15, d=12),
]
I want to write a function that takes in such a list, as well as a target_type
, and returns a list containing only those objects from the original list that conform to the specified target_type
. I have written such a function, as below, and provided (what I believe to be) the appropriate type annotation for my type checker (Pylance, in VSCode).
from typing import Any, Type, TypeVar
T = TypeVar("T", bound=pydantic.BaseModel)
def extract_objects_of_specified_type(
mixed_up_list: list[Any],
target_type: Type[T],
) -> list[T]:
extracted_list: list[T] = []
for each_object in mixed_up_list:
if isinstance(each_object, target_type):
extracted_list.append(each_object)
return extracted_list
Invoking my function with my mixed up list of objects, I obtain the expected list containing only objects of the target_type
.
list_of_only_foos = extract_objects_of_specified_type(
mixed_up_list=non_homogeneous_container, target_type=Foo
)
print (list_of_only_foos)
# Result: [Foo(a=1, b=5), Foo(a=7, b=8)]
However, I have a yellow underline on the line where I invoke my function; Pylance says the argument type is partially unknown
. The bit that's underlined and the associated Pylance report are as below:
# list_of_only_foos: list[Foo] = extract_objects_of_specified_type(
# mixed_up_list=non_homogeneous_container, target_type=Foo
# ) ^^^^^^^^^^^^^^^^^^^^^^^^^
# Linter report:
# Argument type is partially unknown
# Argument corresponds to parameter "mixed_up_list" in function "extract_objects_of_specified_type"
# Argument type is "list[Unknown]"PylancereportUnknownArgumentType
The linter report goes away when I annotate my input list of objects as below:
non_homogeneous_container: list[Foo | Bar] = [
Foo(a=1, b=5),
Foo(a=7, b=8),
Bar(c=5, d=3),
Bar(c=15, d=12),
]
or
non_homogeneous_container: list[pydantic.BaseModel] = [
Foo(a=1, b=5),
Foo(a=7, b=8),
Bar(c=5, d=3),
Bar(c=15, d=12),
]
I find this unsatisfactory because it obliges me to know in advance what objects are expected to be in the list I feed to my function.
I have tried to get around this issue by wrapping my function in a class that employs a Generic
(below), but this made no difference.
class ExtractSpecificType(Generic[T]):
@staticmethod
def extract_objects_of_specified_type(
mixed_up_list: list[Any],
target_type: Type[T],
) -> list[T]:
extracted_list: list[T] = []
for each_object in mixed_up_list:
if isinstance(each_object, target_type):
extracted_list.append(each_object)
return extracted_list
My function as originally constructed does what I want it to do - my question is about WHY the type checker is unhappy. Is my type annotation imprecise in some way? If so, what should I do differently?
Thank you.
EDIT
user2357112's comment and InSync's answer indicate that that this behaviour has something to do with the prevailing type-checker settings about when and where to warn about missing/ambiguous type annotations. For context, I have reproduced my VSCode type-checking settings below.
"python.analysis.typeCheckingMode": "standard",
"python.analysis.diagnosticSeverityOverrides": {
"reportGeneralTypeIssues": true,
"reportUnknownArgumentType": "warning",
"reportUnknownParameterType": "warning",
"reportMissingTypeArgument ": "warning",
"reportMissingParameterType": "warning",
"reportReturnType": "error",
"reportUnusedImport": "warning",
"reportUnnecessaryTypeIgnoreComment": "error"
},
I am using Python 3.11.4, and Pydantic 2.10.0. The following is a toy example of a real-world problem.
I have defined two Pydantic Basemodels, and created a list containing instances of both, as follows.
import pydantic
class Foo(pydantic.BaseModel):
a: int
b: int
class Bar(pydantic.BaseModel):
c: int
d: int
non_homogeneous_container = [
Foo(a=1, b=5),
Foo(a=7, b=8),
Bar(c=5, d=3),
Bar(c=15, d=12),
]
I want to write a function that takes in such a list, as well as a target_type
, and returns a list containing only those objects from the original list that conform to the specified target_type
. I have written such a function, as below, and provided (what I believe to be) the appropriate type annotation for my type checker (Pylance, in VSCode).
from typing import Any, Type, TypeVar
T = TypeVar("T", bound=pydantic.BaseModel)
def extract_objects_of_specified_type(
mixed_up_list: list[Any],
target_type: Type[T],
) -> list[T]:
extracted_list: list[T] = []
for each_object in mixed_up_list:
if isinstance(each_object, target_type):
extracted_list.append(each_object)
return extracted_list
Invoking my function with my mixed up list of objects, I obtain the expected list containing only objects of the target_type
.
list_of_only_foos = extract_objects_of_specified_type(
mixed_up_list=non_homogeneous_container, target_type=Foo
)
print (list_of_only_foos)
# Result: [Foo(a=1, b=5), Foo(a=7, b=8)]
However, I have a yellow underline on the line where I invoke my function; Pylance says the argument type is partially unknown
. The bit that's underlined and the associated Pylance report are as below:
# list_of_only_foos: list[Foo] = extract_objects_of_specified_type(
# mixed_up_list=non_homogeneous_container, target_type=Foo
# ) ^^^^^^^^^^^^^^^^^^^^^^^^^
# Linter report:
# Argument type is partially unknown
# Argument corresponds to parameter "mixed_up_list" in function "extract_objects_of_specified_type"
# Argument type is "list[Unknown]"PylancereportUnknownArgumentType
The linter report goes away when I annotate my input list of objects as below:
non_homogeneous_container: list[Foo | Bar] = [
Foo(a=1, b=5),
Foo(a=7, b=8),
Bar(c=5, d=3),
Bar(c=15, d=12),
]
or
non_homogeneous_container: list[pydantic.BaseModel] = [
Foo(a=1, b=5),
Foo(a=7, b=8),
Bar(c=5, d=3),
Bar(c=15, d=12),
]
I find this unsatisfactory because it obliges me to know in advance what objects are expected to be in the list I feed to my function.
I have tried to get around this issue by wrapping my function in a class that employs a Generic
(below), but this made no difference.
class ExtractSpecificType(Generic[T]):
@staticmethod
def extract_objects_of_specified_type(
mixed_up_list: list[Any],
target_type: Type[T],
) -> list[T]:
extracted_list: list[T] = []
for each_object in mixed_up_list:
if isinstance(each_object, target_type):
extracted_list.append(each_object)
return extracted_list
My function as originally constructed does what I want it to do - my question is about WHY the type checker is unhappy. Is my type annotation imprecise in some way? If so, what should I do differently?
Thank you.
EDIT
user2357112's comment and InSync's answer indicate that that this behaviour has something to do with the prevailing type-checker settings about when and where to warn about missing/ambiguous type annotations. For context, I have reproduced my VSCode type-checking settings below.
"python.analysis.typeCheckingMode": "standard",
"python.analysis.diagnosticSeverityOverrides": {
"reportGeneralTypeIssues": true,
"reportUnknownArgumentType": "warning",
"reportUnknownParameterType": "warning",
"reportMissingTypeArgument ": "warning",
"reportMissingParameterType": "warning",
"reportReturnType": "error",
"reportUnusedImport": "warning",
"reportUnnecessaryTypeIgnoreComment": "error"
},
Share
Improve this question
edited Feb 6 at 2:49
Vin
asked Feb 6 at 1:41
VinVin
1,0371 gold badge8 silver badges16 bronze badges
2
|
1 Answer
Reset to default 3You have reportUnknownArgumentType
enabled, but not strictListInference
, which controls how Pyright/Pylance infers list
types.
When inferring the type of a list, use strict type assumptions. For example, the expression
[1, 'a', 3.4]
could be inferred to be of typelist[Any]
orlist[int | str | float]
. If this setting is true, it will use the latter (stricter) type.
A small example demonstrating how it works:
class A: ...
class B: ...
(playground)
# strictListInference = false (default)
l = [A(), A(), B(), B()]
reveal_type(l) # list[Unknown]
(playground)
# strictListInference = true
l = [A(), A(), B(), B()]
reveal_type(l) # list[A | B]
This setting can be specified using either pyrightconfig.json
or pyproject.toml
:
// pyrightconfig.json
{
"strictListInference": true,
}
# pyproject.toml
[tool.pyright]
strictListInference = true
non_homogeneous_container
, but it's decided to do that when you use it as a function argument instead of when you initialize it, for some reason. – user2357112 Commented Feb 6 at 2:10non_homogeneous_container
aslist[Any]
(which is perfectly accurate), the warning goes away - and the type checker correctly infers the type oflist_of_only_foos
aslist[Foo]
. It's strange that the type checker chose to speak up where and when it did. – Vin Commented Feb 6 at 2:19