# HG changeset patch # User Gregory Szorc <gregory.sz...@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 _______________________________________________ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel