2008年3月29日土曜日

import .. in python but 2.4 or before

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
You can't have relative import if you are using 2.4 or before. We have relative import function 2.5 or later, though.

Of cause you are a good python user (I knew it), definently need to create name hierarchy.

suppose "well named" package like below.

setup.py
MANIFEST
src/
__init__.py
a.py
b.py
One/
__init__.py
a.py
b.py
Two/
__init__.py
a.py
b.py


in setup.py,

setup(name="python-library-wellnamed",
packages=["wellnamed", "wellnamed.One", "wellnamed.Two",],
py_modules=[
"a.py",
"b.py",
"One.a",
"One.b",
"Two.a",
"Two.b",
],
)

It looks OK.


Here my question: in src/One/a.py, you need to import src/a.py. What do you do?

  • One solution is "close your eyes, and use setuptools develop command, so using setuptools instead of standard distutils."
    in this case Any way, you have to understand how it works.

  • Another solution is $PYTHONPATH to let your interpreter to get light module.
  • Other solution "edit your sys.path"
    In this case Any way, you have to find a right way to do it. Thus, understanding setuptools functionality is a sensible choice.


    Very first clue is in page 11 of setuptools presentation.

    • develop installs a package without moving it into site-packages/
    • Paste.egg-link is the poor man's symlink to ~/co/paste
    • easy-install.pth also points to ~/co/paste
    Well, if you are not using rpm or not using py2exe, you should be very happy. If you are using rpm like I do, not so happy. Fear about name collision between package from rpm and your applications.

    When using buildbot(some notes in Japanses found here), I hate to use $PYTHONPATH. You can not package it clean. 'Clean' means you do not need to anything else but svn checkout + setup.py. I think it is evil to do something beside it. You are not AUTOMATED build process if you need to set something by hand.

    Can you change $PYTHONPATH from setup.py? I do not know how, I do not know it is right thing to do.

    On the other hand, I'm very sure that you can use .pth in this case. You can install .pth file with
    What is .pth? Look at documentation of site module(JP).

    package_data = {'One':['One.pth']}

    etc.


    It is still half way. You can not test One/a.py during development.

    some thing like

    /src/One$ python a.py


    .pth solution can't handle this propery. It just too global...
    So, hacking sys.path is right way. Look at concept code is below.

    import sys
    sys.path.append(target dir)

    ok, now we need find target dir.
    first, we must calculate relative thing.
    To get absolute path of doing import,

    import os
    os.path.abspath(__file__)

    Be careful, it give you ".pyc" file instead of ".py".

    What we want to do is:

    in file of src/One/a.py,
    type

    import relative
    relative.imp(dotdot='a', from=__file__)

    to import src/a.py

    And
    type

    import relative
    relative.imp(dotdot='Two.a', from=__file__)

    to import src/Two/a.py

    It looks good idea to define "base path for import" for each imp() call.
    in this case, src/ is base path for import.
    It is very important that keep relative.imp have no side effect on sys.path.
    i.e. imp() has to restore sys.path after import of specified module.


    import os
    import sys
    def imp(**kw):
    path = os.path.abspath(kw["from"])
    module_name = kw["dotdot"]
    base_path = xxx(path, module_name)
    sys.path.append(base_path)
    exec("import %s"%module_name)
    sys.path.remove(base_path)

    implementing xxx is not so though.

    Now, we improve imp().

    import relative
    relative.imp(from='..', module='Two.a', importer=__file__)

    changed arguments and their names. "from" was error prone.
    now "from" means "from" of " from xxx import", not "from" file of where you are looking at.
    we can use os.path.join to generate base_path. Won't be tough. very good.
  • 0 件のコメント: