What's new? | Help | Directory | Sign in
Google
hotwire-shell
An object-oriented hypershell - Developer page
  
  
  
  
    
Search
for
Updated Jun 20, 2008 by dmishd
Labels: Featured
ExtendingHotwire  
Ideas for ways in which people may want to extend Hotwire

Extending Hotwire with custom Python

Hotwire's extension support is fairly basic right now; essentially, it allows you to place arbitrary Python code in a file (ending in .py) in ~/.hotwire/plugins/.

Adding environment variables

Create a file, e.g.: ~/.hotwire/plugins/myenv.py.

import os
os.environ['JAVA_HOME'] = '/usr/java/jdk1.5.0_13'

Then in Hotwire:

py-eval -f ~/.hotwire/plugins/myenv.py

Creating an alias

from hotwire.cmdalias import AliasRegistry
# If you want your alias to expand to a system command, you must specify sys
AliasRegistry.getInstance().insert('make', 'sys make -j 3')
AliasRegistry.getInstance().insert('la', 'ls -a')

Adding a new builtin

You can also create and register new builtins. Looking at existing ones such as LsBuiltin (ls) is a good way to learn by example. You can also look at existing ExternalPlugins .

Also, below is a heavily-commented sample file called fruit.py which demonstrates many of Hotwire's current extension points:

## A sample Hotwire extension showing how to create new objects, builtins,
## renderers, and completers.

import os, sys, stat

import gtk

from hotwire.builtin import Builtin, BuiltinRegistry
from hotwire.completion import Completion,Completer
from hotwire_ui.render import ClassRendererMapping, TreeObjectsRenderer, menuitem

## This is an arbitrary Python object we define here.  It has no dependency or
## relation to Hotwire - your builtin may reuse an object from the Python system
## library, or one that already exists in the Hotwire core (like FilePath).
class Fruit(object):
  flavor = property(lambda self: self._flavor)
  def __init__(self, flavor):
    ## Our object has a single property, named "flavor".
    self._flavor = flavor

## You can fairly easily subclass your own objects from Completer.  Here 
## is one which completes two values.  For more completion samples,
## see hotwire/completion.py in the source tree.
class FruitCompleter(Completer):
  def __init__(self):
    super(FruitCompleter, self).__init__()

  def completions(self, text, cwd, **kwargs):
    for name in ['apple', 'orange']:
      if name.startswith(text):
        yield Completion(name[len(text):], Fruit(name), name)

## A Builtin is the Hotwire term for a Python command.  Here is one which constructs
## fruit objects based on the strings passed to it (note the completer above yielded
## strings, not objects).
## For more about builtins, see hotwire/builtin.py.
class FruitBuiltin(Builtin):
  """A demo builtin that shows you fruit."""
  def __init__(self):
    super(FruitBuiltin, self).__init__('fruit',
                                       ## The output argument is very important; it
                                       ## tells Hotwire what type of object your
                                       ## builtin outputs.
                                       output=Fruit,
                                       ## This argument tells Hotwire our command doesn't
                                       ## have any side effects, which will be useful later
                                       ## when more commands implement undo.
                                       idempotent=True)

  ## We override get_completer to tell Hotwire to use our FruitCompleter.
  def get_completer(self, context, args, i):
    return FruitCompleter()

  ## The execute method gets a "context" object which has attributes such as "cwd" for
  ## the current working directory, "input" for an iterable queue of the input side of
  ## the pipeline, and more (see hotwire/command.py).  
  def execute(self, context, args):
    for flavor in args:
      ## Notice that Hotwire builtins are normally generators (http://www.python.org/dev/peps/pep-0255/).
      yield Fruit(flavor)

## You must call this function to tell Hotwire about your builtin class.  Note that all
## builtins are singletons - you can easily store per-execution state as local variables,
## and the "context" object has an "attribs" dictionary which may also be convenient.
BuiltinRegistry.getInstance().register_user(FruitBuiltin())

## We need to tell Hotwire how to display objects of class Fruit.  In the future,
## Hotwire will gain a more intelligent default renderer which will allow you to explore
## properties, etc.  This renderer subclasses TreeObjectsRenderer which is GtkTreeView
## based.
class FruitRenderer(TreeObjectsRenderer):
    def _setup_view_columns(self):
        ## This single line tells Hotwire to add a column with a title "Flavor", displaying
        ## the "flavor" attribute of the Fruit objects.
        self._insert_propcol('flavor', title='Flavor', ellipsize=False)

    ## This decorator tells Hotwire to create a right-click menu item with that name.
    @menuitem()
    def eat(self, iter):
        fruit = self._model.get_value(iter, 0)
        dlg = gtk.MessageDialog(message_format='You ate %s!' % (fruit.flavor,))
        dlg.run()

## Finally, tell Hotwire to use our FruitRenderer for objects of type Fruit.
ClassRendererMapping.getInstance().register(Fruit, FruitRenderer)

To play with this, save it to ~/.hotwire/plugins/fruit.py. You can load it at runtime with py-eval -f ~/.hotwire/plugins/fruit.py, or restart Hotwire.

Future plans include a Firefox-style extension mechanism.

That's it for now; for hacking Hotwire further, you should probably just get a checkout out of the source tree and dive into HotwireDevelopment.


Sign in to add a comment