
Andrew Dalke wrote:
Hi all,
I'm working with the Akara project. It contains a web server. The server loads extensions from a special directory (let's say "$AKARA" for now). An extension can register handlers for URLs. An example extension might look like:
installs to $AKARA/spam_extension.py (note: only .py files are supported; not even .pyc files) ========================= from akara.services import simple_service
import my_spam # This is part of the distribution, and gets put in site-packages
@simple_service("GET", "http://vikings.protocol.id/") def vikings(say=my_spam.DEFAULT_TEXT): return my_spam.vikings(say) =========================
We want people to be able to distribute Akara plugins and install via setup.py. Ideally I would like to say:
from distutils.core import setup from akara.distutils ... I'm not sure what here ...
setup(name="Spam services", package="my_spam", akara_extensions=["spam_extension.py"] )
To clarify, the development/distribution package looks like: $PACKAGE/setup.py $PACKAGE/README $PACKAGE/spam_extensions.py $PACKAGE/my_spam/__init__.py $PACKAGE/my_spam/dramatis_personae.py $PACKAGE/my_spam/cafe.py
and $PACKAGE/spam_extensions.py goes to $AKARA/spam_extensions.py while $PACKAGE/my_spam is copied to site-packages.
The installation does not need to byte-compile spam_extension.py.
It should also include spam_extension.py in any distribution that it makes.
I looked through the documentation and searched for existing examples, but found nothing which does this. The plugins I found used entry_points, and that's an architecture change which I don't think is appropriate for us.
I won't comment on entry_points, as I have never used them. The way I would do it is by having akara distutils extensions, which define in particular a setup function and associated classes. Here is the code: import shutil from distutils.core import setup as _setup from distutils.core import Command from distutils import log # XXX: you will need to handle setuptools (and distribute, and....) # monkey-patching for the below commands from distutils.command.install import install as old_install from distutils.dist import Distribution as _Distribution # Where to install akara extensions AKARA_SITE = '/tmp' def setup(**kw): new_kw = kw.copy() # Handle commands overriding cmdclass = new_kw.get('cmdclass', {}) if 'install' in cmdclass: install = cmdclass['install'] else: install = old_install class my_install(install): sub_commands = install.sub_commands + [ ('install_akara_extensions', lambda x: True) ] class install_akara_extensions(Command): description = "Command to install akara extensions" user_options = [ ('akara-site=', None, 'Akara plugin directory install'), ] def initialize_options(self): self.install_dir = None self.akara_site = None self.outfiles = [] def finalize_options(self): if self.akara_site is None: self.akara_site = AKARA_SITE def run (self): dist = self.distribution for a in dist.akara_extensions: log.info("Installing akara extension %s in %s" % (a, self.akara_site)) shutil.copy(a, self.akara_site) new_cmdclass = {} new_cmdclass.update(cmdclass) new_cmdclass['install'] = my_install new_cmdclass['install_akara_extensions'] = install_akara_extensions new_kw['cmdclass'] = new_cmdclass # Handle overriden distclass if not 'distclass' in new_kw: Distribution = new_kw['distclass'] else: Distribution = _Distribution class MyDistribution(Distribution): def __init__(self, attrs=None): assert not hasattr(self, 'akara_extensions') if 'akara_extensions' in attrs: self.akara_extensions = attrs['akara_extensions'] else: self.akara_extensions = [] Distribution.__init__(self, attrs) new_kw['distclass'] = MyDistribution return _setup(**new_kw) setup(name="Spam services", packages=["my_spam"], akara_extensions=["spam_extension.py"] ) This is the minimal code to support this semi correctly. Note that many things are not handled correctly: - get_output for install_akara_extensions should be handled - most likely it will break for semi-random distutils extensions, including setuptools and distribute: you will need to detect monkey-patching to decide which Distribution and install commands to override. I have not done it because it requires the extensions to be in a separate package (to conditionally import things depending on detected monkey patching). If you think this is insane, you are not alone :) David