Probably related to https://github.com/microsoft/pyright/issues/191
The code:
import pathlib
import shutil
data_dir = pathlib.Path("/tmp")
archive_path = data_dir / "hello"
shutil.rmtree(archive_path)
The error is:
No overloads for 'shutil.rmtree(archive_path)' match parameters
Argument types: (Path)
I am using the pyright 1.1.8 extension in VSCode.
I think pyright is doing the right thing here. The shutil.rmtree method declaration indicates that its first parameter must be either bytes or _AnyPath, and _AnyPath is a type variable defined as follows:
_AnyPath = TypeVar("_AnyPath", str, os.PathLike[str])
This indicates that the type of the parameter must exactly match either a str or an os.PathLike[str].
You're attempting to pass an argument of type Path which doesn't match the constraint.
So pyright is correctly generating an error based on the typeshed definitions. If you think that the typeshed definition of shutil.rmtree is incorrect, please file a bug against the typeshed repo. Pyright consumes the typeshed definitions direction from that repo.
I wasn't aware of the difference Path and PurePath.
Do you agree that the above code should be ok in term of types? Or am I missing something? Am I using pathlib in the wrong way?
I don't know enough about pathlib to answer your question. The typeshed community would probably be able to provide a definitive answer.
You're attempting to pass an argument of type Path which doesn't match the constraint.
Actually, Path derives from PurePath, which derives from os.PathLike[str] (on Python 3.6+ and in typeshed HEAD), so I believe typeshed is right here.
You're correct about the class hierarchy. Path does derive from os.PathLike[str]. However, rmtree is declared such that it takes a parameter of type _AnyPath. That TypeVar is constrained to be either str or os.PathLike[str]. When a TypeVar is constrained like this, the type needs to match exactly. A subclass is not allowed. If the implementation of rmtree supports subclasses of os.PathLike[str], then the typeshed declaration is wrong (or at least incomplete).
I don't think that is correct. But only does mypy disagree as reference implementation, PEP 484 explicitly says:
Subtypes of types constrained by a type variable should be treated as their respective explicitly listed base types in the context of the type variable.
and then proceeds to give an example. I agree that the later sentence about bound types is a bit misleading, though. I believe the difference is:
from typing import TypeVar
class Foo:
pass
class Bar(Foo):
pass
X = TypeVar("X", Foo, str)
B = TypeVar("B", bound=Foo)
def test1(x: X) -> X:
return x
def test2(x: B) -> B:
return x
reveal_type(test1(Bar())) # revealed type: Foo
reveal_type(test2(Bar())) # revealed type: Bar
Cc @gvanrossum @JukkaL @ambv for clarification from the PEP authors.
Yeah, now that I go back and read the PEP, I think you may be right. I think I misinterpreted the intended meaning of this statement:
type constraints cause the inferred type to be _exactly_ one of the constraint types, while an upper bound just requires that the actual type is a subtype of the boundary type.
Guidance from the PEP authors would be appreciated.
It's an easy change for me to make.
I've made the change so pyright's behavior matches that of mypy.
This is now implemented in version 1.1.9, which I just published.