Search code examples
pythonpython-typingpyright

Python TypeHint a function that returns different types based on input


I have a method which works like this, with it normally being used to return a Path.


from typing import Literal, Union
from pathlib import Path

def get_filename(return_type: Literal["Path", "str"]) -> Union[Path, str]:
    filename = "./foo/bar.csv"

    if return_type == "str":
        return filename
    elif return_type == "Path":
        return Path(filename)
    else:
        raise ValueError(
            f'Return type must be either "Path" or "str", not {return_type}'
        )


file = get_filename(return_type="Path")
print(file.is_file())

On the final line I get the following message from Pylance:

(method) is_file: () -> bool | Unknown Whether this path is a regular file (also True for symlinks pointing to regular files).
Cannot access member "is_file" for type "str" Member "is_file" is unknownPylancereportGeneralTypeIssues

Is there a way to correctly type hint this situation so that Pylance knows file is a Path? Or should I just make it always return Path and have another method which calls get_filename converts the output to string and then return that?

Thanks

Edit 1

I have just realised another, more common scenario:

import pandas as pd

# returns dataframe
df = pd.read_csv(file)

# returns a series, Pylance doesn't know this
series = pd.read_csv(file, squeeze=True)

Here in pandas an input argument can change the output type and Pylance can also not deal with this. For Pylance to know series is a pd.Series you must do:

# return series which pylance is happy with
df = pd.read_csv(file)
series = df.squeeze()

Solution

  • I think you can do this using typing.overload:

    from typing import Literal, overload
    from pathlib import Path
    
    @overload
    def get_filename(return_type: Literal["Path"]) -> Path:...
    
    @overload
    def get_filename(return_type: Literal["str"]) -> str:...
    
    def get_filename(return_type):
        # your code goes here