I am using python type hinting in VS Code.
The user_wildcard
variable can either be a str="*.txt"
, or bool=False
.
No problems here, python can run the code fine. However, VS Code shows a "problem with my code" although there is no problem.
Is there are more "correct" way if type hinting so that I wouldn't get this problem in VS code?
import logging
class FileManager:
def __init__(self):
self.log = logging.getLogger(__name__)
self.cwd = Path(__file__).parent
def go_up(self):
self.cwd = self.cwd.parent
return self
def get_cwd(self) -> Path:
return self.cwd
def cd(self, foldername: str):
path = self.cwd / foldername
if path.is_dir():
self.cwd = path
return self
else:
self.log.error(f"invalid {foldername=}")
def find_file(self, filename: str, single_file: bool=False) -> list | Path:
filepaths = self.cwd.glob(filename)
if single_file:
return next(filepaths)
else:
return [x for x in filepaths]
def get_resource_file(self, use_wildcard: str | bool = False):
self.go_up().cd("resources")
if use_wildcard:
return self.find_file(use_wildcard, single_file=True)
else:
raise NotImplementedError(f"{use_wildcard=}")
I read that support for Union is added from python>3.10, which eliminates the need for from typing import Union
.
However, I have tried using Union but it shows the same problem as well.
The problem is that the find_file
method only expects its first argument filename
to be a str
. But inside get_resource_file
the use_wildcard
argument can be str
or bool
. So calling self.find_file(use_wildcard)
is not type safe.
You can establish some sort of type guard before calling find_file
to ensure that by that time use_wildcard
is definitely a str
(and cannot be a bool
anymore). The simplest way is to check if isinstance(use_wildcard, str)
instead of your current if use_wildcard
.
Additionally, if you really only want to allow use_wildcard
to be a str
or the literal False
(not any bool
, i.e. not True
), you can annotate that method using the typing.Literal
type as str | Literal[False]
. In that case the check if use_wildcard
is technically sufficient because anything "truthy" will necessarily be a (non-empty) str
:
from pathlib import Path
from typing import Any, Literal
class FileManager:
def find_file(self, filename: str, single_file: bool = False) -> list[Any] | Path:
...
def get_resource_file(self, use_wildcard: str | Literal[False] = False):
...
if use_wildcard:
return self.find_file(use_wildcard, single_file=True)
else:
raise NotImplementedError(f"{use_wildcard=}")
But even then I would recommend an explicit check. Though your semantics seems a bit off here anyway, but I assume the code is just not fully worked out yet.