Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to change relative import search path

I'm trying to create an auto_import function which is part of a library: the purpose of this to avoid listing from .x import y many times in __init__ files, only do something this import lib; lib.auto_import(__file__) <- this would search for python files in that folder where the __init__ is present and would import all stuff by exec statement (i.e. exec('from .x import abc')).

My problem is that, somehow the 'from' statement always tries to import .x from lib directory, even if I change the cwd to the directory where the actual __init__ file is placed... How should I solve this? How should I change the search dir for from . statement?

Structure:

$ ls -R
.:
app.py  lib  x

./lib:
__init__.py  auto_import.py

./x:
__init__.py  y

./x/y:
__init__.py  y.py

e.g.: ./x/y/__init__.py contains import lib; lib.auto_import(__file__) auto_import is checking for files in dir of __file__ and import them with exec('from .{} import *') (but this from . is always the lib folder and not the dir of __file__, and that is my question, how to change this to dir of __file__ Of course the whole stuff is imported in app.py like:

import x
print(x.y) 

Thanks

EDIT1: final auto_import (globals() / gns cannot be avoided )

import os, sys, inspect

def auto_import(gns):
  current_frame = inspect.currentframe()
  caller_frame = inspect.getouterframes(current_frame)[1]
  src_file = caller_frame[1]
  for item in os.listdir(os.path.dirname(src_file)):
    item = item.split('.py')[0]

    if item in ['__init__', '__pycache__']:
      continue

    gns.update(__import__(item, gns, locals(), ['*'], 1).__dict__)
like image 997
vpas Avatar asked Dec 06 '25 07:12

vpas


1 Answers

The problem of your approach is that auto_import is defined in lib/auto_import.py so the context for exec('from .x import *') is always lib/. Even though you manage to fix the path problem, lib.auto_import(__file__) will not import anything to the namespace of lib.x.y, because the function locates in another module.

Use the built-in function __import__

Here is the auto_import script:

myimporter.py

# myimporter.py
def __import_siblings__(gns, lns={}):
  for name in find_sibling_names(gns['__file__']):
    gns.update((k,v) for k,v in __import__(name, gns,lns).__dict__.items() if not k.startswith('_'))
import re,os
def find_sibling_names(filename):
  pyfp = re.compile(r'([a-zA-Z]\w*)\.py$')
  files = (pyfp.match(f) for f in os.listdir(os.path.dirname(filename)))
  return set(f.group(1) for f in files if f)

Inside your lib/x/y/__init__.py

#lib/x/y/__init__.py
from myimporter import __import_siblings__
__import_siblings__(globals())

Let's say you have a dummy module that need to be imported to y:

#lib/x/y/dummy.py
def hello():
  print 'hello'

Test it:

import x.y
x.y.hello()

Please be aware that from lib import * is usually a bad habit because of namespace pollution. Use it with caution.

Refs: 1 2

like image 62
gdlmx Avatar answered Dec 07 '25 21:12

gdlmx



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!