Preferences

I wonder how much things would break if all imports were lazy by default.

All my code which uses import probing would fail, such as fallbacks:

  try:
    import module
  except ImportError:
    import slow_module as module
Conditional support testing would also break, like having tests which only run if module2 is available:

  try:
    import module2
  except ImportError:
    def if_has_module2(f):
      return unittest.skip("module2 not available")(f)
  else:
    def if_has_module2(f):
      return f

  @if_has_module2
  class TestModule2Bindings(....
The proto-PEP also gives an example of using

  with suppress_warnings():
    import module3
where some global configuration changes only during import.

In general, "import this" and "import antigravity" - anything with import side-effects - would stop working.

Oh, and as the proto-PEP points out, changes to sys.path and others can cause problems because of the delay between time of lazy import and time of resolution.

Then there's code where you do a long computation then make use of a package which might not be present.

  import database  # remember to install!!
  import qcd_simulation

  universe = qcd_simulation.run(seconds = 30*24*60*60)
  database.save(universe)

All of these would be replaced with "import module; module.__name__" or something to force the import, or by an explicit use to __import__.
You have to opt in with the lazy import keyword no matter what. This pep also prevents lazy import in try catch. I think your concern matters if ‘module’ itself has a lazy import that you want to check exists. With this you now need to be more rigorous in check those sub dependencies.

This can already happen with non top level imports so it is not a necessarily a new issue, but could become more prevalent if there is an overall uptake in this feature for optional dependencies.

My post was a response to the sedatk conjectural "I wonder how much things would break if all imports were lazy by default."

I have zero concerns about this PEP and look forward to its implementation.

With the LazyLoader technique I described at https://www.hackerneue.com/item?id=45467489 , there is no problem:

  >>> import nonexistent_module
  Traceback (most recent call last):
    File "<python-input-2>", line 1, in <module>
      import nonexistent_module
    File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
    File "<frozen importlib._bootstrap>", line 1322, in _find_and_load_unlocked
    File "<frozen importlib._bootstrap>", line 1262, in _find_spec
    File "<python-input-0>", line 8, in find_spec
      base.loader = LazyLoader(base.loader)
                               ^^^^^^^^^^^
  AttributeError: 'NoneType' object has no attribute 'loader'
The implementation should probably convert that exception back to ImportError for you, but the point is that the absence of an implementation can still be detected eagerly while the actual loading occurs lazily.
Ahh, so you do the find first, and keep that around before loading.

I have bad memories of using a network filesystem where my Python app's startup time was 5 or more seconds because of all the small file lookups for the import were really slow.

I fixed it by importing modules in functions, only when needed, so the time went down to less than a second. (It was even better using a zipimport, but for other reasons we didn't use that option.)

If I understand things correctly, your code would have the same several-second delay as it tries to resolve everything?

Yes, if checking for a file is slow, then checking for a file is slow. If you need to know up front whether the module exists, then you can't get around using the "figure out whether the module exists" machinery up front. And if the definition of "a module exists" includes cases where the module is represented by a file whose existence you have to check for, then there's no getting around that, either.

(Trying to do "fallback" logic with lazily-loaded modules is also susceptible to race conditions, of course. What if someone defines the module before you try to use it?)

Maybe nothing would break ?

edit: ok well "xxx in sys.modules" would indeed be a problem

Yeah unfortunately in real world Python code people put side effects in their modules all the time.
In Python? I almost never see that. And I’ve certainly never signed off on a PR that did that.
Recursively importing other modules is itself a side effect.

In fact, all the code you see in the module is "side effects", in a sense. A `class` body, for example, has to actually run at import time, creating the class object and attaching it as an attribute of the module object. Similarly for functions. Even a simple assignment of a constant actually has to run at module import. And all of these things add up.

Further, if there isn't already cached bytecode available for the module, by default it will be written to disk as part of the import process. That's inarguably a side effect.

Side-effect means you're changing state outside the scope the code is running.

Sure thing you can declare globals variable and run anything on a module file global scope (outside funcs and class body), but even that 'global' scope is just an illusion, and everything declared there, as yourself said, is scoped to the module's namespace

(and you can't leak the 'globals' when importing the module unless you explicity do so 'from foo import *'. Think of python's import as eval but safer because it doesn't leaks the results from the module execution)

So for a module to have side-effect (for me) it would either:

- Change/Create attributes from other modules

- Call some other function that does side-effect (reflection builtins? IO stuff)

Maybe your code is awesome, but you're almost certainly using a library which does it.
This. The most egregious example I've ever seen is the inflect library, takes almost 2 seconds(!) to import: https://github.com/jaraco/inflect/issues/212
There will be chaos, all code bases usually have tons of side effects.

This item has no comments currently.

Keyboard Shortcuts

Story Lists

j
Next story
k
Previous story
Shift+j
Last story
Shift+k
First story
o Enter
Go to story URL
c
Go to comments
u
Go to author

Navigation

Shift+t
Go to top stories
Shift+n
Go to new stories
Shift+b
Go to best stories
Shift+a
Go to Ask HN
Shift+s
Go to Show HN

Miscellaneous

?
Show this modal