relative imports with the __import__ function

Discussion in 'Python' started by Chris Colbert, Dec 8, 2009.

  1. I have package tree that looks like this:

    main.py
    package
    ----__init__.py
    ----configuration.ini
    ----server
    --------__init__.py
    --------xmlrpc_server.py
    --------controller.py
    --------reco
    ------------<irrelevant.py's>
    ----segmentation
    --------__init__.py
    --------red_objects.py
    ----<other irrelevant folders>


    main.py launches an instance of xmlrpc_server.py which, in turn,
    imports controller.py.
    controller.py reads configuration.ini to determine which
    module/function to import from the segmentation directory and
    subsequently use.

    that config file specifies the module as 'red_objects' and the
    function as 'segment_red'.

    I am trying to dynamically import that module and func using the
    __import__ statement but keep getting empty module errors.

    In the following code segment, the failing code is uncommented, but
    the commented code works fine:

    seg_mod = 'red_objects'
    smod = __import__('..segmentation.%s' % seg_mod, fromlist=[seg_func], level=-1)

    #from ..segmentation import red_objects
    #smod = red_objects

    I have tried all sorts of values for the 'level' kwarg as well as
    everywhich variation of the dotted relative notation.

    I'm assuming i'm missing something fundamental on the import resolution...

    As an aside, I would like to move main.py inside of the package
    directory, but I dont know if that is possible with what i'm trying to
    do here.

    Thanks for any help.

    Cheers!

    Chris
     
    Chris Colbert, Dec 8, 2009
    #1
    1. Advertisements

  2. Chris Colbert

    Peter Otten Guest

    After some experimentation it turns out you have to provide some context for
    __import__() to determine the absolute location of the requested module. The
    required bit of information is the current module's __name__ attribute which
    you can provide via the globals parameter:

    def import_segmentation(name):
    return getattr(__import__("segmentation." + name, level=2,
    globals=globals()), name)

    Peter
     
    Peter Otten, Dec 8, 2009
    #2
    1. Advertisements

  3. Many thanks Peter!
    Almost like a charm!

    It seems the relative import level is dependent on the location of the
    main entry module. I thought the whole idea of relative imports was to
    make the import independent of the entry point?

    here is the import function i'm using

    def import_segmentation(self):
    # get the segmentation function defined in configuration.ini
    parent_dir = os.path.split(os.path.dirname(__file__))[0]
    prsr = ConfigParser.ConfigParser()
    prsr.read(os.path.join(parent_dir, 'configuration.ini'))
    seg_mod = prsr.get('segmentation', 'module')
    seg_func = prsr.get('segmentation', 'function')
    print __name__
    smod = __import__('segmentation.%s' % seg_mod, globals=globals(),
    fromlist=[seg_func], level=2)
    sfunc = getattr(smod, seg_func)
    return sfunc

    for that import level of 2 to work the tree must look like this:

    main.py
    package
    ----__init__.py
    ----configuration.ini
    ----server
    --------__init__.py
    --------xmlrpc_server.py
    --------controller.py
    --------reco
    ------------<irrelevant.py's>
    ----segmentation
    --------__init__.py
    --------red_objects.py
    ----<other irrelevant folders>

    but if I rearrange the package structure like this (just moving the
    location of main.py):

    package
    ----main.py
    ----__init__.py
    ----configuration.ini
    ----server
    --------__init__.py
    --------xmlrpc_server.py
    --------controller.py
    --------reco
    ------------<irrelevant.py's>
    ----segmentation
    --------__init__.py
    --------red_objects.py
    ----<other irrelevant folders>

    I have to change the import to level=1 or I get this error:
    ValueError: Attempted relative import beyond toplevel package

    I don't understand why the location of my main.py should have ANY
    bearing on relative import resolution.

    But again, i'm probably just being dense and need someone to explain
    it to me in newb speak ;)

    Cheers,

    Chris
     
    Chris Colbert, Dec 9, 2009
    #3
  4. Chris Colbert

    Peter Otten Guest

    You don't have to specify it explicitly, so you can move a module containing

    from .foo import bar

    into another package without changing its source code (provided there is a
    foo submodule with a bar attribute). This includes the trivial case of
    renaming the parent package.

    Of course Python has to know the importing module's location, just like you
    cannot meet me one block north and three blocks west unless you know where I
    currently am.

    See also

    http://www.python.org/dev/peps/pep-0328/#rationale-for-relative-imports

    Peter
     
    Peter Otten, Dec 10, 2009
    #4
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.