facebookincubator / cinder

Cinder is Meta's internal performance-oriented production version of CPython.
https://trycinder.com
Other
3.49k stars 121 forks source link

Add `excluding` argument for `importlib.set_lazy_imports` #80

Closed chuanzzx closed 2 years ago

chuanzzx commented 2 years ago

Summary

Add an optional argument excluding for importlib.set_lazy_imports.

Test Plan

excluding is an optional argument for importlib.set_lazy_imports(). It should be a list or a callback function, containing all the modules would like to be eagerly loaded.

For example, let us have a module called foo.py.

# foo.py
import importlib
import bar

If we call importlib.set_lazy_imports(excluding=["foo"]), two actions will be executed at this moment.

  1. Enable Lazy Imports from now
  2. foo is marked as eagerly loaded in the future

That is, if we import foo and run foo, it will load importlib and bar at the same time.

Pre-req

Create foo.py and bar.py under the same path of python.exe.

# foo.py
print("I'm foooooo.")
import importlib
import bar
assert(not importlib.is_lazy_import(globals(), "bar"))
# bar.py
print("I'm bar!")

Start with/without Lazy Imports

No matter with or without -L, the expected results are the same in the following tests. The only difference is we need to use ./python.exe -L rather than ./python.exe if we would like to start with Lazy Imports at the beginning.

Use a list as a filter

Run

$ ./python.exe
>>> import importlib
>>> importlib.set_lazy_imports(excluding=["foo"])
>>> import foo
>>> assert(importlib.is_lazy_import(globals(), "foo"))
>>> foo

Expected result:

I'm foooooo.
I'm bar!

Use a callback function as a filter

Run

$ ./python.exe
>>> import importlib
>>> def eager_imports(name):
...    return name == "foo"
>>> importlib.set_lazy_imports(excluding=eager_imports)
>>> import foo
>>> assert(importlib.is_lazy_import(globals(), "foo"))
>>> foo  # Force loading `foo`

Expected result:

I'm foooooo.
I'm bar!

Correct the excluding argument by running again

If we would like to change our excluding setting, we can run the command set_lazy_imports with the new excluding argument again.

For example, run

$ ./python.exe
>>> import importlib

Set the first excluding argument as the Hello callback function.

>>> def Hello(name):
...     return "Hello" == name
...
>>>
>>> importlib.set_lazy_imports(excluding=Hello)
>>> import math
>>> importlib.is_lazy_import(globals(), "math")
True
>>> math.pi
3.141592653589793
>>> importlib.is_lazy_import(globals(), "math")
False

Correct the excluding argument by setting nothing.

importlib.set_lazy_imports()

Correct the excluding argument by using the eager_foo callback function.

>>> def eager_foo(name):
...     return name == "foo"
...
>>> importlib.set_lazy_imports(excluding=eager_foo)
>>> import foo
>>> foo
I'm foooooo.
I'm bar!

Wrong type of the argument

No matter with or without -L, if a parameter use in excluding is not a list or a callback, it will return the TypeError message.

>>> import importlib
>>> importlib.set_lazy_imports(excluding=123)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/harperlin/Documents/GitHub/crahsy_0619/Lib/importlib/__init__.py", line 82, in set_lazy_imports
    _imp.set_lazy_imports(excluding)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: set_lazy_imports() argument 1 must be list or callback function, not int
itamaro commented 2 years ago

I'm a bit confused about the semantics. if excluding=["foo"] is specified, this should cause import foo to be eager, but not the imports within foo, no?

chuanzzx commented 2 years ago

I also feel the API's behavior is not intuitive.

My understanding for excluding=["foo"] is closer to eagerly load foo when it is loading. Therefore, if we import foo after setting excluding=["foo"], foo is still a lazy object. However, when we run foo to force loading, foo will eagerly load itself and its sub-modules. That's why the message I'm bar! is in the expected result.

chuanzzx commented 2 years ago

Checking the original description in T120948621 might be helpful!