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