Live data from Hacker News

PEP 810 – Explicit lazy imports

peps.python.org

201–210 of 247 posts

Re: PEP 810 – Explicit lazy imports

#201
post #60

Earlier quoted context omitted.

It's been explained many times before why this is not possible: the library doesn't actually have a version number. The distribution of source code on PyPI has a version number, but the name of this is not connected to the name of any module or package you import in the source code. The distribution can validly define zero or more modules (packages are a subset of modules, represented using the same type in the Pytho…

I know I'm missing something but wouldn't it be possible to just throw an import error when that happens? Would it even break anything? If I try: import numpy==2.1 And let's say numpy didn't expose a version number in a standard (which could be agreed upon in a PEP) field, then it would just throw an import exception. It wouldn't break any old code. And only packages with that explicit field would support the pinned…

> And let's say numpy didn't expose a version number in a standard (which could be agreed upon in a PEP) field, then it would just throw an import exception. It wouldn't break any old code. And only packages with that explicit field would support the pinned version import.

Yes, this part actually is as simple as you imagine. But that means in practical terms that you can't use the feature until at best the next release of Numpy. If you want to say for example that you need at least version 2 (breaking changes, after all), well, there are already 18 published packages that meet that requirement but are unable to communicate that in the new syntax. This can to my understanding be fixed with post releases, but it's a serious burden for maintainers and most projects are just not going to do that sort of thing (it bloats PyPI, too).

And more importantly, that's only one of many problems that need to be solved. And by far the simplest of them.

Re: PEP 810 – Explicit lazy imports

#202
post #197

Earlier quoted context omitted.

I believe the charitable interpretation is that it is not possible without breaking an enormous amount of legacy code. Which does feel close enough to “not possible”. Some situations could be improved by allowing multiple library versions, but this would introduce new headaches elsewhere. I certainly do not want my program to have N copies of numpy, PyTorch, etc because some intermediate library claims to have just-s…

What do you do today to resolve a dependency conflict when an intermediate library has a just-so dependency tree? The charitable interpretation of this proposed feature is that it would handle this case exactly as well as the current situation, if the situation isn't improved by the feature. This feature says nothing about the automatic installation of libraries. This feature is absolutely not about supporting multip…

> What do you do today to resolve a dependency conflict when an intermediate library has a just-so dependency tree?

When an installer resolves dependency conflicts, the project code isn't running. The installer is free to discover new constraints on the fly, and to backtrack. It is in effect all being done "statically", in the sense of being ahead of the time that any other system cares about it being complete and correct.

Python `import` statements on the other hand execute during the program's runtime, at arbitrary separation, with other code intervening.

> This feature says nothing about the automatic installation of libraries.

It doesn't have to. The runtime problems still occur.

I guess I'll have to reproduce the basic problem description from memory again. If you have modules A and B in your project that require conflicting versions of C, you need a way to load both at runtime. But the standard import mechanism already hard-codes the assumptions that i) imports are cached in a key-value store; ii) the values are singleton and client code absolutely may rely on this for correctness; iii) "C" is enough information for lookup. And the ecosystem is further built around the assumption that iv) this system is documented and stable and can be interacted with in many clever ways for metaprogramming. Changing any of this would be incredibly disruptive.

> This feature is absolutely not about supporting multiple simultaneous versions of a library at runtime.

You say that, but you aren't the one who proposed it. And https://news.ycombinator.com/item?id=45467350 says explicitly:

> and support having multiple simultaneous versions of any Python library installed.

Which would really be the only reason for the feature. For the cases where a single version of the third-party code satisfies the entire codebase, the existing packaging mechanisms all work fine. (Plus they properly distinguish between import names and distribution names.)

Re: PEP 810 – Explicit lazy imports

#204

Lazy imports have been proposed before, and were rejected most recently back in 2022: https://discuss.python.org/t/pep-690-lazy-imports-again/1966... . If I recall correctly, lazy imports are a feature supported in Cinder, Meta's version of CPython, and the PEP was driven by folks that worked on Cinder. Last time, a lot of the discussion centered around questions like: Should this be opt-in or opt-out? At what level?…

I also hope this proposal succeeds, but I'm not optimistic. This will break tons of code and introduce a slew of footguns. Import statements fundamentally have side effects, and when and how these side effects are applied will cause mysterious breakages that will keep people up for many nights. This is not fearmongering. There is a reason why the only flavor of Python with lazy imports comes from Meta, which is one o…

> This will break tons of code

I don't see how. It adds a new, entirely optional syntax using a soft keyword. The semantics of existing code do not change. Yes, yes, you anticipated the objection:

> What if these imports were transitive? ... How could you be sure that adding lazy imports wouldn't break any code downstream?

I would need to see concrete examples of how this would be a realistic risk in principle. (My gut reaction is that top-level code in libraries shouldn't be doing the kinds of things that would be problematic here, in the first place. In my experience, the main thing they do at top level is just eagerly importing everything else for convenience, or to establish compatibility aliases.)

But if it were, clearly that's a breaking change, and the library bumps the major version and clients do their usual dependency version management. As you note, type hints work similarly. And "explicitly calling into typing APIs" is more common than you might think; https://pypistats.org/packages/pydantic exists pretty much to do exactly this. It didn't cause major problems.

> Import statements fundamentally have side effects, and when and how these side effects are applied will cause mysterious breakages that will keep people up for many nights.

They do have side effects that can be arbitrarily complex. But someone who opts in to changing import timing and encounters a difficult bug can just roll back the changes. It shouldn't cause extended debugging sessions unless someone really needs the benefits of the deferral. And people in that situation will have been hand-rolling their own workarounds anyway.

> Too many people in this thread hold the view of "importing {pandas, numpy, my weird module that is more tangled than an eight-player game of Twister} takes too long and I will gladly support anything that makes them faster".

I don't think they're under the impression that this necessarily makes things faster. Maybe I haven't seen the same comments you have.

Deferring imports absolutely would allow, for example, pip to do trivial tasks faster — because it could avoid importing unnecessary things at all. As things currently stand, a huge fraction of the vendored codebase will get imported pretty much no matter what. It's analogous to tree shaking, but implicitly, at runtime and without actually removing code.

Yes, this could be deferred to explicitly chosen times to get more or less the same benefit. It would also be more work.

Re: PEP 810 – Explicit lazy imports

#205

doesn't this make the language a little unpredictable in terms of loading times? requiring to touch all parts to fully load the app?

I think a feature like this sees best use in short lived programs (where startup time is a disproportionate percentage of total run time) and programs where really fast startup is essential. There are plenty of places where I could imagine taking advantage of this in my code at work immediately, but I share your concern about unpredictability when libraries we use are also making use of it. It wouldn't be fun to have to dive into dependencies to see what needs to be touched to trigger lazy imports at the most convenient time. Unless I am misunderstanding and a normal import of a module means that all of its lazy imports also become non-lazy?

Re: PEP 810 – Explicit lazy imports

#206
post #108

Earlier quoted context omitted.

In the llm project, plugins can modify the command line arguments, so it's not that simple.

Yea, that's the core problem here: plugins can add new CLI subcommands, which means they all need to be loaded on startup. https://llm.datasette.io/en/stable/plugins/plugin-hooks.html...

Just FWIW, a trick that I'm planning to use for PAPER: first I make separate actual commands — `paper-foo`, `paper-bar` etc. that are each implemented as separate top-level scripts that can import only what they need. Later, the implementation of `paper foo` has the main `paper` script dynamically look up `paper-foo`. (Even a `subprocess.call` would work there but I'd like to avoid that overhead)

Re: PEP 810 – Explicit lazy imports

#207
post #191

Earlier quoted context omitted.

You can “just” use a feature which does not exist yet? How is that something you “just” do?

lazy from __future__ import __lazy_import__

I think Guido's time machine will need some serious overclocking to handle that one!

Re: PEP 810 – Explicit lazy imports

#208
post #67

I don't hate it but I don't love it. It sounds like everyone will start writing `lazy` before essentially every single import, with rare exceptions where eager importing is actually needed. That makes Python code visually noisier. And with no plan to ever change the default, the noise will stay forever. I would have preferred a system where modules opt in to being lazy-loaded, with no extra syntax on the import side.…

I would gladly take a command line flag that I can pass to python that makes all module loading lazy. Unless you are writing scripts or very simple stuff running side effects when modules are loaded should be avoided at all cost anyway.

You can do it today with a few lines of code, although the implementation I show is not particularly robust (since it works by metaprogramming the import system, of course other code could interfere): https://news.ycombinator.com/item?id=45467489

Re: PEP 810 – Explicit lazy imports

#209
post #76
post #67

I don't hate it but I don't love it. It sounds like everyone will start writing `lazy` before essentially every single import, with rare exceptions where eager importing is actually needed. That makes Python code visually noisier. And with no plan to ever change the default, the noise will stay forever. I would have preferred a system where modules opt in to being lazy-loaded, with no extra syntax on the import side.…

Wish pyproject.toml was enhanced to specify lazy loading via regexs.

I don't follow your reasoning. `pyproject.toml` has nothing to do with what happens at runtime. It's about building and packaging the code. It also doesn't say anything related to the modules that will be imported at runtime. It deals in the names of distributions, which are completely independent of `import` statements.

Re: PEP 810 – Explicit lazy imports

#210
post #114

One thing the PEP doesn't really talk about, and that I find very annoying is that many python linters will complain if you don't put all of your imports at the top of the file, so you get lint warnings if you do the most obvious way to implement lazy imports. And that is actually a problem for more than just performance. In some cases, importing at the top might actually just fail. For example if you need a platform…

It is annoying, but most linters will accept a `#noqa E402` comment to ignore it

>most linters will accept

So, they agreed on a common system of linting error codes? Is that documented somewhere?

Post reply on HN