Commit 2d42f3a8 authored by Wichert Akkerman's avatar Wichert Akkerman

Add ability to define extra zopectl commands via setuptools entrypoints.

parent 92771919
......@@ -11,6 +11,10 @@ http://docs.zope.org/zope2/releases/.
Bugs Fixed
++++++++++
Features Added
++++++++++++++
- Add ability to define extra zopectl commands via setuptools entrypoints.
2.12.11 (2010-09-09)
......
......@@ -153,3 +153,40 @@ Troubleshooting
should already be available.
- See the :doc:`CHANGES` for important notes on this version of Zope.
Adding extra commands to Zope
-----------------------------
It is possible to add extra commands to ``zopectl`` by defining *entry points*
in ``setup.py``. Commands have to be put in the ``zopectl.command`` group:
.. code-block:: python
setup(name="MyPackage",
....
entry_points="""
[zopectl.command]
init_app = mypackage.commands:init_application
""")
.. note::
Due to an implementation detail of ``zopectl`` you can not use a minus
character (``-``) in the command name.
This adds a ``init_app`` command that can be used directly from the commandline::
bin\zopectl init_app
The command must be implemented as a python callable. It will be called with
two parameters: the Zope2 application and a tuple with all commandline
arguments. Here is a basic example:
.. code-block:: python
def init_application(app, args):
print 'Initialisating the application'
......@@ -36,10 +36,13 @@ configuration option default_to_interactive is set to false). Use the
action "help" to find out about available actions.
"""
import csv
import os
import sys
import signal
import pkg_resources
import zdaemon
import Zope2.Startup
......@@ -317,6 +320,63 @@ class ZopeCmd(ZDCmd):
print "debug -- run the Zope debugger to inspect your database"
print " manually using a Python interactive shell"
def __getattr__(self, name):
"""Getter to check if an unknown command is implement by an entry point."""
if not name.startswith("do_"):
raise AttributeError(name)
data=list(pkg_resources.iter_entry_points("zopectl.command", name=name[3:]))
if not data:
raise AttributeError(name)
if len(data)>1:
print >>sys.stderr, "Warning: multiple entry points found for command"
return
func=data[0].load()
if not callable(func):
print >>sys.stderr, "Error: %s is not a callable method" % name
return
return self.run_entrypoint(data[0])
def run_entrypoint(self, entry_point):
def go(arg):
# If the command line was something like
# """bin/instance run "one two" three"""
# cmd.parseline will have converted it so
# that arg == 'one two three'. This is going to
# foul up any quoted command with embedded spaces.
# So we have to return to self.options.args,
# which is a tuple of command line args,
# throwing away the "run" command at the beginning.
#
# Further complications: if self.options.args has come
# via subprocess, it may look like
# ['run "arg 1" "arg2"'] rather than ['run','arg 1','arg2'].
# If that's the case, we'll use csv to do the parsing
# so that we can split on spaces while respecting quotes.
if len(self.options.args) == 1:
tup = csv.reader(self.options.args, delimiter=' ').next()
# Remove -c and add command name as sys.argv[0]
cmd = [ 'import sys',
'sys.argv.pop()',
'sys.argv.append(r\'%s\')' % entry_point.name
]
if len(tup) > 1:
argv = tup[1:]
cmd.append('[sys.argv.append(x) for x in %s]; ' % argv)
cmd.extend([
'import pkg_resources',
'import Zope2',
'func=pkg_resources.EntryPoint.parse(\'%s\').load(False)' % entry_point,
'app=Zope2.app()',
'func(app)',
])
cmdline = self.get_startup_cmd(self.options.python, ' ; '.join(cmd))
self._exitstatus = os.system(cmdline)
return go
def do_run(self, args):
if not args:
print "usage: run <script> [args]"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment