Import Python objects from arbitrary locations specified by string.
- 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
$ pip install importloc
- Various locations
- Various targets
- Custom module name
- What if module is already imported?
- What if object does not exist?
The main and most used entity is Location
.
from importloc import Location
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...>
Location('app.__main__:cli').load()
Example
>>> loc = Location('app.__main__:cli')
>>> loc
<ModuleLocation 'app.__main__' obj='cli'>
>>> loc.load()
<function cli at 0x...>
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...>
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'>
Location('app/config.py').load()
Example
>>> loc = Location('app/config.py')
>>> loc
<PathLocation 'app/config.py'>
>>> loc.load()
<module 'config' from '...'>
Location(Path('config.py')).load()
Example
>>> from pathlib import Path
>>> loc = Location(Path('config.py'))
>>> loc
<PathLocation 'config.py'>
>>> loc.load()
<module 'config' from '...'>
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...>]
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'>]
Location('...').load(modname='app_main')
Example
>>> Location('app/config.py:Config').load(modname='app_main')
<class 'app_main.Config'>
Location('...').load(modname=random_name)
Example
>>> from importloc import random_name
>>> Location('app/config.py:Config').load(modname=random_name)
<class 'u....Config'>
The module name conflict can be resolved with one the methods:
reuse
existing module imported beforereload
existing modulereplace
existing modulerename
new module (try to import under new name)raise
exception (default)
For details, see documentation on ConflictResolution.
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
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
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
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
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...'>
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 ...>
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