mirror of
https://github.com/facebook/sapling.git
synced 2024-10-12 17:58:27 +03:00
9dc21f8d0b
Summary: D13853115 adds `edenscm/` to `sys.path` and code still uses `import mercurial`. That has nasty problems if both `import mercurial` and `import edenscm.mercurial` are used, because Python would think `mercurial.foo` and `edenscm.mercurial.foo` are different modules so code like `try: ... except mercurial.error.Foo: ...`, or `isinstance(x, mercurial.foo.Bar)` would fail to handle the `edenscm.mercurial` version. There are also some module-level states (ex. `extensions._extensions`) that would cause trouble if they have multiple versions in a single process. Change imports to use the `edenscm` so ideally the `mercurial` is no longer imported at all. Add checks in extensions.py to catch unexpected extensions importing modules from the old (wrong) locations when running tests. Reviewed By: phillco Differential Revision: D13868981 fbshipit-source-id: f4e2513766957fd81d85407994f7521a08e4de48
173 lines
4.4 KiB
Python
173 lines
4.4 KiB
Python
"""Functions to work around API changes."""
|
|
|
|
# no-check-code -- see T24862348
|
|
|
|
import errno
|
|
import sys
|
|
|
|
from edenscm.mercurial import util
|
|
|
|
|
|
def branchset(repo):
|
|
"""Return the set of branches present in a repo.
|
|
|
|
Works around branchtags() vanishing between 2.8 and 2.9.
|
|
"""
|
|
try:
|
|
return set(repo.branchmap())
|
|
except AttributeError:
|
|
return set(repo.branchtags())
|
|
|
|
|
|
def pickle_load(f):
|
|
import cPickle as pickle
|
|
|
|
f.seek(0)
|
|
return pickle.load(f)
|
|
|
|
|
|
def makememfilectx(repo, memctx, path, data, islink, isexec, copied):
|
|
"""Return a memfilectx
|
|
|
|
Works around API change by 8a0cac20a1ad (first in 4.5).
|
|
"""
|
|
from edenscm.mercurial import context
|
|
|
|
try:
|
|
return context.memfilectx(
|
|
repo=repo,
|
|
path=path,
|
|
data=data,
|
|
islink=islink,
|
|
isexec=isexec,
|
|
copied=copied,
|
|
changectx=memctx,
|
|
)
|
|
except TypeError:
|
|
return context.memfilectx(
|
|
repo=repo, path=path, data=data, islink=islink, isexec=isexec, copied=copied
|
|
)
|
|
|
|
|
|
def filectxfn_deleted(memctx, path):
|
|
"""
|
|
Return None or raise an IOError as necessary if path is deleted.
|
|
|
|
Call as:
|
|
|
|
if path_missing:
|
|
return compathacks.filectxfn_deleted(memctx, path)
|
|
|
|
Works around filectxfn's contract changing between 3.1 and 3.2: 3.2 onwards,
|
|
for deleted files, filectxfn should return None rather than returning
|
|
IOError.
|
|
"""
|
|
if getattr(memctx, "_returnnoneformissingfiles", False):
|
|
return None
|
|
raise IOError(errno.ENOENT, "%s is deleted" % path)
|
|
|
|
|
|
def filectxfn_deleted_reraise(memctx):
|
|
"""
|
|
Return None or reraise exc as necessary.
|
|
|
|
Call as:
|
|
|
|
try:
|
|
# code that raises IOError if the path is missing
|
|
except IOError:
|
|
return compathacks.filectxfn_deleted_reraise(memctx)
|
|
|
|
Works around filectxfn's contract changing between 3.1 and 3.2: 3.2 onwards,
|
|
for deleted files, filectxfn should return None rather than returning
|
|
IOError.
|
|
"""
|
|
exc_info = sys.exc_info()
|
|
if exc_info[1].errno == errno.ENOENT and getattr(
|
|
memctx, "_returnnoneformissingfiles", False
|
|
):
|
|
return None
|
|
raise exc_info[0](exc_info[1])
|
|
|
|
|
|
# copied from hg 3.8
|
|
class _funcregistrarbase(object):
|
|
"""Base of decorator to register a function for specific purpose
|
|
|
|
This decorator stores decorated functions into own dict 'table'.
|
|
|
|
The least derived class can be defined by overriding 'formatdoc',
|
|
for example::
|
|
|
|
class keyword(_funcregistrarbase):
|
|
_docformat = ":%s: %s"
|
|
|
|
This should be used as below:
|
|
|
|
keyword = registrar.keyword()
|
|
|
|
@keyword('bar')
|
|
def barfunc(*args, **kwargs):
|
|
'''Explanation of bar keyword ....
|
|
'''
|
|
pass
|
|
|
|
In this case:
|
|
|
|
- 'barfunc' is stored as 'bar' in '_table' of an instance 'keyword' above
|
|
- 'barfunc.__doc__' becomes ":bar: Explanation of bar keyword"
|
|
"""
|
|
|
|
def __init__(self, table=None):
|
|
if table is None:
|
|
self._table = {}
|
|
else:
|
|
self._table = table
|
|
|
|
def __call__(self, decl, *args, **kwargs):
|
|
return lambda func: self._doregister(func, decl, *args, **kwargs)
|
|
|
|
def _doregister(self, func, decl, *args, **kwargs):
|
|
name = self._getname(decl)
|
|
|
|
if func.__doc__ and not util.safehasattr(func, "_origdoc"):
|
|
doc = func.__doc__.strip()
|
|
func._origdoc = doc
|
|
func.__doc__ = self._formatdoc(decl, doc)
|
|
|
|
self._table[name] = func
|
|
self._extrasetup(name, func, *args, **kwargs)
|
|
|
|
return func
|
|
|
|
def _parsefuncdecl(self, decl):
|
|
"""Parse function declaration and return the name of function in it
|
|
"""
|
|
i = decl.find("(")
|
|
if i >= 0:
|
|
return decl[:i]
|
|
else:
|
|
return decl
|
|
|
|
def _getname(self, decl):
|
|
"""Return the name of the registered function from decl
|
|
|
|
Derived class should override this, if it allows more
|
|
descriptive 'decl' string than just a name.
|
|
"""
|
|
return decl
|
|
|
|
_docformat = None
|
|
|
|
def _formatdoc(self, decl, doc):
|
|
"""Return formatted document of the registered function for help
|
|
|
|
'doc' is '__doc__.strip()' of the registered function.
|
|
"""
|
|
return self._docformat % (decl, doc)
|
|
|
|
def _extrasetup(self, name, func):
|
|
"""Execute exra setup for registered function, if needed
|
|
"""
|
|
pass
|