mirror of
https://github.com/facebook/sapling.git
synced 2024-10-07 15:27:13 +03:00
deprecate: introduce ui.deprecate
Summary: Adds a simple ui.deprecate() function for deprecating code paths. It has several levels of deprecation and can optionally automatically escalate through those levels across a specified time frame. The levels are: - Log - just logs the usage to scuba - Warn - same as Log but also prints a warning to the user - Slow - same as Warn but also sleeps for 2 seconds - OptIn - throws an exception, but hints that the exception can be bypassed by setting 'deprecated.bypass-XXXX=True' - Block - throws an exception that cannot be bypassed I also call it from a few locations that are expected to be deprecated already. Reviewed By: markbt Differential Revision: D21890496 fbshipit-source-id: faddb301888ef75cc71d46ffb7374f3fe3d044bd
This commit is contained in:
parent
e28bde4df3
commit
1d65457032
@ -125,8 +125,11 @@ class dirstate(object):
|
|||||||
opener.makedirs("treestate")
|
opener.makedirs("treestate")
|
||||||
self._mapcls = treestate.treestatemap
|
self._mapcls = treestate.treestatemap
|
||||||
elif istreedirstate:
|
elif istreedirstate:
|
||||||
|
ui.deprecate("treedirstate", "treedirstate is replaced by treestate")
|
||||||
self._mapcls = treedirstate.treedirstatemap
|
self._mapcls = treedirstate.treedirstatemap
|
||||||
else:
|
else:
|
||||||
|
if "eden" not in repo.requirements:
|
||||||
|
ui.deprecate("dirstatemap", "dirstatemap is replaced by treestate")
|
||||||
self._mapcls = dirstatemap
|
self._mapcls = dirstatemap
|
||||||
self._fs = filesystem.physicalfilesystem(root, self)
|
self._fs = filesystem.physicalfilesystem(root, self)
|
||||||
|
|
||||||
|
@ -151,6 +151,10 @@ class Abort(Hint, Context, Component, Exception):
|
|||||||
exitcode = 255
|
exitcode = 255
|
||||||
|
|
||||||
|
|
||||||
|
class DeprecatedError(Abort):
|
||||||
|
__bytes__ = _tobytes
|
||||||
|
|
||||||
|
|
||||||
class UncommitedChangesAbort(Abort):
|
class UncommitedChangesAbort(Abort):
|
||||||
"""Raised if there are uncommited changs and the command requires a clean
|
"""Raised if there are uncommited changs and the command requires a clean
|
||||||
working copy
|
working copy
|
||||||
|
@ -934,30 +934,35 @@ def allsuccessors(obsstore, nodes, ignoreflags=0):
|
|||||||
def marker(repo, data):
|
def marker(repo, data):
|
||||||
movemsg = "obsolete.marker moved to obsutil.marker"
|
movemsg = "obsolete.marker moved to obsutil.marker"
|
||||||
repo.ui.deprecwarn(movemsg, "4.3")
|
repo.ui.deprecwarn(movemsg, "4.3")
|
||||||
|
repo.ui.deprecate("obsolete.marker", movemsg)
|
||||||
return obsutil.marker(repo, data)
|
return obsutil.marker(repo, data)
|
||||||
|
|
||||||
|
|
||||||
def getmarkers(repo, nodes=None, exclusive=False):
|
def getmarkers(repo, nodes=None, exclusive=False):
|
||||||
movemsg = "obsolete.getmarkers moved to obsutil.getmarkers"
|
movemsg = "obsolete.getmarkers moved to obsutil.getmarkers"
|
||||||
repo.ui.deprecwarn(movemsg, "4.3")
|
repo.ui.deprecwarn(movemsg, "4.3")
|
||||||
|
repo.ui.deprecate("obsolete.getmarkers", movemsg)
|
||||||
return obsutil.getmarkers(repo, nodes=nodes, exclusive=exclusive)
|
return obsutil.getmarkers(repo, nodes=nodes, exclusive=exclusive)
|
||||||
|
|
||||||
|
|
||||||
def exclusivemarkers(repo, nodes):
|
def exclusivemarkers(repo, nodes):
|
||||||
movemsg = "obsolete.exclusivemarkers moved to obsutil.exclusivemarkers"
|
movemsg = "obsolete.exclusivemarkers moved to obsutil.exclusivemarkers"
|
||||||
repo.ui.deprecwarn(movemsg, "4.3")
|
repo.ui.deprecwarn(movemsg, "4.3")
|
||||||
|
repo.ui.deprecate("obsolete.exclusivemarkers", movemsg)
|
||||||
return obsutil.exclusivemarkers(repo, nodes)
|
return obsutil.exclusivemarkers(repo, nodes)
|
||||||
|
|
||||||
|
|
||||||
def foreground(repo, nodes):
|
def foreground(repo, nodes):
|
||||||
movemsg = "obsolete.foreground moved to obsutil.foreground"
|
movemsg = "obsolete.foreground moved to obsutil.foreground"
|
||||||
repo.ui.deprecwarn(movemsg, "4.3")
|
repo.ui.deprecwarn(movemsg, "4.3")
|
||||||
|
repo.ui.deprecate("obsolete.foreground", movemsg)
|
||||||
return obsutil.foreground(repo, nodes)
|
return obsutil.foreground(repo, nodes)
|
||||||
|
|
||||||
|
|
||||||
def successorssets(repo, initialnode, cache=None):
|
def successorssets(repo, initialnode, cache=None):
|
||||||
movemsg = "obsolete.successorssets moved to obsutil.successorssets"
|
movemsg = "obsolete.successorssets moved to obsutil.successorssets"
|
||||||
repo.ui.deprecwarn(movemsg, "4.3")
|
repo.ui.deprecwarn(movemsg, "4.3")
|
||||||
|
repo.ui.deprecate("obsolete.successorssets", movemsg)
|
||||||
return obsutil.successorssets(repo, initialnode, cache=cache)
|
return obsutil.successorssets(repo, initialnode, cache=cache)
|
||||||
|
|
||||||
|
|
||||||
@ -1049,6 +1054,7 @@ def _computeobsoleteset(repo):
|
|||||||
def _computeunstableset(repo):
|
def _computeunstableset(repo):
|
||||||
msg = "'unstable' volatile set is deprecated, " "use 'orphan'"
|
msg = "'unstable' volatile set is deprecated, " "use 'orphan'"
|
||||||
repo.ui.deprecwarn(msg, "4.4")
|
repo.ui.deprecwarn(msg, "4.4")
|
||||||
|
repo.ui.deprecate("unstable-obsolete-set", msg)
|
||||||
|
|
||||||
return _computeorphanset(repo)
|
return _computeorphanset(repo)
|
||||||
|
|
||||||
@ -1088,6 +1094,7 @@ def _computeextinctset(repo):
|
|||||||
def _computebumpedset(repo):
|
def _computebumpedset(repo):
|
||||||
msg = "'bumped' volatile set is deprecated, " "use 'phasedivergent'"
|
msg = "'bumped' volatile set is deprecated, " "use 'phasedivergent'"
|
||||||
repo.ui.deprecwarn(msg, "4.4")
|
repo.ui.deprecwarn(msg, "4.4")
|
||||||
|
repo.ui.deprecate("obsolete.bumped", msg)
|
||||||
|
|
||||||
return _computephasedivergentset(repo)
|
return _computephasedivergentset(repo)
|
||||||
|
|
||||||
@ -1121,6 +1128,7 @@ def _computephasedivergentset(repo):
|
|||||||
def _computedivergentset(repo):
|
def _computedivergentset(repo):
|
||||||
msg = "'divergent' volatile set is deprecated, " "use 'contentdivergent'"
|
msg = "'divergent' volatile set is deprecated, " "use 'contentdivergent'"
|
||||||
repo.ui.deprecwarn(msg, "4.4")
|
repo.ui.deprecwarn(msg, "4.4")
|
||||||
|
repo.ui.deprecate("obsolete.divergent", msg)
|
||||||
|
|
||||||
return _computecontentdivergentset(repo)
|
return _computecontentdivergentset(repo)
|
||||||
|
|
||||||
|
@ -681,6 +681,9 @@ def branch(repo, subset, x):
|
|||||||
def bumped(repo, subset, x):
|
def bumped(repo, subset, x):
|
||||||
msg = "'bumped()' is deprecated, " "use 'phasedivergent()'"
|
msg = "'bumped()' is deprecated, " "use 'phasedivergent()'"
|
||||||
repo.ui.deprecwarn(msg, "4.4")
|
repo.ui.deprecwarn(msg, "4.4")
|
||||||
|
repo.ui.deprecate(
|
||||||
|
"bumped-revset", "bumped() has been replaced with phasedivergent()"
|
||||||
|
)
|
||||||
|
|
||||||
return phasedivergent(repo, subset, x)
|
return phasedivergent(repo, subset, x)
|
||||||
|
|
||||||
@ -929,6 +932,9 @@ def destination(repo, subset, x):
|
|||||||
def divergent(repo, subset, x):
|
def divergent(repo, subset, x):
|
||||||
msg = "'divergent()' is deprecated, " "use 'contentdivergent()'"
|
msg = "'divergent()' is deprecated, " "use 'contentdivergent()'"
|
||||||
repo.ui.deprecwarn(msg, "4.4")
|
repo.ui.deprecwarn(msg, "4.4")
|
||||||
|
repo.ui.deprecate(
|
||||||
|
"divergent-revset", "divergent() has been replaced with contentdivergent()"
|
||||||
|
)
|
||||||
|
|
||||||
return contentdivergent(repo, subset, x)
|
return contentdivergent(repo, subset, x)
|
||||||
|
|
||||||
@ -2320,6 +2326,7 @@ def _substringmatcher(pattern, casesensitive=True):
|
|||||||
def unstable(repo, subset, x):
|
def unstable(repo, subset, x):
|
||||||
msg = "'unstable()' is deprecated, " "use 'orphan()'"
|
msg = "'unstable()' is deprecated, " "use 'orphan()'"
|
||||||
repo.ui.deprecwarn(msg, "4.4")
|
repo.ui.deprecwarn(msg, "4.4")
|
||||||
|
repo.ui.deprecate("unstable-revset", "unstable() has been replaced with orphan()")
|
||||||
|
|
||||||
return orphan(repo, subset, x)
|
return orphan(repo, subset, x)
|
||||||
|
|
||||||
|
@ -981,6 +981,9 @@ def showtroubles(repo, **args):
|
|||||||
"""
|
"""
|
||||||
msg = "'troubles' is deprecated, " "use 'instabilities'"
|
msg = "'troubles' is deprecated, " "use 'instabilities'"
|
||||||
repo.ui.deprecwarn(msg, "4.4")
|
repo.ui.deprecwarn(msg, "4.4")
|
||||||
|
repo.ui.deprecate(
|
||||||
|
"troubles-template", "troubles has been replaced with instabilities"
|
||||||
|
)
|
||||||
|
|
||||||
return showinstabilities(repo=repo, **args)
|
return showinstabilities(repo=repo, **args)
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ import sys
|
|||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from enum import Enum
|
from enum import IntEnum
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
import bindings
|
import bindings
|
||||||
@ -156,6 +156,19 @@ _unset = uiconfig._unset
|
|||||||
_reqexithandlers = []
|
_reqexithandlers = []
|
||||||
|
|
||||||
|
|
||||||
|
class deprecationlevel(IntEnum):
|
||||||
|
# Logs usage of the deprecated code path
|
||||||
|
Log = 0
|
||||||
|
# Prints a warning on usage of the deprecated code path
|
||||||
|
Warn = 1
|
||||||
|
# Inserts a 2 second sleep to the deprecated code path
|
||||||
|
Slow = 2
|
||||||
|
# Throws an exception, but a config can be used to opt in to the deprecated feature
|
||||||
|
Optin = 3
|
||||||
|
# Throws a non-bypassable exception
|
||||||
|
Block = 4
|
||||||
|
|
||||||
|
|
||||||
class ui(object):
|
class ui(object):
|
||||||
def __init__(self, src=None):
|
def __init__(self, src=None):
|
||||||
"""Create a fresh new ui object if no src given
|
"""Create a fresh new ui object if no src given
|
||||||
@ -1503,6 +1516,77 @@ class ui(object):
|
|||||||
|
|
||||||
self._logsample(service, *origmsg, **opts)
|
self._logsample(service, *origmsg, **opts)
|
||||||
|
|
||||||
|
def deprecate(
|
||||||
|
self, name, message, maxlevel=deprecationlevel.Log, startstr=None, endstr=None
|
||||||
|
):
|
||||||
|
"""marks a code path as deprecated
|
||||||
|
|
||||||
|
The default behavior is to simply log the usage of the deprecated path,
|
||||||
|
but `maxlevel` can be used to specify stricter deprecation strategies.
|
||||||
|
|
||||||
|
If `start` and `end` are provided, the deprecation level will be slowly
|
||||||
|
increased over the course of the `start` and `end` time, reaching the
|
||||||
|
specified `maxlevel` at the end time.
|
||||||
|
"""
|
||||||
|
level = maxlevel
|
||||||
|
if startstr is not None and endstr is not None:
|
||||||
|
now = time.time()
|
||||||
|
start = util.parsedate(startstr)[0]
|
||||||
|
end = util.parsedate(endstr)[0]
|
||||||
|
# Linearly interpolate to get the current level
|
||||||
|
percent = float(now - start) / float(end - start)
|
||||||
|
level = max(0, min(int(percent * maxlevel), maxlevel))
|
||||||
|
|
||||||
|
self.log(
|
||||||
|
"deprecated",
|
||||||
|
message,
|
||||||
|
feature=name,
|
||||||
|
level=int(level),
|
||||||
|
version=util.version(),
|
||||||
|
)
|
||||||
|
|
||||||
|
bypassed = self.configbool("deprecated", "bypass-%s" % name)
|
||||||
|
if level == deprecationlevel.Block:
|
||||||
|
raise error.DeprecatedError(
|
||||||
|
_("feature '%s' is disabled: %s") % (name, message)
|
||||||
|
)
|
||||||
|
elif level == deprecationlevel.Optin and not bypassed:
|
||||||
|
hint = (
|
||||||
|
_(
|
||||||
|
"set config `deprecated.bypass-%s=True` to temporarily bypass this block"
|
||||||
|
)
|
||||||
|
% name
|
||||||
|
)
|
||||||
|
if endstr is not None and maxlevel == deprecationlevel.Block:
|
||||||
|
hint = _(
|
||||||
|
"set config `deprecated.bypass-%s=True` to bypass this block, but note the feature will be completely disabled on %s"
|
||||||
|
) % (name, endstr)
|
||||||
|
raise error.DeprecatedError(
|
||||||
|
_("feature '%s' is disabled: %s") % (name, message), hint=hint
|
||||||
|
)
|
||||||
|
elif level >= deprecationlevel.Slow and not bypassed:
|
||||||
|
self.warn(
|
||||||
|
_(
|
||||||
|
"warning: sleeping for 2 seconds because feature '%s' is deprecated: %s\n"
|
||||||
|
)
|
||||||
|
% (name, message)
|
||||||
|
)
|
||||||
|
self.warn(
|
||||||
|
_(
|
||||||
|
"note: the feature will be completely disabled soon, so please migrate off\n"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
time.sleep(2)
|
||||||
|
elif level >= deprecationlevel.Warn:
|
||||||
|
self.warn(_("warning: feature '%s' is deprecated: %s\n") % (name, message))
|
||||||
|
self.warn(
|
||||||
|
_(
|
||||||
|
"note: the feature will be completely disabled soon, so please migrate off\n"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.develwarn(_("feature '%s' is deprecated: %s\n") % (name, message))
|
||||||
|
|
||||||
def _computesamplingfilters(self):
|
def _computesamplingfilters(self):
|
||||||
filtermap = {}
|
filtermap = {}
|
||||||
for k in self.configitems("sampling"):
|
for k in self.configitems("sampling"):
|
||||||
|
47
eden/scm/tests/test-deprecate.t
Normal file
47
eden/scm/tests/test-deprecate.t
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#chg-compatible
|
||||||
|
|
||||||
|
$ configure modern
|
||||||
|
|
||||||
|
$ newext deprecatecmd <<EOF
|
||||||
|
> from edenscm.mercurial import registrar
|
||||||
|
> cmdtable = {}
|
||||||
|
> command = registrar.command(cmdtable)
|
||||||
|
> @command('testdeprecate', [], 'hg testdeprecate')
|
||||||
|
> def testdeprecate(ui, repo, level):
|
||||||
|
> ui.deprecate("test-feature", "blah blah message", int(level))
|
||||||
|
> EOF
|
||||||
|
|
||||||
|
$ hg init client
|
||||||
|
$ cd client
|
||||||
|
|
||||||
|
$ hg testdeprecate 0
|
||||||
|
devel-warn: feature 'test-feature' is deprecated: blah blah message
|
||||||
|
at: $TESTTMP/deprecatecmd.py:6 (testdeprecate)
|
||||||
|
$ hg blackbox | grep deprecated
|
||||||
|
* [legacy][deprecated] blah blah message (glob)
|
||||||
|
* [legacy][develwarn] devel-warn: feature 'test-feature' is deprecated: blah blah message (glob)
|
||||||
|
|
||||||
|
$ hg testdeprecate 1
|
||||||
|
warning: feature 'test-feature' is deprecated: blah blah message
|
||||||
|
note: the feature will be completely disabled sooned, so please migrate off
|
||||||
|
|
||||||
|
$ hg testdeprecate 2
|
||||||
|
warning: sleeping for 2 seconds because feature 'test-feature' is deprecated: blah blah message
|
||||||
|
note: the feature will be completely disabled sooned, so please migrate off
|
||||||
|
|
||||||
|
$ hg testdeprecate 3
|
||||||
|
abort: feature 'test-feature' is disabled: blah blah message
|
||||||
|
(set config `deprecated.bypass-test-feature=True` to temporarily bypass this block)
|
||||||
|
[255]
|
||||||
|
|
||||||
|
$ hg testdeprecate 3 --config deprecated.bypass-test-feature=True
|
||||||
|
warning: feature 'test-feature' is deprecated: blah blah message
|
||||||
|
note: the feature will be completely disabled sooned, so please migrate off
|
||||||
|
|
||||||
|
$ hg testdeprecate 4
|
||||||
|
abort: feature 'test-feature' is disabled: blah blah message
|
||||||
|
[255]
|
||||||
|
|
||||||
|
$ hg testdeprecate 4 --config deprecated.bypass-test-feature=True
|
||||||
|
abort: feature 'test-feature' is disabled: blah blah message
|
||||||
|
[255]
|
Loading…
Reference in New Issue
Block a user