Chapter 10. Programming Bazaar

In this chapter, we will look under the hood, and explore a few interesting ways in which you can interact with Bazaar programmatically. This chapter assumes that you have a working knowledge of the Python programming language.

We will start with a quick introduction of the basics—how the main objects of version control are represented in Bazaar, and how to use them. This will enable you to manipulate Bazaar programmatically and provide the essential knowledge to write plugins.

Next, we will explain the details of writing plugins. Plugins are very powerful, and are the standard way to hook into Bazaar's architecture and extend Bazaar in various ways, such as adding new commands, modifying existing commands, or even completely replacing existing commands. Plugins can also be used to implement hooks that can be triggered by various steps in version control operations.

The following topics will be covered in this chapter:

  • Using Bazaar programmatically
  • Creating a plugin
  • Creating a hook

Using Bazaar programmatically

The core functionality of Bazaar is implemented within the bzrlib Python package. A detailed study of Bazaar's architecture is beyond the scope of this book. Instead, we will take a pragmatic approach and show you, through examples, how to access the main objects of version control in Bazaar and other important tips.

The main goal of this chapter is to teach you enough to be able to create your own plugins and make simple modifications to Bazaar's behavior to better suit your needs.

If you would like to know more about Bazaar's internals, this overview should be a good starting point:

http://doc.bazaar.canonical.com/developers/overview.html

Using bzrlib outside of bzr

When using bzrlib within bzr, for example in plugins, the library is already initialized. To ensure that bzrlib functions correctly when using it outside of bzr, for example in your custom scripts, it must be initialized as follows:

>>> import bzrlib
>>> bzrlib.initialize()

Additionally, if you want to use a functionality that is implemented in plugins, for example working with branches on Launchpad, then you must load the plugins manually, as follows:

>>> bzrlib.plugin.load_plugins()

This should be a fast operation, as plugins normally use lazy initialization so that their main implementation is only loaded when really used.

This example loads all the plugins at the default plugin path locations in the same way as they are loaded when using the bzr command. Optionally, you can pass to the function a list of paths to limit the plugin discovery process to the specified locations.

Accessing Bazaar objects

When implementing plugins or trying to do simple operations in Bazaar, it can be difficult to find the right modules to access the right objects, to get the information you need. The aim of this section is to show a couple of examples for accessing various objects of Bazaar's version control model.

The main classes and methods that will be demonstrated are as follows:

  • bzrlib.branch.Branch
  • bzrlib.config.BranchConfig
  • bzrlib.revision.Revision
  • bzrlib.revisiontree.RevisionTree
  • bzrlib.log.LongLogFormatter

We will demonstrate various methods for accessing Bazaar's objects using the branch:

lp:~bzrbook/bzrbook-examples/bzr-summary

You can follow the same steps as in the examples by preparing a local branch, as follows:

$ bzr branch lp:~bzrbook/bzrbook-examples/bzr-summary /tmp/summary -r20
$ cd /tmp/summary

We used the specific revision 20 to match with the operations in the examples.

Accessing branch data

A branch is one of the most important objects in Bazaar. The class to work with branches is named Branch in the bzrlib.branch module. You can open a local branch by specifying its path in the filesystem as follows:

>>> from bzrlib.branch import Branch
>>> branch = Branch.open('.')

In this example, we specified "." as the path, meaning the current directory.

You can open remote branches in the same way, however, if the protocol is implemented in a plugin such as lp: for branches on Launchpad, then you must load the required plugins before using this method.

A Branch object has several interesting methods and attributes, such as the following:

  • repository: This is the Bazaar repository associated with the branch, as a CHKInventoryRepository object
  • revno():This returns the last revision number, as an integer
  • last_revision():This returns the last revision ID, as a string
  • control_url: This is the path to the .bzr/branch directory of the branch, as a string
  • get_config():This returns the branch configuration data, as a BranchConfig object

Accessing branch configuration values

The class to work with branch configuration data is named BranchConfig in the bzrlib.config module. An easy way to access the configuration of a branch is by opening the branch and then using the get_config() method on it. For example:

>>> from bzrlib.branch import Branch
>>> branch = Branch.open('.')
>>> config = branch.get_config()

This is especially useful for accessing the key-value properties in the .bzr/branch/branch.conf file, as follows:

>>> config.get_user_option('parent_location')
u'bzr+ssh://bazaar.launchpad.net/~bzrbook/bzrbook-examples/bzr-summary/'

If the specified configuration variable does not exist, the method returns None.

Accessing revision history

The class to work with the revision history is named Revision in the bzrlib.revision module. An easy way to access the revisions is by opening a branch and then using the get_revision() method on its associated repository. For example:

>>> from bzrlib.branch import Branch
>>> branch = Branch.open('.')
>>> rev_id = branch.last_revision()
>>> revision = branch.repository.get_revision(rev_id)

A Revision object has several interesting methods to access the revision information. For example:

  • get_summary(): This returns the commit message of the revision
  • get_history(branch.repository): This takes a Repository object as parameter and returns the ordered list of revision IDs in the branch

Accessing the contents of a revision

The class to work with the content of files and the shape of the tree of revisions is named RevisionTree in the bzrlib.revisiontree module. An easy way to get a RevisionTree object is from a branch and a revision ID. For example:

>>> from bzrlib.branch import Branch
>>> branch = Branch.open('.')
>>> rev_id = branch.last_revision()
>>> tree = branch.repository.revision_tree(rev_id)

An easy way to list files in the tree is by using the iter_entries_by_dir method. For example:

>>> tree.lock_read()
<InventoryRevisionTree instance at 1019ac7d0, rev_id='janos@axiom-20130105162648-iwv0yb9o5etwyvzh'>
>>> iter = tree.iter_entries_by_dir()
>>> print iter.next()
(u'', CHKInventoryDirectory('tree_root-20121223122411-46c0o678h271d3jk-1', u'', parent_id=None, revision='janos@axiom-20121223160417-9uz9ynbehy0il02t'))
>>> print iter.next()
(u'README', InventoryFile('readme-20121223154742-2ymrdwwoa9j1wva0-1', u'README', parent_id='tree_root-20121223122411-46c0o678h271d3jk-1', sha1='dd28845af2cdeb1b56bd67b34c4823533405d654', len=764, revision=janos@axiom-20121230121603-50u69s9ch8o7eg41))
>>> print iter.next()
(u'__init__.py', InventoryFile('__init__.py-20121230094916-m7i3mv0mikwdipb9-1', u'__init__.py', parent_id='tree_root-20121223122411-46c0o678h271d3jk-1', sha1='285dce023ba62f899b592543b6627cc5a27c9341', len=761, revision=janos@axiom-20130105162648-iwv0yb9o5etwyvzh))
>>> tree.unlock()

In order to iterate over the entries in the tree, we must first lock the tree object. Each iteration returns a tuple of two elements:

  • The relative path of the file or directory from the project root
  • The inventory object representing the entry, which can be a CHKInventoryDirectory object in case of a directory, and an InventoryFile object in case of a file

The first entry is the root directory of the project, thus its relative path is an empty string, and it is a CHKInventoryDirectory object. The ordering of entries is the same as in the output of the bzr ls command:

$ bzr ls --show-ids 
README           readme-20121223154742-2ymrdwwoa9j1wva0-1
__init__.py      __init__.py-20121230094916-m7i3mv0mikwdipb9-1
cmd_summary.py   cmd_summary.py-20121230115024-py0vs3wj3oqux5z2-1
setup.py         setup.py-20121230120402-zfu8im6iax17fl7r-1
tests/           tests-20121230123000-bh7lacxmlglvq30b-1

The inventory object contains very important information, such as the file ID, which can be used to access file content.

By using a RevisionTree object and the file ID, you can access the contents of files using the get_file(file_id) method. For example:

>>> print tree.get_file('readme-20130108195909-jmwgut5e1y6x608x-1').readlines()

The get_file method returns a file-like object. In this example, we used the readline() method to print the list of lines in the file, omitting the actual output for brevity.

Formatting revision info using a log format

The classes handling the formatting of the revision information are derived from the LogFormatter class in the bzrlib.log module. For each log format that you can use on the command line, there exists a different implementation of the LogFormatter class, for example, the default long format is handled by LongLogFormatter.

Formatting revision information using a log formatter involves the following steps:

  1. Get the Revision object of the revision you want to format.
  2. Create a LogRevision object by using the Revision object and the revision number.
  3. Create the formatter using a file-like object to write to as a constructor parameter.
  4. Use the formatter to format the LogRevision object.

For example, you can format the last revision and print to the standard output by using the long log formatter, as follows:

>>> from bzrlib.branch import Branch
>>> branch = Branch.open('.')
>>> from bzrlib.log import LongLogFormatter, LogRevision
>>> revno, rev_id = branch.last_revision_info()
>>> revision = branch.repository.get_revision(rev_id)
>>> log_revision = LogRevision(rev=revision, revno=revno)
>>> from sys import stdout
>>> formatter = LongLogFormatter(stdout)
>>> formatter.log_revision(log_revision)
------------------------------------------------------------
revno: 20
committer: Janos Gyerik <janos@axiom>
branch nick: summary
timestamp: Sat 2013-01-05 17:26:48 +0100
message:

The preceding command made the plugin docstring multiline. The output is identical to the output of the command bzr log -r20 --long.

More examples

You will find more examples and practical tips at http://doc.bazaar.canonical.com/developers/integration.html.

Locating BZRLIB

Throughout the chapter, we will make references to the location BZRLIB. By that we will mean always the base path of the bzrlib Python package, as it was created during the installation of Bazaar. You can find this directory in the output of bzr version. For example:

$ bzr version
Bazaar (bzr) 2.5.0
  Python interpreter: /usr/bin/python2.6 2.6.1
  Python standard library: /System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6
  Platform: Darwin-10.8.0-i386-64bit
  bzrlib: /Library/Python/2.6/site-packages/bzrlib
  Bazaar configuration: /Users/janos/.bazaar
  Bazaar log file: /Users/janos/.bzr.log

In this example, the path of BZRLIB is /Library/Python/2.6/site-packages/bzrlib, from the line that contains "bzrlib:"

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.71.28