0

I have a package whose structure is like this:

/backends/
    __init__.py
    abc.py
    def.py
    ghi.py
    xyz.py
    common.py

The modules abc.py, def.py, ghi.py and xyz.py contain some common functions e.g. func0() and func1().

In the common.py module I am importing * from all modules like this:

from abc import *
from def import *
from ghi import *
from xyz import *

I don't think this is a Pythonic way to do this. Think about a few tens of such modules.

What I want is to have a single line statement which imports * from all the modules in the package. Something like this:

from backends import *

I tried this link, but couldn't get what I wanted. I created a variable __all__ in the __init__.py and assigned the list of all modules to it. The I put this import line in the common.py module:

from . import *

And then I tried to access the function func0() which is present in any module (except in __init__.py and common.py) in the package. But it raised an error which reads

ValueError: Attempted relative import in non-package

I need a detailed answer.

Community
  • 1
  • 1
Iqbal
  • 2,094
  • 1
  • 20
  • 28

2 Answers2

0

Here is a solution I tried myself and it worked,
I'll presume that you will be working with common.py as your main module where you'll be importing the rest modules in it, so:

1 - In the __init__.py file, add:

import os
import glob
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if os.path.basename(f)[:-3] != 'common']

2 - In common.py add:

import sys
from os import path
sys.path.append( path.dirname(path.dirname(path.abspath(__file__))))
#print sys.path  #for debugging

import backends
#from backends import *   #up to you, but previous is better
Iron Fist
  • 10,739
  • 2
  • 18
  • 34
  • 2
    I would reject this in a code review. All it takes is a `junk.py` file in the module directory to screw everything up. Explicit is better than implicit. An `__all__` declaration with static names is explicit; scraping the directory for names is implicit. – msw May 16 '15 at 19:30
0

Voila! Found a solution which I am completely satisfied with. :-)

I left the __init__.py module untouched. And instead put the following codes in the beginning of the common.py module.

import os

pwd = os.path.dirname(os.path.realpath(__file__))
file_names = os.listdir(pwd)

for name in file_names:
    if ".pyc" not in name and "__init__" not in name and "common" not in name and ".py" in name:
        exec "from "+name[:-3]+" import *"

It worked like charm. Though I don't know whether this is the best solution. I guess in Python 3.x, exec statement will be like following:

exec("from "+name[:-3]+" import *")
Iqbal
  • 2,094
  • 1
  • 20
  • 28