Extension Distribution¶
Python Packages¶
Extensions are packaged and distributed as Python packages (Python Eggs or Python Wheels). This allows for automatic detection of installed extensions, packaging of static files, and dependency checking.
Extension packages are pretty much like any other Python package. It uses
setuptools
and a setup.py
file to define the package
contents and to build the package. There are a few additional features that
are provided by Review Board, which will be covered in this guide.
Defining an Entry Point¶
To facilitate the auto-detection of installed extensions, a
reviewboard.extensions
Python Entry Point must be defined for each
extension class in your package. These are defined
like so:
entry_points={
'reviewboard.extensions': [
'sample_extension = sample_extension.extension:SampleExtension',
],
},
This tells the Python packaging system that there’s a Review Board extension
named sample_extension
that points to
sample_extension.extension.SampleExtension
. Once the package is
installed, Review Board will be able to use this registered entry point to
locate your extension.
Here is an example of a full setup.py
file defining this entry point:
from reviewboard.extensions.packaging import setup
from setuptools import find_packages
setup(
name='sample_extension',
version='0.1',
description='Description of extension',
author='Your Name',
packages=find_packages(),
entry_points={
'reviewboard.extensions': [
'sample_extension = sample_extension.extension:SampleExtension',
],
},
)
Packaging Static Media Files¶
Packages that contain CSS or JavaScript should define those in the extension
class’s css_bundles
and
js_bundles
dictionaries.
These bundles will be automatically compiled, minified, and packaged for you.
There’s nothing else you need to do.
See Extension Static Media Files for more information on bundles.
Packaging Templates/Data Files¶
If your package needs to ship templates or other data files, you’ll need
to include these in your package’s MANIFEST.in
file. Please see
the MANIFEST.in documentation for
the format of this file.
This file will live in the same directory as your setup.py
.
Your MANIFEST.in
might look something like this:
include sample_extension/templates/*.html
include sample_extension/templates/*.txt
include README
include LICENSE
Dependencies¶
Your package can specify a list of dependencies, which are other packages that
will be installed when your package is installed. This is specified as an
install_requires
parameter to
setup()
. See the official
documentation
for how to specify dependencies.
Warning
Don’t specify reviewboard
, djblets
, Django
, or any other Review
Board dependency in your own list. While your package may indeed require
Review Board or one of its dependencies, this runs the risk (in certain
cases) of accidentally upgrading all or part of your Review Board install
when installing your package.
Your setup.py
might look like:
from reviewboard.extensions.packaging import setup
from setuptools import find_packages
setup(
name='sample_extension',
version='0.1',
description='Description of extension',
author='Your Name',
packages=find_packages(),
entry_points={
'reviewboard.extensions': [
'sample_extension = sample_extension.extension:SampleExtension',
],
},
install_requires=[
'PythonPackageIDependOn>=0.1',
],
)
In addition, extensions can have a run-time dependency on another extension,
forcing that extension to be enabled when yours is enabled. This is done by
specifying the required extensions’ IDs in the
requirements
list. For
example:
class SampleExtension(Extension):
requirements = [
'other_extension.extension.OtherExtension',
]
Building a Package¶
You’re now ready to build your package! Before you do, let’s talk setup and deployment options.
Note
If you’re running Review Board 2.5.7 or older, and you’re working with static media files, you’ll need to install a couple of modules using npm:
$ sudo npm install -g less uglifyjs
If you’re looking to distribute your package publicly (such as on the Python Package Index, you’ll want to build this as a Wheel, Egg, and maybe a Source Distribution (“sdist”). You can build all three with one command:
$ python setup.py bdist_wheel bdist_egg sdist
That will produce builds in the dist/
directory.
If this is for internal use, you can get away with just one package format. We recommend Wheels, as these are the new standard for Python packaging. You can build just the Wheel by running:
$ python setup.py bdist_wheel
Note
If you get an error about bdist_wheel
not being a valid command, you
will need to update your pip
package and install wheel
:
$ pip install -U pip
$ pip install wheel
Developing Against Your Package¶
If you’re actively testing your package against Review Board, you don’t want to keep rebuilding the package every time you make a change. Instead, you’ll want to install your package in development mode:
$ python setup.py develop
This basically tells the Python packaging system that the installed package lives in your source tree. The entry points will be registered and you’ll be able to enable the extension in Review Board. It’s the recommended way to iterate on your package while you test.
Note
Due to some differences in how the package is prepared, this will require testing against a Review Board development server, instead of a production install.