I have a class with an initializer that looks like:
def __init__(self, val : dict):
if not isinstance(val, dict):
raise ValueError('val argument is not a dictionary')
self._val = val
But if I do that, pyright or pylint (not yet sure which one) says:
Type analysis indicates code is unreachable
now that code should be unreachable, but given that it’s just a type hint, it would possibly be reachable, and that’s precisely the check for. How should I proceed here?
This might be a pylance diagnostic, I couldn’t get pyright to spit out that error. pyright in strict mode will complain however about the isinstance check. mypy will not complain at all, even with --warn-unreachable, mypy will avoid emitting unreachable errors for branches that unconditionally raise an exception, which I think is the most sensible option, because as you said, even if the code should be unreachable in a correct program it might still be reachable in an incorrect one and the fact that it always raises an exception indicates that this branch is used for error reporting.
Reachability is a part that isn’t really specified in the type system, so the various tools will disagree about when to report an error for unreachable code. Some will be more strict than others and rely on comments for explicitly silencing the warning.
Although I believe in the case of pylance/pyright reachability is just an informational diagnostic that the editor can display however it wants. So their stance may be to just ignore the diagnostic. Although I personally certainly prefer mypy’s pragmatic approach to this.
I see, pylance is the standard LSP for pyright, the reachability diagnostics from pyright are usually only exposed through the LSP and are supposed to be informational only. You could also just turn those diagnostics off and rely on pylint’s more simple W0101 instead.
Although mypy/pyright would probably give you better results for things like exhaustiveness checks, where you could still have some dead code that can only be detected through type analysis. However in mypy’s case you will need to enable warn_unreachable to get any reachability diagnostics, since they are off by default.
var_1 = {"one": 1, "two":2}
var_2 = 25
class tester:
def __init__(self, val : dict):
# if not isinstance(val, dict):
if type(val) != dict:
raise ValueError('val argument is not a dictionary')
self._val = val
obj_1 = tester(var_1)
First of all types should be compared by identity and not equality i.e. you should use is not rather than !=, but all you’re really doing here is trying to trick the type checker by using a check it doesn’t narrow types on.
The reason type checkers don’t narrow is because it doesn’t cover subclasses of dict, which is the other reason this is bad, do you want to punish everyone that uses a defaultdict or OrderedDict instead of a dict? You would introduce runtime errors that won’t be caught by the type checker and it’s just overall hostile API design.
Yes, I agree with David. Disabling/turning off that particular warning or switching type checkers to make it go away is probably what you want to do.
Type checkers are static code analysis tools and don’t work at runtime. This type checking code does operate at runtime. In a sense, if you have type checking you could get rid of this type checking code altogether but that would only be a solution for you but not for anyone working on your code without a type checker.
If this is for a library, I’d leave it in since other people using this code might not be using a type checker. If this is for an application where you and the other developers are definitely using type checkers, I’d take it out.