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/. ...@@ -11,6 +11,10 @@ http://docs.zope.org/zope2/releases/.
Bugs Fixed Bugs Fixed
++++++++++ ++++++++++
Features Added
++++++++++++++
- Add ability to define extra zopectl commands via setuptools entrypoints.
2.12.11 (2010-09-09) 2.12.11 (2010-09-09)
......
...@@ -153,3 +153,40 @@ Troubleshooting ...@@ -153,3 +153,40 @@ Troubleshooting
should already be available. should already be available.
- See the :doc:`CHANGES` for important notes on this version of Zope. - 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 ...@@ -36,10 +36,13 @@ configuration option default_to_interactive is set to false). Use the
action "help" to find out about available actions. action "help" to find out about available actions.
""" """
import csv
import os import os
import sys import sys
import signal import signal
import pkg_resources
import zdaemon import zdaemon
import Zope2.Startup import Zope2.Startup
...@@ -317,6 +320,63 @@ class ZopeCmd(ZDCmd): ...@@ -317,6 +320,63 @@ class ZopeCmd(ZDCmd):
print "debug -- run the Zope debugger to inspect your database" print "debug -- run the Zope debugger to inspect your database"
print " manually using a Python interactive shell" 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): def do_run(self, args):
if not args: if not args:
print "usage: run <script> [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