changeset 8110:f713a37564c0

vcs: drop the superfluous and leaky hgcompat "layer" Explicit is better. And gives less pyflakes noise.
author Mads Kiilerich <mads@kiilerich.com>
date Thu, 02 Jan 2020 20:39:13 +0100
parents e527cc2ce8dc
children 4eacfdf08b9a
files kallithea/controllers/compare.py kallithea/controllers/pullrequests.py kallithea/lib/hooks.py kallithea/lib/middleware/simplehg.py kallithea/lib/utils.py kallithea/lib/vcs/backends/git/repository.py kallithea/lib/vcs/backends/hg/__init__.py kallithea/lib/vcs/backends/hg/changeset.py kallithea/lib/vcs/backends/hg/inmemory.py kallithea/lib/vcs/backends/hg/repository.py kallithea/lib/vcs/backends/hg/ssh.py kallithea/lib/vcs/backends/hg/workdir.py kallithea/lib/vcs/utils/hgcompat.py kallithea/tests/vcs/test_hg.py
diffstat 14 files changed, 105 insertions(+), 92 deletions(-) [+]
line wrap: on
line diff
--- a/kallithea/controllers/compare.py	Thu Jan 02 00:44:56 2020 +0100
+++ b/kallithea/controllers/compare.py	Thu Jan 02 20:39:13 2020 +0100
@@ -30,6 +30,7 @@
 import logging
 import re
 
+import mercurial.unionrepo
 from tg import request
 from tg import tmpl_context as c
 from tg.i18n import ugettext as _
@@ -43,7 +44,6 @@
 from kallithea.lib.base import BaseRepoController, render
 from kallithea.lib.graphmod import graph_data
 from kallithea.lib.utils2 import ascii_bytes, ascii_str, safe_int, safe_str
-from kallithea.lib.vcs.utils.hgcompat import unionrepo
 from kallithea.model.db import Repository
 
 
@@ -97,7 +97,7 @@
         elif alias == 'hg':
             # case two independent repos
             if org_repo != other_repo:
-                hgrepo = unionrepo.makeunionrepository(other_repo.baseui,
+                hgrepo = mercurial.unionrepo.makeunionrepository(other_repo.baseui,
                                                        other_repo.path,
                                                        org_repo.path)
                 # all ancestors of other_rev will be in other_repo and
--- a/kallithea/controllers/pullrequests.py	Thu Jan 02 00:44:56 2020 +0100
+++ b/kallithea/controllers/pullrequests.py	Thu Jan 02 20:39:13 2020 +0100
@@ -29,6 +29,7 @@
 import traceback
 
 import formencode
+import mercurial.unionrepo
 from tg import request
 from tg import tmpl_context as c
 from tg.i18n import ugettext as _
@@ -44,7 +45,6 @@
 from kallithea.lib.page import Page
 from kallithea.lib.utils2 import ascii_bytes, safe_bytes, safe_int, safe_str
 from kallithea.lib.vcs.exceptions import ChangesetDoesNotExistError, EmptyRepositoryError
-from kallithea.lib.vcs.utils.hgcompat import unionrepo
 from kallithea.model.changeset_status import ChangesetStatusModel
 from kallithea.model.comment import ChangesetCommentsModel
 from kallithea.model.db import ChangesetStatus, PullRequest, PullRequestReviewer, Repository, User
@@ -533,7 +533,7 @@
                             # Note: org_scm_instance.path must come first so all
                             # valid revision numbers are 100% org_scm compatible
                             # - both for avail_revs and for revset results
-                            hgrepo = unionrepo.makeunionrepository(org_scm_instance.baseui,
+                            hgrepo = mercurial.unionrepo.makeunionrepository(org_scm_instance.baseui,
                                                                    org_scm_instance.path,
                                                                    other_scm_instance.path)
                         else:
--- a/kallithea/lib/hooks.py	Thu Jan 02 00:44:56 2020 +0100
+++ b/kallithea/lib/hooks.py	Thu Jan 02 20:39:13 2020 +0100
@@ -28,12 +28,13 @@
 import os
 import time
 
+import mercurial.scmutil
+
 from kallithea.lib import helpers as h
 from kallithea.lib.exceptions import UserCreationError
 from kallithea.lib.utils import action_logger, make_ui, setup_cache_regions
 from kallithea.lib.utils2 import ascii_str, get_hook_environment, safe_str, safe_unicode
 from kallithea.lib.vcs.backends.base import EmptyChangeset
-from kallithea.lib.vcs.utils.hgcompat import revrange
 from kallithea.model.db import Repository, User
 
 
@@ -108,7 +109,7 @@
     Note: This hook is not only logging, but also the side effect invalidating
     cahes! The function should perhaps be renamed.
     """
-    revs = [ascii_str(repo[r].hex()) for r in revrange(repo, [b'%s:%s' % (node, node_last)])]
+    revs = [ascii_str(repo[r].hex()) for r in mercurial.scmutil.revrange(repo, [b'%s:%s' % (node, node_last)])]
     process_pushed_raw_ids(revs)
     return 0
 
--- a/kallithea/lib/middleware/simplehg.py	Thu Jan 02 00:44:56 2020 +0100
+++ b/kallithea/lib/middleware/simplehg.py	Thu Jan 02 20:39:13 2020 +0100
@@ -32,10 +32,11 @@
 import os
 import urllib
 
+import mercurial.hgweb
+
 from kallithea.lib.base import BaseVCSController, get_path_info
 from kallithea.lib.utils import make_ui
 from kallithea.lib.utils2 import safe_str, safe_unicode
-from kallithea.lib.vcs.utils.hgcompat import hgweb_mod
 
 
 log = logging.getLogger(__name__)
@@ -139,10 +140,10 @@
         str_repo_name = safe_str(parsed_request.repo_name)
         repo_path = os.path.join(safe_str(self.basepath), str_repo_name)
         baseui = make_ui(repo_path=repo_path)
-        hgweb_app = hgweb_mod.hgweb(repo_path, name=str_repo_name, baseui=baseui)
+        hgweb_app = mercurial.hgweb.hgweb(repo_path, name=str_repo_name, baseui=baseui)
 
         def wrapper_app(environ, start_response):
-            environ['REPO_NAME'] = str_repo_name # used by hgweb_mod.hgweb
+            environ['REPO_NAME'] = str_repo_name # used by mercurial.hgweb.hgweb
             return hgweb_app(environ, start_response)
 
         return wrapper_app
--- a/kallithea/lib/utils.py	Thu Jan 02 00:44:56 2020 +0100
+++ b/kallithea/lib/utils.py	Thu Jan 02 20:39:13 2020 +0100
@@ -34,6 +34,8 @@
 from distutils.version import StrictVersion
 
 import beaker
+import mercurial.config
+import mercurial.ui
 from beaker.cache import _cache_decorate
 from tg.i18n import ugettext as _
 
@@ -46,7 +48,6 @@
 from kallithea.lib.vcs.exceptions import RepositoryError, VCSError
 from kallithea.lib.vcs.utils.fakemod import create_module
 from kallithea.lib.vcs.utils.helpers import get_scm
-from kallithea.lib.vcs.utils.hgcompat import config, ui
 from kallithea.model import meta
 from kallithea.model.db import RepoGroup, Repository, Setting, Ui, User, UserGroup, UserLog
 
@@ -327,12 +328,12 @@
     Create an Mercurial 'ui' object based on database Ui settings, possibly
     augmenting with content from a hgrc file.
     """
-    baseui = ui.ui()
+    baseui = mercurial.ui.ui()
 
     # clean the baseui object
-    baseui._ocfg = config.config()
-    baseui._ucfg = config.config()
-    baseui._tcfg = config.config()
+    baseui._ocfg = mercurial.config.config()
+    baseui._ucfg = mercurial.config.config()
+    baseui._tcfg = mercurial.config.config()
 
     sa = meta.Session()
     for ui_ in sa.query(Ui).all():
@@ -356,7 +357,7 @@
         hgrc_path = os.path.join(repo_path, '.hg', 'hgrc')
         if os.path.isfile(hgrc_path):
             log.debug('reading hgrc from %s', hgrc_path)
-            cfg = config.config()
+            cfg = mercurial.config.config()
             cfg.read(hgrc_path)
             for section in ui_sections:
                 for k, v in cfg.items(section):
--- a/kallithea/lib/vcs/backends/git/repository.py	Thu Jan 02 00:44:56 2020 +0100
+++ b/kallithea/lib/vcs/backends/git/repository.py	Thu Jan 02 20:39:13 2020 +0100
@@ -18,6 +18,8 @@
 import urllib2
 from collections import OrderedDict
 
+import mercurial.url  # import httpbasicauthhandler, httpdigestauthhandler
+import mercurial.util  # import url as hg_url
 from dulwich.config import ConfigFile
 from dulwich.objects import Tag
 from dulwich.repo import NotGitRepository, Repo
@@ -28,7 +30,6 @@
 from kallithea.lib.vcs.exceptions import (
     BranchDoesNotExistError, ChangesetDoesNotExistError, EmptyRepositoryError, RepositoryError, TagAlreadyExistError, TagDoesNotExistError)
 from kallithea.lib.vcs.utils import ascii_str, date_fromtimestamp, makedate, safe_str, safe_unicode
-from kallithea.lib.vcs.utils.hgcompat import hg_url, httpbasicauthhandler, httpdigestauthhandler
 from kallithea.lib.vcs.utils.lazy import LazyProperty
 from kallithea.lib.vcs.utils.paths import abspath, get_user_home
 
@@ -168,7 +169,7 @@
             url = url[url.find('+') + 1:]
 
         handlers = []
-        url_obj = hg_url(url)
+        url_obj = mercurial.util.url(url)
         test_uri, authinfo = url_obj.authinfo()
         if not test_uri.endswith('info/refs'):
             test_uri = test_uri.rstrip('/') + '/info/refs'
@@ -181,8 +182,8 @@
             passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
             passmgr.add_password(*authinfo)
 
-            handlers.extend((httpbasicauthhandler(passmgr),
-                             httpdigestauthhandler(passmgr)))
+            handlers.extend((mercurial.url.httpbasicauthhandler(passmgr),
+                             mercurial.url.httpdigestauthhandler(passmgr)))
 
         o = urllib2.build_opener(*handlers)
         o.addheaders = [('User-Agent', 'git/1.7.8.0')]  # fake some git
--- a/kallithea/lib/vcs/backends/hg/__init__.py	Thu Jan 02 00:44:56 2020 +0100
+++ b/kallithea/lib/vcs/backends/hg/__init__.py	Thu Jan 02 20:39:13 2020 +0100
@@ -9,6 +9,8 @@
     :copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak.
 """
 
+from kallithea.lib.vcs.utils import hgcompat
+
 from .changeset import MercurialChangeset
 from .inmemory import MercurialInMemoryChangeset
 from .repository import MercurialRepository
@@ -19,3 +21,5 @@
     'MercurialRepository', 'MercurialChangeset',
     'MercurialInMemoryChangeset', 'MercurialWorkdir',
 ]
+
+hgcompat.monkey_do()
--- a/kallithea/lib/vcs/backends/hg/changeset.py	Thu Jan 02 00:44:56 2020 +0100
+++ b/kallithea/lib/vcs/backends/hg/changeset.py	Thu Jan 02 20:39:13 2020 +0100
@@ -1,13 +1,16 @@
 import os
 import posixpath
 
+import mercurial.archival
+import mercurial.node
+import mercurial.obsutil
+
 from kallithea.lib.vcs.backends.base import BaseChangeset
 from kallithea.lib.vcs.conf import settings
 from kallithea.lib.vcs.exceptions import ChangesetDoesNotExistError, ChangesetError, ImproperArchiveTypeError, NodeDoesNotExistError, VCSError
 from kallithea.lib.vcs.nodes import (
     AddedFileNodesGenerator, ChangedFileNodesGenerator, DirNode, FileNode, NodeKind, RemovedFileNodesGenerator, RootNode, SubModuleNode)
 from kallithea.lib.vcs.utils import ascii_bytes, ascii_str, date_fromtimestamp, safe_str, safe_unicode
-from kallithea.lib.vcs.utils.hgcompat import archival, hex, obsutil
 from kallithea.lib.vcs.utils.lazy import LazyProperty
 from kallithea.lib.vcs.utils.paths import get_dirs_for_path
 
@@ -72,17 +75,17 @@
 
     @LazyProperty
     def successors(self):
-        successors = obsutil.successorssets(self._ctx._repo, self._ctx.node(), closest=True)
+        successors = mercurial.obsutil.successorssets(self._ctx._repo, self._ctx.node(), closest=True)
         if successors:
             # flatten the list here handles both divergent (len > 1)
             # and the usual case (len = 1)
-            successors = [hex(n)[:12] for sub in successors for n in sub if n != self._ctx.node()]
+            successors = [mercurial.node.hex(n)[:12] for sub in successors for n in sub if n != self._ctx.node()]
 
         return successors
 
     @LazyProperty
     def predecessors(self):
-        return [hex(n)[:12] for n in obsutil.closestpredecessors(self._ctx._repo, self._ctx.node())]
+        return [mercurial.node.hex(n)[:12] for n in mercurial.obsutil.closestpredecessors(self._ctx._repo, self._ctx.node())]
 
     @LazyProperty
     def bookmarks(self):
@@ -271,7 +274,7 @@
         cnt = 0
         for cs in reversed([x for x in fctx.filelog()]):
             cnt += 1
-            hist.append(hex(fctx.filectx(cs).node()))
+            hist.append(mercurial.node.hex(fctx.filectx(cs).node()))
             if limit is not None and cnt == limit:
                 break
 
@@ -320,7 +323,7 @@
         elif prefix.strip() == '':
             raise VCSError("Prefix cannot be empty")
 
-        archival.archive(self.repository._repo, stream, ascii_bytes(self.raw_id),
+        mercurial.archival.archive(self.repository._repo, stream, ascii_bytes(self.raw_id),
                          kind, prefix=prefix, subrepos=subrepos)
 
     def get_nodes(self, path):
--- a/kallithea/lib/vcs/backends/hg/inmemory.py	Thu Jan 02 00:44:56 2020 +0100
+++ b/kallithea/lib/vcs/backends/hg/inmemory.py	Thu Jan 02 20:39:13 2020 +0100
@@ -1,9 +1,11 @@
 import datetime
 
+import mercurial.context
+import mercurial.node
+
 from kallithea.lib.vcs.backends.base import BaseInMemoryChangeset
 from kallithea.lib.vcs.exceptions import RepositoryError
 from kallithea.lib.vcs.utils import ascii_str, safe_bytes
-from kallithea.lib.vcs.utils.hgcompat import hex, memctx, memfilectx
 
 
 class MercurialInMemoryChangeset(BaseInMemoryChangeset):
@@ -51,7 +53,7 @@
             # check if this path is added
             for node in self.added:
                 if node.path == path:
-                    return memfilectx(_repo, memctx, path=node.path,
+                    return mercurial.context.memfilectx(_repo, memctx, path=node.path,
                         data=node.content,
                         islink=False,
                         isexec=node.is_executable,
@@ -60,7 +62,7 @@
             # or changed
             for node in self.changed:
                 if node.path == path:
-                    return memfilectx(_repo, memctx, path=node.path,
+                    return mercurial.context.memfilectx(_repo, memctx, path=node.path,
                         data=node.content,
                         islink=False,
                         isexec=node.is_executable,
@@ -77,7 +79,8 @@
         if date and isinstance(date, datetime.datetime):
             date = date.strftime('%a, %d %b %Y %H:%M:%S')
 
-        commit_ctx = memctx(repo=self.repository._repo,
+        commit_ctx = mercurial.context.memctx(
+            repo=self.repository._repo,
             parents=parents,
             text=b'',
             files=self.get_paths(),
@@ -98,7 +101,7 @@
         # Update vcs repository object & recreate mercurial _repo
         # new_ctx = self.repository._repo[node]
         # new_tip = ascii_str(self.repository.get_changeset(new_ctx.hex()))
-        self.repository.revisions.append(ascii_str(hex(n)))
+        self.repository.revisions.append(ascii_str(mercurial.node.hex(n)))
         self._repo = self.repository._get_repo(create=False)
         self.repository.branches = self.repository._get_branches()
         tip = self.repository.get_changeset()
--- a/kallithea/lib/vcs/backends/hg/repository.py	Thu Jan 02 00:44:56 2020 +0100
+++ b/kallithea/lib/vcs/backends/hg/repository.py	Thu Jan 02 20:39:13 2020 +0100
@@ -17,12 +17,27 @@
 import urllib2
 from collections import OrderedDict
 
+import mercurial.commands
+import mercurial.error
+import mercurial.exchange
+import mercurial.hg
+import mercurial.hgweb
+import mercurial.httppeer
+import mercurial.match
+import mercurial.mdiff
+import mercurial.node
+import mercurial.patch
+import mercurial.scmutil
+import mercurial.sshpeer
+import mercurial.tags
+import mercurial.ui
+import mercurial.url
+import mercurial.util
+
 from kallithea.lib.vcs.backends.base import BaseRepository, CollectionGenerator
 from kallithea.lib.vcs.exceptions import (
     BranchDoesNotExistError, ChangesetDoesNotExistError, EmptyRepositoryError, RepositoryError, TagAlreadyExistError, TagDoesNotExistError, VCSError)
 from kallithea.lib.vcs.utils import ascii_str, author_email, author_name, date_fromtimestamp, makedate, safe_bytes, safe_str, safe_unicode
-from kallithea.lib.vcs.utils.hgcompat import (
-    Abort, RepoError, RepoLookupError, clone, diffopts, get_contact, hex, hg_url, httpbasicauthhandler, httpdigestauthhandler, httppeer, localrepo, match_exact, nullid, patch, peer, scmutil, sshpeer, tag, ui)
 from kallithea.lib.vcs.utils.lazy import LazyProperty
 from kallithea.lib.vcs.utils.paths import abspath
 
@@ -62,7 +77,7 @@
                            type(repo_path))
 
         self.path = abspath(repo_path)
-        self.baseui = baseui or ui.ui()
+        self.baseui = baseui or mercurial.ui.ui()
         # We've set path and ui, now we can set _repo itself
         self._repo = self._get_repo(create, src_url, update_after_clone)
 
@@ -118,10 +133,10 @@
         for bn, _heads, node, isclosed in sorted(self._repo.branchmap().iterbranches()):
             if isclosed:
                 if closed:
-                    bt[safe_unicode(bn)] = ascii_str(hex(node))
+                    bt[safe_unicode(bn)] = ascii_str(mercurial.node.hex(node))
             else:
                 if normal:
-                    bt[safe_unicode(bn)] = ascii_str(hex(node))
+                    bt[safe_unicode(bn)] = ascii_str(mercurial.node.hex(node))
         return bt
 
     @LazyProperty
@@ -136,7 +151,7 @@
             return {}
 
         return OrderedDict(sorted(
-            ((safe_unicode(n), ascii_str(hex(h))) for n, h in self._repo.tags().items()),
+            ((safe_unicode(n), ascii_str(mercurial.node.hex(h))) for n, h in self._repo.tags().items()),
             reverse=True,
             key=lambda x: x[0],  # sort by name
         ))
@@ -167,8 +182,8 @@
             date = datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S')
 
         try:
-            tag(self._repo, name, changeset._ctx.node(), message, local, user, date)
-        except Abort as e:
+            mercurial.tags.tag(self._repo, name, changeset._ctx.node(), message, local, user, date)
+        except mercurial.error.Abort as e:
             raise RepositoryError(e.message)
 
         # Reinitialize tags
@@ -197,9 +212,9 @@
         local = False
 
         try:
-            tag(self._repo, name, nullid, message, local, user, date)
+            mercurial.tags.tag(self._repo, name, mercurial.commands.nullid, message, local, user, date)
             self.tags = self._get_tags()
-        except Abort as e:
+        except mercurial.error.Abort as e:
             raise RepositoryError(e.message)
 
     @LazyProperty
@@ -256,12 +271,12 @@
             self.get_changeset(rev1)
         self.get_changeset(rev2)
         if path:
-            file_filter = match_exact(path)
+            file_filter = mercurial.match.exact(path)
         else:
             file_filter = None
 
-        return b''.join(patch.diff(self._repo, rev1, rev2, match=file_filter,
-                          opts=diffopts(git=True,
+        return b''.join(mercurial.patch.diff(self._repo, rev1, rev2, match=file_filter,
+                          opts=mercurial.mdiff.diffopts(git=True,
                                         showfunc=True,
                                         ignorews=ignore_whitespace,
                                         context=context)))
@@ -284,7 +299,7 @@
         if url.startswith(b'ssh:'):
             # in case of invalid uri or authentication issues, sshpeer will
             # throw an exception.
-            sshpeer.instance(repoui or ui.ui(), url, False).lookup(b'tip')
+            mercurial.sshpeer.instance(repoui or mercurial.ui.ui(), url, False).lookup(b'tip')
             return True
 
         url_prefix = None
@@ -292,7 +307,7 @@
             url_prefix, url = url.split(b'+', 1)
 
         handlers = []
-        url_obj = hg_url(url)
+        url_obj = mercurial.util.url(url)
         test_uri, authinfo = url_obj.authinfo()
         url_obj.passwd = b'*****'
         cleaned_uri = str(url_obj)
@@ -302,8 +317,8 @@
             passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
             passmgr.add_password(*authinfo)
 
-            handlers.extend((httpbasicauthhandler(passmgr),
-                             httpdigestauthhandler(passmgr)))
+            handlers.extend((mercurial.url.httpbasicauthhandler(passmgr),
+                             mercurial.url.httpdigestauthhandler(passmgr)))
 
         o = urllib2.build_opener(*handlers)
         o.addheaders = [('Content-Type', 'application/mercurial-0.1'),
@@ -326,7 +341,7 @@
         if not url_prefix: # skip svn+http://... (and git+... too)
             # now check if it's a proper hg repo
             try:
-                httppeer.instance(repoui or ui.ui(), url, False).lookup(b'tip')
+                mercurial.httppeer.instance(repoui or mercurial.ui.ui(), url, False).lookup(b'tip')
             except Exception as e:
                 raise urllib2.URLError(
                     "url [%s] does not look like an hg repo org_exc: %s"
@@ -352,12 +367,12 @@
                 if not update_after_clone:
                     opts.update({'noupdate': True})
                 MercurialRepository._check_url(url, self.baseui)
-                clone(self.baseui, url, self.path, **opts)
+                mercurial.commands.clone(self.baseui, url, self.path, **opts)
 
                 # Don't try to create if we've already cloned repo
                 create = False
-            return localrepo.instance(self.baseui, self.path, create=create)
-        except (Abort, RepoError) as err:
+            return mercurial.localrepo.instance(self.baseui, self.path, create=create)
+        except (mercurial.error.Abort, mercurial.error.RepoError) as err:
             if create:
                 msg = "Cannot create repository at %s. Original error was %s" \
                     % (self.path, err)
@@ -377,7 +392,7 @@
 
     @LazyProperty
     def contact(self):
-        return safe_unicode(get_contact(self._repo.ui.config)
+        return safe_unicode(mercurial.hgweb.common.get_contact(self._repo.ui.config)
                             or b'Unknown')
 
     @LazyProperty
@@ -416,8 +431,8 @@
         try:
             if isinstance(revision, int):
                 return ascii_str(self._repo[revision].hex())
-            return ascii_str(scmutil.revsymbol(self._repo, revision).hex())
-        except (IndexError, ValueError, RepoLookupError, TypeError):
+            return ascii_str(mercurial.scmutil.revsymbol(self._repo, revision).hex())
+        except (IndexError, ValueError, mercurial.error.RepoLookupError, TypeError):
             msg = ("Revision %s does not exist for %s" % (revision, self))
             raise ChangesetDoesNotExistError(msg)
         except (LookupError, ):
@@ -445,7 +460,7 @@
         except LookupError:
             msg = ("Ambiguous identifier %s:%s for %s" % (ref_type, ref_name, self.name))
             raise ChangesetDoesNotExistError(msg)
-        except RepoLookupError:
+        except mercurial.error.RepoLookupError:
             msg = ("Revision %s:%s does not exist for %s" % (ref_type, ref_name, self.name))
             raise ChangesetDoesNotExistError(msg)
         if revs:
@@ -533,7 +548,7 @@
                 revspec = b'all()'
             if max_revisions:
                 revspec = b'limit(%s, %d)' % (revspec, max_revisions)
-            revisions = scmutil.revrange(self._repo, [revspec])
+            revisions = mercurial.scmutil.revrange(self._repo, [revspec])
         else:
             revisions = self.revisions
 
@@ -550,11 +565,10 @@
         Tries to pull changes from external location.
         """
         url = self._get_url(url)
-        other = peer(self._repo, {}, url)
+        other = mercurial.hg.peer(self._repo, {}, url)
         try:
-            from mercurial import exchange
-            exchange.pull(self._repo, other, heads=None, force=None)
-        except Abort as err:
+            mercurial.exchange.pull(self._repo, other, heads=None, force=None)
+        except mercurial.error.Abort as err:
             # Propagate error but with vcs's type
             raise RepositoryError(str(err))
 
@@ -581,7 +595,7 @@
 
         config = self._repo.ui
         if config_file:
-            config = ui.ui()
+            config = mercurial.ui.ui()
             for path in config_file:
                 config.readconfig(path)
         return config.config(section, name)
--- a/kallithea/lib/vcs/backends/hg/ssh.py	Thu Jan 02 00:44:56 2020 +0100
+++ b/kallithea/lib/vcs/backends/hg/ssh.py	Thu Jan 02 20:39:13 2020 +0100
@@ -14,8 +14,8 @@
 
 import logging
 
-from mercurial import hg
-from mercurial.wireprotoserver import sshserver
+import mercurial.hg
+import mercurial.wireprotoserver
 
 from kallithea.lib.utils import make_ui
 from kallithea.lib.vcs.backends.ssh import BaseSshHandler
@@ -61,6 +61,6 @@
             baseui.setconfig(b'hooks', b'pretxnopen._ssh_reject', b'python:kallithea.lib.hooks.rejectpush')
             baseui.setconfig(b'hooks', b'prepushkey._ssh_reject', b'python:kallithea.lib.hooks.rejectpush')
 
-        repo = hg.repository(baseui, safe_bytes(self.db_repo.repo_full_path))
+        repo = mercurial.hg.repository(baseui, safe_bytes(self.db_repo.repo_full_path))
         log.debug("Starting Mercurial sshserver for %s", self.db_repo.repo_full_path)
-        sshserver(baseui, repo).serve_forever()
+        mercurial.wireprotoserver.sshserver(baseui, repo).serve_forever()
--- a/kallithea/lib/vcs/backends/hg/workdir.py	Thu Jan 02 00:44:56 2020 +0100
+++ b/kallithea/lib/vcs/backends/hg/workdir.py	Thu Jan 02 20:39:13 2020 +0100
@@ -1,7 +1,8 @@
+import mercurial.merge
+
 from kallithea.lib.vcs.backends.base import BaseWorkdir
 from kallithea.lib.vcs.exceptions import BranchDoesNotExistError
 from kallithea.lib.vcs.utils import ascii_bytes, ascii_str
-from kallithea.lib.vcs.utils.hgcompat import hg_merge
 
 
 class MercurialWorkdir(BaseWorkdir):
@@ -20,4 +21,4 @@
             raise BranchDoesNotExistError
 
         raw_id = self.repository.branches[branch]
-        hg_merge.update(self.repository._repo, ascii_bytes(raw_id), False, False, None)
+        mercurial.merge.update(self.repository._repo, ascii_bytes(raw_id), False, False, None)
--- a/kallithea/lib/vcs/utils/hgcompat.py	Thu Jan 02 00:44:56 2020 +0100
+++ b/kallithea/lib/vcs/utils/hgcompat.py	Thu Jan 02 20:39:13 2020 +0100
@@ -2,28 +2,12 @@
 Mercurial libs compatibility
 """
 
-import mercurial
-from mercurial import archival, config, demandimport, discovery, httppeer, localrepo
-from mercurial import merge as hg_merge
-from mercurial import obsutil, patch, scmutil, sshpeer, ui, unionrepo
-from mercurial.commands import clone, nullid, pull
-from mercurial.context import memctx, memfilectx
-from mercurial.discovery import findcommonoutgoing
-from mercurial.error import Abort, RepoError, RepoLookupError
-from mercurial.hg import peer
-from mercurial.hgweb import hgweb_mod
-from mercurial.hgweb.common import get_contact
-from mercurial.match import exact as match_exact
-from mercurial.match import match
-from mercurial.mdiff import diffopts
-from mercurial.node import hex, nullrev
-from mercurial.scmutil import revrange
-from mercurial.tags import tag
-from mercurial.url import httpbasicauthhandler, httpdigestauthhandler
-from mercurial.util import url as hg_url
+import mercurial.localrepo
 
 
-# workaround for 3.3 94ac64bcf6fe and not calling largefiles reposetup correctly, and test_archival failing
-localrepo.localrepository._lfstatuswriters = [lambda *msg, **opts: None]
-# 3.5 7699d3212994 added the invariant that repo.lfstatus must exist before hitting overridearchive
-localrepo.localrepository.lfstatus = False
+def monkey_do():
+    """Apply some Mercurial monkey patching"""
+    # workaround for 3.3 94ac64bcf6fe and not calling largefiles reposetup correctly, and test_archival failing
+    mercurial.localrepo.localrepository._lfstatuswriters = [lambda *msg, **opts: None]
+    # 3.5 7699d3212994 added the invariant that repo.lfstatus must exist before hitting overridearchive
+    mercurial.localrepo.localrepository.lfstatus = False
--- a/kallithea/tests/vcs/test_hg.py	Thu Jan 02 00:44:56 2020 +0100
+++ b/kallithea/tests/vcs/test_hg.py	Thu Jan 02 20:39:13 2020 +0100
@@ -235,7 +235,7 @@
         assert node.kind == NodeKind.FILE
         assert node.content == readme
 
-    @mock.patch('kallithea.lib.vcs.backends.hg.repository.diffopts')
+    @mock.patch('mercurial.mdiff.diffopts')
     def test_get_diff_does_not_sanitize_zero_context(self, mock_diffopts):
         zero_context = 0
 
@@ -243,7 +243,7 @@
 
         mock_diffopts.assert_called_once_with(git=True, showfunc=True, ignorews=False, context=zero_context)
 
-    @mock.patch('kallithea.lib.vcs.backends.hg.repository.diffopts')
+    @mock.patch('mercurial.mdiff.diffopts')
     def test_get_diff_sanitizes_negative_context(self, mock_diffopts):
         negative_context = -10
         zero_context = 0