Watusimoto
Watusimoto

Reputation: 1888

Problem with Python type hinting and standard libs

The following code works as expected, but the os.path.join produces a type error using pyright in VSCode, where shown.

# python 3.6.9
# pyright 1.1.25
# windows 10
# vscode 1.42.1
import os
import tempfile

with tempfile.TemporaryDirectory() as tmpfolder:
    name = "hello.txt"
    path = os.path.join(tmpfolder, name)
    # No overloads for 'os.path.join(tmpfolder, name)' match parameters
    #   Argument types: (TypeVar['AnyStr', str, bytes], Literal['hello.txt'])

print(path)

I think I understand the immediate cause of the problem, but contend it should not be happening. Given that, I have some questions:

  1. Is this the idiomatic way to write this code?
  2. Is the problem in tempfile, os, pyright, or me?
  3. If I cannot upgrade Python, what is the best (i.e. least clunky) way to suppress the error?

Upvotes: 1

Views: 1118

Answers (1)

Michael0x2a
Michael0x2a

Reputation: 64058

This seems like a limitation of pyright.

In short, the tempfile.TemporaryDirectory class is typed to be generic with respect to AnyStr. However, your example code omits specifying the generic type, leaving it up to the type checker to infer something appropriate.

In this case, I think there are several reasonable things for a type checker to do:

  1. Pick some default generic type based on the typevar, such as 'str' or 'Union[str, bytes]'. For example, mypy ends up picking 'str' by default, giving 'tmpfolder' a type of 'str'.
  2. Pick some placeholder type like either 'Any', the dynamic type, or NoReturn (aka 'bottom' aka 'nothing'). Both types are a valid subtype of every type, so are guaranteed to be valid placeholders and not cause downstream errors. This is what pyre and pytype does -- they deduce 'tmpfolder' has type 'Any' and 'nothing' respectively.
  3. Attempt to infer the correct type based on context. Some type checkers may attempt to do this, but I don't know of any that handles this particular case perfectly.
  4. Report an error and ask the user to specify the desired generic type.

What pyright seems to do instead is to just "leak" the generic variable. There is perhaps a principled reason why pyright is deciding to do this that I'm overlooking, but IMO this seems like a bug.


To answer your other questions, your example program is idiomatic Python, and type checkers should ideally support it without modification.

Adding a # type: ignore comment to the line with the error is the PEP 484 sanctioned way of suppressing error messages. I'm not familiar enough with pyright to know if it has a different preferred way of suppressing errors.

Upvotes: 1

Related Questions