Skip to main content

importloc

Import Python objects from arbitrary locations specified by string.

license pypi python versions tests coverage tested with multipython uses docsub mypy uv Ruff

Features

  • Minimalistic fully typed package
  • Import from files or named modules
  • Import deeply nested objects
  • Import all instances or all subclasses
  • Configurable module name conflict resolution
  • Atomicity: on import error, new module is removed, and previous, if any, is restored

Installation

$ pip install importloc

Usage

Quick start

The main and most used entity is Location.

from importloc import Location

Various locations

Import from file

Location('app/config.py:conf').load()

Example

>>> loc = Location('app/config.py:conf')
>>> loc
<PathLocation 'app/config.py' obj='conf'>
>>> loc.load()
<config.Config object at 0x...>

Import from module

Location('app.__main__:cli').load()

Example

>>> loc = Location('app.__main__:cli')
>>> loc
<ModuleLocation 'app.__main__' obj='cli'>
>>> loc.load()
<function cli at 0x...>

Distinguish file and module locations

Location('./config.py:conf').load()

Example

>>> loc = Location('config.py:conf')
>>> loc
<ModuleLocation 'config.py' obj='conf'>
>>> loc.load()
Traceback (most recent call last):
    ...
ModuleNotFoundError: No module named 'config.py'...

Use relative path (similar to Docker bind mount). Path separator will result in PathLocation instead of ModuleLocation.

>>> loc = Location('./config.py:conf')
>>> loc
<PathLocation 'config.py' obj='conf'>
>>> loc.load()
<config.Config object at 0x...>

Various targets

Import nested class

Location('app/config.py:Config.Nested').load()

Example

>>> loc = Location('app/config.py:Config.Nested')
>>> loc
<PathLocation 'app/config.py' obj='Config.Nested'>
>>> loc.load()
<class 'config.Config.Nested'>

Import module as a whole

Location('app/config.py').load()

Example

>>> loc = Location('app/config.py')
>>> loc
<PathLocation 'app/config.py'>
>>> loc.load()
<module 'config' from '...'>

Use Path object when loading module

Location(Path('config.py')).load()

Example

>>> from pathlib import Path
>>> loc = Location(Path('config.py'))
>>> loc
<PathLocation 'config.py'>
>>> loc.load()
<module 'config' from '...'>

Import all instances of some type

get_instances(Location('app.__main__').load(), Callable)

Example

>>> from collections.abc import Callable
>>> from importloc import get_instances
>>> loc = Location('app.__main__')
>>> loc
<ModuleLocation 'app.__main__'>
>>> get_instances(loc.load(), Callable)
[<function cli at 0x...>]

Import all subclasses

get_subclasses(Location('app.errors').load(), Exception)

Example

>>> from importloc import get_subclasses
>>> loc = Location('app.errors')
>>> loc
<ModuleLocation 'app.errors'>
>>> get_subclasses(loc.load(), Exception)
[<class 'app.errors.Error1'>, <class 'app.errors.Error2'>]

Custom module name

Use different module name

Location('...').load(modname='app_main')

Example

>>> Location('app/config.py:Config').load(modname='app_main')
<class 'app_main.Config'>

Generate module name at run time

Location('...').load(modname=random_name)

Example

>>> from importloc import random_name
>>> Location('app/config.py:Config').load(modname=random_name)
<class 'u....Config'>

What if module is already imported?

The module name conflict can be resolved with one the methods:

  • reuse existing module imported before
  • reload existing module
  • replace existing module
  • rename new module (try to import under new name)
  • raise exception (default)

For details, see documentation on ConflictResolution.

Module name conflict raises error by default

Location('...').load()

Example

>>> Location('app/config.py:Config').load()
<class 'config.Config'>
>>> Location('app/config.py:Config').load()
Traceback (most recent call last):
    ...
importloc.exc.ModuleNameConflict: Module "config" is already imported

Reuse module that is already imported

Location('...').load(on_conflict='reuse')

Example

>>> C = Location('app/config.py:Config').load()
>>> C
<class 'config.Config'>
>>> old_id = id(C)
>>> C = Location('app/config.py:Config').load(on_conflict='reuse')
>>> C
<class 'config.Config'>
>>> # C is the same object:
>>> id(C) == old_id
True

Reload module that is already imported

Location('...').load(on_conflict='reload')

Example

>>> import sys
>>> C = Location('app/config.py:Config').load()
>>> C
<class 'config.Config'>
>>> old_id = id(C)
>>> mod_id = id(sys.modules['config'])
>>> C = Location('app/config.py:Config').load(on_conflict='reload')
>>> C
<class 'config.Config'>
>>> # module object remains the same after reloading:
>>> id(sys.modules['config']) == mod_id
True
>>> # C is the new object from reloaded module:
>>> id(C) == old_id
False

Replace old module with imported one

Location('...').load(on_conflict='replace')

Example

>>> import sys
>>> C = Location('app/config.py:Config').load()
>>> C
<class 'config.Config'>
>>> mod_id = id(sys.modules['config'])
>>> C = Location('app/config.py:Config').load(on_conflict='replace')
>>> C
<class 'config.Config'>
>>> # module object is the new one:
>>> id(sys.modules['config']) == mod_id
False

Load module under different generated name

Location('...').load(on_conflict='rename', rename=random_name)

Example

>>> from importloc import random_name
>>> Location('app/config.py').load()
<module 'config' from ...>
>>> Location('app/config.py').load(on_conflict='rename', rename=random_name)
<module 'u...'>

Combine override and rename

Location('...').load(modname='...', on_conflict='rename', rename=random_name)

Example

>>> from importloc import random_name
>>> Location('app/config.py').load(modname='app_config')
<module 'app_config' from ...>
>>> Location('app/config.py').load(
...     modname='app_config', on_conflict='rename', rename=random_name
... )
<module 'u...' from ...>

What if object does not exist?

Missing object causes AttributeError

When module was imported but requested object does not exist, AttributeError is raised.

Example

>>> Location('app/config.py:unknown').load()
Traceback (most recent call last):
    ...
AttributeError: object has no attribute 'unknown'
>>> # due to import atomicity, module 'config' was removed
>>> import sys
>>> 'config' in sys.modules
False

See also

Metadata

Release files for importloc 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for importloc 0.3.1
File Size Uploaded Provenance Actions
importloc-0.3.1.tar.gz 92.8 kB Without provenance Details for importloc-0.3.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for importloc 0.3.1
File Interpreter ABI Platform Actions
importloc-0.3.1-py3-none-any.whl Python 3 none any Details for importloc-0.3.1-py3-none-any.whl

Total release size: 104.7 kB

Release files / importloc-0.3.1.tar.gz

Download URL importloc-0.3.1.tar.gz
Size 92.8 kB
Tags Source
SHA-256 checksum
How to use checksums
2c4fb82bbeb41c176f2a0bb52cc36b1c94fa01fa0a5e6ca2567254cb12648a0d
BLAKE2b-256 checksum
How to use checksums
98b4435ce77fb255e5d341de926bf81ccd2b8defe3a0c604b4f8e82660c6eb8f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.5.13

Release files / importloc-0.3.1-py3-none-any.whl

Download URL importloc-0.3.1-py3-none-any.whl
Size 11.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
84f5baaf7f02a488125e722a7e1d15cd5150dfd1287a77085e5aa0e55fdd4d03
BLAKE2b-256 checksum
How to use checksums
5e02867c521489bf0da6189cac33fd28349e697e353fcd52db0e8c75af19c9a7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.5.13

Security for importloc 0.3.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page