| Presentation: | Ian Bicking |
|---|
Q: What are Eggs?
Q: What are Eggs?
A: They are a kind of package you download
Q: What are Eggs?
A: No, I mean they are format for installation that pkg_resources knows how to read
Q: What's this pkg_resources ?
Q: What's this pkg_resources ?
A: It's a module for working with Eggs
Q: What's this pkg_resources ?
A: It's in setuptools.
Q: What's this setuptools ?
Q: What's this setuptools ?
A: It builds Eggs.
Q: What's this setuptools ?
A: It's really the package that does all of "this"
Q: What is "this"?
Q: What is "this"?
A: Plugins
Q: What's a plugin?
Q: What's a plugin?
A: Ok, let's stop now
Some of the concepts:
But really it's not that bad. We Love Egg!

From a source distribution::
MyPackage/
MyPackage.egg-info/
requires.txt
top_level.txt
entry_points.txt
mypackage/
__init__.py (etc)
An Egg installed:
site-packages/
MyPackage-1.0-py2.4.egg/
EGG-INFO/
requires.txt (etc)
mypackage/
__init__.py (etc)
Your package is only importable when the Egg directory is on sys.path...
site-packages/
MyPackage-1.0-py2.4.egg/
mypackage/
__init__.py (etc)
site-packages/easy-install.pth adds entries to sys.path -- a list of installed Eggs that are activated by default:
/Users/ianb/co/Paste /Library/Frameworks/Python.framework/Versions/2.4/lib/ ...python2.4/site-packages/RuleDispatch-0.5a0.dev- ...py2.4-macosx-10.3-ppc.egg /Library/Frameworks/Python.framework/Versions/2.4/lib/ ...python2.4/site-packages/PyProtocols-1.0a0-py2.4- ...macosx-10.3-ppc.egg
Metadata files in .egg-info (or EGG-INFO):
Users can add more metadata in .egg-info
Back to the topic of plugins...
Models:
A list of public objects in a distribution/Egg; example setup.py:
setup(...
entry_points="""
[console_scripts]
paster = paste.script.command:run
[paste.app_factory]
test = paste.script.testapp:TestApplication
""")
Objects sorted into "groups":
[console_scripts] are command-line scripts
application
[distutils.commands] points to a disutils command like python setup.py sdist
[python.template.plugin] is a class that represents a templating language
Entry points have "names": paster, sdist, cheetah
Names point to actual Python objects:
paster = paste.script.command:run
Translates (roughly) to:
from paste.script.command import run
Loading an entry point:
import pkg_resources
ob = pkg_resources.load_entry_point(
'Commentary', 'paste.app_factory', 'main')
wsgiapp = ob({}, storage='/tmp')
There are several other functions in pkg_resources for listing and inspecting entry points
Several strategies for finding plugins -
Explicit enumeration: a list of plugins to enable; specified with:
Scan for plugins:
by entry point group -- to find commands for paster help:
for ep in pkg_resources.iter_entry_points(
'paste.global_paster_command'):
print ep.name
by group + name -- to find something that renders Cheetah templates:
for ep in pkg_resources.iter_entry_points(
'python.template.plugin', 'cheetah'):
plugin = ep.load()
Entry points have metadata:
Depedencies can be conditional on the entry point:
[paste.app_factory] main = mypackage.wsgiapp:make_app [web]
[web] is an extra:
setup( ...
extras_require={'web': ['Wareweb']},
)
Plugins do not inject themselves
Injection example (this is bad!):
# Activate a plugin: __import__(plugin_name)
With a bad plugin:
# install self:
import master_application
from plugin_name import listener
master_application.event_listeners.append(
listener.Listener(master_application.app_instance))
Objects loaded as entry points should act, they should not be read
Example:
app_installer = load_entry_point(
'AnApplication', 'paste.app_installer', 'main')
app_installer.install(cmd, output_dir, vars)
Not:
vars = load_entry_point(...) copy_dir(vars.default_templates, output_dir + '/templates') install_database(dbname, vars.initial_db_data)
But I like declarative...
Abstract base classes are one solution:
from appinstaller import AbstractInstaller
class MyInstaller(AbstractInstaller):
default_templates = 'templates/'
initial_db_data = [...]
For people who are even Too Lazy To Use An ABC:
[paste.app_install] mypackage = paste.script.appinstall:Installer
Using that plugin:
ob = pkg_resources.load_entry_point(...)
ob = Installer(MyPackageDist, 'paste.app_install',
'mypackage')
What the ABC looks like...
class Installer(object): ...
def config_content(self, command, vars):
meta_name = 'paste_deploy_config.ini_tmpl'
tmpl = Template(self.dist.get_metadata(meta_name),
searchList=[vars])
return tmpl(**vars)
And another ABC technique...
class Installer(object): ...
def packages(self):
for line in self.dist.get_metadata_lines('top_level.txt'):
line = line.strip()
if not line and not line.startswith('#'):
yield line
def setup_config(self, ...):
for package in self.packages():
try:
mod = import_module(mod_name)
except ImportError:
continue
mod.setup_config(...)
"Paste Deploy" represents one pattern for creating objects, for web applications:
[app:someapp] use = egg:SomePackage#somepoint foo = bar
Means:
ob = pkg_resources.load_entry_point(
'SomePackage', 'paste.app_factory', 'somepoint')
app = ob(global_conf, foo='bar')
I'm probably out of time; I didn't talk about:
For your users to install plugins:
$ easy_install -f http://plugin-index... \ -d plugin_dir \ PluginName
A plugin index is:
Just a page with links to packages
A wiki is insecure!
<a href=".../PackageName-X.Y.tar.gz">... <a href="http://svn-repository#egg=PackageName-dev">...
easy_install:
Now I've petered out, hopefully we didn't get this far.