How do I type hint a filename in a function?

Question:

What is a best way in Python to hint a filename,
so that it’s acceptable to pass anything into a function that you can open as a file?

Especially both strings and files found via Pathlib.

def myfunc(filename: str) -> None:
    with open(filename) as f1:
        # do something here
Asked By: 576i

||

Answers:

I think what you are looking for is Structural Typing, which is not yet supported. It is proposed in PEP 544.

In the mean time, you could do a half-way effort by annotating with Union[str, bytes, os.PathLike].

Answered By: lxop

PEP 519 recommends using typing.Union[str, bytes, os.PathLike]

Answered By: Eric Langlois

As Eric said,

PEP 519 recommends using typing.Union[str, bytes, os.PathLike].

and this is the easiest option.

But you should consider also _typeshed.AnyPath: it supports all kinds of paths in accordance with the different versions, and it is the default typing hint in the built-in library for filenames, such as in the function open() itself.
Importing it results in your type helper recognising that the input should be a filename, and may help type hinting paths. It has also the variations _typeshed.StrPath for strings only and _typeshed.BytesPath for bytestrings only. Here for their definition.

However, you can’t just import the typeshed module, as it doesn’t exist at runtime. The easiest solution to this is to import it only during type-checking (because it’s the only time you need it):

from typing import TYPE_CHECKING
AnyPath = None
if TYPE_CHECKING:
    from _typeshed import AnyPath

Lastly, in the current 3.10 beta build, AnyPath has been renamed to StrOrBytesPath, in order to separate the strings and the bytestrings from the paths of the Path module, and another AnyPath wont be seen soon. So, if you are planning to input only str filenames, you can use _typeshed.StrPath, or just give up and use typing.Union[str, bytes, os.PathLike].

Answered By: Remmar00

As of PEP 604 (https://peps.python.org/pep-0604/) and python 3.10, the | operator can now be used for unions of type hints.

So the OPs example could now look like:

def myfunc(filename: str | os.PathLike) -> None:
    with open(filename) as f1:
Answered By: MJB