bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
7204.1.5
by Jelmer Vernooij
Import plugin-development.txt. |
1 |
Developing a plugin |
2 |
=================== |
|
3 |
||
4 |
Naming |
|
5 |
------ |
|
6 |
||
7 |
By convention, most plugins are named brz-xxx and are installed into a |
|
8 |
directory called xxx. Note that the directory name must be a legal |
|
9 |
Python package name, so a plugin called brz-xxx-yyy need to be installed |
|
7204.1.6
by Jelmer Vernooij
Review comments. |
10 |
into a directory called xxx_yyy, i.e. '-' in a plugin name should be mapped to |
7204.1.5
by Jelmer Vernooij
Import plugin-development.txt. |
11 |
an underscore in the directory name. |
12 |
||
13 |
||
14 |
Licensing |
|
15 |
--------- |
|
16 |
||
17 |
We encourage plugin authors to make their plugins publicly available |
|
7204.1.6
by Jelmer Vernooij
Review comments. |
18 |
under the same license as Breezy itself, namely GPL v2 or later. However, there |
7204.1.5
by Jelmer Vernooij
Import plugin-development.txt. |
19 |
is no requirement to do so. You are free to create Breezy plugins for |
20 |
private or internal use within your company and not distribute them. |
|
21 |
||
22 |
By sharing your work, a larger number of people benefit. In our experience, |
|
23 |
plugin developers also benefit heavily by having more users involved in |
|
24 |
the design, testing, bug fixing and longer term maintenance. In other words, |
|
25 |
sharing leads to a better plugin faster. |
|
26 |
||
27 |
||
28 |
Testing |
|
29 |
------- |
|
30 |
||
31 |
To ensure your plugin under development is available to Breezy, set |
|
32 |
the ``BRZ_PLUGIN_PATH`` environment variable to its parent directory. |
|
33 |
Alternatively, you may wish to develop your plugin within a directory |
|
34 |
under your personal plugins area (``~/.bazaar/plugins`` on GNU/Linux) |
|
35 |
or put a symbolic link in that area pointing to your plugin under |
|
36 |
test. Finally you can use ``BRZ_PLUGINS_AT`` to point to a specific |
|
37 |
directory for a specific plugin (separated by your platform's value of |
|
38 |
os.pathsep), e.g. |
|
39 |
||
40 |
export BRZ_PLUGINS_AT=qbrz@/home/me/qbrz:explorer@/home/me/explorer |
|
41 |
||
42 |
You can disable loading plugins with ``BRZ_DISABLE_PLUGINS``. |
|
43 |
||
44 |
If you want to stop loading all but installed plugins you can use: |
|
45 |
||
46 |
BRZ_PLUGIN_PATH=-site |
|
47 |
||
48 |
We also encourage plugin developers to provide tests for their plugin. |
|
49 |
When you run ``brz selftest``, Breezy will scan all its plugins to see if |
|
50 |
they contain a function named ``test_suite()``. For each plugin that does, |
|
51 |
it calls the function and adds any resulting tests to the master test suite. |
|
52 |
To run just the tests for plugin xxx, the command is:: |
|
53 |
||
7204.1.6
by Jelmer Vernooij
Review comments. |
54 |
brz selftest -s bp.xxx |
7204.1.5
by Jelmer Vernooij
Import plugin-development.txt. |
55 |
|
56 |
||
57 |
Providing help |
|
58 |
-------------- |
|
59 |
||
60 |
Plugins in this guide have their documentation automatically |
|
61 |
generated from the online help provided for a plugin. Sections are |
|
62 |
ordered as follows: |
|
63 |
||
64 |
1. High level introduction |
|
65 |
2. Plugin-specific help topics (COMING SOON) |
|
66 |
3. Commands defined or extended by the plugin. |
|
67 |
||
68 |
High level help is specified in the docstring for the ``__init__.py`` module. |
|
69 |
You can register plugin-specific help topics in ``__init__.py`` like this:: |
|
70 |
||
71 |
_xxx_tutorial = """XXX Tutorial |
|
72 |
||
73 |
Welcome to xxx, your new best friend. ... |
|
74 |
""" |
|
75 |
topic_registry.register('xxx-tutorial', |
|
76 |
_xxx_tutorial, |
|
77 |
'How to use xxx') |
|
78 |
||
79 |
Command level help is specified in the docstring for the relevant |
|
80 |
``cmd_xxx`` Command class. |
|
81 |
||
82 |
.. note:: |
|
83 |
||
84 |
The final documentation needs to be in ReST format. Keep in mind though |
|
85 |
that the documentation should also be readable via ``brz help xxx`` so |
|
86 |
it's best to keep markup to a reasonable minimum. |
|
87 |
||
88 |
||
89 |
Providing custom code via hooks |
|
90 |
------------------------------- |
|
91 |
||
92 |
Hooks let you provide custom code at certain processing points. |
|
93 |
The available hook point are documented in the `User Reference |
|
94 |
<http://doc.bazaar-vcs.org/development/en/user-reference/index.html#hooks>`_. |
|
95 |
||
96 |
Adding a new hook is done with, for example:: |
|
97 |
||
98 |
import breezy.branch |
|
99 |
breezy.branch.Branch.hooks.install_named_hook('post_push', post_push_hook, |
|
100 |
'My post_push hook') |
|
101 |
||
102 |
For more information on how to write hooks, |
|
103 |
see http://doc.bazaar-vcs.org/development/en/user-guide/hooks.html. |
|
104 |
||
105 |
||
106 |
Defining a new command |
|
107 |
---------------------- |
|
108 |
||
109 |
Breezy commands are defined as subclasses of ``breezy.commands.Command``, the |
|
110 |
command name is specified by the name of the subclass, and they must be |
|
111 |
registered into ``brz`` with the ``breezy.commands.register_command`` function |
|
112 |
at module import time. |
|
113 |
||
114 |
To define the ``brz foo-bar`` command:: |
|
115 |
||
116 |
from breezy.commands import Command, register_command |
|
117 |
||
118 |
class cmd_foo_bar(Command): |
|
119 |
# see breezy/builtins.py for information about what to put here |
|
120 |
pass |
|
121 |
||
122 |
register_command(cmd_foo_bar) |
|
123 |
||
124 |
If the class name starts with ``cmd_``, the prefix will get dropped |
|
125 |
and ``_`` will be replaced by ``-`` characters. |
|
126 |
||
127 |
||
128 |
Managing data |
|
129 |
------------- |
|
130 |
||
131 |
Plugin data falls into several categories: |
|
132 |
||
133 |
* Configuration settings. |
|
134 |
* Data the user can see and version control. |
|
135 |
* Data behind the scenes. |
|
136 |
||
137 |
Configuration settings are often stored in ``branch.conf``, |
|
138 |
``locations.conf`` or ``bazaar.conf``. |
|
139 |
||
140 |
User-visible data for a plugin called xxx should be stored in |
|
7204.1.6
by Jelmer Vernooij
Review comments. |
141 |
``.bzrmeta/xxx``. If mutiple files are desirable, make ``.bzrmeta/xxx`` |
142 |
a directory or give them a common prefix within ``.bzrmeta``, e.g. |
|
7204.1.5
by Jelmer Vernooij
Import plugin-development.txt. |
143 |
``xxx-foo``, ``xxx-bar``. |
144 |
||
7204.1.6
by Jelmer Vernooij
Review comments. |
145 |
Data managed behind the scenes should be stored in ``.bzr``. |
7204.1.5
by Jelmer Vernooij
Import plugin-development.txt. |
146 |
Depending on the nature of the data, it may belong in a subdirectory |
147 |
within there, e.g. ``checkout``, ``branch`` or ``repository``. |
|
148 |
It's your responsibility to ensure behind-the-scenes data is |
|
149 |
propagated and merged appropriately via custom code. You may want |
|
7204.1.6
by Jelmer Vernooij
Review comments. |
150 |
to use existing hooks for this or ask for new hooks to help. |
7204.1.5
by Jelmer Vernooij
Import plugin-development.txt. |
151 |
|
152 |
Useful metadata |
|
153 |
--------------- |
|
154 |
||
155 |
It is highly recommended that plugins define a version number. This |
|
156 |
is displayed by ``brz plugins`` and by the ``qplugins`` GUI dialog. |
|
157 |
To do this, define ``version_info`` in ``__init__.py`` like this:: |
|
158 |
||
159 |
version_info = (1, 2, 0, 'beta', 1) |
|
160 |
||
161 |
Plugins can also declare other useful metadata such as a mimimum |
|
162 |
breezy version, new transports and storage formats. See |
|
163 |
`Plugin API <plugin-api>`_ for details. |
|
164 |
||
165 |
||
166 |
Performance tips |
|
167 |
---------------- |
|
168 |
||
169 |
When brz starts up, it imports every plugin, so plugins can degrade |
|
170 |
performance when they're not being used. However, sub-modules are not |
|
171 |
loaded, only the main name. |
|
172 |
||
173 |
One way you can avoid this slowdown is by putting most of your code |
|
174 |
in sub-modules, so that the plugin, itself, is small. All you really |
|
175 |
need in the ``__init__.py`` is the plugin's Command classes, the |
|
176 |
commands to register them, and the optional ``test_suite()``. |
|
177 |
||
178 |
Another way to reduce your plugin's overhead is to use the breezy |
|
179 |
lazy_import functionality. That looks something like this:: |
|
180 |
||
181 |
from breezy.lazy_import import lazy_import |
|
182 |
lazy_import(globals(), """ |
|
183 |
from breezy import ( |
|
184 |
branch as _mod_branch, |
|
185 |
option, |
|
186 |
workingtree, |
|
187 |
) |
|
188 |
""") |
|
189 |
||
190 |
Lazy importing only works for packages and modules, not classes or |
|
191 |
functions. It defers the import until you actually need it. |
|
192 |
||
193 |
||
194 |
Learning more |
|
195 |
------------- |
|
196 |
||
7204.1.6
by Jelmer Vernooij
Review comments. |
197 |
`Integrating with Bazaar <http://wiki.bazaar.canonical.com/Integrating_with_Bazaar>`_ |
7204.1.5
by Jelmer Vernooij
Import plugin-development.txt. |
198 |
explains how to do such operations as ``add``, ``commit``, ``log`` and more. |
199 |
||
200 |
Reference documentation on some key APIs is provided below. For a more |
|
201 |
detailed reference, see the `complete breezy API documentation <api/index>`_. |
|
202 |
||
203 |
||
204 |
Mini API Reference |
|
205 |
------------------ |
|
206 |
||
207 |
Command Class |
|
208 |
~~~~~~~~~~~~~ |
|
209 |
||
210 |
Base class for commands. Commands are the heart of the command-line brz |
|
211 |
interface. |
|
212 |
||
213 |
The command object mostly handles the mapping of command-line parameters into |
|
214 |
one or more breezy operations, and of the results into textual output. |
|
215 |
||
216 |
Commands normally don't have any state. All their arguments are passed in to |
|
217 |
the run method. (Subclasses may take a different policy if the behaviour of the |
|
218 |
instance needs to depend on e.g. a shell plugin and not just its Python class.) |
|
219 |
||
220 |
The docstring for an actual command should give a single-line summary, then a |
|
221 |
complete description of the command. A grammar description will be inserted. |
|
222 |
||
223 |
aliases |
|
224 |
Other accepted names for this command. |
|
225 |
||
226 |
takes_args |
|
227 |
List of argument forms, marked with whether they are optional, |
|
228 |
repeated, etc. |
|
229 |
||
230 |
For example: ``['to_location', 'from_branch?', 'file*']`` means: |
|
231 |
||
232 |
* 'to_location' is required |
|
233 |
* 'from_branch' is optional |
|
234 |
* 'file' can be specified 0 or more times |
|
235 |
||
236 |
takes_options |
|
237 |
List of options that may be given for this command. These can be either |
|
238 |
strings, referring to globally-defined options, or option objects. |
|
239 |
Retrieve through options(). |
|
240 |
||
241 |
hidden |
|
242 |
If true, this command isn't advertised. This is typically for commands |
|
243 |
intended for expert users. |
|
244 |
||
245 |
run() |
|
246 |
Actually run the command. This is invoked with the options and arguments |
|
247 |
bound to keyword parameters. |
|
248 |
||
249 |
Return 0 or None if the command was successful, or a non-zero shell error |
|
250 |
code if not. It's OK for this method to allow an exception to raise up. |
|
251 |
||
252 |
||
253 |
register_command Function |
|
254 |
~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
255 |
||
256 |
Utility function to help register a command. |
|
257 |
||
258 |
param *cmd* |
|
259 |
Command subclass to register |
|
260 |
||
261 |
param *decorate* |
|
262 |
If true, allow overriding an existing command of the same name; the old |
|
263 |
command is returned by this function. Otherwise it is an error to try to |
|
264 |
override an existing command. |