from m1 import T1
from m2 import T2
from m3 import f
x = f()
assert isinstance(x, T1, T2)
Perhaps you could have a v_import that imported all versions of a symbol used throughout your project? Ts = v_from_import_all(
“foo”,
“T”,
)
assert isinstance(x, *Ts)
For static analysis, your type checker could understand what v_import does, how it works, and which symbols will be actually be there at runtime but yes, it’s starting to seem extremely complicated!What you do with the return_value defines the behaviour you expect from it so to that extent you can rely on that instead of using isinstance:
x: Union[T1, T2] = f()
print(x.foo() ** 12.3)
Perhaps some function could build that Union type for you? It would be a pain to make it by hand if you had 50x different third-party dependencies each pulling in a slightly different requests (but which as far as you are concerned all return some small part of that package that are all compatible.)If you’re importing a module to use it in some way you’re also declaring some kind of version dependency / compatability on it too, so that’s another thing your static analysis could check for you. That would actually be incredibly useful:
1/ Do your dependencies import an older version of requests than you do?
2/ Does it matter, and why? (eg x.foo() only exists in version 4.5 onwards, but m1 imports 4.4.)