Skip to content

Commit

Permalink
Merge pull request #83 from tonyseek/export-plugin-mechanism
Browse files Browse the repository at this point in the history
Load exporting formats as entry points.
  • Loading branch information
nickstenning committed Sep 20, 2014
2 parents d382ef9 + f16b964 commit a7d5c1a
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 17 deletions.
36 changes: 36 additions & 0 deletions doc/export.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,39 @@ When exporting to upstart, each process gets its own .conf file.
::

$ honcho export -c web=1,worker=2,worker_low=1 -a honcho upstart /etc/init


Adding New Format Support
-------------------------

You can support new exporting formats by writing plugins. Honcho discovers
exporting plugins with the `entry points mechanism`_ of setuptools.

First, you need to write a class inherited from :class:`honcho.export.base.BaseExport`
and override the :meth:`~honcho.export.base.BaseExport.render` method. Inside
the ``render`` method, ``self.get_template('foo.html', __package__, 'data/templates')``
could be used to find your Jinja2 template files. There are some exists
exporting classes (e.g. :class:`honcho.export.upstart.Export`) which could be
consulted.

Next, you can create a ``setup.py`` file for building distribution package, and
specify the prepared exporting classes in the ``entry_points`` section. For
example::

from setuptools import setup

setup(
name='honcho-foo',
...
entry_points={
'honcho_exporters': [
'honcho_foo.export.foo:FooExport',
'honcho_foo.export.foobar:FooBarExport',
],
},
)

After installed, the format supporting by the new exporting implementation can
be discovered by the ``honcho export`` command.

.. _`entry points mechanism`: https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins
15 changes: 7 additions & 8 deletions honcho/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
import sys
from collections import defaultdict
from pkg_resources import iter_entry_points

from honcho import __version__
from honcho.process import Popen
Expand All @@ -16,10 +17,10 @@
level=logging.INFO)
log = logging.getLogger(__name__)

PATH = os.path.dirname(__file__)
BASENAME = os.path.basename(os.getcwd())

EXPORT_CHOICES = ['supervisord', 'upstart']
export_choices = dict((_export.name, _export)
for _export in iter_entry_points('honcho_exporters'))

try:
# Python 3
Expand Down Expand Up @@ -93,10 +94,8 @@ def command_export(args):
env = _read_env(procfile_path, args.env)
concurrency = _parse_concurrency(args.concurrency)

mod = __import__('.'.join(['honcho', 'export', args.format]),
fromlist=['Export'])

export = mod.Export(procfile, args, env, concurrency)
export_class = export_choices[args.format].load()
export = export_class(procfile, args, env, concurrency)
export.export()

parser_export = subparsers.add_parser(
Expand Down Expand Up @@ -126,11 +125,11 @@ def command_export(args):
parser_export.add_argument(
'location',
help="folder to export to",
default=EXPORT_CHOICES[0], type=str, metavar="LOCATION")
type=str, metavar="LOCATION")
parser_export.add_argument(
'format',
help="format in which to export",
default=EXPORT_CHOICES[0], choices=EXPORT_CHOICES,
choices=list(export_choices),
type=str, metavar="FORMAT")


Expand Down
20 changes: 16 additions & 4 deletions honcho/export/base.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from __future__ import print_function

import os
import pwd
import sys
from honcho.command import CommandError, PATH
from pkg_resources import resource_filename

from honcho.command import CommandError

try:
from jinja2 import Template
Expand Down Expand Up @@ -60,9 +63,18 @@ def _write(self, filename, content):
raise CommandError("Can not write to file {0}"
.format(path))

def get_template(self, name):
path = os.path.join(PATH, 'data/export/', self.options.format, name)

def get_template(self, name, package, directory='data/export/'):
"""Gets a Jinja2 template from specified directory.
:param name: the name of specified template file.
:param package: the top-level package for located the template
directory.
:param directory: the template directory which contains the template
file.
:returns: a :class:`jinja2.Template` instance.
"""
relative_path = os.path.join(directory, self.options.format, name)
path = resource_filename(package, relative_path)
try:
return Template(open(path).read())
except IOError:
Expand Down
3 changes: 2 additions & 1 deletion honcho/export/supervisord.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ def render(self, procfile, options, environment, concurrency):
'concurrency': concurrency
}
filename = "{0}.conf".format(options.app)
content = self.get_template("supervisord.conf").render(context)
template = self.get_template('supervisord.conf', package='honcho')
content = template.render(context)
return [(filename, content)]
11 changes: 8 additions & 3 deletions honcho/export/upstart.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,28 @@ def render(self, procfile, options, environment, concurrency):
'shell': options.shell
}

master_template = self.get_template('process_master.conf',
package='honcho')
process_template = self.get_template('process.conf', package='honcho')
app_template = self.get_template('master.conf', package='honcho')

for name, cmd in compat.iteritems(procfile.processes):
ctx = context.copy()
ctx.update({'command': cmd,
'name': name})

master = "{0}-{1}.conf".format(options.app, name)
master_content = self.get_template("process_master.conf").render(ctx)
master_content = master_template.render(ctx)
files.append((master, master_content))

for num in compat.xrange(1, concurrency[name] + 1):
ctx.update({'num': num})
process = "{0}-{1}-{2}.conf".format(options.app, name, num)
process_content = self.get_template("process.conf").render(ctx)
process_content = process_template.render(ctx)
files.append((process, process_content))

app = "{0}.conf".format(options.app)
app_content = self.get_template("master.conf").render(context)
app_content = app_template.render(context)

files.append((app, app_content))

Expand Down
6 changes: 5 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
entry_points={
'console_scripts': [
'honcho=honcho.command:main'
]
],
'honcho_exporters': [
'upstart=honcho.export.upstart:Export',
'supervisord=honcho.export.supervisord:Export',
],
}
)

0 comments on commit a7d5c1a

Please sign in to comment.