[TransWarp] Component hierarchy traversal
Radek Kanovsky
rk at dat.cz
Fri Oct 10 11:24:19 EDT 2003
On Wed, Oct 01, 2003 at 02:46:29PM -0400, Phillip J. Eby wrote:
> This issue in general is rather interesting. None of the approaches to
> dealing with it are perfect: it seems there is a distinct tension between
> broad resource discoverability, and lazy creation of those same resources.
I think that genereal solution would require some changes in peak.binding.
Bellow is my solution of problem with special command invocation
without hierarchy traversal. Command here is specialy marked component
method that can be searched and invoked. It relies on explicit command
registration. Every command must have following signature:
someCommand(self, [arg_with_default1 [,arg_with_default2 [...]]])
Application must have component hierarchy root that implements
ICommandRoot interface. Every command registeres itself via
registerCommand binding. CommandRoot has Main method that parses
signatures of commands and creates automaticaly appropriate help message
and allows command invocation by name and optionaly by component
path. Without argument Main outputs commands summary. Components with
registered commands must be binded with uponAssembly flag set to True.
Simple example is at the end.
Regards,
Radek K
from peak.api import binding, adapt, protocols, storage, config
from peak.config.config_components import ConfigurationRoot
import sys
__all__ = ["ICommandRoot", "CommandRoot", "registerCommand"]
class ICommandRoot (config.IConfigurationRoot) :
"""Root component that is able to invoke registered commands."""
def _registerCommand (comp, cmdname, cmd) :
"""Registers command cmd of component comp upon assembly."""
def Main (argv) :
"""Main method."""
class registerCommand (binding.Attribute) :
cmdname = cmd = None
def __init__ (self, **kw) :
if len(kw) != 1 :
raise ValueError("One keyword argument required (name=Command)")
self.cmdname, self.cmd = kw.items()[0]
super(registerCommand, self).__init__(uponAssembly=True)
def computeValue (self, comp, instanceDict, attrName) :
root = adapt(binding.getRootComponent(comp), ICommandRoot)
root._registerCommand(comp, self.cmdname, self.cmd)
return self.cmd
class CommandRoot (ConfigurationRoot) :
protocols.advise(
instancesProvide = [ICommandRoot],
)
def __init__ (self, **kw) :
super(CommandRoot, self).__init__(None, **kw)
def uponAssembly(self, *arg, **kw) :
# XXX Ensure that .ini files are parsed first
self.__instance_offers__
super(CommandRoot, self).uponAssembly(*arg, **kw)
_commandRegistry = binding.Make(dict)
_options = binding.Make(dict)
_internal_options = {"abort":1, "help":1}
def _registerCommand (self, comp, cmdname, cmd) :
fc = cmd.func_code
opts = fc.co_varnames[1:fc.co_argcount]
if cmd.func_defaults and len(opts) != len(cmd.func_defaults) :
raise SyntaxError(
"Command %s of component %s has positional " \
"argument without default value" % (cmd, comp))
for o in opts :
if o in self._internal_options :
raise SyntaxError(
"Argument %s is for internal purpose (%s of %s)" % (
o, cmd, comp))
self._options[o] = 1
cr = self._commandRegistry.setdefault(cmdname, {})
cr[str(binding.getComponentPath(comp))] = (comp, cmd)
def Usage (self) :
sys.stderr.write(
"%s [OPTIONS] [COMMAND [COMP1 [COMP2 [...]]]]\n"
"\n"
"Avalilable commands:\n" % self._argv[0]
)
cmds = self._commandRegistry.keys()
cmds.sort()
for c in cmds :
sys.stderr.write(" %s\n" % c)
sys.stderr.write(
"\n"
"Internal options:\n"
" --abort call abortTransaction finally\n"
" --help prints this help\n"
"\n"
"Command options:\n"
)
for o in self._options.iterkeys() :
sys.stderr.write(" --%s=PYTHON_EXPR\n" % o)
sys.stderr.write("\n")
sys.exit(0)
_argv = binding.Make(lambda s,d,a: sys.argv)
def Main (self) :
import getopt
try :
opts, args = getopt.getopt(self._argv[1:], "",
self._internal_options.keys() +
["%s=" % o for o in self._options.iterkeys()]
)
except getopt.error, e :
sys.stderr.write(str(e) + "\n")
sys.stderr.write("try ``%s --help''\n" % self._argv[0])
sys.exit(1)
abort = False
kw = {}
for o,v in opts :
if o == "--help" :
self.Usage()
elif o == "--abort" :
abort = True
else :
kw[o[2:]] = eval(v)
if not args :
print "Registered commands:"
print
for cmd, cr in self._commandRegistry.iteritems() :
print "Command %r:" % cmd
for p, comp in cr.iteritems() :
print " ", p, " ", comp[0]
print
sys.exit(0)
command = args[0]
paths = args[1:]
if not command in self._commandRegistry :
sys.stderr.write("Command %s not registered\n" % command)
sys.stderr.write("try ``%s --help''\n" % self._argv[0])
sys.exit(1)
if kw.get("verbose") :
print "+"
print "+ START TRANSACTION"
storage.begin(self)
for path, comp_cmd in self._commandRegistry[command].iteritems() :
if paths and path not in paths :
continue
comp, cmd = comp_cmd
fc = cmd.func_code
opts = fc.co_varnames[1:fc.co_argcount]
fkw = dict([(k,v) for k,v in kw.iteritems() if k in opts ])
if kw.get("verbose") :
print "+"
print "+ COMMAND %s/%s(%s)" % (
path, command,
",".join([("%s=%s" % (k,v)) for k,v in fkw.iteritems()]))
cmd(comp, **fkw)
if abort :
if kw.get("verbose") :
print "+"
print "+ ABORT TRANSACTION"
storage.abort(self)
else :
if kw.get("verbose") :
print "+"
print "+ COMMIT TRANSACTION"
storage.commit(self)
if __name__ == '__main__' :
class Foo (binding.Component) :
def cmdFoo (self, arg1=12) :
print '%r.cmdFoo(arg1=%r)' % (self, arg1)
cmdFoo = registerCommand(foo=cmdFoo)
class Bar (Foo) :
def cmdBar (self, arg1=12, arg2=42) :
print '%r.cmdBar(arg1=%r, arg2=%r)' % (self, arg1, arg2)
cmdBar = registerCommand(bar=cmdBar)
class Application (CommandRoot) :
foo = binding.Make(Foo, uponAssembly=True)
bar = binding.Make(Bar, uponAssembly=True)
Application().Main()
More information about the PEAK
mailing list