Skip to main content

A Python library that lets you delay module imports to when the import is used.

Project description

delayed-import

delayed-import is a Python library that lets you selectively change the behavior of import statements to delay the actual import action. It can be enabled/disabled per module and should work mostly transparently otherwise.

Installation

Install delayed-import with any Python package manager.

Motivation

Suppose you have a module mod1 that imports a library function, defines a function of its own, and optionally calls both on some user input when executed as a script:

import sys

from package1.package2 import func1

def func2(text: str) -> str:
    return f"func2: {text}"

if __name__ == "__main__":
    user_input = sys.argv[1]
    print(func1(user_input))
    print(func2(user_input))

When executing this as a script, it works as expected. But maybe another module would like to import mod1 as a library and only call func2. To do so, func1 must first be imported, which may depending on the structure of the containing packages entail a lot of extra work.

To make this module lighter to import, you could move the import of func1 to inside the if __name__ == "__main__": block. But this is not always an easy refactor and might lead to many repeated import statements. This library offers an alternative, by making the import delayed: this means that when you write the import statement, the name func1 is assigned a wrapper object that will resolve to the actual func1 when it is used. The lazy-object-proxy package is used for this.

The modified example would look like this:

import delayed_import

# Enable delayed imports in the current module and all submodules.
delayed_import.enable(__name__)

import sys

from package1.package2 import func1

def func2(text: str) -> str:
    return f"func2: {text}"

if __name__ == "__main__":
    user_input = sys.argv[1]
    print(func1(user_input))
    print(func2(user_input))

This code will function the same, except that the imports of package1 and package1.package2 are delayed until the moment they are used, when func1 is called. That means that if this file is imported as a module, they will not be imported at all. (Actually, the import of sys is similarly delayed, though it is likely already imported by this point.) It would be roughly equivalent to the following:

def func2(text: str) -> str:
    return f"func2: {text}"

if __name__ == "__main__":
    import sys
    user_input = sys.argv[1]
    from package1.package2 import func1
    print(func1(user_input))
    print(func2(user_input))

Usage

To use in a module, import the library and call enable with the module name:

import delayed_import

delayed_import.enable(__name__)

This makes all import statements that are executed in that module from that point on delayed. It is possible to later disable delayed imports by calling disable with the module name.

import delayed_import

delayed_import.enable(__name__)

# ...delayed imports...

delayed_import.disable(__name__)

# ...regular imports...

A block of imports can also be delayed by using enable as a context manager.

import delayed_import

with delayed_import.enable(__name__):
    # ...delayed imports...

# ...regular imports...

When enabling delayed imports, this will also affect submodules, allowing you to enable/disable them for entire code trees at once. You can mix enabling and disabling, i.e. if a enables them, and a.b disables them, then in a.c they will be enabled and in a.b.d they will be disabled.

It is possible to enable delayed imports for any module, not just for the current one. However, since delayed imported objects are not equivalent to their non-delayed equivalents, it might be necessary to adjust the importing code to take this difference into account.

Caveats

The main caveats have to do with the wrapping of the imported objects. This means that the imported object is not actually the same object as the one in the imported module. However, lazy-object-proxy wraps the object pretty thouroughly, so this is rarely observable. The biggest difference is that referential equality will be broken. So the following will fail with an AssertionError:

import delayed_import

delayed_import.enable(__name__)

# x1 wraps x, to delay the import.
from package1 import x as x1

delayed_import.disable(__name__)

# x2 is the real x, since delayed imports are disabled.
from package1 import x as x2

# This will work...
assert x1 == x2

# ...but this will fail!
assert x1 is x2

Moreover, because a new wrapper is created for every import statement, importing the same object twice will also not compare equal with is.

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

License

MIT

Acknowledgements

README template from https://www.makeareadme.com.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

delayed_import-0.1.0.tar.gz (12.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

delayed_import-0.1.0-py3-none-any.whl (8.1 kB view details)

Uploaded Python 3

File details

Details for the file delayed_import-0.1.0.tar.gz.

File metadata

  • Download URL: delayed_import-0.1.0.tar.gz
  • Upload date:
  • Size: 12.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for delayed_import-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1389e1c6a06804d702e18216ba312d8e6ce1570c7c041c1550b6b4cd8d426b76
MD5 96c3941bf9657afb51f0c03ee7549ad6
BLAKE2b-256 73efdcbeb4e98c60891b9b8bd4ac690097c529ea7ff1bac6dfae1a6ee87b64e1

See more details on using hashes here.

Provenance

The following attestation bundles were made for delayed_import-0.1.0.tar.gz:

Publisher: python-publish.yml on nardi/python-delayed-import

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file delayed_import-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: delayed_import-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for delayed_import-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3b249a885f347a00a1ed64d3340842c45d154fc1216ec20134b0e762b27746b9
MD5 82de4a18a701d11ef8b6147d8ce5831f
BLAKE2b-256 fcd5fb603b40be4b3bf5d2dd31e4db3d0c5fe72b0d1a7ef7a9ebdf28e53f5bec

See more details on using hashes here.

Provenance

The following attestation bundles were made for delayed_import-0.1.0-py3-none-any.whl:

Publisher: python-publish.yml on nardi/python-delayed-import

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page