Define imports and Python project structure to allow running locally and when installed
Question: I am working on a Python project and started using a setup.py file to install
it. I followed some guides on how to structure the project and everything is
working great after installing it.

My problem is that I "lost" the ability to run my application from inside the
project's directory, locally so to speak without installing it, for testing
purposes. Here's how the directory layout looks like:

    
    
    project_dir/
    ├── bin
    │   └── app.py
    ├── data
    │   └── data.conf
    ├── app_module
    │   └── __init__.py
    ├── LICENSE
    ├── README.md
    └── setup.py
    

The problem has to do with the imports. In `app.py` I have `import app_module`
and it works as intended after it gets installed to the proper directory by
running `python setup.py install`. If I want to run it by doing `python
bin/app.py` it obviously doesn't work.

What am I doing wrong and how could I get this to work? Ideally I want to be
able to run it from inside the directory and it should still work when
installed with the `setup.py` script.


Answer: if your `bin/app.py` is only doing:

    
    
    import app_module
    app_module.run() # or whatever you called the main() function
    

you could (should!) use an [`entry_points` (search for
`console_scripts`)](https://pythonhosted.org/setuptools/setuptools.html#dynamic-
discovery-of-services-and-plugins).

And while running your uninstalled code, you would do either:

`python -m app_module` or `python app_module/__init__.py` (assuming you have a
`__main__` section inside this file, otherwise look at how
[`json.tool`](https://hg.python.org/cpython/file/62235755609f/Lib/json/tool.py)
is doing.

## Edit

Here is a basic example, assuming your library is named "app_module".

### setup.py

    
    
    import imp
    import os
    import setuptools
    
    module_name = 'app_module'
    module = imp.load_module(
        module_name,
        *imp.find_module(module_name, [os.path.dirname(__file__)])
    )
    
    base = 'data'
    data_files = [
        (
            '/usr/local/share/appdata/' + module_name + root[len(base):],
            [os.path.join(root, f) for f in files]
        ) for root, dirs, files in os.walk(base)
    ]
    
    setuptools.setup(
        name=module_name,
        version=module.__version__,
        classifiers=(
            'Environment :: Console',
            'Operating System :: POSIX :: Linux',
        ),
        packages=(
            module_name,
        ),
        entry_points={
            # the executable will be called 'app' (not 'app.py')
            'console_scripts': ['app = ' + module_name + ':main'],
            # it does not seems unnecessarily complicated to me, it's just three lines
        },
        data_files=data_files,
    )
    

### app_module/__init__.py

    
    
    import optparse
    import sys
    
    __version__ = '1.2.3'
    
    def main():
        parser = optparse.OptionParser(version=__version__)
        parser.add_option('-m', '--man', action='store_true')
    
        opt, args = parser.parse_args()
    
        if opt.man:
            help(__name__)
            sys.exit()
    
        print opt, args
    
    
    if __name__ == '__main__':
        main()
    

### And now...

To run it:

    
    
    python /full-or-replative/path/to/app_module/__init__.py
    # or
    export PYTHONPATH=/full-or-replative/path/to/app_module/
    python -m app_module
    

To install and run it:

    
    
    # test in a virtualenv
    workon test
    python setup.py install
    app -h
    

