I have a config.py file which has a list of constants, such as:
# config.py
NAME = 'John'
AGE = 23
In another file, I import this file as a module and then pass it as a parameter to other functions. I used ModuleType as the type for this parameter.
import config
from types import ModuleType
def f1(config: ModuleType) -> None:
print(config.NAME)
The problem is when I run pyright linter, it reports an error:
79:30 - error: Cannot access member "NAME" for type "ModuleType"
Member "NAME" is unknown (reportGeneralTypeIssues)
What's the correct way to type hint the config to avoid these errors?
By far the easiest and most convenient way to handle this is to just not annotate the
config
argument, or annotate it asAny
. You can provide a more specific annotation, but it gets extremely awkward.The problem with your existing annotation is that your
f1
is annotated as taking arbitrary modules as arguments, and arbitrary modules may not have aNAME
attribute. (AlsoModuleType
is intypes
, nottyping
.) A correct, specific annotation forf1
would specify that it takes something with aNAME
attribute, which you can specify with a custom protocol class:but you'll have to do this for everything you want to define in
config
, and it'll get even more awkward if you want to allow optional config definitions inconfig
.Also, if you try to pass
config
as an argument tof1
now, it still won't work, because when you pass a module as an argument, mypy treats it as just a generic module, and doesn't consider its contents. (I don't know what pyright does, but that's how mypy handles it.) You would have to explicitly castconfig
:which is extremely awkward. Plus, once you have this cast in place, mypy won't report an error even if
config
doesn't have aNAME
attribute, so you've gained no safety at all from all this awkward work.