klozovin
klozovin

Reputation: 2373

Creating aliases for Python packages?

I have a directory, let's call it Storage full of packages with unwieldy names like mypackage-xxyyzzww, and of course Storage is on my PYTHONPATH. Since packages have long unmemorable names, all of the packages are symlinked to friendlier names, such as mypackage.

Now, I don't want to rely on file system symbolic links to do this, instead I tried mucking around with sys.path and sys.modules. Currently I'm doing something like this:

import imp
imp.load_package('mypackage', 'Storage/mypackage-xxyyzzww')

How bad is it to do things this way, and is there a chance this will break in the future? One funny thing is that there's even no mention of imp.load_package function in the docs.

EDIT: besides not relying on symbolic links, I can't use PYTHONPATH variable anymore.

Upvotes: 5

Views: 13068

Answers (3)

Terrel Shumway
Terrel Shumway

Reputation: 464

importlib may be more appropriate, as it uses/implements the PEP302 mechanism.

Follow the DictImporter example, but override find_module to find the real filename and store it in the dict, then override load_module to get the code from the found file.

You shouldn't need to use sys.path once you've created your Storage module

#from importlib import abc
import imp
import os
import sys
import logging
logging.basicConfig(level=logging.DEBUG)

dprint = logging.debug


class MyImporter(object):
    def __init__(self,path):
        self.path=path
        self.names = {}

    def find_module(self,fullname,path=None):
        dprint("find_module({fullname},{path})".format(**locals()))
        ml = imp.find_module(fullname,path)
        dprint(repr(ml))
        raise ImportError


    def load_module(self,fullname):
        dprint("load_module({fullname})".format(**locals()))
        return imp.load_module(fullname)
        raise ImportError


def load_storage( path, modname=None ):
    if modname is None:
        modname = os.path.basename(path)

    mod = imp.new_module(modname)
    sys.modules[modname] = mod
    assert mod.__name__== modname
    mod.__path__=[path]
    #sys.meta_path.append(MyImporter(path))
    mod.__loader__= MyImporter(path)
    return mod

if __name__=="__main__":
    load_storage("arbitrary-path-to-code/Storage")

    from Storage import plain
    from Storage import mypkg

Then when you import Storage.mypackage, python will immediately use your importer without bothering to look on sys.path

That doesn't work. The code above does work to import ordinary modules under Storage without requiring Storage to be on sys.path, but both 3.1 and 2.6 seem to ignore the loader attribute mentioned in PEP302. If I uncomment the sys.meta_path line, 3.1 dies with StackOverflow, and 2.6 dies with ImportError. hmmm... I'm out of time now, but may look at it later.

Upvotes: 4

Paulo Scardine
Paulo Scardine

Reputation: 77339

Packages are just entries in the namespace. You should not name your path components with anything that is not a legal python variable name.

Upvotes: 0

Garrett Hyde
Garrett Hyde

Reputation: 5617

Instead of using imp, you can assign different names to imported modules.

import mypackage_xxyyzzww as mypackage

If you then create a __init__.py file inside of Storage, you can add several of the above lines to make importing easier.

Storage/__init__.py:

import mypackage_xxyyzzww as mypackage
import otherpackage_xxyyzzww as otherpackage

Interpreter:

>>> from Storage import mypackage, otherpackage

Upvotes: 10

Related Questions