Problem:
I have a bunch python build scripts, all named bob.py
. They are
scattered all around a mono repo. I want to be able to import from
one another and be treated as bobs
package/namespace.
For example, I have the following 2 files:
projects/a/bob.py
projects/b/bob.py
From a/bob.py
I want to be able to import b/bob.py
like so:
# a/bob.py
import bobs.b.bob as b
# use b
...
Solution:
# loader.py
def add_bob_scripts_to_python_path(search_path):
root_dir = find_root_dir()
bobs_dir = create_bobs_dir(root_dir)
bob_files = find_bob_files(search_path)
for bob in bob_files:
create_symbolic_link(search_path, bobs_dir, bob)
add_to_sys_path(root_dir)
def find_root_dir():
path = '/tmp/_bobs'
pathlib.Path(path).mkdir(exist_ok=True)
return path
def create_bobs_dir(root):
bobs_dir = os.path.join(root, 'bobs')
pathlib.Path(bobs_dir).mkdir(exist_ok=True)
return bobs_dir
def find_bob_files(search_path, name='bob.py'):
bobs = []
for path, dirs, files in os.walk(search_path, topdown=True):
dirs[:] = [d for d in dirs]
if name in files:
bobs.append(os.path.join(path, name))
return bobs
def create_symbolic_link(search_path, bobs_dir, bob_file):
relative_path = os.path.relpath(bob_file, search_path)
target = bob_file
link_name = os.path.join(bobs_dir, relative_path)
pathlib.Path(link_name).parent.mkdir(parents=True, exist_ok=True)
if not os.path.exists(link_name):
os.symlink(bob_file, link_name)
def add_to_sys_path(root_dir):
sys.path.append(root_dir)
import os
import pathlib
import sys
Testing
Here is an end-to-end test that creates a/bob.py
and b/bob.py
and test the loader.py
file.
# loader_e2e.py
def run_e2e(test_dir):
script_A_path = os.path.join(test_dir, 'a/bob.py')
script_B_path = os.path.join(test_dir, 'b/bob.py')
create_python_file(path=script_A_path, content=a_bob())
create_python_file(path=script_B_path, content=b_bob())
result = execute_script(script_A_path)
assert result == 'hello from B\n'
print('success!')
def create_python_file(path, content):
pathlib.Path(path).parent.mkdir(exist_ok=True)
with open(path, 'w', encoding='utf8') as w:
w.write(content)
def execute_script(path):
return subprocess.check_output(['python3', path]).decode('utf8')
def a_bob():
return f'''
import sys
sys.path.append('{os.getcwd()}')
import loader
import pathlib
import os
loader.add_bob_scripts_to_python_path(pathlib.Path(__file__).parent.parent)
import bobs.b.bob as b
print(b.hello())
'''
def b_bob():
return '''
def hello():
return 'hello from B'
'''
import os
import subprocess
import tempfile
import pathlib
import shutil
if __name__ == '__main__':
try:
with tempfile.TemporaryDirectory() as temp_dir:
pathlib.Path(temp_dir).mkdir(exist_ok=True)
run_e2e(temp_dir)
finally:
shutil.rmtree('/tmp/_bobs')
and to run it, place the two files in a directory and run:
$ python3 loader_e2e.py