[Zope-Checkins] SVN: Zope/branches/2.12/ Add ability to define extra zopectl commands via setuptools entrypoints.
Wichert Akkerman
wichert at wiggy.net
Thu Sep 9 08:54:45 EDT 2010
Log message for revision 116267:
Add ability to define extra zopectl commands via setuptools entrypoints.
Changed:
U Zope/branches/2.12/doc/CHANGES.rst
U Zope/branches/2.12/doc/operation.rst
U Zope/branches/2.12/src/Zope2/Startup/zopectl.py
-=-
Modified: Zope/branches/2.12/doc/CHANGES.rst
===================================================================
--- Zope/branches/2.12/doc/CHANGES.rst 2010-09-09 12:54:18 UTC (rev 116266)
+++ Zope/branches/2.12/doc/CHANGES.rst 2010-09-09 12:54:45 UTC (rev 116267)
@@ -11,8 +11,12 @@
Bugs Fixed
++++++++++
+Features Added
+++++++++++++++
+- Add ability to define extra zopectl commands via setuptools entrypoints.
+
2.12.11 (2010-09-09)
--------------------
Modified: Zope/branches/2.12/doc/operation.rst
===================================================================
--- Zope/branches/2.12/doc/operation.rst 2010-09-09 12:54:18 UTC (rev 116266)
+++ Zope/branches/2.12/doc/operation.rst 2010-09-09 12:54:45 UTC (rev 116267)
@@ -153,3 +153,40 @@
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'
+
+
Modified: Zope/branches/2.12/src/Zope2/Startup/zopectl.py
===================================================================
--- Zope/branches/2.12/src/Zope2/Startup/zopectl.py 2010-09-09 12:54:18 UTC (rev 116266)
+++ Zope/branches/2.12/src/Zope2/Startup/zopectl.py 2010-09-09 12:54:45 UTC (rev 116267)
@@ -36,10 +36,13 @@
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 @@
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]"
More information about the Zope-Checkins
mailing list