SimpleExampleEgg

Author: Todd Greenwood
Title: SimpleExampleEgg
Keywords: python, egg, setuptools, distutils, tutorial, doctests, unit tests, deploy, install, example
Version: 0.1
License: GPL v. 2
Date: Dec 2005

This aims to be a super simple example of python, setuptools, docutils, unit-tests, and eggs.

Contents

Introduction

Hopefully, this will clarify, in my own mind, at least, how to make super simple apps that have all sorts of cool extras such as working unit tests, launch scripts, deploy and install flawlessly...etc. Ideally, I'll be able to write apps that 'just work'. So my end users can just point some tool (easy_install) at a file (an egg) and voila, a working app.

Here's what I'd like to have happen for this exercise:

Along the way, I'll play around with:

Note, this documentation was written using Mikolaj Machowski's Vim reStructured Text plugin (vst). It is the coolest thing since the intraweb.

Basic Source Files

  1. dir listing:

    apple.py
    docs
    __init__.py
    orange.py
    simpletests.py
    

  2. apple.py:

    import sys
    
    def make_pie(who):
    	"""
    >>> import apple as a
    >>> a.make_pie('Todd')
    'Todd likes pie!!!'
    	"""
    	return '%s likes pie!!!'%who
    #
    def _test():
    	import doctest
    	return doctest.testmod()
    #
    if __name__ == "__main__":
        _test()
        if len(sys.argv)>1: print make_pie(sys.argv[1])
        

  3. Let's run this tiny app to see what we get:

    $ python apple.py Mr.Guido

    Mr.Guido likes pie!!!
    

  4. setup.py:

    from ez_setup import use_setuptools
    use_setuptools()
    from setuptools import setup, find_packages
    #
    setup(name = "SimpleExampleEgg",
        version = "0.1",
        description = "test",
        author = "Todd Greenwood",
        author_email = "t.greenwoodgeer@gmail.com",
        url = "http://www.angelfire.com/planet/tango",
    	entry_points = {'console_scripts': [
    		'make_apple_pie = fruit.apple.make_pie'
    		]},
        packages = find_packages(exclude=['ez_setup'] ),
    	package_data = {'':['docs/*.html', 'docs/*.rest','docs/*.vim']},
        test_suite = 'fruit.simpletests.getTestSuite',
        license = "GNU Lesser General Public License",
        classifiers = [
            "Development Status :: 1 - Alpha",
            "Intended Audience :: Developers",
            "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
            "Programming Language :: Python",
            "Topic :: Database :: Front-Ends",
        ],
        zip_safe=True,
        )
        

  5. simpletests.py:

    import apple
    import unittest
    import doctest
    #
    def getTestSuite():
    	suite = unittest.TestSuite()
    	for mod in apple,:
    	    suite.addTest(doctest.DocTestSuite(mod))
    	return suite
    #
    runner = unittest.TextTestRunner()
    runner.run(getTestSuite())
        

Tests and Building

  1. Let's run the test suite:

    $ python simpletests.py

    .
    ----------------------------------------------------------------------
    Ran 1 test in 0.012s
    
    OK
    

  2. Now, let's run the test suite from setuptools:

    $ python setup.py test

    running test
    running egg_info
    writing ./SimpleExampleEgg.egg-info/PKG-INFO
    writing top-level names to ./SimpleExampleEgg.egg-info/top_level.txt
    writing entry points to ./SimpleExampleEgg.egg-info/entry_points.txt
    running build_ext
    .
    ----------------------------------------------------------------------
    Ran 1 test in 0.012s
    
    OK
    Doctest: fruit.apple.make_pie ... ok
    
    ----------------------------------------------------------------------
    Ran 1 test in 0.001s
    
    OK
    

  3. Next, let's make sure the build system works. But first, I should mention that this documentation is generated via a vim pluggin.

Building the REST docs

  1. So, while from a command line you would execute:

    $ python setup bdist_egg

    Because this doc lives in ./fruit/docs/readme.rest, I will use the pbu utility to walk the directory tree and launch setup.py. If you would like to build the docs, here is the process:

    1. install pbu:

       $ easy_install buildutils
      
    2. build the egg using the 'pbu' utility:

       $ cd ./fruit/docs
       $ pbu dbist_egg
      
  2. So that's exactly what the docs do. So, to see all this in action, build the docs :

     $ vim readme.rest -s runvim.vim
    
     BTW - runvim.vim is just a quickie script to execute :Vsti within vim,
     write, save, and exit vim.
    
  3. Back to the build system, let's do a build :

    running bdist_egg
    running egg_info
    writing ./SimpleExampleEgg.egg-info/PKG-INFO
    writing top-level names to ./SimpleExampleEgg.egg-info/top_level.txt
    writing entry points to ./SimpleExampleEgg.egg-info/entry_points.txt
    installing library code to build/bdist.linux-i686/egg
    running install_lib
    warning: install_lib: 'build/lib' does not exist -- no Python modules to install
    creating build/bdist.linux-i686/egg
    creating build/bdist.linux-i686/egg/EGG-INFO
    copying ./SimpleExampleEgg.egg-info/PKG-INFO -> build/bdist.linux-i686/egg/EGG-INFO
    copying ./SimpleExampleEgg.egg-info/top_level.txt -> build/bdist.linux-i686/egg/EGG-INFO
    copying ./SimpleExampleEgg.egg-info/entry_points.txt -> build/bdist.linux-i686/egg/EGG-INFO
    creating 'dist/SimpleExampleEgg-0.1-py2.4.egg' and adding 'build/bdist.linux-i686/egg' to it
    removing 'build/bdist.linux-i686/egg' (and everything under it)
    

  4. And lastly, let's check to make sure that we have what we expect in the output:

    ``unzip -l /home/tgreenwo/active/SimpleExampleEgg/dist/SimpleExampleEgg-0.1-py2.4.egg'``

    Archive:  /home/tgreenwo/active/SimpleExampleEgg/dist/SimpleExampleEgg-0.1-py2.4.egg
      Length     Date   Time    Name
     --------    ----   ----    ----
          290  12-14-05 12:11   fruit/orange.py
          286  12-14-05 12:11   fruit/apple.py
            0  12-14-05 12:13   fruit/__init__.py
          240  12-14-05 12:26   fruit/simpletests.py
          556  12-14-05 14:16   fruit/orange.pyc
          625  12-14-05 14:16   fruit/apple.pyc
          124  12-14-05 14:16   fruit/__init__.pyc
          537  12-14-05 14:16   fruit/simpletests.pyc
        14863  12-14-05 12:36   fruit/docs/traditional.css
         6051  12-14-05 13:30   fruit/docs/readme.rest
        12978  12-14-05 13:07   fruit/docs/readme.html
         6464  12-14-05 12:43   fruit/docs/default.css
        14140  12-14-05 12:36   fruit/docs/oldstyle.css
          141  12-14-05 12:36   fruit/docs/vstrc.vim
          532  12-14-05 14:16   EGG-INFO/PKG-INFO
            6  12-14-05 14:16   EGG-INFO/top_level.txt
            0  12-14-05 14:16   EGG-INFO/zip-safe
     --------                   -------
        57833                   17 files
    

Backtracking

Some things I should mention....

The ez_setup.py file

  1. Per the Peak website, install ez_setup.py as an svn external thingy. Here are the steps:

     $ cd SimpleExampleEgg/
     $ svn propedit svn:externals .
     # enter this in the editor: 
     ez_setup svn://svn.eby-sarna.com/svnroot/ez_setup
     # slurp down the latest ez_setup file  
     $ svn update
     # you should see the ez_setup dir now..., if you don't, try
     $ svn propget svn:externals
     ez_setup svn://svn.eby-sarna.com/svnroot/ez_setup
    

1. TODO: figure out how to link from project to project in svn, so i only have one project that has to source to the web.

Create a PiPy account:

  1. this part is easy:

     $ python setup.py register
    
  2. now just follow the instructions..:

     running register
     We need to know who you are, so please choose either:
     1. use your existing login,
     2. register as a new user,
     3. have the server generate a new password for you (and email it to you),
       or
     4. quit
     Your selection [default 1]:
    

App Deployment

Now, we want to do your basic 'app' stuff:

* script generation * build * deploy * test

Script Generation

The idea here is that if I have some cool command line app, then how do I access it when it's all tucked inside an egg? The generated wrapper scripts do just that.

  1. add entry points to setup.py:

     setup(
     ...
     entry_points = {'console_scripts': [
         'make_apple_pie = fruit.apple.make_pie'
         ]},
    

Building

We did this already, but, we'll do it again here.

  1. Build the app into an egg:

    $ python setup bdist_egg

    running bdist_egg
    running egg_info
    writing ./SimpleExampleEgg.egg-info/PKG-INFO
    writing top-level names to ./SimpleExampleEgg.egg-info/top_level.txt
    writing entry points to ./SimpleExampleEgg.egg-info/entry_points.txt
    installing library code to build/bdist.linux-i686/egg
    running install_lib
    warning: install_lib: 'build/lib' does not exist -- no Python modules to install
    creating build/bdist.linux-i686/egg
    creating build/bdist.linux-i686/egg/EGG-INFO
    copying ./SimpleExampleEgg.egg-info/PKG-INFO -> build/bdist.linux-i686/egg/EGG-INFO
    copying ./SimpleExampleEgg.egg-info/top_level.txt -> build/bdist.linux-i686/egg/EGG-INFO
    copying ./SimpleExampleEgg.egg-info/entry_points.txt -> build/bdist.linux-i686/egg/EGG-INFO
    creating 'dist/SimpleExampleEgg-0.1-py2.4.egg' and adding 'build/bdist.linux-i686/egg' to it
    removing 'build/bdist.linux-i686/egg' (and everything under it)
    

  2. And lastly, let's check to make sure that we have what we expect in the output:

    ``unzip -l /home/tgreenwo/active/SimpleExampleEgg/fruit/docs/dist/SimpleExampleEgg-0.1-py2.4.egg'``

    Archive:  /home/tgreenwo/active/SimpleExampleEgg/fruit/docs/dist/SimpleExampleEgg-0.1-py2.4.egg
      Length     Date   Time    Name
     --------    ----   ----    ----
          532  12-14-05 14:54   EGG-INFO/PKG-INFO
            1  12-14-05 14:54   EGG-INFO/top_level.txt
           57  12-14-05 14:54   EGG-INFO/entry_points.txt
            0  12-14-05 14:54   EGG-INFO/zip-safe
     --------                   -------
          590                   4 files
    

Deploy Locally (Dev Mode)

  1. create a deployment directory to house the egg links:

     $ cd working
     $ mkdir deploy
    
  2. add the 'deploy' directory to your pythonpath:

    		export PYTHONPATH=/home/tgreenwo/working/twisted:/home/tgreenwo/working/django:/home/tgreenwo/working/deploy
    

  3. install the egg locally in dev mode:

    $ python setup.py develop

    running develop
    running egg_info
    writing ./SimpleExampleEgg.egg-info/PKG-INFO
    writing top-level names to ./SimpleExampleEgg.egg-info/top_level.txt
    writing entry points to ./SimpleExampleEgg.egg-info/entry_points.txt
    running build_ext
    Creating /usr/lib/python2.4/site-packages/SimpleExampleEgg.egg-link (link to .)
    error: /usr/lib/python2.4/site-packages/SimpleExampleEgg.egg-link: Permission denied
    

  4. this time, we specify the deploy directory:

    $ python setup.py develop -d /home/tgreenwo/working/deploy

    running develop
    running egg_info
    writing ./SimpleExampleEgg.egg-info/PKG-INFO
    writing top-level names to ./SimpleExampleEgg.egg-info/top_level.txt
    writing entry points to ./SimpleExampleEgg.egg-info/entry_points.txt
    running build_ext
    Creating /home/tgreenwo/data/working/deploy/SimpleExampleEgg.egg-link (link to .)
    Installing make_apple_pie script to /home/tgreenwo/working/deploy
    
    Installed /home/tgreenwo/active/SimpleExampleEgg/fruit/docs
    
    Because this distribution was installed --multi-version or --install-dir,
    before you can import modules from this package in an application, you
    will need to 'import pkg_resources' and then use a 'require()' call
    similar to one of these examples, in order to select the desired version:
    
        pkg_resources.require("SimpleExampleEgg")  # latest installed version
        pkg_resources.require("SimpleExampleEgg==0.1")  # this exact version
        pkg_resources.require("SimpleExampleEgg>=0.1")  # this version or higher
    
    Processing dependencies for SimpleExampleEgg==0.1
    

Deploy to my ftp web site

  1. display passwords *******NOT TO BE PUBLISHED*********

    url {readbang:cat /home/tgreenwo/active/SimpleExampleEgg/fruit/docs/passwords | grep -v # | awk '{print $1}'}

    username {readbang:cat /home/tgreenwo/active/SimpleExampleEgg/fruit/docs/passwords | grep -v # | awk '{print $2}'}

    password {readbang:cat /home/tgreenwo/active/SimpleExampleEgg/fruit/docs/passwords | grep -v # | awk '{print $3}'}

Deploy To pipy

Install the app

Download

Install

Run Unit Tests