9

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?

metatoaster
  • 17,419
  • 5
  • 55
  • 66
Waket Zheng
  • 5,065
  • 2
  • 17
  • 30
  • 2
    I don't think there is a difference. – Grismar Jun 05 '22 at 03:38
  • 2
    there is no meaningful difference ... other than in the option they chose they could swap out the import and still refer to it as TestClient – Joran Beasley Jun 05 '22 at 03:40
  • 4
    There is a difference for downstream consumers of the library that use a type checker. See, e.g. https://github.com/google/jax/pull/7606 for an explanation of why one might use the "from ... import X as X" pattern. – Graham501617 Jul 11 '22 at 10:45

1 Answers1

14

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 thread and thread.)

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 equivalent from ... import ... as ... form. (UPDATE: To clarify, the intention here is that only names imported using the form X as X will be exported, i.e. the name before and after as must be the same.)

Which means the library is likely following that particular convention to facilitate the re-export of the TestClient name from the stub (module) file that was referenced in the question. As a matter of fact, looking at git blame for the relevant file in the packages pointed to this commit (direct link to relevant diff for the file) which referenced this issue, which contains a similar brief discussion to address the exact type hinting issue; this was done 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).

metatoaster
  • 17,419
  • 5
  • 55
  • 66