Mercurial > hg
changeset 40595:419d703115b0
help: add internals.extensions topic
I think it's better to include the API overview in core as we now have the
internals section in our help system.
Retrieved from the wiki, and formatted as reST. Several wiki links are
removed since they are invalid in the help. The sections about example
extension are removed at all as they seemed too verbose.
https://www.mercurial-scm.org/wiki/WritingExtensions
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Mon, 12 Nov 2018 21:28:54 +0900 |
parents | 234c2d8c9e48 |
children | 252396a6a3f2 |
files | contrib/wix/help.wxs mercurial/help.py mercurial/help/internals/extensions.txt tests/test-help.t |
diffstat | 4 files changed, 332 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/contrib/wix/help.wxs Sun Nov 04 12:51:14 2018 +0900 +++ b/contrib/wix/help.wxs Mon Nov 12 21:28:54 2018 +0900 @@ -47,6 +47,7 @@ <File Id="internals.censor.txt" Name="censor.txt" /> <File Id="internals.changegroups.txt" Name="changegroups.txt" /> <File Id="internals.config.txt" Name="config.txt" /> + <File Id="internals.extensions.txt" Name="extensions.txt" /> <File Id="internals.linelog.txt" Name="linelog.txt" /> <File Id="internals.requirements.txt" Name="requirements.txt" /> <File Id="internals.revlogs.txt" Name="revlogs.txt" />
--- a/mercurial/help.py Sun Nov 04 12:51:14 2018 +0900 +++ b/mercurial/help.py Mon Nov 12 21:28:54 2018 +0900 @@ -311,6 +311,8 @@ loaddoc('changegroups', subdir='internals')), (['config'], _('Config Registrar'), loaddoc('config', subdir='internals')), + (['extensions', 'extension'], _('Extension API'), + loaddoc('extensions', subdir='internals')), (['requirements'], _('Repository Requirements'), loaddoc('requirements', subdir='internals')), (['revlogs'], _('Revision Logs'),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/help/internals/extensions.txt Mon Nov 12 21:28:54 2018 +0900 @@ -0,0 +1,321 @@ +Extensions allow the creation of new features and using them directly from +the main hg command line as if they were built-in commands. The extensions +have full access to the *internal* API. + +Use of Mercurial's internal API very likely makes your code subject to +Mercurial's license. Before going any further, read the License page. + +There are NO guarantees that third-party code calling into Mercurial's +internals won't break from release to release. If you do use Mercurial's API +for published third-party code, we expect you to test your code before each +major Mercurial release. This will prevent various bug reports from your users +when they upgrade their copy of Mercurial. + +File Layout +=========== + +Extensions are usually written as simple python modules. Larger ones are +better split into multiple modules of a single package (see the convert +extension). The package root module gives its name to the extension and +implements the ``cmdtable`` and optional callbacks described below. + +Command table +============= + +To write your own extension, your python module can provide an optional dict +named ``cmdtable`` with entries describing each command. A command should be +registered to the ``cmdtable`` by ``@command`` decorator. + +Example using ``@command`` decorator (requires Mercurial 1.9):: + + from mercurial import cmdutil + from mercurial.i18n import _ + + cmdtable = {} + command = cmdutil.command(cmdtable) + + @command('print-parents', + [('s', 'short', None, _('print short form')), + ('l', 'long', None, _('print long form'))], + _('[options] node')) + def printparents(ui, repo, node, **opts): + ... + +The cmdtable dictionary +----------------------- + +The ``cmdtable`` dictionary uses as key the new command names, and, as value, +a tuple containing: + +1. the function to be called when the command is used. +2. a list of options the command can take. +3. a command line synopsis for the command (the function docstring is used for + the full help). + +List of options +--------------- + +All the command flag options are documented in the mercurial/fancyopts.py +sources. + +The options list is a list of tuples containing: + +1. the short option letter, or ``''`` if no short option is available + (for example, ``o`` for a ``-o`` option). +2. the long option name (for example, ``option`` for a ``--option`` option). +3. a default value for the option. +4. a help string for the option (it's possible to omit the "hg newcommand" + part and only the options and parameter substring is needed). + +Command function signatures +--------------------------- + +Functions that implement new commands always receive a ``ui`` and usually +a ``repo`` parameter. The rest of parameters are taken from the command line +items that don't start with a dash and are passed in the same order they were +written. If no default value is given in the parameter list they are required. + +If there is no repo to be associated with the command and consequently no +``repo`` passed, then ``norepo=True`` should be passed to the ``@command`` +decorator:: + + @command('mycommand', [], norepo=True) + def mycommand(ui, **opts): + ... + +For examples of ``norepo``, see the convert extension. + +Command function docstrings +=========================== + +The docstring of your function is used as the main help text, shown by +``hg help mycommand``. The docstring should be formatted using a simple +subset of reStructuredText markup. The supported constructs include: + +Paragraphs:: + + This is a paragraph. + + Paragraphs are separated + by blank lines. + +A verbatim block is introduced with a double colon followed by an indented +block. The double colon is turned into a single colon on display:: + + Some text:: + + verbatim + text + !! + +We have field lists:: + + :key1: value1 + :key2: value2 + +Bullet lists:: + + - foo + - bar + +Enumerated lists:: + + 1. foo + 2. bar + +Inline markup:: + + ``*bold*``, ``monospace``, :hg:`command` + +Mark Mercurial commands with ``:hg:`` to make a nice link to the corresponding +documentation. We'll expand the support if new constructs can be parsed +without too much trouble. + +Communicating with the user +=========================== + +Besides the ``ui`` methods, like ``ui.write(*msg)`` or +``ui.prompt(msg, default="y")``, an extension can add help text for each +of its commands and the extension itself. + +The module docstring will be used as help string when ``hg help extensionname`` +is used and, similarly, the help string for a command and the docstring +belonging to the function that's wrapped by the command will be shown when +``hg help command`` is invoked. + +Setup Callbacks +=============== + +Extensions are loaded in phases. All extensions are processed in a given phase +before the next phase begins. In the first phase, all extension modules are +loaded and registered with Mercurial. This means that you can find all enabled +extensions with ``extensions.find`` in the following phases. + +ui setup +-------- + +Extensions can implement an optional callback named ``uisetup``. ``uisetup`` +is called when the extension is first loaded and receives a ui object:: + + def uisetup(ui): + # ... + +Extension setup +--------------- + +Extensions can implement an optional callback named ``extsetup``. It is +called after all the extension are loaded, and can be useful in case one +extension optionally depends on another extension. Signature:: + + def extsetup(): + # ... + +Mercurial version 8e6019b16a7d and later (that is post-1.3.1) will pass +a ``ui``` argument to ``extsetup``:: + + def extsetup(ui): + # ... + +Command table setup +------------------- + +After ``extsetup``, the ``cmdtable`` is copied into the global command table +in Mercurial. + +Repository setup +---------------- + +Extensions can implement an optional callback named ``reposetup``. It is +called after the main Mercurial repository initialization, and can be used +to setup any local state the extension might need. + +As other command functions it receives an ``ui`` object and a ``repo`` object +(no additional parameters for this, though):: + + def reposetup(ui, repo): + #do initialization here. + +It is important to take into account that the ``ui`` object that is received +by the ``reposetup`` function is not the same as the one received by the +``uisetup`` and ``extsetup`` functions. This is particularly important when +setting up hooks as described in the following section, since not all hooks +use the same ``ui`` object and hence different hooks must be configured in +different setup functions. + +Wrapping methods on the ui and repo classes +------------------------------------------- + +Because extensions can be loaded *per repository*, you should avoid using +``extensions.wrapfunction()`` on methods of the ``ui`` and ``repo`` objects. +Instead, create a subclass of the specific class of the instance passed into +the ``*setup()`` hook; e.g. use ``ui.__class__`` as the base class, then +reassign your new class to ``ui.__class__`` again. Mercurial will then use +your updated ``ui`` or ``repo`` instance only for repositories where your +extension is enabled (or copies thereof, reusing your new class). + +For example:: + + def uisetup(ui): + class echologui(ui.__class__): + def log(self, service, *msg, **opts): + if msg: + self.write('%s: %s\n' % (service, msg[0] % msg[1:])) + super(echologui, self).log(service, *msg, **opts) + + ui.__class__ = echologui + +Configuring Hooks +================= + +Some extensions must use hooks to do their work. These required hooks can +be configured manually by the user by modifying the ``[hook]`` section of +their hgrc, but they can also be configured automatically by calling the +``ui.setconfig('hooks', ...)`` function in one of the setup functions +described above. + +The main difference between manually modifying the hooks section in the hgrc +and using ``ui.setconfig()`` is that when using ``ui.setconfig()`` you have +access to the actual hook function object, which you can pass directly to +``ui.setconfig()``, while when you use the hooks section of the hgrc file +you must refer to the hook function by using the +``python:modulename.functioname`` idiom (e.g. ``python:hgext.notify.hook``). + +For example:: + + # Define hooks -- note that the actual function name it irrelevant. + def preupdatehook(ui, repo, **kwargs): + ui.write("Pre-update hook triggered\n") + + def updatehook(ui, repo, **kwargs): + ui.write("Update hook triggered\n") + + def uisetup(ui): + # When pre-<cmd> and post-<cmd> hooks are configured by means of + # the ui.setconfig() function, you must use the ui object passed + # to uisetup or extsetup. + ui.setconfig("hooks", "pre-update.myextension", preupdatehook) + + def reposetup(ui, repo): + # Repository-specific hooks can be configured here. These include + # the update hook. + ui.setconfig("hooks", "update.myextension", updatehook) + +Note how different hooks may need to be configured in different setup +functions. In the example you can see that the ``update`` hook must be +configured in the ``reposetup`` function, while the ``pre-update`` hook +must be configured on the ``uisetup`` or the ``extsetup`` functions. + +Marking compatible versions +=========================== + +Every extension should use the ``testedwith`` variable to specify Mercurial +releases it's known to be compatible with. This helps us and users diagnose +where problems are coming from:: + + testedwith = '2.0 2.0.1 2.1 2.1.1 2.1.2' + +Do not use the ``internal`` marker in third-party extensions; we will +immediately drop all bug reports mentioning your extension if we catch you +doing this. + +Similarly, an extension can use the ``buglink`` variable to specify how users +should report issues with the extension. This link will be included in the +error message if the extension produces errors:: + + buglink = 'https://bitbucket.org/USER/REPO/issues' + +Wrap up: what belongs where? +============================ + +You will find here a list of most common tasks, based on setups from the +extensions included in Mercurial core. + +uisetup +------- + +* Changes to ``ui.__class__`` . The ``ui`` object that will be used to run + the command has not yet been created. Changes made here will affect ``ui`` + objects created after this, and in particular the ``ui`` that will be passed + to ``runcommand`` +* Command wraps (``extensions.wrapcommand``) +* Changes that need to be visible by other extensions: because initialization + occurs in phases (all extensions run ``uisetup``, then all run ``extsetup``), + a change made here will be visible by other extensions during ``extsetup``. +* Monkeypatches or function wraps (``extensions.wrapfunction``) of ``dispatch`` + module members +* Setup of ``pre-*`` and ``post-*`` hooks +* ``pushkey`` setup + +extsetup +-------- + +* Changes depending on the status of other extensions. (``if extensions.find('mq')``) +* Add a global option to all commands +* Extend revsets + +reposetup +--------- + +* All hooks but ``pre-*`` and ``post-*`` +* Modify configuration variables +* Changes to ``repo.__class__``, ``repo.dirstate.__class__``
--- a/tests/test-help.t Sun Nov 04 12:51:14 2018 +0900 +++ b/tests/test-help.t Mon Nov 12 21:28:54 2018 +0900 @@ -1066,6 +1066,7 @@ censor Censor changegroups Changegroups config Config Registrar + extensions Extension API requirements Repository Requirements revlogs Revision Logs wireprotocol Wire Protocol @@ -3404,6 +3405,13 @@ Config Registrar </td></tr> <tr><td> + <a href="/help/internals.extensions"> + extensions + </a> + </td><td> + Extension API + </td></tr> + <tr><td> <a href="/help/internals.requirements"> requirements </a>