Skip to main content

Import Python objects from arbitrary locations specified by string

Project description

importloc

Import Python objects from arbitrary locations specified by string.

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

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:

  • 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

```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

importloc-0.3.0.tar.gz (89.5 kB view details)

Uploaded Source

Built Distribution

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

importloc-0.3.0-py3-none-any.whl (10.3 kB view details)

Uploaded Python 3

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

Hashes for importloc-0.3.0.tar.gz
Algorithm Hash digest
SHA256 800fbab1ed15d54ef9c00d2f63621d49c5b0b69ec9a74c701b67cd210a916e58
MD5 d60d729f7f72d31afbcf111fac5423ef
BLAKE2b-256 992fe15e437f2f61ef72c2e49f64749351ecc82faa2aacb659b3231e09ec06ef

See more details on using hashes here.

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

Hashes for importloc-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 be904a42646ecc4e7ea7798adb1c1b94d4f8685b5c062e1457fe89a7cbfc4eb8
MD5 d4f21d0ccecc2b0e9827e8956720c1e6
BLAKE2b-256 a1dcc7621b0de44af6fd0e2d95baada6aa7f4c4856c002616d384cfe0b92e54a

See more details on using hashes here.

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