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")
|
||||
self._mapcls = treestate.treestatemap
|
||||
elif istreedirstate:
|
||||
ui.deprecate("treedirstate", "treedirstate is replaced by treestate")
|
||||
self._mapcls = treedirstate.treedirstatemap
|
||||
else:
|
||||
if "eden" not in repo.requirements:
|
||||
ui.deprecate("dirstatemap", "dirstatemap is replaced by treestate")
|
||||
self._mapcls = dirstatemap
|
||||
self._fs = filesystem.physicalfilesystem(root, self)
|
||||
|
||||
|
@ -151,6 +151,10 @@ class Abort(Hint, Context, Component, Exception):
|
||||
exitcode = 255
|
||||
|
||||
|
||||
class DeprecatedError(Abort):
|
||||
__bytes__ = _tobytes
|
||||
|
||||
|
||||
class UncommitedChangesAbort(Abort):
|
||||
"""Raised if there are uncommited changs and the command requires a clean
|
||||
working copy
|
||||
|
@ -934,30 +934,35 @@ def allsuccessors(obsstore, nodes, ignoreflags=0):
|
||||
def marker(repo, data):
|
||||
movemsg = "obsolete.marker moved to obsutil.marker"
|
||||
repo.ui.deprecwarn(movemsg, "4.3")
|
||||
repo.ui.deprecate("obsolete.marker", movemsg)
|
||||
return obsutil.marker(repo, data)
|
||||
|
||||
|
||||
def getmarkers(repo, nodes=None, exclusive=False):
|
||||
movemsg = "obsolete.getmarkers moved to obsutil.getmarkers"
|
||||
repo.ui.deprecwarn(movemsg, "4.3")
|
||||
repo.ui.deprecate("obsolete.getmarkers", movemsg)
|
||||
return obsutil.getmarkers(repo, nodes=nodes, exclusive=exclusive)
|
||||
|
||||
|
||||
def exclusivemarkers(repo, nodes):
|
||||
movemsg = "obsolete.exclusivemarkers moved to obsutil.exclusivemarkers"
|
||||
repo.ui.deprecwarn(movemsg, "4.3")
|
||||
repo.ui.deprecate("obsolete.exclusivemarkers", movemsg)
|
||||
return obsutil.exclusivemarkers(repo, nodes)
|
||||
|
||||
|
||||
def foreground(repo, nodes):
|
||||
movemsg = "obsolete.foreground moved to obsutil.foreground"
|
||||
repo.ui.deprecwarn(movemsg, "4.3")
|
||||
repo.ui.deprecate("obsolete.foreground", movemsg)
|
||||
return obsutil.foreground(repo, nodes)
|
||||
|
||||
|
||||
def successorssets(repo, initialnode, cache=None):
|
||||
movemsg = "obsolete.successorssets moved to obsutil.successorssets"
|
||||
repo.ui.deprecwarn(movemsg, "4.3")
|
||||
repo.ui.deprecate("obsolete.successorssets", movemsg)
|
||||
return obsutil.successorssets(repo, initialnode, cache=cache)
|
||||
|
||||
|
||||
@ -1049,6 +1054,7 @@ def _computeobsoleteset(repo):
|
||||
def _computeunstableset(repo):
|
||||
msg = "'unstable' volatile set is deprecated, " "use 'orphan'"
|
||||
repo.ui.deprecwarn(msg, "4.4")
|
||||
repo.ui.deprecate("unstable-obsolete-set", msg)
|
||||
|
||||
return _computeorphanset(repo)
|
||||
|
||||
@ -1088,6 +1094,7 @@ def _computeextinctset(repo):
|
||||
def _computebumpedset(repo):
|
||||
msg = "'bumped' volatile set is deprecated, " "use 'phasedivergent'"
|
||||
repo.ui.deprecwarn(msg, "4.4")
|
||||
repo.ui.deprecate("obsolete.bumped", msg)
|
||||
|
||||
return _computephasedivergentset(repo)
|
||||
|
||||
@ -1121,6 +1128,7 @@ def _computephasedivergentset(repo):
|
||||
def _computedivergentset(repo):
|
||||
msg = "'divergent' volatile set is deprecated, " "use 'contentdivergent'"
|
||||
repo.ui.deprecwarn(msg, "4.4")
|
||||
repo.ui.deprecate("obsolete.divergent", msg)
|
||||
|
||||
return _computecontentdivergentset(repo)
|
||||
|
||||
|
@ -681,6 +681,9 @@ def branch(repo, subset, x):
|
||||
def bumped(repo, subset, x):
|
||||
msg = "'bumped()' is deprecated, " "use 'phasedivergent()'"
|
||||
repo.ui.deprecwarn(msg, "4.4")
|
||||
repo.ui.deprecate(
|
||||
"bumped-revset", "bumped() has been replaced with phasedivergent()"
|
||||
)
|
||||
|
||||
return phasedivergent(repo, subset, x)
|
||||
|
||||
@ -929,6 +932,9 @@ def destination(repo, subset, x):
|
||||
def divergent(repo, subset, x):
|
||||
msg = "'divergent()' is deprecated, " "use 'contentdivergent()'"
|
||||
repo.ui.deprecwarn(msg, "4.4")
|
||||
repo.ui.deprecate(
|
||||
"divergent-revset", "divergent() has been replaced with contentdivergent()"
|
||||
)
|
||||
|
||||
return contentdivergent(repo, subset, x)
|
||||
|
||||
@ -2320,6 +2326,7 @@ def _substringmatcher(pattern, casesensitive=True):
|
||||
def unstable(repo, subset, x):
|
||||
msg = "'unstable()' is deprecated, " "use 'orphan()'"
|
||||
repo.ui.deprecwarn(msg, "4.4")
|
||||
repo.ui.deprecate("unstable-revset", "unstable() has been replaced with orphan()")
|
||||
|
||||
return orphan(repo, subset, x)
|
||||
|
||||
|
@ -981,6 +981,9 @@ def showtroubles(repo, **args):
|
||||
"""
|
||||
msg = "'troubles' is deprecated, " "use 'instabilities'"
|
||||
repo.ui.deprecwarn(msg, "4.4")
|
||||
repo.ui.deprecate(
|
||||
"troubles-template", "troubles has been replaced with instabilities"
|
||||
)
|
||||
|
||||
return showinstabilities(repo=repo, **args)
|
||||
|
||||
|
@ -26,7 +26,7 @@ import sys
|
||||
import tempfile
|
||||
import time
|
||||
import traceback
|
||||
from enum import Enum
|
||||
from enum import IntEnum
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import bindings
|
||||
@ -156,6 +156,19 @@ _unset = uiconfig._unset
|
||||
_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):
|
||||
def __init__(self, src=None):
|
||||
"""Create a fresh new ui object if no src given
|
||||
@ -1503,6 +1516,77 @@ class ui(object):
|
||||
|
||||
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):
|
||||
filtermap = {}
|
||||
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