When reading source code of fastapi, this line make me fuzzy:
from starlette.testclient import TestClient as TestClient
Why not just: from starlette.testclient import TestClient
?
From the point of view of executable code, there is absolutely no difference in terms of the Python bytecode being generated by the two different code examples (using Python 3.9):
>>> dis.dis('from starlette.testclient import TestClient as TestClient')
1 0 LOAD_CONST 0 (0)
2 LOAD_CONST 1 (('TestClient',))
4 IMPORT_NAME 0 (starlette.testclient)
6 IMPORT_FROM 1 (TestClient)
8 STORE_NAME 1 (TestClient)
10 POP_TOP
12 LOAD_CONST 2 (None)
14 RETURN_VALUE
>>> dis.dis('from starlette.testclient import TestClient')
1 0 LOAD_CONST 0 (0)
2 LOAD_CONST 1 (('TestClient',))
4 IMPORT_NAME 0 (starlette.testclient)
6 IMPORT_FROM 1 (TestClient)
8 STORE_NAME 1 (TestClient)
10 POP_TOP
12 LOAD_CONST 2 (None)
14 RETURN_VALUE
As shown, they are exactly identical. (Related question asked before existence of PEP-0484).
However, the comment by Graham501617 noted how modern type hinting validators (such as mypy
) accept this particular syntax to denote the re-export of that imported name (the other being the __all__
, which thankfully they did end up correctly supporting as that has been a standard syntax to denote symbols to (re-)export since Python 2). Specifically, as per the description of Stub Files in the referenced PEP 0484, quote:
- Modules and variables imported into the stub are not considered exported from the stub unless the import uses the
import ... as ...
form or the equivalentfrom ... import ... as ...
form. (UPDATE: To clarify, the intention here is that only names imported using the formX as X
will be exported, i.e. the name before and afteras
must be the same.)
Looking at git blame
for the relevant file in the packages pointed to this commit (direct link to relevant diff for the file) indicated that this particular type hinting issue was being addressed (as part of this issue) to ensure mypy will treat those imported names as re-export, thus allowing the usage of the --no-implicit-reexport
flag (which --strict
has likely implicitly enabled).
This particular re-export syntax however is very much strict about the "name before and after as
must be the same", the syntax referred in the related question (i.e. import foo.bar as bar
) can be found in recommendation by certain modern packages (e.g. PyTorch recommends the use of import torch.nn as nn
, as discussed in this question), does not in fact allow the bar
(or nn
) symbol be re-exported from the current module as foo.bar
is not the same as bar
(likewise torch.nn
is not the same as nn
, as the whole set of tokens torch.nn
is evaluated instead of the just the final identifier after the final .
).