When using os.getenv
to retrieve environment variables, the default behavior returns a type of Optional[str]
. This is problematic as any downstream methods/functions that utilize these variables will likely be defined to accept a str
type explicitly. Is there an accepted usage to get around this issue or enforce the str
return type?
In the stub file definition for getenv
in typeshed you can find that getenv
can have a return type of Optional[str]
or Union[str, T_]
depending on the usage of the default
kwarg.
The four options I can see as yet are:
Optional[str]
types as arguments. This doesn't feel particularly right as a function/method may not be structured in a way that the Optional
type makes sense. i.e. the operation has no reason for a particular argument to be None
.default
kwarg for getenv
and provide a str
default value. This seems more correct, but requires that one set a default value for every usage of getenv
. The only problem I can see with this is that doing so may be confounding for testing or usage in different environments.getenv
to be a str. I really don't like this as it expects the environment to always be properly configured which, in my experience, is not a good assumption.Find below an example that raises a mypy error.
import os
SOME_VAR = os.getenv("SOME_VAR")
def some_func(val: str) -> None:
print(f"Loaded env var: {val}")
some_func(SOME_VAR)
The above raises the mypy error:
error: Argument 1 to "some_func" has incompatible type "Optional[str]"; expected "str"
tl;dr Use os.environ['SOME_VAR']
if you're sure it's always there
os.getenv
can and does return None
-- mypy is being helpful in showing you have a bug there:
>>> repr(os.getenv('DOES_NOT_EXIST'))
'None'
>>> repr(os.getenv('USER'))
"'asottile'"
Alternatively, you can convince mypy that it is of the type you expect in two different ways:
x = os.getenv('SOME_VAR')
assert x is not None, x
# mypy will believe that it is non-None after this point
utilizing a cast:
from typing import cast
x = cast(str, os.getenv('SOME_VAR'))
# mypy will believe that it is a `str` after this point
(the cast has some downsides in that it is never checked, whereas the assertion will hopefully lead to a test failure)
I would suggest not ignoring this error / working around it and instead use os.environ['SOME_VAR']
for things you expect to always be there, or write a condition to check for the error case when it is missing
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With