Import Python objects from arbitrary locations specified by string
Project description
importloc
Import Python objects from arbitrary locations specified by string.
Features
- Minimalistic fully typed package
- Importable locations: files and named modules
- Handy helper utils
- Configurable resolution when module is already imported:
reuse,reload,replace,rename,raise - Atomic import: on
ImportError, old module with the same name will be restored, and partially initialized module will be removed
Installation
$ pip install importloc
Usage
from importloc import Location
Various locations
Import from file
```python
Location('app/config.py:conf').load()
```
>>> loc = Location('app/config.py:conf')
>>> loc
<PathLocation 'app/config.py' obj='conf'>
>>> loc.load()
<config.Config object at 0x...>
Import from module
```python
Location('app.__main__:cli').load()
```
>>> loc = Location('app.__main__:cli')
>>> loc
<ModuleLocation 'app.__main__' obj='cli'>
>>> loc.load()
<function cli at 0x...>
Distinguish file and module locations
```python
Location('./config.py:conf').load()
```
>>> 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 explicitly relative path. 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
```python
Location('app/config.py:Config.Nested').load()
```
>>> 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
```python
Location('app/config.py').load()
```
>>> loc = Location('app/config.py')
>>> loc
<PathLocation 'app/config.py'>
>>> loc.load()
<module 'config' from '...'>
Use Path object when loading module
```python
Location(Path('config.py')).load()
```
>>> from pathlib import Path
>>> loc = Location(Path('config.py'))
>>> loc
<PathLocation 'config.py'>
>>> loc.load()
<module 'config' from '...'>
Import all instances of some type
```python
get_instances(Location('app.__main__').load(), Callable)
```
>>> 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
```python
get_subclasses(Location('app.errors').load(), Exception)
```
>>> 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
```python
Location('...').load(modname='app_main')
```
>>> Location('app/config.py:Config').load(modname='app_main')
<class 'app_main.Config'>
Generate module name at run time
```python
Location('...').load(modname=random_name)
```
>>> 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:
reuseexisting module imported beforereloadexisting modulereplaceexisting modulerenamenew module (try to import under new name)raiseexception (default)
For details, see documentation on ConflictResolution.
Module name conflict raises error by default
```python
Location('...').load()
```
>>> 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
```python
Location('...').load(on_conflict='reuse')
```
>>> 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
```python
Location('...').load(on_conflict='reload')
```
>>> 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
```python
Location('...').load(on_conflict='replace')
```
>>> 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
```python
Location('...').load(on_conflict='rename', rename=random_name)
```
>>> 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
```python
Location('...').load(modname='...', on_conflict='rename', rename=random_name)
```
>>> 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?
When module was imported but requested object does not exist, AttributeError is raised.
See also
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file importloc-0.3.0.tar.gz.
File metadata
- Download URL: importloc-0.3.0.tar.gz
- Upload date:
- Size: 89.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.5.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
800fbab1ed15d54ef9c00d2f63621d49c5b0b69ec9a74c701b67cd210a916e58
|
|
| MD5 |
d60d729f7f72d31afbcf111fac5423ef
|
|
| BLAKE2b-256 |
992fe15e437f2f61ef72c2e49f64749351ecc82faa2aacb659b3231e09ec06ef
|
File details
Details for the file importloc-0.3.0-py3-none-any.whl.
File metadata
- Download URL: importloc-0.3.0-py3-none-any.whl
- Upload date:
- Size: 10.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.5.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be904a42646ecc4e7ea7798adb1c1b94d4f8685b5c062e1457fe89a7cbfc4eb8
|
|
| MD5 |
d4f21d0ccecc2b0e9827e8956720c1e6
|
|
| BLAKE2b-256 |
a1dcc7621b0de44af6fd0e2d95baada6aa7f4c4856c002616d384cfe0b92e54a
|