Extending Bravo

Bravo is designed to be highly extensible. This document is a short guide to the basics of writing extension code for Bravo.

Asynchronous Ideas

Bravo, being built on Twisted, has inherited most of the concepts of asynchronous control flow from Twisted, and uses them liberally. Nearly every plugin method is permitted to return a Deferred in place of their actual return value.

Exocet and You

Bravo uses a library called Exocet to help it with plugin discovery. Exocet is a remarkably powerful library which customizes the way imports are done. Instead of importing plugins by name, or package, Exocet can be asked to load a plugin. When Exocet loads plugins, all import statements in the plugin are transformed to go through Exocet, so Exocet (and by extension, Bravo) can modify what your plugins import.

So, what does this mean for you, the plugin author? Well, there are a few things to keep in mind...


Exocet can blacklist imports, preventing them from actually happening and keeping your plugin from loading. Bravo uses this ability to blacklist a smattering of standard library modules from plugins.

Some of these blacklisted modules are chosen for security reasons, while others are chosen because they will cause slow or buggy behavior. If you think you absolutely need one of these modules, consider carefully whether the listed reason for it being on the blacklist is relevant and reasonable.

The following modules are blacklisted becuse they can be used to crash the server:

  • ctypes

The following modules are blacklisted because they can be used to examine the internals of the server or bypass Exocet’s protections:

  • gc
  • imp
  • inspect

The following modules are blacklisted because they conflict with, or are slow compared to, Twisted’s own systems:

  • asyncore
  • multiprocessing
  • socket
  • subprocess
  • thread
  • threading


Exocet supports parameterization of imports. Specifically, imports of modules which don’t actually exist can be rewritten to provide faked, or synthetic, modules. For an example, consider the following snippet of code:

from bravo.parameters import example

This snippet brings the example name into the global namespace for the module, obviously, but what might not be obvious is that bravo.parameters doesn’t actually exist! It is a synthetic module created by the plugin loader.

A word of warning: If the plugin loader decides not to offer any parameters to plugins, then your plugin will not load at that time. This is important because it means that you probably should not try to do things like from bravo import parameters. Import exactly the names you need to import; don’t have imports which do nothing.

Of course, if you want to have a name available, but it is ultimately optional, the following is legal and works fine:

    from bravo.parameters import example
except ImportError:
    example = None

The following parameters might be available:

  • factory: The factory owning this instance of the plugin.

The Flexibility of Commands

Bravo’s command interface is designed to feel like a regular class instead of a specialized plugin, while still providing lots of flexibility to authors. Let’s look at a simple plugin:

class Hello(object):
    Say hello to the world.


    def chat_command(self, username, parameters):
        greeting = "Hello, %s!" % username
        yield greeting

    name = "hello"
    aliases = tuple()
    usage = ""

This command is a simple greeter which merely echoes a salutation to its caller. It is an IChatCommand, so it only works in the in-game chat, but that should not be a problem, since there is an internal, invisible adaptation from IChatCommand to IConsoleCommand. This means that chat commands are also valid console commands, without any action on your part! Pretty cool, huh?

So, how does this plugin actually work? Well, nearly every line of this plugin is required. The first thing you’ll notice is that this plugin has a class docstring. Docstrings on commands are required; the docstring is used to provide help text. As with all chat commands, this plugin implements(IChatCommand), which lets it be discovered as a command.

The plugin implements the required chat_command(username, parameters), which will be called when a player uses the command. An interesting thing to note is that this plugin yields its return value; commands may return any iterable of lines, including a generator!

Finally, the plugin finishes with more required interface attributes: a name which will be used to call the command, a (possibly empty) list of aliases which can also be used to call the command, and a (possibly empty) usage string.

Project Versions

Table Of Contents

Previous topic


Next topic


This Page