1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
|
==========
Plugin API
==========
:Date: 2009-01-23
.. contents::
Introduction
============
bzrlib has a very flexible internal structure allowing plugins for many
operations. Plugins can add commands, new storage formats, diff and merge
features and more. This document provides an overview of the API and
conventions for plugin authors.
If you're writing a plugin and have questions not addressed by this
document, please ask us.
See also
--------
* `Bazaar Developer Documentation Catalog <../index.html>`_.
* `Bazaar Plugins Guide <http://doc.bazaar.canonical.com/plugins/en/plugin-development.html>`_ for
more suggestions about particular APIs.
Structure of a plugin
=====================
Plugins are Python modules under ``bzrlib.plugins``. They can be installed
either into the PYTHONPATH in that location, or in ~/.bazaar/plugins.
Plugins should have a setup.py.
As for other Python modules, the name of the directory must match the
expected name of the plugin.
Plugin metadata before installation
===================================
Plugins can export a summary of what they provide, and what versions of bzrlib
they are compatible with. This allows tools to be written to work with plugins,
such as to generate a directory of plugins, or install them via a
symlink/checkout to ~/.bazaar/plugins.
This interface allows bzr to interrogate a plugin without actually loading
it. This is useful because loading a plugin may have side effects such
as registering or overriding commands, or the plugin may raise an error,
if for example a prerequisite is not present.
Metadata protocol
-----------------
A plugin that supports the bzr plugin metadata protocol will do two
things. Firstly, the ``setup.py`` for the plugin will guard the call to
``setup()``::
if __name__ == 'main':
setup(...)
Secondly, the setup module will have one or more of the following variables
present at module scope. Any variables that are missing will be given the
defaults from the table. An example of every variable is provided after
the full list.
+------------------------+---------+----------------------------------------+
| Variable | Default | Definition |
+========================+=========+========================================+
| bzr_plugin_name | None | The name the plugin package should be |
| | | given on disk. The plugin is then |
| | | available to python at |
| | | bzrlib.plugins.NAME |
+------------------------+---------+----------------------------------------+
| bzr_commands | [] | A list of the commands that the plugin |
| | | provides. Commands that already exist |
| | | in bzr and are decorated by the plugin |
| | | do not need to be listed (but it is not|
| | | harmful if you do list them). |
+------------------------+---------+----------------------------------------+
| bzr_plugin_version | None | A version_info 5-tuple with the plugins|
| | | version. |
+------------------------+---------+----------------------------------------+
| bzr_minimum_version | None | A version_info 3-tuple for comparison |
| | | with the bzrlib minimum and current |
| | | version, for determining likely |
| | | compatibility. |
+------------------------+---------+----------------------------------------+
| bzr_maximum_version | None | A version_info 3-tuple like |
| | | bzr_minimum_version but checking the |
| | | upper limits supported. |
+------------------------+---------+----------------------------------------+
| bzr_control_formats | {} | A dictionary of descriptions of version|
| | | control directories. See |
| | | `Control Formats` below. |
+------------------------+---------+----------------------------------------+
| bzr_checkout_formats | {} | A dictionary of tree_format_string -> |
| | | human description strings, for tree |
| | | formats that drop into the |
| | | ``.bzr/checkout`` metadir system. |
+------------------------+---------+----------------------------------------+
| bzr_branch_formats | {} | As bzr_checkout_formats but for |
| | | branches. |
+------------------------+---------+----------------------------------------+
| bzr_repository_formats | {} | As bzr_checkout_formats but for |
| | | repositories. |
+------------------------+---------+----------------------------------------+
| bzr_transports | [] | URL prefixes for which this plugin |
| | | will register transports. |
+------------------------+---------+----------------------------------------+
Control Formats
---------------
Because disk format detection for formats that bzr does not understand at
all can be useful, we allow a declarative description of the shape of a
control directory. Each description has a name for showing to users, and a
dictonary of relative paths, and the content needed at each path. Paths
that end in '/' are required to be directories and the value for that key
is ignored. Other paths are required to be regular files, and the value
for that key is either None, in which case the file is statted but the
content is ignored, or a literal string which is compared against for
the content of the file. Thus::
# (look for a .hg directory)
bzr_control_formats = {"Mercurial":{'.hg/': None}}
# (look for a file called .svn/format with contents 4\n).
bzr_control_formats = {"Subversion":{'.svn/format': '4\n'}}
Example
-------
An example setup.py follows::
#!/usr/bin/env python2.4
from distutils.core import setup
bzr_plugin_name = 'demo'
bzr_commands = [
'new-command',
]
bzr_branch_formats = {
"Branch label on disk\n":"demo branch",
}
bzr_control_formats = {"Subversion":{'.svn/format': '4\n'}}
bzr_transports = ["hg+ssh://"]
bzr_plugin_version = (1, 3, 0, 'dev', 0)
bzr_minimum_version = (1, 0, 0)
if __name__ == 'main':
setup(name="Demo",
version="1.3.0dev0",
description="Demo plugin for plugin metadata.",
author="Canonical Ltd",
author_email="bazaar@lists.canonical.com",
license = "GNU GPL v2",
url="https://launchpad.net/bzr-demo",
packages=['bzrlib.plugins.demo',
'bzrlib.plugins.demo.tests',
],
package_dir={'bzrlib.plugins.demo': '.'})
Plugin metadata after installation
==================================
After a plugin has been installed, metadata can be more easily obtained by
looking inside the module object -- in other words, for variables defined
in the plugin's ``__init__.py``.
Help and documentation
----------------------
The module docstring is used as the plugin description shown by ``bzr
plugins``. As with all Python docstrings, the first line should be a
short complete sentence summarizing the plugin. The full docstring is
shown by ``bzr help PLUGIN_NAME``.
This is a user-visible docstring so should be prefixed with ``__doc__ =``
to ensure help works under ``python -OO`` with docstrings stripped.
API version
-----------
Plugins can and should declare that they depend on a particular version of
bzrlib like so::
from bzrlib.api import require_api
require_api(bzrlib, (1, 11, 0))
Please see `API versioning <api-versioning.html>`_ for more details on the API
metadata protocol used by bzrlib.
Plugin version
--------------
The plugin should expose a version tuple to describe its own version.
Some plugins use a version number that corresponds to the version of bzr
they're released against, but you can use whatever you want. For example::
version_info = (1, 10, 0)
Detecting whether code's being loaded as a plugin
-------------------------------------------------
You may have a Python module that can be used as a bzr plugin and also in
other places. To detect whether the module is being loaded by bzr, use
something like this::
if __name__ == 'bzrlib.plugins.loggerhead':
# register with bzrlib...
Plugin performance
==================
Plugins should avoid doing work or loading code from the plugin or
external libraries, if they're just installed but not actually active,
because this slows down every invocation of bzr. The bzrlib APIs
generally allow the plugin to 'lazily' register methods to invoke if a
particular disk format or seen or a particular command is run.
Plugin registrations
====================
The plugin ``__init__.py`` runs when the plugin is loaded during bzr
startup. Generally the plugin won't want to actually do anything at this
time other than register or override functions to be called later.
The plugin can import bzrlib and call any function.
Some interesting APIs are described in `Bazaar Plugins Guide <http://doc.bazaar.canonical.com/plugins/en/plugin-development.html>`_.
Publishing your plugin
======================
When your plugin is basically working you might like to share it with
other people. Here are some steps to consider:
* make a project on Launchpad.net like
<https://launchpad.net/bzr-fastimport>
and publish the branches or tarballs there
* include the plugin in <http://wiki.bazaar.canonical.com/BzrPlugins>
* post about it to the ``bazaar-announce`` list at ``lists.canonical.com``
..
vim: ft=rst tw=74 ai shiftwidth=4
|