[[PageOutline(2-5,Contents,pullout)]] = Plugin Development Guide This page documents some best practices and guidelines for plugin development. It is a community driven document, and everyone is encouraged to contribute to it. If you have questions or comments, please raise them on the [trac:MailingList Trac-dev MailingList]. == Licensing Plugin authors are encouraged to clearly indicate how the contribution is licensed. This is important for both users and future developers of your plugin, because if you choose to no longer support the plugin, the terms under which someone else can adopt and develop it are clear. It is also important for users and administrators who need to understand the terms and conditions under which they can use or modify the code. Trac-Hacks is an open source community driven by voluntary contributions and made successful by collaboration. Therefore we encourage the use of licenses that foster collaboration and minimise restrictions on future use of the code. Trac has adopted the [trac:TracLicense BSD 3-Clause license], and use of the same license in any plugin code is encouraged. One of the many benefits to adopting this license is that any plugin code can be integrated into the Trac core. The following steps are suggested to add a license: 1. Add the `license` keyword in `setup.py` ([browser:/plantumlmacro/trunk/setup.py@14688:23 example]). 1. Add a license header to every Python source file ([browser:/plantumlmacro/trunk/plantuml/macro.py@14688:1-9 example]). {{{#!python # -*- coding: utf-8 -*- # # Copyright (C) YYYY-YYYY your name here # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. }}} Note: For an executable file such as `setup.py`, the header will follow the line `#!/usr/bin/env python` ([browser:/plantumlmacro/trunk/setup.py@14688:1 example]). 1. Add a license header to every RSS and XHTML Genshi template (example: '''todo'''). {{{#!text/html }}} The use of the XML comment marker as shown is important, so that the text does not get rendered to the output. Make sure not to use the alternate form, which is rendered to the output as a hidden comment: `` 1. Add a `COPYING` file with the license text in the top-level directory of the project ([browser:/plantumlmacro/trunk/COPYING example]). 1. Add an appropriate tag to the wiki page: [[ListTagged(realm:wiki license, format=compact)]] Additional tags can be created for additional or more descriptive license types. Currently it is not recommended to add license text to static resources (ie file in `htdocs`), since doing so will increase the size of the content that is sent to the client. This issue will be addressed in the Trac core when support is added for minimization (trac:#10672). == Metadata for Single-File Plugins Plugins are typically packaged using setuptools egg format, but Trac also supports single-file plugins. A single-file plugin is a single `.py` file that is placed in the project or shared `plugins` directory. While the metadata for a packaged plugin is stored in its `setup.py` file, the metadata for a single-file plugin can added using file-scope attributes. The supported attributes and their aliases are: `author`, `author_email`, `home_page` (`url`), `license`, `trac` and `version` (`revision`): {{{#!python version = "$Rev$" home_page = "https://trac-hacks.org/wiki/MyAmazingPlugin" license = "3-Clause BSD" author = "Joe Bloggs" author_email = "trac@python.org" }}} The attributes should be self-explanatory with the possible exception of the `trac` attribute. The `trac` attribute is used to direct bug reports to an issue tracker that differs from `home_page`. If the plugin is hosted on trac-hacks.org and the `home_page` attribute is set to point to the project wiki page, the `trac` attribute will not need to be set. For files stored in Subversion, [http://svnbook.red-bean.com/en/1.8/svn.advanced.props.special.keywords.html Keyword Substitution] is supported for the `version` (`revision`) attribute. {{{#!python version = '$Rev$' }}} The file's `svn:keywords` property must be edited to append `Rev`. Note that the aliases `Revision`, `LastChangedRevision` and `HeadURL` are not supported by Trac. {{{#!sh svn propedit svn:keywords MyAmazingMacro.py }}} == Coding Style Authors are encouraged to conform to the [trac:TracDev/CodingStyle Trac Style Guide] and [http://legacy.python.org/dev/peps/pep-0008/ PEP-0008 style guide]. There are numerous other recommendations, and while some are mainly a matter of personal aesthetics and preference, others are suggested for review and adoption: * [wikipedia:Yoda_conditions Yoda conditions] as [comment:ticket:11622:24 suggested by lkraav]: * makes comparisons more obvious by putting the constant up-front * erroneous assignment ('=' instead of '==') can't slip though unnoticed Example of common style: {{{ not req.authname or req.authname == 'anonymous' }}} The same example in 'Yoda condition' style: {{{ not req.authname or 'anonymous' == req.authname }}} == Assert Minimum Trac Version Requirement A common method of specifying a minimum required Trac version is to add an installation requirement in `setup.py`, for example: `install_requires = ['Trac >= 0.12']`. However, this causes numerous problems, some of which are documented in #9800, and is not recommended. One of the most negative consequences is that setuptools may download and install a newer version of Trac during installation of a plugin. The result can be an unintended upgrade of a user's installation (#10607). A better approach is to place the following in the package `__init__.py`, modifying `min_trac_version` as appropriate for your plugin: {{{#!python import pkg_resources pkg_resources.require('Trac >= 1.0') }}} You should still specify that Trac is an installation requirement, but without enforcing a version requirement: `install_requires = ['Trac']`. The check in `__init__.py` is performed at runtime, so the egg can be built in an environment that does not satisfy the installation requirements. One use-case for this behavior is building the egg on a development computer and uploading it through the plugin admin page. An error such as the following will be seen in the logs if the plugin fails to load due to a failed requirement: {{{ 02:39:22 PM Trac[loader] DEBUG: Loading changelog.ChangeLogMacro from /home/user/Workspace/clm/lib/python2.7/site-packages/ChangeLogMacro-0.2-py2.7.egg 02:39:22 PM Trac[loader] ERROR: Skipping "changelog.ChangeLogMacro = changelog.ChangeLogMacro": (version conflict "VersionConflict: (Trac 0.12 (/home/user/Workspace/clm/trac-0.12), Requirement.parse('Trac>=1.0'))") }}} == Documenting required and optional components The docstring for a Component class is displayed as the Component description on the plugin admin panel. It is recommended that the description be prefixed with `[required]` or `[optional]`, to guide end-users in enabling the proper Components. The `[extra]` descriptor can also used for features that have specialized or narrow use-cases. == Publishing Packages to PyPI //Forthcoming//.