An import statement instructs the Python import loader to perform several operations. For example, the statement from a.b import Foo as Bar
causes the following steps to be performed at runtime:
- Load and execute module
a
if it hasn’t previously been loaded. Cache a reference toa
. - Load and execute submodule
b
if it hasn’t previously been loaded. - Store a reference to submodule
b
to the variableb
within modulea
’s namespace. - Look up attribute
Foo
within moduleb
. - Assign the value of attribute
Foo
to a local variable calledBar
.
If another source file were to subsequently execute the statement import a
, it would observe b
in the namespace of a
as a side effect of step 3 in the the earlier import operation. Relying on such side effects leads to fragile code because a change in execution ordering or a modification to one module can break code in another module. Reliance on such side effects is therefore considered a bug by Pyright, which intentionally does not attempt to model such side effects.
Pyright models two loader side effects that are considered safe and are commonly used in Python code.
-
If an import statement targets a multi-part module name and does not use an alias, all modules within the multi-part module name are assumed to be loaded. For example, the statement
import a.b.c
is treated as though it is three back-to-back import statements:import a
,import a.b
andimport a.b.c
. This allows for subsequent use of all symbols ina
,a.b
, anda.b.c
. If an alias is used (e.g.import a.b.c as abc
), this is assumed to load only modulec
. A subsequentimport a
would not provide access toa.b
ora.b.c
. -
If an
__init__.py
file includes an import statement of the formfrom .a import b
, the local variablea
is assigned a reference to submodulea
. This statement form is treated as though it is two back-to-back import statements:from . import a
followed byfrom .a import b
.
All other module loader side effects are intentionally not modeled by Pyright and should not be relied upon in code. Examples include:
-
If one module contains the statement
import a.b
and a second module includesimport a
, the second module should not rely on the fact thata.b
is now accessible as a side effect of the first module’s import. -
If a module contains the statement
import a.b
in the global scope and a function that includes the statementimport a
orimport a.c
, the function should not assume that it can accessa.b
. This assumption might or might not be safe depending on execution order. -
If a module contains the statements
import a.b as foo
andimport a
, code within that module should not assume that it can accessa.b
. Such an assumption might be safe depending on the relative order of the statements and the order in which they are executed, but it leads to fragile code.