Greedo
Greedo

Reputation: 5543

os.PathLike[Any] vs os.PathLike[str]

I've seen lines like that in typeshed:

https://github.com/python/typeshed/blob/994b69ef8f18e76689daca3947879c3d7f76173e/stdlib/_typeshed/__init__.pyi#L77

But os.PathLike doesn't appear to be generic. It doesn't let strings be passed.

import os
import pathlib

def test(f: os.PathLike[str]):
    print(pathlib.Path(f))


test(r"C:\Program Files")

The above snippet fails Mypy.

Upvotes: 12

Views: 9386

Answers (1)

Alex Waygood
Alex Waygood

Reputation: 7559

The source code that you link to in your question shows that os.PathLike is an Abstract Base Class that has a single abstractmethod, __fspath__. Due to the implementation of __subclasshook__, any classes that define __fspath__ are considered to be subclasses of os.PathLike even if PathLike is not in the class's method resolution order.

The str data type, however, does not have an __fspath__ method. As such, it does not conform to the PathLike interface, and so it makes sense that MyPy should reject an argument of type str if an argument of type PathLike was expected.

If your function can accept either a str object or a PathLike object, you should annotate the argument as being of type Union[str, PathLike[str]], much as typeshed does here.

As an aside, I'm slightly confused as to why you say "os.PathLike doesn't appear to be generic". The class defines __class_getitem__, so it's perfectly parametriseable at runtime. In Python >= 3.9:

>>> from os import PathLike
>>> PathLike[str]
os.PathLike[str]
>>> PathLike[bytes]
os.PathLike[bytes]
>>> PathLike['idk, anything you like really']
os.PathLike['idk, anything you like really']

Upvotes: 12

Related Questions