hgext/commitextras.py
author Yuya Nishihara <yuya@tcha.org>
Sun, 24 Apr 2016 18:41:23 +0900
changeset 34337 89aec1834a86
parent 33606 806351695c6a
child 35030 901a18b03e00
permissions -rw-r--r--
templatekw: add new-style template expansion to {manifest} The goal is to allow us to easily access to nested data. The dot operator will be introduced later so we can write '{p1.files}' instead of '{revset("p1()") % "{files}"}' for example. In the example above, 'p1' needs to carry a mapping dict along with its string representation. If it were a list or a dict, it could be wrapped semi-transparently with the _hybrid class, but for non-list/dict types, it would be difficult to proxy all necessary functions to underlying value type because several core operations may conflict with the ones of the underlying value: - hash(value) should be different from hash(wrapped(value)), which means dict[wrapped(value)] would be invalid - 'value == wrapped(value)' would be false, breaks 'ifcontains' - len(wrapped(value)) may be either len(value) or len(iter(wrapped(value))) So the wrapper has no proxy functions and its scope designed to be minimal. It's unwrapped at eval*() functions so we don't have to care for a wrapped object unless it's really needed: # most template functions just call evalfuncarg() unwrapped_value = evalfuncarg(context, mapping, args[n]) # if wrapped value is needed, use evalrawexp() maybe_wrapped_value = evalrawexp(context, mapping, args[n]) Another idea was to wrap every template variable with a tagging class, but which seemed uneasy without a static type checker. This patch updates {manifest} to a mappable as an example.

# commitextras.py
#
# Copyright 2013 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

'''adds a new flag extras to commit (ADVANCED)'''

from __future__ import absolute_import

import re

from mercurial.i18n import _
from mercurial import (
    commands,
    error,
    extensions,
    registrar,
)

cmdtable = {}
command = registrar.command(cmdtable)
testedwith = 'ships-with-hg-core'

usedinternally = {
    'amend_source',
    'branch',
    'close',
    'histedit_source',
    'topic',
    'rebase_source',
    'intermediate-source',
    '__touch-noise__',
    'source',
    'transplant_source',
}

def extsetup(ui):
    entry = extensions.wrapcommand(commands.table, 'commit', _commit)
    options = entry[1]
    options.append(('', 'extra', [],
        _('set a changeset\'s extra values'), _("KEY=VALUE")))

def _commit(orig, ui, repo, *pats, **opts):
    origcommit = repo.commit
    try:
        def _wrappedcommit(*innerpats, **inneropts):
            extras = opts.get('extra')
            if extras:
                for raw in extras:
                    if '=' not in raw:
                        msg = _("unable to parse '%s', should follow "
                                "KEY=VALUE format")
                        raise error.Abort(msg % raw)
                    k, v = raw.split('=', 1)
                    if not k:
                        msg = _("unable to parse '%s', keys can't be empty")
                        raise error.Abort(msg % raw)
                    if re.search('[^\w-]', k):
                        msg = _("keys can only contain ascii letters, digits,"
                                " '_' and '-'")
                        raise error.Abort(msg)
                    if k in usedinternally:
                        msg = _("key '%s' is used internally, can't be set "
                                "manually")
                        raise error.Abort(msg % k)
                    inneropts['extra'][k] = v
            return origcommit(*innerpats, **inneropts)

        # This __dict__ logic is needed because the normal
        # extension.wrapfunction doesn't seem to work.
        repo.__dict__['commit'] = _wrappedcommit
        return orig(ui, repo, *pats, **opts)
    finally:
        del repo.__dict__['commit']