[PATCH] extensions: formalize concept of experimental extensions

Jun Wu quark at fb.com
Sun Mar 12 22:28:40 EDT 2017


If I read it correctly, this means an "experimental" extension will end up
with:

             | ext=        | ext=!beta
  ----------------------------------------
  old client | load        | do not load
  new client | do not load | load

I think that's confusing.

I think we can have an "[extensions:experimental]" section instead, which
will only be processed by new mercurial.

Excerpts from Gregory Szorc's message of 2017-03-12 17:41:49 -0700:
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc at gmail.com>
> # Date 1489364702 25200
> #      Sun Mar 12 17:25:02 2017 -0700
> # Node ID 6a85d5031daf1ab3a5cb3c6705a46367d5e0de29
> # Parent  1c3352d7eaf24533ad52d4b8a024211e9189fb0b
> extensions: formalize concept of experimental extensions
> 
> Per discussions at the Sprint, we would like to be more
> welcoming to shipping experimental extensions with the official
> Mercurial distribution so they are more easily available to
> end-users.
> 
> A concern with "experimental" extensions is where to put them and
> how to differentiate them as "experimental" to end-users.
> 
> One idea is to put them in a special location or name them in
> such a way that "experimental" (or some other label) is in the
> name end-users use to load them. A problem with this is that if
> the extension "graduates" to fully-supported status, users have to
> update their configs to load the extension at the new name (or
> Mercurial maintains a mapping table, which excludes 3rd party
> extensions from having this benefit).
> 
> This patch formalizes the concept of experimental extensions and
> does so in a way that is user-friendly and allows experimental
> extensions to gracefully graduate to non-experimental status without
> end-user intervention. It does so through 2 mechanisms:
> 
> 1. Extensions now set the "experimental" attribute to mark themselves
>    as experimental
> 2. The extension loader only loads experimental extensions if the
>    config option value of the extension is "!beta"
> 
> If a user attempts to load an experimental mechanism using the
> normal "extensions.<name>=" syntax, a warning message is printed
> saying the extension is "in trial mode" and tells them how to
> activate it. If the value is "!beta" (e.g. extensions.foo=!beta),
> the extension is loaded. This requires explicit affirmation from
> the end-user that an extension is "beta." This should mitigate
> any surprises from a user using an extension without realizing it
> is experimental.
> 
> Because the old extension loading code interpreted a leading "!"
> as "do not load," the "!beta" syntax results in old clients not
> loading experimental extensions. This is a graceful failure.
> 
> A drawback of using "!beta" is that an explicit path to the
> extension cannot be specified at this time. This means the
> extension's name must correspond to a module in the "hgext" or
> "hgext3rd" packages. I think this is acceptable.
> 
> I purposefully chose to use "beta" for the end-user facing value.
> From my experience, users are scared of the "experimental" label.
> In most cases, "experimental" features in Mercurial are more stable
> than the other end of the spectrum. So I wanted to use a label
> that is more reflective of reality, isn't scary, and doesn't
> require strong English knowledge. I consulted a thesaurus for
> suitable synonyms of "experimental" and couldn't find anything
> "just right." So, I used "beta." This is a common technical term
> that most people relate to (thanks, Gmail!) and it accurately
> reflects the state of most extensions that Mercurial will
> distribute in "experimental" form. For users who don't know
> what it means, the translated message printed without "!beta"
> can provide more context.
> 
> diff --git a/mercurial/extensions.py b/mercurial/extensions.py
> --- a/mercurial/extensions.py
> +++ b/mercurial/extensions.py
> @@ -119,9 +119,9 @@ def _reportimporterror(ui, err, failed, 
>               % (failed, _forbytes(err), next))
>      if ui.debugflag:
>          ui.traceback()
>  
> -def load(ui, name, path):
> +def load(ui, name, path, allowexperimental=False):
>      if name.startswith('hgext.') or name.startswith('hgext/'):
>          shortname = name[6:]
>      else:
>          shortname = name
> @@ -141,8 +141,15 @@ def load(ui, name, path):
>          ui.warn(_('(third party extension %s requires version %s or newer '
>                    'of Mercurial; disabling)\n') % (shortname, minver))
>          return
>  
> +    experimental = getattr(mod, 'experimental', None)
> +    if experimental and not allowexperimental:
> +        ui.warn(_('(extension %s is in trial mode and requires explicit '
> +                  'opt-in by setting extensions.%s=!beta; disabling)\n') %
> +                (name, name))
> +        return
> +
>      _extensions[shortname] = mod
>      _order.append(shortname)
>      for fn in _aftercallbacks.get(shortname, []):
>          fn(loaded=True)
> @@ -166,14 +173,18 @@ def _runextsetup(name, ui):
>  def loadall(ui):
>      result = ui.configitems("extensions")
>      newindex = len(_order)
>      for (name, path) in result:
> +        allowexperimental = False
>          if path:
> -            if path[0:1] == '!':
> +            if path.startswith('!beta'):
> +                allowexperimental = True
> +                path = ''
> +            elif path[0:1] == '!':
>                  _disabledextensions[name] = path[1:]
>                  continue
>          try:
> -            load(ui, name, path)
> +            load(ui, name, path, allowexperimental=allowexperimental)
>          except KeyboardInterrupt:
>              raise
>          except Exception as inst:
>              inst = _forbytes(inst)
> diff --git a/tests/test-extension.t b/tests/test-extension.t
> --- a/tests/test-extension.t
> +++ b/tests/test-extension.t
> @@ -1556,4 +1556,17 @@ Test synopsis and docstring extending
>    $ hg help bookmarks | grep GREPME
>    hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
>        GREPME make sure that this is in the help!
>  
> +  $ cd ..
> +
> +Experimental extensions require explicit opt-in via "!beta" value
> +
> +  $ cat > experimental.py << EOF
> +  > experimental = True
> +  > EOF
> +
> +  $ hg init experimental
> +  $ hg -R experimental --config extensions.path=./path.py --config extensions.experimental= log
> +  (extension experimental is in trial mode and requires explicit opt-in by setting extensions.experimental=!beta; disabling)
> +
> +  $ hg -R experimental --config extensions.path=./path.py --config extensions.experimental=!beta log


More information about the Mercurial-devel mailing list