sapling/mercurial
Gregory Szorc 2afb6aff3e extensions: refuse to load extensions if minimum hg version not met
As the author of several 3rd party extensions, I frequently see bug
reports from users attempting to run my extension with an old version
of Mercurial that I no longer support in my extension. Oftentimes, the
extension will import just fine. But as soon as we run extsetup(),
reposetup(), or get into the guts of a wrapped function, we encounter
an exception and abort. Today, Mercurial will print a message about
extensions that don't have a "testedwith" declaring explicit
compatibility with the current version.

The existing mechanism is a good start. But it isn't as robust as I
would like. Specifically, Mercurial assumes compatibility by default.
This means extension authors must perform compatibility checking in
their extsetup() or we wait and see if we encounter an abort at
runtime. And, compatibility checking can involve a lot of code and
lots of error checking. It's a lot of effort for extension authors.

Oftentimes, extension authors know which versions of Mercurial there
extension works on and more importantly where it is broken.

This patch introduces a magic "minimumhgversion" attribute in
extensions. When found, the extension loading mechanism will compare
the declared version against the current Mercurial version. If the
extension explicitly states we require a newer Mercurial version, a
warning is printed and the extension isn't loaded beyond importing
the Python module. This causes a graceful failure while alerting
the user of the compatibility issue.

I would be receptive to the idea of making the failure more fatal.
However, care would need to be taken to not criple every hg command.
e.g. the user may use `hg config` to fix the hgrc and if we aborted
trying to run that, the user would effectively be locked out of `hg`!

A potential future improvement to this functionality would be to catch
ImportError for the extension/module and parse the source code for
"minimumhgversion = 'XXX'" and do similar checking. This way we could
give more information about why the extension failed to load.
2015-11-24 15:16:25 -08:00
..
default.d mergetools.rc: find OSX FileMerge in the new location inside Xcode 4.3 2015-10-16 11:37:34 +02:00
help format: create new repository as 'generaldelta' by default 2015-11-02 17:33:18 +00:00
hgweb hgweb: extract factory function of httpservice object 2015-10-31 22:15:16 +09:00
httpclient global: mass rewrite to use modern exception syntax 2015-06-23 22:20:08 -07:00
pure osutil: remove Python 2.4 errno conversion workaround 2015-06-22 10:11:31 -07:00
templates paper: show current revision on file log page 2015-11-13 18:31:58 +08:00
__init__.py
ancestor.py ancestor: use absolute_import 2015-08-07 19:45:48 -07:00
archival.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
base85.c base85: clean up function definition style 2015-11-11 19:10:45 -05:00
bdiff.c bdiff: avoid a memory error on malloc failure 2013-10-30 16:03:42 -05:00
bookmarks.py bookmarks: use repo._bookmarks.recordchange instead of repo._bookmarks.write 2015-11-17 12:49:57 -08:00
branchmap.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
bundle2.py bundle2: attribute remote failures to remote (issue4788) 2015-10-24 00:39:22 +01:00
bundlerepo.py bundlerepo: properly extract compressed changegroup from bundle2 2015-10-19 16:01:55 +02:00
byterange.py global: mass rewrite to use modern exception syntax 2015-06-23 22:20:08 -07:00
changegroup.py changegroup: back code change of b5988e1d3dcb out 2015-11-06 13:01:15 -05:00
changelog.py reachableroots: construct and sort baseset in revset module 2015-08-28 11:14:24 +09:00
cmdutil.py ui: remove labeled argument from popbuffer 2015-11-24 11:23:10 -08:00
commands.py serve: unify cmdutil.service() calls of commandserver and hgweb 2015-10-31 22:17:05 +09:00
commandserver.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
config.py config: use absolute_import 2015-08-08 00:28:53 -07:00
context.py context: avoid extra parents lookups 2015-11-21 19:21:01 -08:00
copies.py spelling: trivial spell checking 2015-10-17 00:58:46 +02:00
crecord.py spelling: trivial spell checking 2015-10-17 00:58:46 +02:00
dagparser.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
dagutil.py dagutil: use absolute_import 2015-08-08 19:04:09 -07:00
demandimport.py demandimport: don't enable when running under PyPy 2015-11-21 22:28:01 -08:00
destutil.py destmerge: extract logic based on branch heads in its own function 2015-10-15 03:15:54 +01:00
diffhelpers.c diffhelpers: fix botched return statement from e48d70075d8a 2015-01-28 13:34:20 -05:00
dirs.c dirs._addpath: reinstate use of Py_CLEAR 2015-04-07 20:43:04 -07:00
dirstate.py util: drop statmtimesec 2015-11-19 13:15:17 -06:00
discovery.py discovery: pass pushop to _nowarnheads 2015-11-10 11:16:25 -08:00
dispatch.py dispatch: use versiontuple() 2015-11-24 14:23:46 -08:00
dummycert.pem ssl: on OS X, use a dummy cert to trick Python/OpenSSL to use system CA certs 2014-09-26 02:19:48 +02:00
encoding.py encoding: extend test cases for utf8b 2015-11-02 17:17:33 -06:00
error.py error: add a structured exception for unsupported merge records 2015-11-17 14:10:12 -08:00
exchange.py exchange: do not attempt clone bundle if local repo is non-empty (issue4932) 2015-11-03 12:16:54 -08:00
exewrapper.c exewrapper: add comments about PYTHONHOME 2015-10-14 12:23:49 +02:00
extensions.py extensions: refuse to load extensions if minimum hg version not met 2015-11-24 15:16:25 -08:00
fancyopts.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
filelog.py filelog: use absolute_import 2015-08-08 19:11:42 -07:00
filemerge.py filemerge: in ':prompt', use ':fail' tool rather than returning directly 2015-11-24 10:58:35 -08:00
fileset.py fileset: add missing() predicate (issue4925) 2015-11-18 20:55:32 +01:00
formatter.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
graphmod.py graphmod: compute slow revset query once prior to reachableroots (issue4782) 2015-09-08 23:00:44 +09:00
hbisect.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
help.py help: replace some str.split() calls by str.partition() or str.rpartition() 2015-11-02 23:37:14 +08:00
hg.py spelling: trivial spell checking 2015-10-17 00:58:46 +02:00
hook.py hooks: back e7b51de6e8eb out 2015-11-06 11:08:11 -05:00
httpconnection.py httpconnection: remove a mutable default argument 2015-09-24 00:54:30 -07:00
httppeer.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
i18n.py i18n: use absolute_import 2015-08-08 19:28:49 -07:00
keepalive.py global: mass rewrite to use modern exception syntax 2015-06-23 22:20:08 -07:00
localrepo.py localrepo: improve docstring for revset methods 2015-11-21 11:07:30 -08:00
lock.py lock: add a way to prevent locks from being inherited 2015-10-06 13:13:31 -07:00
lsprof.py lsprof: support PyPy (issue4573) 2015-11-21 23:26:22 -08:00
lsprofcalltree.py
mail.py mail: drop python 2.5 self.sock.read workaround 2015-10-15 17:24:42 -04:00
manifest.c lazymanifest: prevent leak when updating an entry more than once 2015-04-11 11:56:21 -04:00
manifest.py manifest: skip fastdelta if the change is large 2015-11-05 18:56:40 -08:00
match.py spelling: trivial spell checking 2015-10-17 00:58:46 +02:00
mdiff.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
merge.py merge: move almost all change/delete conflicts to resolve phase (BC) (API) 2015-11-25 14:25:33 -08:00
minirst.py spelling: trivial spell checking 2015-10-17 00:58:46 +02:00
mpatch.c mpatch: rewrite pointer overflow checks 2013-12-11 18:33:42 -06:00
namespaces.py namespaces: use absolute_import 2015-08-08 19:42:58 -07:00
node.py node: add 'nullhex', hex-encoded nullid 2015-11-16 11:23:32 -08:00
obsolete.py obsstore: make the readonly attribute accessible 2015-10-15 12:45:34 +01:00
osutil.c osutil: make statfiles check for interrupts periodically 2015-11-17 13:47:14 -08:00
parser.py parser: move unescape helper from templater 2015-09-10 23:25:10 +09:00
parsers.c parsers: fix width of datalen variable in fm1readmarkers 2015-11-07 17:43:20 +09:00
patch.py spelling: trivial spell checking 2015-10-17 00:58:46 +02:00
pathencode.c pathencode: check result of .digest() method in sha1hash 2015-08-18 16:32:41 -04:00
pathutil.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
peer.py peer: use absolute_import 2015-08-08 19:45:45 -07:00
phases.py phase: improve retractboundary perf 2015-11-07 16:11:49 -08:00
posix.py posix: fix test-permissions regression 2015-11-09 15:53:11 -06:00
progress.py spelling: trivial spell checking 2015-10-17 00:58:46 +02:00
pushkey.py pushkey: use absolute_import 2015-08-08 19:57:27 -07:00
pvec.py pvec: replace 'ctx._repo' with 'ctx.repo()' 2015-03-12 23:18:20 -04:00
py3kcompat.py py3kcompat: drop unused export 2014-05-13 15:22:36 -05:00
repair.py strip: pass source and url to bundle2 processing 2015-10-20 16:01:33 +02:00
repoview.py repoview: use absolute_import 2015-08-08 19:58:05 -07:00
revlog.py revlog: improve documentation 2015-11-22 16:23:20 -08:00
revset.py revset: speed up '_matchfiles' 2015-11-18 23:23:03 -08:00
scmposix.py config: don't read the same config file twice 2014-09-04 21:36:35 +02:00
scmutil.py format: create new repository as 'generaldelta' by default 2015-11-02 17:33:18 +00:00
scmwindows.py windows: read all global config files, not just the first (issue4491) (BC) 2015-10-12 20:13:12 +02:00
setdiscovery.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
similar.py cleanup: eradicate long lines 2012-05-12 15:54:54 +02:00
simplemerge.py simplemerge: move conflict warning message to filemerge 2015-10-09 13:54:52 -07:00
sshpeer.py spelling: trivial spell checking 2015-10-17 00:58:46 +02:00
sshserver.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
sslutil.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
statichttprepo.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
store.py l10n: use %d instead of %s for numbers 2015-10-14 22:29:03 -04:00
streamclone.py streamclone: support for producing and consuming stream clone bundles 2015-10-17 11:14:52 -07:00
strutil.py strutil: use absolute_import 2015-08-08 20:01:40 -07:00
subrepo.py git-subrepos: revert respects specified location to save .orig files 2015-11-10 14:29:13 -08:00
tagmerge.py tagmerge: use absolute_import 2015-08-08 20:10:46 -07:00
tags.py tags: create new sortdict for performance reasons 2015-11-12 13:16:04 -08:00
templatefilters.py templatefilters: try round-trip utf-8 conversion by json filter (issue4933) 2015-11-04 23:48:15 +09:00
templatekw.py templatekw: add {changes}, {distance} and {tag} to the {latesttag} keyword 2015-08-24 23:30:17 -04:00
templater.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
transaction.py spelling: fix typo in transaction error messages 2015-10-17 15:28:02 -05:00
treediscovery.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
ui.py ui: remove labeled argument from popbuffer 2015-11-24 11:23:10 -08:00
unionrepo.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00
url.py url: drop support for python2.5 2015-10-15 17:21:08 -04:00
util.h util: fix the check for non-C99 compilers (issue4605) 2015-04-20 22:21:57 -05:00
util.py util: add versiontuple() for returning parsed version information 2015-11-24 14:23:51 -08:00
verify.py verify: add a hook that can let extensions manipulate file lists 2015-11-04 12:14:18 -05:00
win32.py win32: use absolute_import 2015-08-08 18:52:59 -07:00
windows.py windows: insert file positioning call between reads and writes 2015-09-27 18:46:53 -07:00
wireproto.py wireproto: move clonebundles command from extension (issue4931) 2015-11-03 12:31:33 -08:00
worker.py error: get Abort from 'error' instead of 'util' 2015-10-08 12:55:45 -07:00