D10936: cmdutil: fix newandmodified file accounting for --interactive commits
dploch created this revision. Herald added a reviewer: hg-reviewers. Herald added a subscriber: mercurial-patches. REVISION SUMMARY `originalchunks` is a misleading name, because it only contains header objects, which are flattened to selected hunks by the filter function. As such, `chunks not in originalchunks` is always True and misleading, because hunk objects never compare equal to header objects. This change fixes the internal naming and removes the useless parameter from the method. This change also fixes https://bz.mercurial-scm.org/show_bug.cgi?id=6533, by considering the filtered headers, rather than the hunks, when determining new and modified files. If a file is renamed + edited, and the edited hunks are deselected (but the file is not), the filtered chunks will contain a header for the file (because it's .special()) but but no hunks. REPOSITORY rHG Mercurial BRANCH default REVISION DETAIL https://phab.mercurial-scm.org/D10936 AFFECTED FILES mercurial/cmdutil.py tests/test-commit-interactive.t CHANGE DETAILS diff --git a/tests/test-commit-interactive.t b/tests/test-commit-interactive.t --- a/tests/test-commit-interactive.t +++ b/tests/test-commit-interactive.t @@ -1713,20 +1713,58 @@ record this change to 'plain3'? (enter ? for help) [Ynesfdaq?] y + +Rename file but discard edits + + $ echo content > new-file + $ hg add -q new-file + $ hg commit -qm 'new file' + $ hg mv new-file renamed-file + $ echo new-content >> renamed-file + $ hg commit -i -d '24 0' -m content-rename< y + > n + > EOF + diff --git a/new-file b/renamed-file + rename from new-file + rename to renamed-file + 1 hunks, 1 lines changed + examine changes to 'new-file' and 'renamed-file'? + (enter ? for help) [Ynesfdaq?] y + + @@ -1,1 +1,2 @@ + content + +new-content + record this change to 'renamed-file'? + (enter ? for help) [Ynesfdaq?] n + + $ hg status + M renamed-file + ? editedfile.orig + ? editedfile.rej + ? editor.sh + $ hg diff + diff -r a006b0d78ce9 renamed-file + --- a/renamed-file Thu Jan 01 00:00:24 1970 + + +++ b/renamed-file Thu Jan 01 00:00:00 1970 + + @@ -1,1 +1,2 @@ + content + +new-content + The #if execbit block above changes the hash here on some systems $ hg status -A plain3 C plain3 $ hg tip - changeset: 32:* (glob) + changeset: 34:a006b0d78ce9 tag: tip user:test - date:Thu Jan 01 00:00:23 1970 + - summary: moving_files + date:Thu Jan 01 00:00:24 1970 + + summary: content-rename Editing patch of newly added file $ hg update -C . - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cat > editor.sh << '__EOF__' > cat "$1" | sed "s/first/very/g" > tt > mv tt "$1" @@ -1737,7 +1775,7 @@ > This is the third line > __EOF__ $ hg add newfile - $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit -i -d '23 0' -medit-patch-new < y > e > EOF @@ -1770,7 +1808,7 @@ $ cd folder $ echo "foo" > bar $ hg add bar - $ hg commit -i -d '23 0' -mnewfilesubdir < y > y > EOF @@ -1786,15 +1824,15 @@ The #if execbit block above changes the hashes here on some systems $ hg tip -p - changeset: 34:* (glob) + changeset: 36:6d2ed8a25e2a tag: tip user:test - date:Thu Jan 01 00:00:23 1970 + + date:Thu Jan 01 00:00:26 1970 + summary: newfilesubdir diff -r * -r * folder/bar (glob) --- /dev/nullThu Jan 01 00:00:00 1970 + - +++ b/folder/bar Thu Jan 01 00:00:23 1970 + + +++ b/folder/bar Thu Jan 01 00:00:26 1970 + @@ -0,0 +1,1 @@ +foo diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py --- a/mercurial/cmdutil.py +++ b/mercurial/cmdutil.py @@ -346,18 +346,19 @@ return isinstance(x, hunkclasses) -def newandmodified(chunks, originalchunks): +def isheader(x): +headerclasses = (crecordmod.uiheader, patch.header) +return isinstance(x, headerclasses) + + +def newandmodified(chunks): newlyaddedandmodifiedfiles = set() alsorestore = set() for chunk in chunks: -if ( -ishunk(chunk) -and chunk.header.isnewfile() -and chunk not in originalchunks -): -newlyaddedandmodifiedfiles.add(chunk.header.filename()) +if isheader(chunk) and chunk.isnewfile(): +newlyaddedandmodifiedfiles.add(chunk.filename()) alsorestore.update( -set(chunk.header.files()) - {chunk.header.filename()} +set(chunk.files()) - {chunk.filename()} ) return newlyaddedandmodifiedfiles, alsorestore @@ -517,12 +518,12 @@ diffopts.git = True diffopts.showfunc = True originaldiff = patch.diff(repo, changes=status, opts=diffopts) -originalchunks =
D8730: error: unify the error message formats for 'rebase' and 'unshelve'
dploch created this revision. Herald added a reviewer: durin42. Herald added a reviewer: hg-reviewers. Herald added a subscriber: mercurial-patches. REPOSITORY rHG Mercurial BRANCH default REVISION DETAIL https://phab.mercurial-scm.org/D8730 AFFECTED FILES mercurial/error.py tests/test-absorb-unfinished.t tests/test-bookmarks-rebase.t tests/test-copytrace-heuristics.t tests/test-fix.t tests/test-largefiles-update.t tests/test-merge-halt.t tests/test-narrow-rebase.t tests/test-rebase-abort.t tests/test-rebase-backup.t tests/test-rebase-bookmarks.t tests/test-rebase-check-restore.t tests/test-rebase-collapse.t tests/test-rebase-conflicts.t tests/test-rebase-dest.t tests/test-rebase-detach.t tests/test-rebase-inmemory.t tests/test-rebase-interruptions.t tests/test-rebase-mq-skip.t tests/test-rebase-mq.t tests/test-rebase-obsolete.t tests/test-rebase-parameters.t tests/test-rebase-partial.t tests/test-rebase-transaction.t tests/test-resolve.t tests/test-sparse-profiles.t tests/test-sparse.t CHANGE DETAILS diff --git a/tests/test-sparse.t b/tests/test-sparse.t --- a/tests/test-sparse.t +++ b/tests/test-sparse.t @@ -200,7 +200,7 @@ temporarily included 2 file(s) in the sparse checkout for merging merging hide warning: conflicts while merging hide! (edit, then use 'hg resolve --mark') - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] $ hg debugsparse diff --git a/tests/test-sparse-profiles.t b/tests/test-sparse-profiles.t --- a/tests/test-sparse-profiles.t +++ b/tests/test-sparse-profiles.t @@ -200,7 +200,7 @@ merging data.py warning: conflicts while merging backend.sparse! (edit, then use 'hg resolve --mark') warning: conflicts while merging data.py! (edit, then use 'hg resolve --mark') - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] $ rm *.orig $ ls -A diff --git a/tests/test-resolve.t b/tests/test-resolve.t --- a/tests/test-resolve.t +++ b/tests/test-resolve.t @@ -520,7 +520,7 @@ warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark') warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark') warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark') - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] Test when commands.resolve.confirm config option is not set: diff --git a/tests/test-rebase-transaction.t b/tests/test-rebase-transaction.t --- a/tests/test-rebase-transaction.t +++ b/tests/test-rebase-transaction.t @@ -107,7 +107,7 @@ rebasing 3:c26739dbe603 "C" (C) merging conflict warning: conflicts while merging conflict! (edit, then use 'hg resolve --mark') - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] $ hg tglog o 5: D diff --git a/tests/test-rebase-partial.t b/tests/test-rebase-partial.t --- a/tests/test-rebase-partial.t +++ b/tests/test-rebase-partial.t @@ -84,7 +84,7 @@ rebasing 2:ef8c0fe0897b "D" (D) merging file warning: conflicts while merging file! (edit, then use 'hg resolve --mark') - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] $ hg rebase --abort rebase aborted diff --git a/tests/test-rebase-parameters.t b/tests/test-rebase-parameters.t --- a/tests/test-rebase-parameters.t +++ b/tests/test-rebase-parameters.t @@ -479,7 +479,7 @@ $ hg rebase -s 2 -d 1 --tool internal:fail rebasing 2:e4e3f3546619 "c2b" (tip) - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] $ hg summary diff --git a/tests/test-rebase-obsolete.t b/tests/test-rebase-obsolete.t --- a/tests/test-rebase-obsolete.t +++ b/tests/test-rebase-obsolete.t @@ -1032,7 +1032,7 @@ rebasing 19:b82fb57ea638 "willconflict second version" merging willconflict warning: conflicts while merging willconflict! (edit, then use 'hg resolve --mark') - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] $ hg resolve --mark willconflict @@ -1787,7 +1787,7 @@ rebasing 1:2ec65233581b "B" merging D warning: conflicts while merging D! (edit, then use 'hg resolve --mark') - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] $ cp -R . $TESTTMP/hidden-state2 @@ -1872,7 +1872,7 @@ rebasing 3:055a42cdd887 "d" merging d warning: conflicts while merging d! (edit, then
D8713: error: normalize "unresolved conflicts" error messages with a custom class
dploch created this revision. Herald added a reviewer: durin42. Herald added a reviewer: martinvonz. Herald added a reviewer: hg-reviewers. Herald added a subscriber: mercurial-patches. REPOSITORY rHG Mercurial BRANCH default REVISION DETAIL https://phab.mercurial-scm.org/D8713 AFFECTED FILES hgext/rebase.py mercurial/error.py mercurial/shelve.py tests/test-absorb-unfinished.t tests/test-bookmarks-rebase.t tests/test-copytrace-heuristics.t tests/test-fix.t tests/test-largefiles-update.t tests/test-merge-halt.t tests/test-narrow-rebase.t tests/test-rebase-abort.t tests/test-rebase-backup.t tests/test-rebase-bookmarks.t tests/test-rebase-check-restore.t tests/test-rebase-collapse.t tests/test-rebase-conflicts.t tests/test-rebase-dest.t tests/test-rebase-detach.t tests/test-rebase-inmemory.t tests/test-rebase-interruptions.t tests/test-rebase-mq-skip.t tests/test-rebase-mq.t tests/test-rebase-obsolete.t tests/test-rebase-parameters.t tests/test-rebase-partial.t tests/test-rebase-transaction.t tests/test-resolve.t tests/test-sparse-profiles.t tests/test-sparse.t CHANGE DETAILS diff --git a/tests/test-sparse.t b/tests/test-sparse.t --- a/tests/test-sparse.t +++ b/tests/test-sparse.t @@ -200,7 +200,7 @@ temporarily included 2 file(s) in the sparse checkout for merging merging hide warning: conflicts while merging hide! (edit, then use 'hg resolve --mark') - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] $ hg debugsparse diff --git a/tests/test-sparse-profiles.t b/tests/test-sparse-profiles.t --- a/tests/test-sparse-profiles.t +++ b/tests/test-sparse-profiles.t @@ -200,7 +200,7 @@ merging data.py warning: conflicts while merging backend.sparse! (edit, then use 'hg resolve --mark') warning: conflicts while merging data.py! (edit, then use 'hg resolve --mark') - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] $ rm *.orig $ ls -A diff --git a/tests/test-resolve.t b/tests/test-resolve.t --- a/tests/test-resolve.t +++ b/tests/test-resolve.t @@ -520,7 +520,7 @@ warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark') warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark') warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark') - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] Test when commands.resolve.confirm config option is not set: diff --git a/tests/test-rebase-transaction.t b/tests/test-rebase-transaction.t --- a/tests/test-rebase-transaction.t +++ b/tests/test-rebase-transaction.t @@ -107,7 +107,7 @@ rebasing 3:c26739dbe603 "C" (C) merging conflict warning: conflicts while merging conflict! (edit, then use 'hg resolve --mark') - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] $ hg tglog o 5: D diff --git a/tests/test-rebase-partial.t b/tests/test-rebase-partial.t --- a/tests/test-rebase-partial.t +++ b/tests/test-rebase-partial.t @@ -84,7 +84,7 @@ rebasing 2:ef8c0fe0897b "D" (D) merging file warning: conflicts while merging file! (edit, then use 'hg resolve --mark') - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] $ hg rebase --abort rebase aborted diff --git a/tests/test-rebase-parameters.t b/tests/test-rebase-parameters.t --- a/tests/test-rebase-parameters.t +++ b/tests/test-rebase-parameters.t @@ -479,7 +479,7 @@ $ hg rebase -s 2 -d 1 --tool internal:fail rebasing 2:e4e3f3546619 "c2b" (tip) - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] $ hg summary diff --git a/tests/test-rebase-obsolete.t b/tests/test-rebase-obsolete.t --- a/tests/test-rebase-obsolete.t +++ b/tests/test-rebase-obsolete.t @@ -1032,7 +1032,7 @@ rebasing 19:b82fb57ea638 "willconflict second version" merging willconflict warning: conflicts while merging willconflict! (edit, then use 'hg resolve --mark') - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] $ hg resolve --mark willconflict @@ -1787,7 +1787,7 @@ rebasing 1:2ec65233581b "B" merging D warning: conflicts while merging D! (edit, then use 'hg resolve --mark') - unresolved conflicts (see hg resolve, then hg rebase --continue) + unresolved conflicts (see 'hg resolve', then 'hg rebase --continue') [1] $ cp -R . $TESTTMP/hidden-state2 @@ -1872,7 +1872,7 @@ rebasing
D8712: black: format some files in preparation for subsequent changes
dploch created this revision. Herald added a reviewer: martinvonz. Herald added a reviewer: hg-reviewers. Herald added a subscriber: mercurial-patches. REPOSITORY rHG Mercurial BRANCH default REVISION DETAIL https://phab.mercurial-scm.org/D8712 AFFECTED FILES hgext/rebase.py mercurial/error.py mercurial/shelve.py mercurial/state.py CHANGE DETAILS diff --git a/mercurial/state.py b/mercurial/state.py --- a/mercurial/state.py +++ b/mercurial/state.py @@ -4,10 +4,7 @@ # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. - -""" -This file contains class to wrap the state for commands and other -related logic. +"""This file contains class to wrap the state for commands and other related logic. All the data related to the command state is stored as dictionary in the object. The class has methods using which the data can be stored to disk in a file under @@ -21,18 +18,11 @@ from .i18n import _ -from . import ( -error, -pycompat, -util, -) +from . import error, pycompat, util from .utils import cborutil if pycompat.TYPE_CHECKING: -from typing import ( -Any, -Dict, -) +from typing import Any, Dict for t in (Any, Dict): assert t @@ -40,6 +30,7 @@ class cmdstate(object): """a wrapper class to store the state of commands like `rebase`, `graft`, + `histedit`, `shelve` etc. Extensions can also use this to write state files. All the data for the state is stored in the form of key-value pairs in a @@ -54,6 +45,7 @@ def __init__(self, repo, fname): """ repo is the repo object + fname is the file name in which data should be stored in .hg directory """ self._repo = repo @@ -71,7 +63,7 @@ """ if not isinstance(version, int): raise error.ProgrammingError( -b"version of state file should be an integer" +b'version of state file should be an integer' ) with self._repo.vfs(self.fname, b'wb', atomictemp=True) as fp: @@ -81,13 +73,15 @@ def _read(self): """reads the state file and returns a dictionary which contain -data in the same format as it was before storing""" + +data in the same format as it was before storing +""" with self._repo.vfs(self.fname, b'rb') as fp: try: int(fp.readline()) except ValueError: raise error.CorruptedState( -b"unknown version of state file found" +b'unknown version of state file found' ) return cborutil.decodeall(fp.read())[0] @@ -103,6 +97,7 @@ class _statecheck(object): """a utility class that deals with multistep operations like graft, + histedit, bisect, update etc and check whether such commands are in an unfinished conditition or not and return appropriate message and hint. @@ -140,6 +135,7 @@ def statusmsg(self): """returns the hint message corresponding to the command for + hg status --verbose """ if not self._statushint: @@ -156,6 +152,7 @@ def hint(self): """returns the hint message corresponding to an interrupted + operation """ if not self._cmdhint: @@ -177,6 +174,7 @@ def isunfinished(self, repo): """determines whether a multi-step operation is in progress + or not """ if self._opname == b'merge': @@ -197,13 +195,14 @@ reportonly=False, continueflag=False, stopflag=False, -cmdmsg=b"", -cmdhint=b"", -statushint=b"", +cmdmsg=b'', +cmdhint=b'', +statushint=b'', abortfunc=None, continuefunc=None, ): """this registers a new command or operation to unfinishedstates + opname is the name the command or operation fname is the file name in which data should be stored in .hg directory. It is None for merge command. @@ -256,7 +255,7 @@ clearable=True, cmdmsg=_(b'last update was interrupted'), cmdhint=_(b"use 'hg update' to get a consistent checkout"), -statushint=_(b"To continue:hg update ."), +statushint=_(b'To continue:hg update .'), ) addunfinished( b'bisect', diff --git a/mercurial/shelve.py b/mercurial/shelve.py --- a/mercurial/shelve.py +++ b/mercurial/shelve.py @@ -53,10 +53,7 @@ util, vfs as vfsmod, ) -from .utils import ( -dateutil, -stringutil, -) +from .utils import dateutil, stringutil backupdir = b'shelve-backup' shelvedir = b'shelved' diff --git a/mercurial/error.py b/mercurial/error.py --- a/mercurial/error.py +++ b/mercurial/error.py @@ -4,7 +4,6 @@ # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -
D8714: state: support validated declaration of nested unfinished ops
dploch created this revision. Herald added a reviewer: hg-reviewers. Herald added a subscriber: mercurial-patches. REVISION SUMMARY This enables extensions to define commands that delgate to rebase, evolve, etc. one or more times to also have their own unfinished states for the full sequence of operations without monkey-patching _unfinishedstates. REPOSITORY rHG Mercurial BRANCH default REVISION DETAIL https://phab.mercurial-scm.org/D8714 AFFECTED FILES mercurial/state.py CHANGE DETAILS diff --git a/mercurial/state.py b/mercurial/state.py --- a/mercurial/state.py +++ b/mercurial/state.py @@ -16,6 +16,8 @@ from __future__ import absolute_import +import contextlib + from .i18n import _ from . import error, pycompat, util @@ -114,6 +116,7 @@ reportonly, continueflag, stopflag, +childopnames, cmdmsg, cmdhint, statushint, @@ -127,6 +130,7 @@ self._reportonly = reportonly self._continueflag = continueflag self._stopflag = stopflag +self._childopnames = childopnames self._cmdmsg = cmdmsg self._cmdhint = cmdhint self._statushint = statushint @@ -185,6 +189,7 @@ # A list of statecheck objects for multistep operations like graft. _unfinishedstates = [] +_unfinishedstatesbyname = {} def addunfinished( @@ -195,6 +200,7 @@ reportonly=False, continueflag=False, stopflag=False, +childopnames=[], cmdmsg=b'', cmdhint=b'', statushint=b'', @@ -217,6 +223,8 @@ `--continue` option or not. stopflag is a boolean that determines whether or not a command supports --stop flag +childopnames is a list of other opnames this op uses as sub-steps of its +own execution. They must already be added. cmdmsg is used to pass a different status message in case standard message of the format "abort: cmdname in progress" is not desired. cmdhint is used to pass a different hint message in case standard @@ -237,17 +245,69 @@ reportonly, continueflag, stopflag, +childopnames, cmdmsg, cmdhint, statushint, abortfunc, continuefunc, ) + if opname == b'merge': _unfinishedstates.append(statecheckobj) else: +# This check enforces that for any op 'foo' which depends on op 'bar', +# 'foo' comes before 'bar' in _unfinishedstates. This ensures that +# getrepostate() always returns the most specific applicable answer. +for childopname in childopnames: +if childopname not in _unfinishedstatesbyname: +raise error.ProgrammingError( +_(b'op %s depends on unknown op %s') % (opname, childopname) +) + _unfinishedstates.insert(0, statecheckobj) +if opname in _unfinishedstatesbyname: +raise error.ProgrammingError(_(b'op %s registered twice') % opname) +_unfinishedstatesbyname[opname] = statecheckobj + + +@contextlib.contextmanager +def delegating(repo, opname, childopname): +"""context wrapper for delegations from opname to childopname. + +requires that childopname was specified when opname was registered. + +Usage: + def my_command_foo_that_uses_rebase(...): +... +with state.delegating(repo, 'foo', 'rebase'): + _run_rebase(...) +... +""" + +s = _unfinishedstatesbyname[parentopname] +if not s: +raise error.ProgrammingError(_(b'unknown op %s') % opname) +if childopname not in s._childopnames: +raise error.ProgrammingError( +_(b'op %s does not delegate to %s') % (opname, childopname) +) +c = _unfinishedstatesbyname[childopname] + +newskipstates = set(repo.ui.configlist(b'commands', b'status.skipstates')) +newskipstates.add(childopname) +with repo.ui.configoverride( +{(b'commands', b'status.skipstates'): newskipstates} +): +try: +yield +except error.ConflictResolutionRequired as e: +# Rewrite conflict resolution advice for the parent opname. +if e.opname == childopname: +raise error.ConflictResolutionRequired(opname) +raise e + addunfinished( b'update', To: dploch, #hg-reviewers Cc: mercurial-patches, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D7462: py3: make doc strings containing the deprected '\.' escape sequence raw strings
Closed by commit rHGbfbbf48d51e8: py3: make doc strings containing deprecated \. escape sequence raw strings (authored by dploch). This revision was automatically updated to reflect the committed changes. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D7462?vs=18252=18262 CHANGES SINCE LAST ACTION https://phab.mercurial-scm.org/D7462/new/ REVISION DETAIL https://phab.mercurial-scm.org/D7462 AFFECTED FILES mercurial/match.py CHANGE DETAILS diff --git a/mercurial/match.py b/mercurial/match.py --- a/mercurial/match.py +++ b/mercurial/match.py @@ -554,7 +554,7 @@ class patternmatcher(basematcher): -"""Matches a set of (kind, pat, source) against a 'root' directory. +r"""Matches a set of (kind, pat, source) against a 'root' directory. >>> kindpats = [ ... (b're', br'.*\.c$', b''), @@ -1172,7 +1172,7 @@ def patkind(pattern, default=None): -'''If pattern is 'kind:pat' with a known kind, return kind. +r'''If pattern is 'kind:pat' with a known kind, return kind. >>> patkind(br're:.*\.c$') 're' To: dploch, #hg-reviewers, dlax, pulkit Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D7462: py3: make doc strings containing the deprected '\.' escape sequence raw strings
dploch created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D7462 AFFECTED FILES mercurial/match.py CHANGE DETAILS diff --git a/mercurial/match.py b/mercurial/match.py --- a/mercurial/match.py +++ b/mercurial/match.py @@ -543,7 +543,7 @@ class patternmatcher(basematcher): -"""Matches a set of (kind, pat, source) against a 'root' directory. +r"""Matches a set of (kind, pat, source) against a 'root' directory. >>> kindpats = [ ... (b're', br'.*\.c$', b''), @@ -1152,7 +1152,7 @@ def patkind(pattern, default=None): -'''If pattern is 'kind:pat' with a known kind, return kind. +r'''If pattern is 'kind:pat' with a known kind, return kind. >>> patkind(br're:.*\.c$') 're' To: dploch, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D7372: remotefilelog: handle **kwargs correctly when overriding changelog.add()
Closed by commit rHGc5f6f58f6c71: remotefilelog: handle **kwargs correctly when overriding changelog.add() (authored by dploch). This revision was automatically updated to reflect the committed changes. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D7372?vs=18050=18052 CHANGES SINCE LAST ACTION https://phab.mercurial-scm.org/D7372/new/ REVISION DETAIL https://phab.mercurial-scm.org/D7372 AFFECTED FILES hgext/remotefilelog/__init__.py CHANGE DETAILS diff --git a/hgext/remotefilelog/__init__.py b/hgext/remotefilelog/__init__.py --- a/hgext/remotefilelog/__init__.py +++ b/hgext/remotefilelog/__init__.py @@ -719,9 +719,9 @@ remotefilelog.remotefilelog, b'addrawrevision', addrawrevision ) -def changelogadd(orig, self, *args): +def changelogadd(orig, self, *args, **kwargs): oldlen = len(self) -node = orig(self, *args) +node = orig(self, *args, **kwargs) newlen = len(self) if oldlen != newlen: for oldargs in pendingfilecommits: To: dploch, #hg-reviewers, pulkit Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D7372: remotefilelog: handle **kwargs correctly when overriding changelog.add()
dploch created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D7372 AFFECTED FILES hgext/remotefilelog/__init__.py CHANGE DETAILS diff --git a/hgext/remotefilelog/__init__.py b/hgext/remotefilelog/__init__.py --- a/hgext/remotefilelog/__init__.py +++ b/hgext/remotefilelog/__init__.py @@ -719,9 +719,9 @@ remotefilelog.remotefilelog, b'addrawrevision', addrawrevision ) -def changelogadd(orig, self, *args): +def changelogadd(orig, self, *args, **kwargs): oldlen = len(self) -node = orig(self, *args) +node = orig(self, *args, **kwargs) newlen = len(self) if oldlen != newlen: for oldargs in pendingfilecommits: To: dploch, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D5534: merge: make local file storage in the .hg/merge directory extensible
This revision was automatically updated to reflect the committed changes. Closed by commit rHG8c222bec97da: merge: make local file storage in the .hg/merge directory extensible (authored by dploch, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D5534?vs=13086=13108 REVISION DETAIL https://phab.mercurial-scm.org/D5534 AFFECTED FILES mercurial/merge.py CHANGE DETAILS diff --git a/mercurial/merge.py b/mercurial/merge.py --- a/mercurial/merge.py +++ b/mercurial/merge.py @@ -478,6 +478,13 @@ f.write(_pack(format, key, len(data), data)) f.close() +@staticmethod +def getlocalkey(path): +"""hash the path of a local file context for storage in the .hg/merge +directory.""" + +return hex(hashlib.sha1(path).digest()) + def add(self, fcl, fco, fca, fd): """add a new (potentially?) conflicting file the merge state fcl: file context for local, @@ -488,11 +495,11 @@ note: also write the local version to the `.hg/merge` directory. """ if fcl.isabsent(): -hash = nullhex +localkey = nullhex else: -hash = hex(hashlib.sha1(fcl.path()).digest()) -self._repo.vfs.write('merge/' + hash, fcl.data()) -self._state[fd] = [MERGE_RECORD_UNRESOLVED, hash, fcl.path(), +localkey = mergestate.getlocalkey(fcl.path()) +self._repo.vfs.write('merge/' + localkey, fcl.data()) +self._state[fd] = [MERGE_RECORD_UNRESOLVED, localkey, fcl.path(), fca.path(), hex(fca.filenode()), fco.path(), hex(fco.filenode()), fcl.flags()] @@ -551,15 +558,15 @@ MERGE_RECORD_DRIVER_RESOLVED): return True, 0 stateentry = self._state[dfile] -state, hash, lfile, afile, anode, ofile, onode, flags = stateentry +state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry octx = self._repo[self._other] extras = self.extras(dfile) anccommitnode = extras.get('ancestorlinknode') if anccommitnode: actx = self._repo[anccommitnode] else: actx = None -fcd = self._filectxorabsent(hash, wctx, dfile) +fcd = self._filectxorabsent(localkey, wctx, dfile) fco = self._filectxorabsent(onode, octx, ofile) # TODO: move this to filectxorabsent fca = self._repo.filectx(afile, fileid=anode, changectx=actx) @@ -577,8 +584,8 @@ flags = flo if preresolve: # restore local -if hash != nullhex: -f = self._repo.vfs('merge/' + hash) +if localkey != nullhex: +f = self._repo.vfs('merge/' + localkey) wctx[dfile].write(f.read(), flags) f.close() else: To: dploch, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D5534: merge: make local file storage in the .hg/merge directory extensible
dploch created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This is similar to remotefilelog's 'getlocalkey' method, which must be overridden by systems which rely on full path names for access control purposes. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D5534 AFFECTED FILES mercurial/merge.py CHANGE DETAILS diff --git a/mercurial/merge.py b/mercurial/merge.py --- a/mercurial/merge.py +++ b/mercurial/merge.py @@ -478,6 +478,13 @@ f.write(_pack(format, key, len(data), data)) f.close() +@staticmethod +def getlocalkey(path): +"""hash the path of a local file context for storage in the .hg/merge +directory.""" + +return hex(hashlib.sha1(path).digest()) + def add(self, fcl, fco, fca, fd): """add a new (potentially?) conflicting file the merge state fcl: file context for local, @@ -488,11 +495,11 @@ note: also write the local version to the `.hg/merge` directory. """ if fcl.isabsent(): -hash = nullhex +localkey = nullhex else: -hash = hex(hashlib.sha1(fcl.path()).digest()) -self._repo.vfs.write('merge/' + hash, fcl.data()) -self._state[fd] = [MERGE_RECORD_UNRESOLVED, hash, fcl.path(), +localkey = mergestate.getlocalkey(fcl.path()) +self._repo.vfs.write('merge/' + localkey, fcl.data()) +self._state[fd] = [MERGE_RECORD_UNRESOLVED, localkey, fcl.path(), fca.path(), hex(fca.filenode()), fco.path(), hex(fco.filenode()), fcl.flags()] @@ -551,15 +558,15 @@ MERGE_RECORD_DRIVER_RESOLVED): return True, 0 stateentry = self._state[dfile] -state, hash, lfile, afile, anode, ofile, onode, flags = stateentry +state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry octx = self._repo[self._other] extras = self.extras(dfile) anccommitnode = extras.get('ancestorlinknode') if anccommitnode: actx = self._repo[anccommitnode] else: actx = None -fcd = self._filectxorabsent(hash, wctx, dfile) +fcd = self._filectxorabsent(localkey, wctx, dfile) fco = self._filectxorabsent(onode, octx, ofile) # TODO: move this to filectxorabsent fca = self._repo.filectx(afile, fileid=anode, changectx=actx) @@ -577,8 +584,8 @@ flags = flo if preresolve: # restore local -if hash != nullhex: -f = self._repo.vfs('merge/' + hash) +if localkey != nullhex: +f = self._repo.vfs('merge/' + localkey) wctx[dfile].write(f.read(), flags) f.close() else: To: dploch, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2935: fancyopts: fix rendering of customopt defaults in help text
This revision was automatically updated to reflect the committed changes. Closed by commit rHG979c8ce9022d: fancyopts: fix rendering of customopt defaults in help text (authored by dploch, committed by ). CHANGED PRIOR TO COMMIT https://phab.mercurial-scm.org/D2935?vs=7257=7263#toc REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2935?vs=7257=7263 REVISION DETAIL https://phab.mercurial-scm.org/D2935 AFFECTED FILES mercurial/help.py tests/test-help.t CHANGE DETAILS diff --git a/tests/test-help.t b/tests/test-help.t --- a/tests/test-help.t +++ b/tests/test-help.t @@ -716,15 +716,23 @@ $ cat > helpext.py < import os - > from mercurial import commands, registrar + > from mercurial import commands, fancyopts, registrar > + > def func(arg): + > return '%sfoo' % arg + > class customopt(fancyopts.customopt): + > def newstate(self, oldstate, newparam, abort): + > return '%sbar' % oldstate > cmdtable = {} > command = registrar.command(cmdtable) > > @command(b'nohelp', - > [(b'', b'longdesc', 3, b'x'*90), + > [(b'', b'longdesc', 3, b'x'*67), > (b'n', b'', None, b'normal desc'), - > (b'', b'newline', b'', b'line1\nline2')], + > (b'', b'newline', b'', b'line1\nline2'), + > (b'', b'callableopt', func, b'adds foo'), + > (b'', b'customopt', customopt(''), b'adds bar'), + > (b'', b'customopt-withdefault', customopt('foo'), b'adds bar')], > b'hg nohelp', > norepo=True) > @command(b'debugoptADV', [(b'', b'aopt', None, b'option is (ADVANCED)')]) @@ -786,10 +794,14 @@ options: - --longdesc VALUE x - x (default: 3) - -n -- normal desc - --newline VALUE line1 line2 + --longdesc VALUE + +xxx (default: 3) + -n --normal desc + --newline VALUE line1 line2 + --callableopt VALUE adds foo + --customopt VALUE adds bar + --customopt-withdefault VALUE adds bar (default: foo) (some details hidden, use --verbose to show complete help) diff --git a/mercurial/help.py b/mercurial/help.py --- a/mercurial/help.py +++ b/mercurial/help.py @@ -20,6 +20,7 @@ encoding, error, extensions, +fancyopts, filemerge, fileset, minirst, @@ -84,7 +85,10 @@ if shortopt: so = '-' + shortopt lo = '--' + longopt -if default: + +if isinstance(default, fancyopts.customopt): +default = default.defaultvalue +if default and not callable(default): # default is of unknown type, and in Python 2 we abused # the %s-shows-repr property to handle integers etc. To # match that behavior on Python 3, we do str(default) and To: dploch, #hg-reviewers, yuja Cc: yuja, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2937: fancyopts: prevent mutation of the default value in customopts
This revision was automatically updated to reflect the committed changes. Closed by commit rHGef6215df2402: fancyopts: prevent mutation of the default value in customopts (authored by dploch, committed by ). CHANGED PRIOR TO COMMIT https://phab.mercurial-scm.org/D2937?vs=7258=7264#toc REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2937?vs=7258=7264 REVISION DETAIL https://phab.mercurial-scm.org/D2937 AFFECTED FILES mercurial/fancyopts.py mercurial/help.py CHANGE DETAILS diff --git a/mercurial/help.py b/mercurial/help.py --- a/mercurial/help.py +++ b/mercurial/help.py @@ -87,7 +87,7 @@ lo = '--' + longopt if isinstance(default, fancyopts.customopt): -default = default.defaultvalue +default = default.getdefaultvalue() if default and not callable(default): # default is of unknown type, and in Python 2 we abused # the %s-shows-repr property to handle integers etc. To diff --git a/mercurial/fancyopts.py b/mercurial/fancyopts.py --- a/mercurial/fancyopts.py +++ b/mercurial/fancyopts.py @@ -208,20 +208,27 @@ __metaclass__ = abc.ABCMeta def __init__(self, defaultvalue): -self.defaultvalue = defaultvalue +self._defaultvalue = defaultvalue def _isboolopt(self): return False +def getdefaultvalue(self): +"""Returns the default value for this opt. + +Subclasses should override this to return a new value if the value type +is mutable.""" +return self._defaultvalue + @abc.abstractmethod def newstate(self, oldstate, newparam, abort): """Adds newparam to oldstate and returns the new state. On failure, abort can be called with a string error message.""" class _simpleopt(customopt): def _isboolopt(self): -return isinstance(self.defaultvalue, (bool, type(None))) +return isinstance(self._defaultvalue, (bool, type(None))) def newstate(self, oldstate, newparam, abort): return newparam @@ -235,6 +242,9 @@ return self.callablefn(newparam) class _listopt(customopt): +def getdefaultvalue(self): +return self._defaultvalue[:] + def newstate(self, oldstate, newparam, abort): oldstate.append(newparam) return oldstate @@ -313,7 +323,7 @@ defmap[name] = _defaultopt(default) # copy defaults to state -state[name] = defmap[name].defaultvalue +state[name] = defmap[name].getdefaultvalue() # does it take a parameter? if not defmap[name]._isboolopt(): To: dploch, #hg-reviewers, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2935: fancyopts: fix rendering of customopt defaults in help text
dploch added a comment. In https://phab.mercurial-scm.org/D2935#47329, @yuja wrote: > Looks good, but can you split this to two patches? > > https://www.mercurial-scm.org/wiki/ContributingChanges#Submission_checklist Done, sorry. getdefaultvalue() change is now in https://phab.mercurial-scm.org/D2937 REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2935 To: dploch, #hg-reviewers, yuja Cc: yuja, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2935: fancyopts: fix rendering of customopt defaults in help text
dploch updated this revision to Diff 7257. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2935?vs=7255=7257 REVISION DETAIL https://phab.mercurial-scm.org/D2935 AFFECTED FILES mercurial/help.py CHANGE DETAILS diff --git a/mercurial/help.py b/mercurial/help.py --- a/mercurial/help.py +++ b/mercurial/help.py @@ -20,6 +20,7 @@ encoding, error, extensions, +fancyopts, filemerge, fileset, minirst, @@ -84,7 +85,10 @@ if shortopt: so = '-' + shortopt lo = '--' + longopt -if default: + +if isinstance(default, fancyopts.customopt): +default = default.defaultvalue +if default and not callable(default): # default is of unknown type, and in Python 2 we abused # the %s-shows-repr property to handle integers etc. To # match that behavior on Python 3, we do str(default) and To: dploch, #hg-reviewers, yuja Cc: yuja, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2937: fancyopts: prevent mutation of the default value in customopts
dploch created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2937 AFFECTED FILES mercurial/fancyopts.py mercurial/help.py tests/test-help.t CHANGE DETAILS diff --git a/tests/test-help.t b/tests/test-help.t --- a/tests/test-help.t +++ b/tests/test-help.t @@ -716,15 +716,23 @@ $ cat > helpext.py < import os - > from mercurial import commands, registrar + > from mercurial import commands, fancyopts, registrar > + > def func(arg): + > return '%sfoo' % arg + > class customopt(fancyopts.customopt): + > def newstate(self, oldstate, newparam, abort): + > return '%sbar' % oldstate > cmdtable = {} > command = registrar.command(cmdtable) > > @command(b'nohelp', - > [(b'', b'longdesc', 3, b'x'*90), + > [(b'', b'longdesc', 3, b'x'*67), > (b'n', b'', None, b'normal desc'), - > (b'', b'newline', b'', b'line1\nline2')], + > (b'', b'newline', b'', b'line1\nline2'), + > (b'', b'callableopt', func, b'adds foo'), + > (b'', b'customopt', customopt(''), b'adds bar'), + > (b'', b'customopt-withdefault', customopt('foo'), b'adds bar')], > b'hg nohelp', > norepo=True) > @command(b'debugoptADV', [(b'', b'aopt', None, b'option is (ADVANCED)')]) @@ -786,10 +794,14 @@ options: - --longdesc VALUE x - x (default: 3) - -n -- normal desc - --newline VALUE line1 line2 + --longdesc VALUE + +xxx (default: 3) + -n --normal desc + --newline VALUE line1 line2 + --callableopt VALUE adds foo + --customopt VALUE adds bar + --customopt-withdefault VALUE adds bar (default: foo) (some details hidden, use --verbose to show complete help) diff --git a/mercurial/help.py b/mercurial/help.py --- a/mercurial/help.py +++ b/mercurial/help.py @@ -87,7 +87,7 @@ lo = '--' + longopt if isinstance(default, fancyopts.customopt): -default = default.defaultvalue +default = default.getdefaultvalue() if default and not callable(default): # default is of unknown type, and in Python 2 we abused # the %s-shows-repr property to handle integers etc. To diff --git a/mercurial/fancyopts.py b/mercurial/fancyopts.py --- a/mercurial/fancyopts.py +++ b/mercurial/fancyopts.py @@ -208,20 +208,27 @@ __metaclass__ = abc.ABCMeta def __init__(self, defaultvalue): -self.defaultvalue = defaultvalue +self._defaultvalue = defaultvalue def _isboolopt(self): return False +def getdefaultvalue(self): +"""Returns the default value for this opt. + +Subclasses should override this to return a new value if the value type +is mutable.""" +return self._defaultvalue + @abc.abstractmethod def newstate(self, oldstate, newparam, abort): """Adds newparam to oldstate and returns the new state. On failure, abort can be called with a string error message.""" class _simpleopt(customopt): def _isboolopt(self): -return isinstance(self.defaultvalue, (bool, type(None))) +return isinstance(self._defaultvalue, (bool, type(None))) def newstate(self, oldstate, newparam, abort): return newparam @@ -235,6 +242,9 @@ return self.callablefn(newparam) class _listopt(customopt): +def getdefaultvalue(self): +return self._defaultvalue[:] + def newstate(self, oldstate, newparam, abort): oldstate.append(newparam) return oldstate @@ -313,7 +323,7 @@ defmap[name] = _defaultopt(default) # copy defaults to state -state[name] = defmap[name].defaultvalue +state[name] = defmap[name].getdefaultvalue() # does it take a parameter? if not defmap[name]._isboolopt(): To: dploch, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2935: fancyopts: fix rendering of customopt defaults in help text
dploch created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Also make a getdefaultvalue() function to prevent unwanted mutation. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2935 AFFECTED FILES mercurial/fancyopts.py mercurial/help.py tests/test-help.t CHANGE DETAILS diff --git a/tests/test-help.t b/tests/test-help.t --- a/tests/test-help.t +++ b/tests/test-help.t @@ -716,15 +716,23 @@ $ cat > helpext.py < import os - > from mercurial import commands, registrar + > from mercurial import commands, fancyopts, registrar > + > def func(arg): + > return '%sfoo' % arg + > class customopt(fancyopts.customopt): + > def newstate(self, oldstate, newparam, abort): + > return '%sbar' % oldstate > cmdtable = {} > command = registrar.command(cmdtable) > > @command(b'nohelp', - > [(b'', b'longdesc', 3, b'x'*90), + > [(b'', b'longdesc', 3, b'x'*67), > (b'n', b'', None, b'normal desc'), - > (b'', b'newline', b'', b'line1\nline2')], + > (b'', b'newline', b'', b'line1\nline2'), + > (b'', b'callableopt', func, b'adds foo'), + > (b'', b'customopt', customopt(''), b'adds bar'), + > (b'', b'customopt-withdefault', customopt('foo'), b'adds bar')], > b'hg nohelp', > norepo=True) > @command(b'debugoptADV', [(b'', b'aopt', None, b'option is (ADVANCED)')]) @@ -786,10 +794,14 @@ options: - --longdesc VALUE x - x (default: 3) - -n -- normal desc - --newline VALUE line1 line2 + --longdesc VALUE + +xxx (default: 3) + -n --normal desc + --newline VALUE line1 line2 + --callableopt VALUE adds foo + --customopt VALUE adds bar + --customopt-withdefault VALUE adds bar (default: foo) (some details hidden, use --verbose to show complete help) diff --git a/mercurial/help.py b/mercurial/help.py --- a/mercurial/help.py +++ b/mercurial/help.py @@ -20,6 +20,7 @@ encoding, error, extensions, +fancyopts, filemerge, fileset, minirst, @@ -84,7 +85,10 @@ if shortopt: so = '-' + shortopt lo = '--' + longopt -if default: + +if isinstance(default, fancyopts.customopt): +default = default.getdefaultvalue() +if default and not callable(default): # default is of unknown type, and in Python 2 we abused # the %s-shows-repr property to handle integers etc. To # match that behavior on Python 3, we do str(default) and diff --git a/mercurial/fancyopts.py b/mercurial/fancyopts.py --- a/mercurial/fancyopts.py +++ b/mercurial/fancyopts.py @@ -208,20 +208,27 @@ __metaclass__ = abc.ABCMeta def __init__(self, defaultvalue): -self.defaultvalue = defaultvalue +self._defaultvalue = defaultvalue def _isboolopt(self): return False +def getdefaultvalue(self): +"""Returns the default value for this opt. + +Subclasses should override this to return a new value if the value type +is mutable.""" +return self._defaultvalue + @abc.abstractmethod def newstate(self, oldstate, newparam, abort): """Adds newparam to oldstate and returns the new state. On failure, abort can be called with a string error message.""" class _simpleopt(customopt): def _isboolopt(self): -return isinstance(self.defaultvalue, (bool, type(None))) +return isinstance(self._defaultvalue, (bool, type(None))) def newstate(self, oldstate, newparam, abort): return newparam @@ -235,6 +242,9 @@ return self.callablefn(newparam) class _listopt(customopt): +def getdefaultvalue(self): +return self._defaultvalue[:] + def newstate(self, oldstate, newparam, abort): oldstate.append(newparam) return oldstate @@ -313,7 +323,7 @@ defmap[name] = _defaultopt(default) # copy defaults to state -state[name] = defmap[name].defaultvalue +state[name] = defmap[name].getdefaultvalue() # does it take a parameter? if not defmap[name]._isboolopt(): To: dploch, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2090: fancyopts: add support for custom multi-arg opts in fancyopts.py
This revision was automatically updated to reflect the committed changes. dploch marked 2 inline comments as done. Closed by commit rHG2ed36fec5321: fancyopts: add support for custom multi-arg opts in fancyopts.py (authored by dploch, committed by ). CHANGED PRIOR TO COMMIT https://phab.mercurial-scm.org/D2090?vs=5981=5984#toc REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2090?vs=5981=5984 REVISION DETAIL https://phab.mercurial-scm.org/D2090 AFFECTED FILES mercurial/fancyopts.py CHANGE DETAILS diff --git a/mercurial/fancyopts.py b/mercurial/fancyopts.py --- a/mercurial/fancyopts.py +++ b/mercurial/fancyopts.py @@ -7,7 +7,9 @@ from __future__ import absolute_import +import abc import functools +import types from .i18n import _ from . import ( @@ -201,6 +203,64 @@ parsedargs.extend(args[pos:]) return parsedopts, parsedargs +class customopt(object): +"""Manage defaults and mutations for any type of opt.""" + +__metaclass__ = abc.ABCMeta + +def __init__(self, defaultvalue): +self.defaultvalue = defaultvalue + +def _isboolopt(self): +return False + +@abc.abstractmethod +def newstate(self, oldstate, newparam, abort): +"""Adds newparam to oldstate and returns the new state. + +On failure, abort can be called with a string error message.""" + +class _simpleopt(customopt): +def _isboolopt(self): +return isinstance(self.defaultvalue, (bool, types.NoneType)) + +def newstate(self, oldstate, newparam, abort): +return newparam + +class _callableopt(customopt): +def __init__(self, callablefn): +self.callablefn = callablefn +super(_callableopt, self).__init__(None) + +def newstate(self, oldstate, newparam, abort): +return self.callablefn(newparam) + +class _listopt(customopt): +def newstate(self, oldstate, newparam, abort): +oldstate.append(newparam) +return oldstate + +class _intopt(customopt): +def newstate(self, oldstate, newparam, abort): +try: +return int(newparam) +except ValueError: +abort(_('expected int')) + +def _defaultopt(default): +"""Returns a default opt implementation, given a default value.""" + +if isinstance(default, customopt): +return default +elif callable(default): +return _callableopt(default) +elif isinstance(default, list): +return _listopt(default[:]) +elif type(default) is type(1): +return _intopt(default) +else: +return _simpleopt(default) + def fancyopts(args, options, state, gnu=False, early=False, optaliases=None): """ read args, parse options, and store options in state @@ -220,6 +280,7 @@ list - parameter string is added to a list integer - parameter strings is stored as int function - call function with parameter + customopt - subclass of 'customopt' optaliases is a mapping from a canonical option name to a list of additional long options. This exists for preserving backward compatibility @@ -250,18 +311,13 @@ argmap['-' + short] = name for n in onames: argmap['--' + n] = name -defmap[name] = default +defmap[name] = _defaultopt(default) # copy defaults to state -if isinstance(default, list): -state[name] = default[:] -elif callable(default): -state[name] = None -else: -state[name] = default +state[name] = defmap[name].defaultvalue # does it take a parameter? -if not (default is None or default is True or default is False): +if not defmap[name]._isboolopt(): if short: short += ':' onames = [n + '=' for n in onames] @@ -301,21 +357,13 @@ boolval = False name = argmap[opt] obj = defmap[name] -t = type(obj) -if callable(obj): -state[name] = defmap[name](val) -elif t is type(1): -try: -state[name] = int(val) -except ValueError: -raise error.Abort(_('invalid value %r for option %s, ' - 'expected int') % (val, opt)) -elif t is type(''): -state[name] = val -elif t is type([]): -state[name].append(val) -elif t is type(None) or t is type(False): +if obj._isboolopt(): state[name] = boolval +else: +def abort(s): +raise error.Abort( +_('invalid value %r for option %s, %s') % (val, opt, s)) +state[name] = defmap[name].newstate(state[name], val, abort) # return unparsed args return args To: dploch, #hg-reviewers, durin42, indygreg Cc: durin42, indygreg, mercurial-devel ___ Mercurial-devel mailing list
D2090: fancyopts: add support for custom multi-arg opts in fancyopts.py
dploch added a comment. In https://phab.mercurial-scm.org/D2090#38775, @indygreg wrote: > Out of curiosity, do you think it would be possible to implement an option that behaved like a boolean when given in isolation but also optionally accepted a value? My use case is I want `hg serve --open` to automatically open a web browser pointing at the started server and `hg serve --open chrome` to open Chrome instead of my default web browser. I'm not sure if that's a good idea to implement in the parser though. It could possibly lead to ambiguous argument parsing. It feels like a bad idea. If we want the parsing to be unambiguous, then '--open' must always come at the end of the command line if the default option is desired. If there were a separate flag with the same semantics, you couldn't specify both: 'hg serve --open --foo' would pass "--foo" as the argument to --open. If we stipulate that the optional argument only counts as an argument if it doesn't look like a flag (^-(-)?[^\d-].*$), it sort of works, but that feels pretty messy. INLINE COMMENTS > indygreg wrote in fancyopts.py:258-261 > `isinstance()` is preferred here. Although the integer check could be a bit > wonky if Python 2's `long` ever comes into play. It's actually wonkier than expected... :) > indygreg wrote in fancyopts.py:313-317 > Oh, your code was copied from here. I suppose it is mostly OK then. Although > bringing this into modernity as part of the refactor wouldn't hurt... Sure, modernized. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2090 To: dploch, #hg-reviewers, durin42, indygreg Cc: durin42, indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2090: fancyopts: add support for custom multi-arg opts in fancyopts.py
dploch updated this revision to Diff 5981. dploch marked 4 inline comments as done. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2090?vs=5330=5981 REVISION DETAIL https://phab.mercurial-scm.org/D2090 AFFECTED FILES mercurial/fancyopts.py CHANGE DETAILS diff --git a/mercurial/fancyopts.py b/mercurial/fancyopts.py --- a/mercurial/fancyopts.py +++ b/mercurial/fancyopts.py @@ -7,7 +7,9 @@ from __future__ import absolute_import +import abc import functools +import types from .i18n import _ from . import ( @@ -201,6 +203,65 @@ parsedargs.extend(args[pos:]) return parsedopts, parsedargs +class customopt(object): +"""Manage defaults and mutations for any type of opt.""" + +__metaclass__ = abc.ABCMeta + +def __init__(self, defaultvalue): +self.defaultvalue = defaultvalue + +def _isboolopt(self): +return False + +@abc.abstractmethod +def newstate(self, oldstate, newparam, abort): +"""Adds newparam to oldstate and returns the new state. + +On failure, abort can be called with a string error message.""" + +class _simpleopt(customopt): +def _isboolopt(self): +return isinstance(self.defaultvalue, (bool, types.NoneType)) + +def newstate(self, oldstate, newparam, abort): +return newparam + +class _callableopt(customopt): +def __init__(self, callablefn): +self.callablefn = callablefn +super(_callableopt, self).__init__(None) + +def newstate(self, oldstate, newparam, abort): +return self.callablefn(newparam) + +class _listopt(customopt): +def newstate(self, oldstate, newparam, abort): +oldstate.append(newparam) +return oldstate + +class _intopt(customopt): +def newstate(self, oldstate, newparam, abort): +try: +return int(newparam) +except ValueError: +abort(_('expected int')) + +def _defaultopt(default): +"""Returns a default opt implementation, given a default value.""" + +if isinstance(default, customopt): +return default +elif callable(default): +return _callableopt(default) +elif isinstance(default, list): +return _listopt(default[:]) +# isinstance(True, int) returns True for some reason. +elif isinstance(default, (int, long)) and not isinstance(default, bool): +return _intopt(default) +else: +return _simpleopt(default) + def fancyopts(args, options, state, gnu=False, early=False, optaliases=None): """ read args, parse options, and store options in state @@ -220,6 +281,7 @@ list - parameter string is added to a list integer - parameter strings is stored as int function - call function with parameter + customopt - subclass of 'customopt' optaliases is a mapping from a canonical option name to a list of additional long options. This exists for preserving backward compatibility @@ -250,18 +312,13 @@ argmap['-' + short] = name for n in onames: argmap['--' + n] = name -defmap[name] = default +defmap[name] = _defaultopt(default) # copy defaults to state -if isinstance(default, list): -state[name] = default[:] -elif callable(default): -state[name] = None -else: -state[name] = default +state[name] = defmap[name].defaultvalue # does it take a parameter? -if not (default is None or default is True or default is False): +if not defmap[name]._isboolopt(): if short: short += ':' onames = [n + '=' for n in onames] @@ -301,21 +358,13 @@ boolval = False name = argmap[opt] obj = defmap[name] -t = type(obj) -if callable(obj): -state[name] = defmap[name](val) -elif t is type(1): -try: -state[name] = int(val) -except ValueError: -raise error.Abort(_('invalid value %r for option %s, ' - 'expected int') % (val, opt)) -elif t is type(''): -state[name] = val -elif t is type([]): -state[name].append(val) -elif t is type(None) or t is type(False): +if obj._isboolopt(): state[name] = boolval +else: +def abort(s): +raise error.Abort( +_('invalid value %r for option %s, %s') % (val, opt, s)) +state[name] = defmap[name].newstate(state[name], val, abort) # return unparsed args return args To: dploch, #hg-reviewers, durin42, indygreg Cc: durin42, indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2090: fancyopts: add support for custom multi-arg opts in fancyopts.py
dploch added a comment. Friendly ping! This is my first commit so I'm not sure if more information or changes are expected; please let me know if there's anything I'm missing. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2090 To: dploch, #hg-reviewers, durin42 Cc: durin42, indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2090: fancyopts: add support for custom multi-arg opts in fancyopts.py
dploch added a comment. In https://phab.mercurial-scm.org/D2090#34906, @indygreg wrote: > The fancyopts code is some of the oldest in Mercurial. We've been wanting to rewrite it for a while. This patch seems like an interesting and more powerful direction to take the parser. > > Out of curiosity, do you have an intended use case in mind? Will that use case be in Mercurial itself or is this for an extension? I didn't have a use case for Mercurial itself in mind, but I wouldn't be surprised if there was one. My intended use case is the 'csv' flag example in the commit description: We have a lot of flags in our internal extensions that require the ["alice,bob", "charlie"] -> ["alice", "bob", "charlie"] behavior, so it would be really nice to be able to declare a shareable customopt for these instead of needing to individually wrap every applicable `opts['flag']` lookup. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2090 To: dploch, #hg-reviewers Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2090: fancyopts: add support for custom multi-arg opts in fancyopts.py
dploch created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This allows for more complex multi-arg opt logic, such as "--sum 1 --sum 2" -> 3, "--csv alice,bob --csv charlie" -> ["alice","bob","charlie"]. The current support for callables is insufficient for this. This is done by introducing a 'customopt' class which can be extended for more powerful opts logic. All existing opt-types are converted to use this class, simplifying the fancyopts() logic. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2090 AFFECTED FILES mercurial/fancyopts.py CHANGE DETAILS diff --git a/mercurial/fancyopts.py b/mercurial/fancyopts.py --- a/mercurial/fancyopts.py +++ b/mercurial/fancyopts.py @@ -7,6 +7,7 @@ from __future__ import absolute_import +import abc import functools from .i18n import _ @@ -201,6 +202,66 @@ parsedargs.extend(args[pos:]) return parsedopts, parsedargs +class customopt(object): +"""Manage defaults and mutations for any type of opt.""" + +__metaclass__ = abc.ABCMeta + +def __init__(self, defaultvalue): +self.defaultvalue = defaultvalue + +def _isboolopt(self): +return False + +@abc.abstractmethod +def newstate(self, oldstate, newparam, abort): +"""Adds newparam to oldstate and returns the new state. + +On failure, abort can be called with a string error message.""" + +class _simpleopt(customopt): +def _isboolopt(self): +t = type(self.defaultvalue) +return t is type(False) or t is type(None) + +def newstate(self, oldstate, newparam, abort): +return newparam + +class _callableopt(customopt): +def __init__(self, callable): +self.callable = callable +super(_callableopt, self).__init__(None) + +def newstate(self, oldstate, newparam, abort): +return self.callable(newparam) + +class _listopt(customopt): +def newstate(self, oldstate, newparam, abort): +oldstate.append(newparam) +return oldstate + +class _intopt(customopt): +def newstate(self, oldstate, newparam, abort): +try: +return int(newparam) +except ValueError: +abort(_('expected int')) + +def _defaultopt(default): +"""Returns a default opt implementation, given a default value.""" + +t = type(default) +if isinstance(default, customopt): +return default +elif callable(default): +return _callableopt(default) +elif t is type([]): +return _listopt(default[:]) +elif t is type(1): +return _intopt(default) +else: +return _simpleopt(default) + def fancyopts(args, options, state, gnu=False, early=False, optaliases=None): """ read args, parse options, and store options in state @@ -220,6 +281,7 @@ list - parameter string is added to a list integer - parameter strings is stored as int function - call function with parameter + customopt - subclass of 'customopt' optaliases is a mapping from a canonical option name to a list of additional long options. This exists for preserving backward compatibility @@ -250,18 +312,13 @@ argmap['-' + short] = name for n in onames: argmap['--' + n] = name -defmap[name] = default +defmap[name] = _defaultopt(default) # copy defaults to state -if isinstance(default, list): -state[name] = default[:] -elif callable(default): -state[name] = None -else: -state[name] = default +state[name] = defmap[name].defaultvalue # does it take a parameter? -if not (default is None or default is True or default is False): +if not defmap[name]._isboolopt(): if short: short += ':' onames = [n + '=' for n in onames] @@ -301,21 +358,13 @@ boolval = False name = argmap[opt] obj = defmap[name] -t = type(obj) -if callable(obj): -state[name] = defmap[name](val) -elif t is type(1): -try: -state[name] = int(val) -except ValueError: -raise error.Abort(_('invalid value %r for option %s, ' - 'expected int') % (val, opt)) -elif t is type(''): -state[name] = val -elif t is type([]): -state[name].append(val) -elif t is type(None) or t is type(False): +if obj._isboolopt(): state[name] = boolval +else: +def abort(s): +raise error.Abort( +_('invalid value %r for option %s, %s') % (val, opt, s)) +state[name] = defmap[name].newstate(state[name], val, abort) # return unparsed args return args To: dploch, #hg-reviewers Cc: mercurial-devel