distcontrib
===========

| Code_ | Bugs_ | Forums_ | License_ | Contact_

.. _Code : http://code.launchpad.net/distcontrib
.. _Bugs : http://bugs.launchpad.net/distcontrib
.. _Forums : http://answers.launchpad.net/distcontrib
.. _License : http://opensource.org/licenses/BSD-3-Clause
.. _Contact : http://launchpad.net/~frgomes

Python package ``distcontrib`` contributes utility functions to Distutils, extending its
functionalities, like integration with Cython build and a launcher for doctest. 

The primary reason for the existence of ``distcontrib`` is making life a lot easier
when you write ``setup.py`` for your projects. You can create a template ``setup.py``
file and simply copy it to all new or existing projects you have, without any 
modification, in most situations. Under the covers, ``distcontrib`` finds several 
bits and pieces about your project and *automagically* configures itself
so that you don't have to adjust your setup.py file every time you create a new
project.


See also: `distcontrib-migrate`_

Usage
-----

This is an example of how your ``setup.py`` would look like::

    #!/usr/bin/env python
        
    from setuptools import find_packages
    from distutils.core import setup
    from Cython.Distutils import build_ext as cython_build
    import distcontrib as du

     ##
    # This block contains settings you will eventually need to change
    ###
    
    import myapp as myapp   #--- adjust to your package name

    PACKAGE      = myapp.pkg_name
    VERSION      = myapp.pkg_version
    DESCRIPTION  = myapp.pkg_description
    LICENSE      = myapp.pkg_license
    URL          = myapp.pkg_url
    AUTHOR       = myapp.pkg_author
    AUTHOR_EMAIL = myapp.pkg_email
    KEYWORDS     = myapp.pkg_keywords
    REQUIREMENTS = myapp.pkg_requirements
    LONG_DESCRIPTION = du.tools.read('README')
    CLASSIFIERS      = [ 'License :: ' + LICENSE,
                         'Operating System :: OS Independent',
                         'Programming Language :: Python',
                         'Programming Language :: Cython',
                         'Development Status :: 3 - Alpha',
                         'Intended Audience :: Developers',
                         'Environment :: Console' ]
    
     ##
    # From this point on, it's unlikely you will be changing anything.
    ###
    
    PACKAGES      = find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"])
    PACKAGES_DATA = du.tools.findall_package_data(PACKAGES)
    EXT_MODULES   = du.tools.find_ext_modules(PACKAGES)
    
    setup(
        name=PACKAGE,
        version=VERSION,
        description=DESCRIPTION,
        url=URL,
        author=AUTHOR,
        author_email=AUTHOR_EMAIL,
        long_description=LONG_DESCRIPTION,
        license=LICENSE,
        keywords=KEYWORDS,
        classifiers=CLASSIFIERS,
        packages=PACKAGES,
        package_data=PACKAGES_DATA,
        cmdclass={ 'build_ext' : cython_build,
                   'doctest'   : du.doctest,
                   'zap'       : du.zap, },
        ext_modules=EXT_MODULES,
        install_requires=REQUIREMENTS
    )

Then create under your ``myapp/__init__.py`` file something like this::

    #!/usr/bin/env python
    
    pkg_name         = __name__ if __package__ is None else __package__
    pkg_description  = 'This application does everything you can imagine'
    pkg_version      = '0.1.0'
    pkg_license      = 'OSI Approved :: BSD License'
    pkg_url          = 'http://' + pkg_name + '.readthedocs.org/'
    pkg_author       = 'Richard Gomes http://rgomes-info.blogspot.com'
    pkg_email        = 'rgomes.info@gmail.com'
    pkg_keywords     = [ 'artificial','intelligence','magic','sorcery','voodoo' ]
    pkg_requirements = [ 'lxml', 'sqlalchemy' ]

Then you can do enter from command line::

    $ python setup.py zap        # clean on steroids
    $ python setup.py doctest    # run your doctests
    $ python setup.py build_ext  # build with Cython


Command *zap* cleans a lot more stuff than command *clean* does, being ideal as a step before committing changes to the source control or creating a backup copy of your working folder.

Command *doctest* runs all doctests, from all your packages. If you find that thre are doctests not being run, please make sure you have created ``__init__.py`` files in all packages.

Special cases
^^^^^^^^^^^^^

In certain circumstances, you may have to guarantee that your ``setup.py`` installs a minumun set of essential requirements which, if not installed, may prevent your ``setup.py`` from running properly. By borrowing function ``install_requirements`` from package ``distcontrib.bootstrap`` and calling it on the top of your ``setup.py``, you can install these essential requirements, as shown below::

    #!/usr/bin/env python
     
    ESSENTIAL = [ 'distribute', 'version', 'Cython', 'distcontrib', 'distcontrib-migrate' ]

    # This function was copied verbatim from distcontrib.bootstrap
    # In certain situations, you are not sure if distcontrib is installed, then
    # makes sense to have this function straight on the top of your setup.py
    def install_requirements(requirements, verbose=True):
        import os, pip
        pip_args = list()
        if verbose:
            print('Installing requirements: ' + str(requirements))
            pip_args.append( '--verbose' )
        proxy = os.environ['http_proxy']
        if proxy:
            pip_args.append('--proxy')
            pip_args.append(proxy)
            if verbose:
                print('http_proxy=' + proxy)
        pip_args.append('install')
        for req in requirements:
            pip_args.append( req )
        pip.main(initial_args = pip_args)


    try:
        from setuptools import find_packages
        from distutils.core import setup
        from Cython.Distutils import build_ext as cython_build
        import distcontrib as du
        import distcontrib_migrate as dm
    except:
        #-- import distcontrib.bootstrap
        #-- distcontrib.bootstrap.install_requirements( ESSENTIAL )
        install_requirements( ESSENTIAL )
 
        # do it again
        from setuptools import find_packages
        from distutils.core import setup
        from Cython.Distutils import build_ext as cython_build
        import distcontrib as du
        import distcontrib_migrate as dm

    ... the rest of your setup.py comes here


Support
-------

  - Bugs: https://bugs.launchpad.net/distcontrib
  - Forums : https://answers.launchpad.net/distcontrib
  - Sources: https://code.launchpad.net/distcontrib


.. _`distcontrib-migrate`: http://distcontrib-migrate.readthedocs.org/

