# PEP 810: Explicit lazy imports

**URL:** <https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131>\
**Category:** PEPs\
**Created:** [October 3, 2025, 11:50am UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131 "2025-10-03T11:50:19Z")\
**Posts on this page:** 20\
**Page:** 14

<div class="post-metadata">

**Author:** ![pablogsal](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pablogsal/32/32246_2.png) [@pablogsal](https://discuss.python.org/u/pablogsal)\
**Post date:** [October 9, 2025, 8:08am UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/268 "2025-10-09T08:08:50Z")

</div>

> [@hprodh](#):
>
> Probably a lazy imported object can have a ` __lazy__ ` attribute, explicitly centralizing an interface with the proxy. I can foresee a bunch of uses now :
> 
> ```python
> lazy import mymodule
> 
> mymodule. __lazy__.check() # perform any kind of quick tests to check whether the import system will succeed to import (module init errors apart) and return boolean if yes
> mymodule. __lazy__.get_path() # seek and return path to module root file or folder, like importlib.util.find_spec
> mymodule. __lazy__.raw_dict() # seek the __raw_dict__ by parsing
> mymodule. __lazy__.reify()
> 
> hasattr(mymodule, ' __lazy__') # check if mymodule was reified
> 
> ```

Notice the hypothetical ` __raw_dict__ ` only makes sense across module boundaries when inspecting module objects:

```python
# x.py

lazy import a
lazy import b

def foo(): ...

# y.py

import x

# If we do here x. __dict__ then both x.a and x.b will be reified so we
# need some way to know that they are there but without triggering the
# reification

print(f"names in x are: {x. __raw_dict__.keys()}")

```

> [@encukou](#):
>
> I appreciate that you’ve discussed this a lot, and studied the effects on large codebases, but… could ` __dict__ ` itself stay raw?

We are not closed to this if people agree that this is the best option. It means we are technically exposing lazy objects a bit more, also outside the module. Apart from the options highlighted in the PEP we didn’t went with this option initially so is also a bit easier to activate global mode, but I am happy to reevaluate it as its another option to the ` __raw_dict__ ` conundrum. I think @brittanyrey may have more info on how much this matters in practice in the wild.

> [@encukou](#):
>
> Imdividual objects? Also yes. If there’s no free function for `get()`, IMO the method should be made into a dunder that’s a no-op on real module objects (` __reified__ `?). Otherwise you always need an isinstance check before calling it:
> 
> ```python
> # action at a distance -- perhaps in another module, maybe in usercustomize for REPLs
> import json
> 
> ... # ... some time later ...
> 
> lazy import json
> json.get() # AttributeError!
> 
> ```

Notice the `get()` method only happens if you somehow stumped into a proxy object via `globals()` the new hypothetical ` __raw_dict__ ` or ` __dict__ ` if we use your proposal.

```python
>>> lazy import exampl
>>> print(globals()['example'])
<lazy_import 'example'>
>>> print(globals()['example'].get())
<module 'example' from '/Users/pgalindo3/github/python/main/example.py'>

```

Also note that any attempt to use will cause it to reify so its really hard to ever see a lazy object:

```python
# lel.py
print("FORTRAN 77 ROCKS")

def foo(): ...

```

then:

```python
>>> lazy import lel
>>> x = globals()['lel']
>>> print(x)
FORTRAN 77 ROCKS
<module 'lel' from '/Users/pgalindo3/github/python/main/lel.py'>

```

or

```python
>>> lazy from lel import foo
>>>
>>> x = globals()['foo']
>>> def print_type(x):
... print(type(x))
...
>>> print_type(x)
FORTRAN 77 ROCKS
<class 'function'>

```

Perhaps this can motivate why trying to reify on `module. __dict__ ` is worth doing and exposing ` __raw_dict__ ` or some free function that does that: it “hides” the lazy objects more. Not that they are a problem if they appear (that’s fine!) but is nice that is **rare** for them to show up.

---

<div class="post-metadata">

**Author:** ![hprodh](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/hprodh/32/22529_2.png) [@hprodh](https://discuss.python.org/u/hprodh)\
**Post date:** [October 9, 2025, 8:36am UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/269 "2025-10-09T08:36:06Z")

</div>

The idea I have under the attribute ` __lazy__ ` is that it explicitly gathers everything that can be called on the proxy without reification. It feels really visual in the code.  
Thus maybe a natural way for the raw dict will become `mymodule. __lazy__. __dict__ `.

---

<div class="post-metadata">

**Author:** ![pablogsal](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pablogsal/32/32246_2.png) [@pablogsal](https://discuss.python.org/u/pablogsal)\
**Post date:** [October 9, 2025, 8:39am UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/271 "2025-10-09T08:39:03Z")

</div>

> [@hprodh](#):
>
> The idea I have under the attribute ` __lazy__ ` is that it explicitly gathers everything that can be called on the proxy without reification. It feels really visual in the code.

I think I get what you say but just to be absolutely clear: the ` __lazy__ ` attribute must then be on the **module** object not on the **proxy**. Note that this is because ` __raw_dict__ ` would be an attribute on the module object not on any proxy.

---

<div class="post-metadata">

**Author:** ![encukou](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/encukou/32/2461_2.png) [@encukou](https://discuss.python.org/u/encukou)\
**Post date:** [October 9, 2025, 9:01am UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/272 "2025-10-09T09:01:25Z")

</div>

Thanks for the reply!  
I hope my thoughts are useful; I won’t mind if you let practicality beat purity.

I now see more clearly where this clashes with my mental model: when I type ` __dict__ `, I’m _asking_ for the internals.  
Currently, ` __dict__ ` is the way to get an object’s raw namespace – uncooked by descriptors or inheritance. Adding ` __raw_dict__ ` layer below that\[1\] means I’ll need to remember what’s cooked at which layer.

* * *

1. regardless of whether it’s exposed as ` __raw_dict__ ` or `globals()`

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [October 9, 2025, 9:13am UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/273 "2025-10-09T09:13:41Z")

</div>

> [@encukou](#):
>
> I now see more clearly where this clashes with my mental model: when I type ` __dict__ `, I’m _asking_ for the internals.  
> Currently, ` __dict__ ` is the way to get an object’s raw namespace – uncooked by descriptors or inheritance. Adding ` __raw_dict__ ` layer below that means I’ll need to remember what’s cooked at which layer.

I agree that `module. __dict__ ` is supposed to give us the module’s raw namespace to begin with, but we should also recommend a canonical way of obtaining the “cooked” namespace of a module going forward. Should `vars(module)` be the function to return a cooked namespace then? Or should there be a new helper function?

---

<div class="post-metadata">

**Author:** ![encukou](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/encukou/32/2461_2.png) [@encukou](https://discuss.python.org/u/encukou)\
**Post date:** [October 9, 2025, 9:35am UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/274 "2025-10-09T09:35:11Z")

</div>

Yeah, it seems `vars()` would fit the bill; decoupling `vars()` from ` __dict__ ` could be an explicit change in the PEP. Free builtins generally do cook the things they access.

> [@pablogsal](#):
>
> Should we include something like a free function to force reification on objects (or the entire module) instead?

That’s another option: after reifying the whole module you can use ` __dict__ ` (or ` __raw_dict__ `, or `globals()` in the current PEP), and getting that would stay _O(1)_. [edit: It would also stay _practically infallible_].

---

<div class="post-metadata">

**Author:** ![AndersMunch](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/andersmunch/32/12823_2.png) [@AndersMunch](https://discuss.python.org/u/AndersMunch)\
**Post date:** [October 9, 2025, 9:37am UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/275 "2025-10-09T09:37:54Z")

</div>

Great PEP.

About the `from X lazy import Y` syntax alternative: If I see

```python
from A.B.C.D lazy import E

```

then I expect the resolution of `A.B.C.D` to be eager. After all, Python is generally left-to-right, so I don’t expect anything to the left of the `lazy` soft keyword to be lazy.

IMHO the PEP gets it right, and even if there weren’t a syntactical ambiguity, I would still prefer `lazy` to go first.

---

<div class="post-metadata">

**Author:** ![pganssle](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pganssle/32/245_2.png) [@pganssle](https://discuss.python.org/u/pganssle)\
**Post date:** [October 9, 2025, 2:30pm UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/276 "2025-10-09T14:30:24Z")

</div>

> [@encukou](#):
>
> > [@pablogsal](#):
> >
> > Should lazy imports be forbidden in `with` blocks?
> 
> For now? Yes. This can be easily added in the future.

Since the main reason to allow them is a backwards compatibility shim (see [this post](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/176) and [this one](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/225)), it actually is important that this be allowed from day one if it’s going to be allowed.

---

<div class="post-metadata">

**Author:** ![jamestwebber](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jamestwebber/32/12799_2.png) [@jamestwebber](https://discuss.python.org/u/jamestwebber)\
**Post date:** [October 9, 2025, 3:02pm UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/277 "2025-10-09T15:02:18Z")

</div>

Is a backport actually going to have the same behavior as a PEP 810 lazy import? I thought part of the motivation for this PEP is that the behavior _can’t_ be implemented the same way by a package that modifies the import machinery\[1\].

A backport that has subtly different behavior sounds like a trap.

* * *

1. if it can, why would we need new syntax?

---

<div class="post-metadata">

**Author:** ![MegaIng](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/megaing/32/16162_2.png) [@MegaIng](https://discuss.python.org/u/MegaIng)\
**Post date:** [October 9, 2025, 3:14pm UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/278 "2025-10-09T15:14:04Z")

</div>

> [@jamestwebber](#):
>
> Is a backport actually going to have the same behavior as a PEP 810 lazy import? I thought part of the motivation for this PEP is that the behavior _can’t_ be implemented the same way by a package that modifies the import machinery [if it can, why would we need new syntax?]

They syntax has no relation to why this needs to be build into the language.

The true “innovation” in this PEP isn’t the lazy import system itself, but the proxy object that has unique interactions upon being looked at. \[1\]

That part can’t be back-ported. But other things can. Specifically you can make a pure-python proxy object that just forwards attribute accesses and only proxy modules. This is a change in behavior, and most notably, `from` imports aren’t going to be lazy.

> [@jamestwebber](#):
>
> A backport that has subtly different behavior sounds like a trap.

With the idea that lazy imports are only for performance improvements, this shouldn’t be an issue. The performance behavior/import order differs between python 3.13 and 3.14, but that can be fine

* * *

1. I am honestly surprised there haven’t been complains about this. Previous discussions for proxy objects always had ideological opponents.

---

<div class="post-metadata">

**Author:** ![jamestwebber](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jamestwebber/32/12799_2.png) [@jamestwebber](https://discuss.python.org/u/jamestwebber)\
**Post date:** [October 9, 2025, 4:42pm UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/279 "2025-10-09T16:42:51Z")

</div>

> [@MegaIng](#):
>
> With the idea that lazy imports are only for performance improvements, this shouldn’t be an issue. The performance behavior/import order differs between python 3.13 and 3.14, but that can be fine

I was trying to understand what a “backport” can accomplish that the other implementations can’t do. It’s not that lazy imports are currently impossible–there are multiple ways to do it.

This was in the context of the “should lazy imports be allowed in `with` statements” discussion. One argument for allowing it was that it enables a particular style of backport. It doesn’t seem to me that such a backport would be more useful than using any other lazy import system. Either way, you’re using it until PEP 810 is available for all the versions you support, and then I imagine you’d refactor to use the new syntax and drop a dependency.

---

<div class="post-metadata">

**Author:** ![pganssle](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pganssle/32/245_2.png) [@pganssle](https://discuss.python.org/u/pganssle)\
**Post date:** [October 9, 2025, 6:28pm UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/280 "2025-10-09T18:28:16Z")

</div>

> [@jamestwebber](#):
>
> This was in the context of the “should lazy imports be allowed in `with` statements” discussion. One argument for allowing it was that it enables a particular style of backport. It doesn’t seem to me that such a backport would be more useful than using any other lazy import system. Either way, you’re using it until PEP 810 is available for all the versions you support, and then I imagine you’d refactor to use the new syntax and drop a dependency.

For many backports and backwards-compatibility shims, the goal is for the semantics to work as closely as possible as you need, and you just keep adding epicycles to your shims until you get it working, and then test as well as you can on the entire supported matrix.

You don’t always need to have identical semantics before and after (though it is nice if you can achieve that). In this case, one concrete example I can see where semantic differences are fine is the global flag. That flag doesn’t exist in Python 3.14, so you know no one running your library will be using it and you don’t have to try to implement it in earlier versions. If you try to keep consistent semantics and just use whatever lazy imports you are using _already_, you will actually be thwarting the global flag in Python 3.15, so you actually would prefer to switch to the standard lazy import system in 3.15 even if the semantics are slightly different from the one you were able to implement in 3.14.

---

<div class="post-metadata">

**Author:** ![mikeshardmind](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mikeshardmind/32/14381_2.png) [@mikeshardmind](https://discuss.python.org/u/mikeshardmind)\
**Post date:** [October 9, 2025, 7:48pm UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/281 "2025-10-09T19:48:12Z")

</div>

I’d prefer the pep remain simpler and focused on consistent behavior for the future, and not concern itself with backportability. There are plenty of clever tools available that forbidding the lazy import within a contextmanager shouldn’t block anyone doing “reasonable effort” backporting. (it can’t be backported in whole). Additionally, Many libraries who have a need for lazy imports already have something in use, with no clear benefit to switch to a backport.

At the very worst, `exec(_invalid_in_3_15_import_version_as_str, ...)` is trivial.

More elegant solutions like generating a second version of the file and picking which one to use when a library is installed (once) are also possible.

---

<div class="post-metadata">

**Author:** ![ofek](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ofek/32/1033_2.png) [@ofek](https://discuss.python.org/u/ofek)\
**Post date:** [October 9, 2025, 8:26pm UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/282 "2025-10-09T20:26:44Z")

</div>

Something occurred to me earlier that isn’t specifically related to runtime behavior but will nonetheless affect everyone. I think the PEP should offer guidance or a rejection of guidance on formatting import statements.

I would be extremely perturbed if formatters that support import groups (e.g. stdlib, 3p, 1p, relative, etc.) didn’t put lazy imports at the beginning or end of each group.

---

<div class="post-metadata">

**Author:** ![brettcannon](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/brettcannon/32/34895_2.png) [@brettcannon](https://discuss.python.org/u/brettcannon)\
**Post date:** [October 9, 2025, 9:57pm UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/283 "2025-10-09T21:57:26Z")

</div>

> [@pelson](#):
>
> it isn’t obvious that a `lazy from a.b import Thing; Thing()` should need to have imported `a/ __init__.py` in a lazy world.

It actually does require `a` be imported. When you import a submodule or subpackage, two key things happen:

1. The module is added to `sys.modules`, e.g. `"a.b" in sys.modules`
2. The module is added as an attribute on the containing pacakge, e.g. `hasattr(a, "b")`

That step 2 would be confusing in a lazy world (e.g. what if there’s name conflict between something in the ` __init__.py` and the submodule?).

But regardless, the fact that you always import the package(s) above you is so old that not doing it would break a ton of code. And if your code wouldn’t break then your ` __init__.py` is probably empty and thus you don’t need it to be lazy for performance reasons. And if it does have stuff then there’s a good chance it has side-effects and thus you need it to be imported before the submodule is.

Personally, I would find the semantics harder to understand. As they stand now, I know that if I import something that everything above it is also imported. But making everything lazy means never knowing what is or is not lazy and then having to try and reason about the effects of that.

---

<div class="post-metadata">

**Author:** ![pganssle](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pganssle/32/245_2.png) [@pganssle](https://discuss.python.org/u/pganssle)\
**Post date:** [October 9, 2025, 10:45pm UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/284 "2025-10-09T22:45:35Z")

</div>

> [@mikeshardmind](#):
>
> I’d prefer the pep remain simpler and focused on consistent behavior for the future, and not concern itself with backportability.

I would argue that allowing lazy imports _is_ the simpler option, since forbidding them requires a more complicated implementation and requires specifying things like “when a lazy import appears in a context manager using the ` __lazy_modules__ ` mechanism, it is not an error but rather imported eagerly”.

This complexity is worth paying if there is a good reason, but the primary reason is that you _can_ use context managers to catch exceptions and in the absence of a good reason to import things in a context manager it’s enough to say, “This will probably be misused if it’s used at all”. This analysis holds up well for `try`/`except` because there’s no good reason to put a deferred operation like that into a `try`/`except` block and it’s a footgun, but `with` blocks are more general than that, and we’ve already identified at least one good reason to use it.

I note that on Github I see [about 10k](https://github.com/search?q=with%20%252F(contextlib%5C.)%253Fsuppress%5C(ImportError%5C)%252F&type=code) uses of `suppress(ImportError)` and 3.5M uses of `except ImportError`. It is also not _necessarily_ a situation where it makes no sense to put a deferred operation in a context manager, since there actually is stuff that is triggered. One could imagine a context manager that replaces ` __lazy_import__ ` to do some sort of weird and horrible magic for some reason.

> [@mikeshardmind](#):
>
> There are plenty of clever tools available that forbidding the lazy import within a contextmanager shouldn’t block anyone doing “reasonable effort” backporting. (it can’t be backported in whole). Additionally, Many libraries who have a need for lazy imports already have something in use, with no clear benefit to switch to a backport.
> 
> At the very worst, `exec(_invalid_in_3_15_import_version_as_str, ...)` is trivial.
> 
> More elegant solutions like generating a second version of the file and picking which one to use when a library is installed (once) are also possible.

At the end of the day, this is an empirical question, in my opinion. If there exists a _better_ way to implement the backport than a context manager and that is the _only_ use or context managers that people can see and there is real danger of people accidentally thinking they are doing one thing with context managers when they are actualy doing another, then I will gladly change my mind.

But at the moment, I don’t think we can vaguely gesture at a clever future that will find a perfectly ergonomic pattern for this when we have a _very_ clean and ergonomic pattern already. This:

```python
__lazy_modules__ = ["foo", "bar"]

with backports.lazy_imports( __lazy_modules__ ):
    import foo
    import bar

```

has several advantages over anything you have mentioned and anything else I can think of:

1. It syntactically looks like imports, which means by default linters, highlighters, dependency checkers, etc, all get “for free” information about what is going on there.
2. All code is centralized in one place _and not generated_, so readers of the code can look at the file itself and see exactly what is going to be executed.
3. The code is the _same_ in all versions
4. It gives the backport implementer the freedom to choose what semantics apply pre-Python 3.15
5. It requires _no hacks_ to seamlessly update you to the new mechanism in 3.15.

The PEP itself doesn’t really need to be concerned with backporting _per se_, but by restricting the use of `with` statements it is cutting off a pretty fundamental mechanism in Python for encapsulating complexity. The backporting case is an example of that and perhaps it is the _only_ example of that, but also the only example of `with` statements causing problems that people have come up with is `contextlib.suppress`, and I think it is just as fair to say, “This is not an especially common pattern and it is easy to encapsulate in a lint rule and the PEP doesn’t need to concern itself with elevating lint errors into syntax errors”

---

<div class="post-metadata">

**Author:** ![pitrou](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pitrou/32/28_2.png) [@pitrou](https://discuss.python.org/u/pitrou)\
**Post date:** [October 10, 2025, 8:13am UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/285 "2025-10-10T08:13:43Z")

</div>

> [@brettcannon](#):
>
> > [@pelson](#):
> >
> > it isn’t obvious that a `lazy from a.b import Thing; Thing()` should need to have imported `a/ __init__.py` in a lazy world.
> 
> It actually does require `a` be imported.

That will make lazy importing much less useful than it could be, though. Take this example:

```python
lazy import numpy.random

```

If that imports `numpy` eagerly then the benefit becomes very slim, since:

```python
>>> %time import numpy
CPU times: user 1.7 s, sys: 11.9 ms, total: 1.71 s
Wall time: 79.5 ms
>>> %time import numpy.random
CPU times: user 2.45 ms, sys: 1.98 ms, total: 4.43 ms
Wall time: 4.23 ms

```

(yes, importing NumPy can consume more than one second of precious CPU time [due to bloody OpenBLAS](https://github.com/numpy/numpy/issues/29193)).

Even with `OMP_NUM_THREADS=1` there is still a 20x difference between the parent import and the submodule import:

```python
>>> %time import numpy
CPU times: user 63.1 ms, sys: 5 ms, total: 68.1 ms
Wall time: 67.5 ms
>>> %time import numpy.random
CPU times: user 2.98 ms, sys: 995 μs, total: 3.98 ms
Wall time: 3.79 ms

```

~~So this current iteration of lazy importing would work poorly with NumPy, one of the most widely used Python packages in the world.~~

**Edit:** I had misunderstood @brettcannon 's comment, see correction by @DavidCEllis below.

---

<div class="post-metadata">

**Author:** ![encukou](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/encukou/32/2461_2.png) [@encukou](https://discuss.python.org/u/encukou)\
**Post date:** [October 10, 2025, 8:19am UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/286 "2025-10-10T08:19:01Z")

</div>

The arguments for allowing `with` are indeed convincing, thanks!

* * *

Meanwhile, I found another issue with the reifying ` __dict__ `: reification can **fail**. Consider a `foo.py` like this:

```python
lazy import fast_foo
lazy import slow_foo

def do_work():
    try:
        # maybe we can use the fast implementation
        fast_foo
    except ImportError:
        # no? no worries
        return slow_foo.do_work()
    fast_foo.do_work()

```

(Not realistic as-is, but you can extend the idea.)  
With the current PEP, you _can’t_ get this module’s ` __dict__ `. The failure cascades down to all the places where hiding lazy objects is meant to help introspection:

```pycon
>>> import foo
>>> help(foo)
No module named 'fast_foo'

```

This suggests that reifying the _whole namespace_ should either:

- be an explicit operation – something you can skip doing or put a `try` around, or
- swallow issues, and leave lazy objects around on failure. But that’s icky (ignoring the swallowing exceptions part: [it’d be good](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/268) if lazy objects show up _rarely_, but IMO it’s more important for them to show up _consistently_).

For me, that means `vars()` should not reify. But a “reify whole module” function that raises `ExceptionGroup` would be nice.

---

<div class="post-metadata">

**Author:** ![DavidCEllis](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/davidcellis/32/9789_2.png) [@DavidCEllis](https://discuss.python.org/u/DavidCEllis)\
**Post date:** [October 10, 2025, 9:13am UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/288 "2025-10-10T09:13:46Z")

</div>

> [@pitrou](#):
>
> > [@brettcannon](#):
> >
> > > [@pelson](#):
> > >
> > > it isn’t obvious that a `lazy from a.b import Thing; Thing()` should need to have imported `a/ __init__.py` in a lazy world.
> > 
> > It actually does require `a` be imported.
> 
> That will make lazy importing much less useful than it could be, though. Take this example:
> 
> ```python
> lazy import numpy.random
> 
> ```
> 
> If that imports `numpy` eagerly then the benefit becomes very slim, since:

I think maybe you missed the `Thing()` at the end of the line in the example which would trigger the import of `a.b` which _requires_ the import of `a`.

`lazy import numpy.random` won’t import `numpy` or `numpy.random` but using `numpy.random.default_rng()` will import `numpy.random` **and** `numpy`.

To be clear, using `importlib.util` as an example on the current build:

```python
import sys
lazy import importlib.util

print("importlib" in sys.modules) # False

importlib.util

print("importlib" in sys.modules) # True

```

---

<div class="post-metadata">

**Author:** ![pitrou](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pitrou/32/28_2.png) [@pitrou](https://discuss.python.org/u/pitrou)\
**Post date:** [October 10, 2025, 9:21am UTC](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131/289 "2025-10-10T09:21:34Z")

</div>

> [@DavidCEllis](#):
>
> `lazy import numpy.random` won’t import `numpy` or `numpy.random` but using `numpy.random.default_rng()` will import `numpy.random` **and** `numpy`.

Ok, thanks for the correction! It appears this is doing what I hoped it would do, then.

[Previous page](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131.md?page=13)

[Next page](https://discuss.python.org/t/pep-810-explicit-lazy-imports/104131.md?page=15)
