Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/json2d/dotrelay
a utility library for declaring relative imports in Python
https://github.com/json2d/dotrelay
boilerplate-reduction module-import workflow
Last synced: about 1 month ago
JSON representation
a utility library for declaring relative imports in Python
- Host: GitHub
- URL: https://github.com/json2d/dotrelay
- Owner: json2d
- License: mit
- Created: 2021-02-15T21:33:26.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2021-09-24T17:14:16.000Z (over 3 years ago)
- Last Synced: 2024-11-18T22:01:37.207Z (2 months ago)
- Topics: boilerplate-reduction, module-import, workflow
- Language: Python
- Homepage:
- Size: 30.3 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# π‘ dotrelay
[![PyPI version](https://badge.fury.io/py/dotrelay.svg)](https://badge.fury.io/py/dotrelay)
[![Build Status](https://travis-ci.com/json2d/dotrelay.svg?branch=main)](https://travis-ci.com/json2d/dotrelay) [![Coverage Status](https://coveralls.io/repos/github/json2d/dotrelay/badge.svg?branch=main)](https://coveralls.io/github/json2d/dotrelay?branch=main)a utility library for declaring relative imports in Python
## Quick install
```bash
pip install dotrelay
```## Basic usage
out-of-the-box here's how to use `dotrelay` to import a module from an ancestor directory containing a `.relay` file:```py
import dotrelaywith dotrelay.Radio(__file__): # π»
import some_relatively_external_module
```## Problem
importing _relatively external_ modules is hard (in Python π)
don't believe? just check out this 10+ years of discussion on the internet:
- https://stackoverflow.com/questions/6323860/sibling-package-importsso forget about importing modules from _another galaxy_:
```
.
βββ andromeda
β βββ ufos.py -- πΈπΈπΈ
βββ milky_way
βββ sol
βββ earth
βββ animals
β βββ __init__.py
β βββ birds.py
β βββ fish.py
βββ lands
β βββ __init__.py
β βββ deserts.py
βββ waters
βββ __init__.py
βββ oceans.py
```in order to import `ufos` into `deserts` you'd need this bit of boilerplate:
```py
# deserts.py
import sys
import os# get directory path containing `andromeda` (relatively from this module's file path)
root_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__) ) ) ) ) )sys.path.append(root_path) # extend module import context
from andromeda import ufos # import that thing we need
sys.path.remove(root_path) # cleanup# finally the real work can begin
def lose_cattle_mysteriously(cattle):
ufos.abduct_cattle(cattle, mode='random')```
commonly referred to as a "`sys.path` hack", this is what we want to provide a better alternative for - it's fairly low level, fairly ugly, noisy and just plain makes the code smelly ππ½
## Solution
so let's make this better - we have the technology!
for starters let's create a `.relay` file in the directory containing `andromeda`, the module we want to import into `oceans`
> **_NOTE:_* this `.relay` file must be in one of `oceans` ancestor directories to be discoverable
```
.
βββ .relay -- π‘
βββ andromeda
β βββ ufos.py -- πΈπΈπΈ
βββ milky_way
βββ sol
βββ earth
βββ animals
β βββ __init__.py
β βββ birds.py
β βββ fish.py
βββ lands
β βββ __init__.py
β βββ deserts.py
βββ waters
βββ __init__.py
βββ oceans.py
```now in `oceans` we can use a `dotrelay.Radio` to _discover the `.relay` file above it_ and _establish a kind of temporary bridge_ for us to import `andromeda` and/or other modules in the relay directory
```py
# deserts.py
import dotrelay
with dotrelay.Radio(__file__): # π»
from andromeda import ufosdef lose_cattle_mysteriously(cattle):
ufos.abduct_cattle(cattle, mode='psuedo-random') # yes it happened
```now the boilerplate has been reduced to something fairly high level, fairly clean, short and sweet
## Common scenarios
fun example aside, lets see how this fits into real world scenarios
### Testing modules
so here's a typical file structure for most python lib projects where there's the main module and some test modules```
.
βββ pything
β βββ __init__.py
β βββ main.py
βββ tests
βββ units.py
```in order to test `pything` it needs to be imported into `units`, and you end up with more of that "`sys.path` hack" bloat:
```py
# units.py
import sys
import os
root_path = os.path.dirname( os.path.dirname( path.abspath(__file__) ) ) # the directory that contains pything
sys.path.append(root_path)import pything
sys.path.remove(root_path) # cleanup
import unittest
# ...
```an awkward thing to have to include in every single test module
with `dotrelay` we simply add the `.relay` file:
```
.
βββ .relay -- π‘
βββ pything
β βββ __init__.py
β βββ main.py
βββ tests
βββ units.py
```and the boilerplate is reduced to:
```py
# tests/units.py
import dotrelay
with dotrelay.Radio(__file__): # π»
import pything
```### Organizing modules
building off the previous example, say `units` were to be moved deeper into the project file structure:
```
.
βββ .relay -- π‘
βββ pything
β βββ __init__.py
β βββ main.py
βββ tests
βββ basic
βββ units.py
```with a "`sys.path` hack" the code for getting the `root_path` would need to be updated since again it's relative to the module's own file path
so really then, overtime, as a project matures, this hack becomes something that needs to be manage.
but that can all be avoided with `dotrelay`. no changes need to be made as long as the `.relay` file remains with one of `units` ancestor directories
### Reading static files
sometimes it's also useful just having the path of the relay directory
```
.
βββ .relay -- π‘
βββ pything
β βββ __init__.py
β βββ main.py
βββ fixtures
β βββ data.json -- π
βββ tests
βββ units.py
```so to read `fixtures/data.json` from `units`:
```py
# tests/units.py
import dotrelay
with dotrelay.Radio(__file__) as rad: # π»
ROOT_PATH = rad.relay_pathimport os, json
DATA_PATH = os.path.join(ROOT_PATH, 'fixtures', 'data.json')
with open(DATA_PATH, 'r') as fp:
DATA = json.load(fp)import unittest
# ...
```echoing the point from the previous example, this feature is pretty usefule when you need to move the static files around in a project