Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating aliases for Python packages?

Tags:

python

package

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.

like image 443
klozovin Avatar asked Nov 03 '10 19:11

klozovin


2 Answers

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
like image 102
Garrett Hyde Avatar answered Sep 21 '22 02:09

Garrett Hyde


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.

like image 38
2 revs Avatar answered Sep 22 '22 02:09

2 revs