Skip to content

Pylance gives a false positive for fdst argument to shutil.copyfileobj #472

@willfrey

Description

@willfrey

Environment data

  • Language Server version: 2020.10.0
  • OS and version: macOS Catalina Version 10.15.7
  • Python version (& distribution if applicable, e.g. Anaconda): Python 3.8.5

Expected behaviour

I don't expect Pylance to report an issue.

Actual behaviour

Consider the following code snippet:

import shutil
from pathlib import Path

source_path = Path("/tmp/source")
source_path.write_text("Hello there!")

destination_path = Path("/tmp/destination")

with source_path.open("rb") as source, destination_path.open("wb") as destination:
    shutil.copyfileobj(source, destination)

assert destination_path.read_text() == "Hello there!"

Pylance will complain about the destination argument to shutil.copyfileobj and raise the following warning:

Argument of type "BufferedWriter" cannot be assigned to parameter "fdst" of type "SupportsWrite[TypeVar('AnyStr')]" in function "copyfileobj"
  TypeVar "_T_contra" is contravariant
    Type "bytes | bytearray | memoryview | array[_T] | mmap" is not compatible with constrained type "AnyStr"

I think this should be fine, though.

Pylance also complains about the builtin open(...) function, which you can see with the following snippet:

import shutil

source_path = "/tmp/source"
with open(source_path, "w") as source:
    source.write("Hello there!")

destination_path = "/tmp/destination"
with open(source_path, "rb") as source, open(destination_path, "wb") as destination:
    shutil.copyfileobj(source, destination)

with open(destination_path) as destination:
    assert destination.read() == "Hello there!"

Logs

I don't think logs will be helpful here but I'm happy to include them if you'd like.

Code Snippet / Additional information

If you pass an integer larger than 1 to the buffering argument to either open(...) or the instance method pathlib.Path.open(...) then the Pylance stubs infer that the returned object is just BinaryIO, which makes Pylance happy. Any of the other return types for the overloads of either function/method make Pylance complain.

I can also make Pylance happy with an explicit cast(BinaryIO, destination) but that seems to defeat the point of type checking catching errors.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions