[Distutils] EasyInstall: suggested usage patterns

Phillip J. Eby pje at telecommunity.com
Wed Jun 15 22:45:50 CEST 2005

At 01:17 PM 6/15/2005 -0500, Ian Bicking wrote:
>Hi -- I'm getting ready to go out of town, so I haven't been able to
>track everything that's gone on in 0.4 (or 0.5 now?), and I might not be
>able to follow up on this conversation very thoroughly, but anyway...
>I feel like we need several suggested ways to use easy_install and eggs,
>given different cases.  This is probably a documentation task; I'm more
>interested in how I *should* use this, rather than how I *can*.  I guess
>that's the same as best practices.  In turn I'd like to echo these best
>practices in any documentation or examples that use easy_install/eggs.
>Anyway, here's some cases:
>* I am developing an application.  I don't have a distutil script for
>the application.

So your application's startup code needs to either require() all the 
libraries you need, or require an egg representing the application, which 
then lists all its dependencies.

To create an egg representing the application, just create a MyApp.egg-info 
directory containing a PKG-INFO with a non-empty version number.  Then you 
can create a depends.txt with your dependencies.

>* I am simultaneously developing distutils-based libraries.  I want to
>edit these during my development process.

Check out their source, run bdist_egg on them, and add their source 
directories to PYTHONPATH or a .pth file.

>* I also have some distutils libraries that I'm not developing, but I
>need to install.

Run 'easy_install whatever' to get them.

>* I also have some libraries laying around that I already installed
>(before easy_install existed).  And some that are from OS packages.

If you have conflicting packages, you'll have to uninstall them.  If 
they're not conflicting, you can create .egg-info directories alongside the 
packages with a PKG-INFO indicating their version numbers, in order for you 
to be able to safely include them in your depends.txt

>* I have other Python stuff on my computer that I don't want to mess up
>because of my ongoing development.

Okay, then you are going to have to use PYTHONPATH or have some kind of 
hook that your application uses to add locations to sys.path.  The built 
eggs, however, can at least be dumped in the same directory as your app's 
startup script.

Also if you're very careful, you can probably do subversion or CVS checkout 
setups such that all your development libraries can live in the same 
directory, with a bunch of .egg-info directories in there as well.  This 
can be a bit tricky to set up, but if you're doing simultaneous development 
of a bunch of libraries, it might be worth the trouble.

>So, given this, what commands do I run?  Where do I put files?

My (evolving) thinking on this is the notion of "project directories", 
where a project directory contains:

  * A setup.py (optional but recommended)
  * A setup.cfg (optional, but useful for configuring easy_install)
  * .egg-info directory and package directory checkouts for all libraries 
I'm developing as part of the project
  * All scripts being developed or used by the project
  * All eggs installed solely for the project's use

>   What if
>I don't have permission to put files in global locations

Use '-d.' and run EasyInstall in the project directory.

>   How do I clean up after myself later?

Wipe out the project directory.

In one of the next phases of EasyInstall development, I plan to add a 
'package' script with various subcommands like 'list', 'upgrade', etc.  I 
was already thinking it would be useful to have a 'package create' command 
that would create a basic project directory for you.

Now that I've thought about your scenario here a bit, I think I'd like to 
also have a 'package develop' that would let you check out packages and 
their .egg-infos (given some kind of configuration of their CVS/SVN 
locations) and add them to your depends.txt if they weren't already mentioned.

I can also envision a variety of tools springing up in package.py for 
manipulating the project environment.

>Later, this becomes...
>* I have developed a web application.  Maybe it also doesn't have a
>distutils script...?

Note that with setuptools, a setup script doesn't have to say much besides 
metadata like name, version, author, etc.  You can use the 
'find_packages()' function to automatically include all packages (perhaps 
excepting those that have been somehow marked as being checked out by 
'package.py develop'), so it's mostly a matter of listing scripts and such.

Note, by the way, that information like author, license, and a number of 
other things could easily be defaulted by a configuration file, too, if set 
in e.g.  the ~/.pydistutils.cfg file on a per-user basis.

>  It could, though currently I don't develop one for
>my web applications.  Also, I sometimes make hot fixes, especially when
>the application is deployed but not yet live.

Surely this could be done by deploying a project directory?

>* How and where should libraries be installed?  How should application
>dependencies be expressed?

Same as before: any directory on sys.path, and via depends.txt

>* Some libraries are internal, and so aren't available from a public
>location.  Maybe on the web with HTTP auth, though I'm more inclined to
>simply keep them in a branch in the private repository.  Or fetch over scp.

Sure; EasyInstall also supports "find_links" pages that list links to 
source archives or eggs, so you can use this technique to access them more 
easily by putting the download pages in your configuration file(s).

>* Should I change my require()s to use a specific version of the
>libraries, so that I don't accidentally upgrade (/break) the application
>when a later application is installed?  How do I manage that process?

That won't prevent breakage, if you end up with a conflict between the 
versions required by multiple components.  Sadly, I have no silver bullet 
for you regarding management.

>* Later a library might be broken and I need to fix it.  Is there
>anything I should do on installation so I can later track who uses what
>library?  Also so I can collect unused versions of libraries.

Commands to do these things should probably be added to the 'package.py' 
script, once it exists.  The information is certainly there.

>* I'd like to distribute some data that doesn't have much (or maybe any)
>code.  This might be a Javascript library, or a skin for the application
>(a bunch of templates and images), or something like that.  Can I
>facilitate that with easy_install?

You need at least a zero-length __init__.py that the data lives alongside, 
but yes.  (i.e., using an otherwise-empty package as a data carrier is fine.)

>Enabling plugins
>* I have library A, and library B.  Library B optionally provides a
>"plugin" to library A, but both are usable in isolation.  Library B
>needs to inject stuff into library A -- i.e., at runtime some code in
>library B enhances library A.  How do I make this work?  How do I make
>library A aware of library B?

I plan to add an 'add_activation_listener()' API to pkg_resources that will 
let library A get callbacks when new eggs are activated (and will also get 
callbacks for the eggs that are already activated when the listener is 
added).  This will let library A ask for files in library B's metadata 
(EGG-INFO/.egg-info) directory, and do any desired automatic 
registration.  Library A will have to define a metadata standard for what 
filename it looks for and what the contents should be.  I expect Paste, 
Twisted, PEAK, and Zope will probably all want to define such standards.

Of course, performance for this is O(nm) where n is the number of listeners 
and m is the number of eggs, but n will usually be small.  However, zipped 
distributions can check for the existence of files with a simple dictionary 
lookup, so performance there will be good.  At some point we'll need to 
start pushing people to make their eggs "compression-ready, for enhanced 
performance", so EasyInstall can be '--zip-ok' by default instead of only 
doing it for packages distributed as .egg files.

>Other People's Code
>* Someone wrote some code I'd like to use.  But it's poorly packaged --
>maybe no setup.py, or maybe a bad one.  For instance, I've decided that
>zpt.sf.net's setup.py is just broken -- you can't use extra_path, no
>package, and provide an __init__.py all at once.  I'd like to write my
>own setup.py, but use that package.  And it's on SF, so I'd like to use
>easy_install to download the package.

Make your setup.py use 'ez_setup' to bootstrap setuptools.  Then create a 
setuptools.package_index.PackageIndex instance, then call its 'download()' 
method to retrieve the desired package, and use 
setuptools.archive_util.extract_archive() to unpack it.  Then, write a flag 
file so that if setup.py is rerun you know you already did all that!  Use 
appropriate directory information in the setup() call so that stuff is 
built from the extracted package, and of course supply correct metadata in 
your parent setup script.  Voila.

>Those are some of the things I'd like to do now -- easy_install doesn't
>have to magically make all of them work wonderfully; if I have to do
>things by hand, keep separate records, write custom code, or whatever,
>that's fine; I just want to know what I should be doing right now for
>each of these cases.  Also, I'm interested in conventions we can define
>so that we all start doing the same thing.

Luckily, it's pretty magically wonderful for most of what you describe, and 
for the rest there mostly already exist plans to take care of 'em.  Mostly, 
the support is still a bit weak on the development side of things, but I 
think we can fix those right up with some "wizards" in package.py to 
perform common operations like creating a package, setting your default 
author name and other data, doing CVS/SVN checkouts, etc.  (And perhaps the 
folks doing Python IDE's will start putting menu items in their systems to 
run these commands, too.)

Also, documentation is really lacking right now, so if folks can contribute 
How-To's, tutorials, and similar material to complement my overview and 
reference docs, it'll help other people start using all this.

More information about the Distutils-SIG mailing list