views:

140

answers:

1

I have a simple python application where my directory structure is as follows:

  • project/
    • main.py
    • config.py
    • plugins/
      • plugin1
      • plugin2
      • ...

Config.py only loads configuration files, it does not contain any configuration info in itself.

I now want to distribute this program, and I thought I'd use setuptools to do it. The file users are expected to use is main.py, so that one clearly goes into /usr/bin and the rest of the files go into /usr/share/project.

But there's one problem: I would somehow need to tell main.py to look for config.py in the share directory. But I can't really be sure where exactly the share directory is since that's up to setuptools, right?

What's the best practice when distributing Python-based applications?

+3  A: 

setuptools install your package in a location which is reachable from python i.e. you can import it:

import project

the problem raise when you do relative imports instead of absolute imports. if your main.py imports config.py it works because they live in the same directory. when you move your main.py to another location like /usr/bin or another location present in PATH environment variable, python try to import config.py from sys.path and not from your package dir. the solution is to use absolute import:

from project import config

now main.py is "movable".

another solution, which i prefer, is using automatic script creation offered by setuptools.

instead of having your code in a

if __name__ == "__main__":
    # here all your beautiful code

statement, put your code in a function (main could be a good name):

def main():
    # put your code here

if __name__ == "__main__":    # not needed, just in case...
    main()

now modify your setup.py:

setup(
    # ...
    entry_points = {
        "console_scripts": [
            # modify script_name with the name you want use from shell
            # $ script_name [params]
            "script_name": "project.main:main",
        ],
    }
)

that's all. after an install setuptools will create a wrapper script which is callable from shell and that calls your main function. now main.py can live in your project directory and you don't need anymore to move it in a bin/ directory. note that setuptools automatically puts this script in the bin/ directory relative to the installation prefix.

es.

python setup.py install --prefix ~/.local

install your project package in

~/.local/lib/python<version>/site-packages/<package_name>

and your script in

~/.local/bin/<script_name>

so be sure that ~/.local/bin is present in your PATH env.

more info at: http://peak.telecommunity.com/DevCenter/setuptools#automatic-script-creation

mg
I guess I'm stupid, but I don't see how this is relevant? Could you elaborate?
pafcu
i think i misunderstood your question. do you need to import `/usr/share/config.py`? this is not a good way to let the user customize the behavior of your application. a better solution is a per user configuration file like `$HOME/.projectrc` ad maybe a global configuration in `/etc/`. if this is your problem i'll correct my answer.
mg
Yes, the issue is importing config.py. And in the question I specifically say that it does not contain any configuration information, it only contains the implementation to load the actual config files. It is a part of the program, not a configuration file or a plugin.To be honest, I don't fully understand the entry_points thing. It might very well be what I'm looking for, but I find the documentation a bit confusing.
pafcu
ok, maybe i got a better understanding of your question. setuptools install your package in a dir which is present in your PYTHONPATH or one that support ".pth" files. now you can, and i think you should, use `from mypackage import config`. even if "main.py" and "config.py" are in the same directory i think that this a good practice. (are you using `import config` in your "main.py"?). with "console_script" entry point you leave the main.py script in your package and automatically setuptools create a wrapper script that call the function you choose.
mg
OK, now I think I get what you mean. Thanks! If you add that info to the original answer I'll give you an upvote.
pafcu