D3300: fix: port most of the way to python 3
This revision was automatically updated to reflect the committed changes. Closed by commit rHG1edf3738e000: fix: port most of the way to python 3 (authored by durin42, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3300?vs=8093&id=8108 REVISION DETAIL https://phab.mercurial-scm.org/D3300 AFFECTED FILES hgext/fix.py CHANGE DETAILS diff --git a/hgext/fix.py b/hgext/fix.py --- a/hgext/fix.py +++ b/hgext/fix.py @@ -66,6 +66,7 @@ mdiff, merge, obsolete, +pycompat, registrar, scmutil, util, @@ -126,6 +127,7 @@ revisions are not forgotten in later ones. The --base flag can be used to override this default behavior, though it is not usually desirable to do so. """ +opts = pycompat.byteskwargs(opts) if opts['all']: if opts['rev']: raise error.Abort(_('cannot specify both "--rev" and "--all"')) @@ -513,7 +515,8 @@ result[name] = Fixer() attrs = ui.configsuboptions('fix', name)[1] for key in FIXER_ATTRS: -setattr(result[name], '_' + key, attrs.get(key, '')) +setattr(result[name], pycompat.sysstr('_' + key), +attrs.get(key, '')) return result def fixernames(ui): To: durin42, #hg-reviewers, pulkit Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3252: tests: use `f --newer` instead of `stat -c` in test-fix.t
yuja added inline comments. INLINE COMMENTS > test-fix.t:495 >adding foo.whole > - $ OLD_MTIME=`stat -c %Y foo.whole` > - $ sleep 1 # mtime has a resolution of one second. > + $ cp foo.whole foo.whole.orig > + $ sleep 2 # mtime has a resolution of one or two seconds. Maybe `cp -p` to not increment a few microseconds? > test-fix.t:499 > + $ f foo.whole --newer foo.whole.orig > + foo.whole: older than foo.whole.orig > This means `mtime(foo.whole) < mtime(foo.whole.orig)`, but we need to assert `mtime(foo.whole.orig) >= mtime(foo.whole)`. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3252 To: durin42, #hg-reviewers, indygreg Cc: yuja, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3303: cborutil: implement support for indefinite length CBOR types
yuja added inline comments. INLINE COMMENTS > cborutil.py:73 > +beginindefinitearray(encoder) > +yield writeitem > +encoder.write(BREAK) I don't think yielding `encoder.encode` would make much sense because an array item can also be a nested indefinite array, in which case, we can't use `writeitem()`. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3303 To: indygreg, #hg-reviewers Cc: yuja, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3305: cmdutil: pass in parsed patch to tryimportone() (API)
This revision was automatically updated to reflect the committed changes. Closed by commit rHGfd1dd79cff20: cmdutil: pass in parsed patch to tryimportone() (API) (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3305?vs=8106&id=8110 REVISION DETAIL https://phab.mercurial-scm.org/D3305 AFFECTED FILES mercurial/cmdutil.py mercurial/commands.py CHANGE DETAILS diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -3089,9 +3089,11 @@ haspatch = False for hunk in patch.split(patchfile): -(msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk, -parents, opts, -msgs, hg.clean) +patchdata = patch.extract(ui, hunk) + +msg, node, rej = cmdutil.tryimportone(ui, repo, patchdata, + parents, opts, + msgs, hg.clean) if msg: haspatch = True ui.note(msg + '\n') diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py --- a/mercurial/cmdutil.py +++ b/mercurial/cmdutil.py @@ -1343,16 +1343,17 @@ # - ctx: the changectx created by import. extrapostimportmap = {} -def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc): +def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc): """Utility function used by commands.import to import a single patch This function is explicitly defined here to help the evolve extension to wrap this part of the import logic. The API is currently a bit ugly because it a simple code translation from the import command. Feel free to make it better. -:hunk: a patch (as a binary string) +:patchdata: a dictionary containing parsed patch data (such as from +``patch.extract()``) :parents: nodes that will be parent of the created commit :opts: the full dict of option passed to the import command :msgs: list to save commit message to. @@ -1362,15 +1363,15 @@ """ # avoid cycle context -> subrepo -> cmdutil from . import context -extractdata = patch.extract(ui, hunk) -tmpname = extractdata.get('filename') -message = extractdata.get('message') -user = opts.get('user') or extractdata.get('user') -date = opts.get('date') or extractdata.get('date') -branch = extractdata.get('branch') -nodeid = extractdata.get('nodeid') -p1 = extractdata.get('p1') -p2 = extractdata.get('p2') + +tmpname = patchdata.get('filename') +message = patchdata.get('message') +user = opts.get('user') or patchdata.get('user') +date = opts.get('date') or patchdata.get('date') +branch = patchdata.get('branch') +nodeid = patchdata.get('nodeid') +p1 = patchdata.get('p1') +p2 = patchdata.get('p2') nocommit = opts.get('no_commit') importbranch = opts.get('import_branch') @@ -1462,7 +1463,7 @@ **pycompat.strkwargs(opts)) extra = {} for idfunc in extrapreimport: -extrapreimportmap[idfunc](repo, extractdata, extra, opts) +extrapreimportmap[idfunc](repo, patchdata, extra, opts) overrides = {} if partial: overrides[('ui', 'allowemptycommit')] = True To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3302: stringutil: support more types with pprint()
This revision was automatically updated to reflect the committed changes. Closed by commit rHG68132a95df31: stringutil: support more types with pprint() (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3302?vs=8103&id=8109 REVISION DETAIL https://phab.mercurial-scm.org/D3302 AFFECTED FILES mercurial/utils/stringutil.py CHANGE DETAILS diff --git a/mercurial/utils/stringutil.py b/mercurial/utils/stringutil.py --- a/mercurial/utils/stringutil.py +++ b/mercurial/utils/stringutil.py @@ -25,15 +25,23 @@ def pprint(o): """Pretty print an object.""" -if isinstance(o, (bytes, bytearray)): +if isinstance(o, bytes): return "b'%s'" % escapestr(o) +elif isinstance(o, bytearray): +# codecs.escape_encode() can't handle bytearray, so escapestr fails +# without coercion. +return "bytearray['%s']" % escapestr(bytes(o)) elif isinstance(o, list): return '[%s]' % (b', '.join(pprint(a) for a in o)) elif isinstance(o, dict): return '{%s}' % (b', '.join( '%s: %s' % (pprint(k), pprint(v)) for k, v in sorted(o.items( elif isinstance(o, bool): return b'True' if o else b'False' +elif isinstance(o, int): +return '%d' % o +elif isinstance(o, float): +return '%f' % o else: raise error.ProgrammingError('do not know how to format %r' % o) To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 7 of 9] export: port _exportsingle() to formatter
On Thu, 12 Apr 2018 09:51:53 -0700, Gregory Szorc wrote: > > +if fm.isplain(): > > +chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts) > > +for chunk, label in chunkiter: > > +fm.plain(chunk, label=label) > > +else: > > +chunkiter = patch.diff(repo, prev, node, match, opts=diffopts) > > +# TODO: make it structured? > > +fm.data(diff=b''.join(chunkiter)) > > > > A concern I have is that we'll want to keep the door open for writing the > diff chunks in a structured manner. That would seemingly require a > different key for the templater. But I think "diff" is fine as the key that > means "a textual diff format recognized by most patch tools." We can > introduce a "diffparts" (or similar) and make "diff" lazily evaluate as > follow-ups, when needed. Yes. Since the key "diff" is used by 'log -Tjson -p', we'll have to leave it as a textual diff. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3306: patch: make extract() a context manager (API)
This revision was automatically updated to reflect the committed changes. Closed by commit rHG5537d8f5e989: patch: make extract() a context manager (API) (authored by indygreg, committed by ). CHANGED PRIOR TO COMMIT https://phab.mercurial-scm.org/D3306?vs=8107&id=8111#toc REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3306?vs=8107&id=8111 REVISION DETAIL https://phab.mercurial-scm.org/D3306 AFFECTED FILES mercurial/cmdutil.py mercurial/commands.py mercurial/patch.py CHANGE DETAILS diff --git a/mercurial/patch.py b/mercurial/patch.py --- a/mercurial/patch.py +++ b/mercurial/patch.py @@ -9,6 +9,7 @@ from __future__ import absolute_import, print_function import collections +import contextlib import copy import difflib import email @@ -192,6 +193,7 @@ ('Node ID', 'nodeid'), ] +@contextlib.contextmanager def extract(ui, fileobj): '''extract patch from data read from fileobj. @@ -209,6 +211,16 @@ Any item can be missing from the dictionary. If filename is missing, fileobj did not contain a patch. Caller must unlink filename when done.''' +fd, tmpname = tempfile.mkstemp(prefix='hg-patch-') +tmpfp = os.fdopen(fd, r'wb') +try: +yield _extract(ui, fileobj, tmpname, tmpfp) +finally: +tmpfp.close() +os.unlink(tmpname) + +def _extract(ui, fileobj, tmpname, tmpfp): + # attempt to detect the start of a patch # (this heuristic is borrowed from quilt) diffre = re.compile(br'^(?:Index:[ \t]|diff[ \t]-|RCS file: |' @@ -218,86 +230,80 @@ re.MULTILINE | re.DOTALL) data = {} -fd, tmpname = tempfile.mkstemp(prefix='hg-patch-') -tmpfp = os.fdopen(fd, r'wb') -try: -msg = pycompat.emailparser().parse(fileobj) + +msg = pycompat.emailparser().parse(fileobj) -subject = msg[r'Subject'] and mail.headdecode(msg[r'Subject']) -data['user'] = msg[r'From'] and mail.headdecode(msg[r'From']) -if not subject and not data['user']: -# Not an email, restore parsed headers if any -subject = '\n'.join(': '.join(map(encoding.strtolocal, h)) -for h in msg.items()) + '\n' +subject = msg[r'Subject'] and mail.headdecode(msg[r'Subject']) +data['user'] = msg[r'From'] and mail.headdecode(msg[r'From']) +if not subject and not data['user']: +# Not an email, restore parsed headers if any +subject = '\n'.join(': '.join(map(encoding.strtolocal, h)) +for h in msg.items()) + '\n' -# should try to parse msg['Date'] -parents = [] +# should try to parse msg['Date'] +parents = [] -if subject: -if subject.startswith('[PATCH'): -pend = subject.find(']') -if pend >= 0: -subject = subject[pend + 1:].lstrip() -subject = re.sub(br'\n[ \t]+', ' ', subject) -ui.debug('Subject: %s\n' % subject) -if data['user']: -ui.debug('From: %s\n' % data['user']) -diffs_seen = 0 -ok_types = ('text/plain', 'text/x-diff', 'text/x-patch') -message = '' -for part in msg.walk(): -content_type = pycompat.bytestr(part.get_content_type()) -ui.debug('Content-Type: %s\n' % content_type) -if content_type not in ok_types: -continue -payload = part.get_payload(decode=True) -m = diffre.search(payload) -if m: -hgpatch = False -hgpatchheader = False -ignoretext = False +if subject: +if subject.startswith('[PATCH'): +pend = subject.find(']') +if pend >= 0: +subject = subject[pend + 1:].lstrip() +subject = re.sub(br'\n[ \t]+', ' ', subject) +ui.debug('Subject: %s\n' % subject) +if data['user']: +ui.debug('From: %s\n' % data['user']) +diffs_seen = 0 +ok_types = ('text/plain', 'text/x-diff', 'text/x-patch') +message = '' +for part in msg.walk(): +content_type = pycompat.bytestr(part.get_content_type()) +ui.debug('Content-Type: %s\n' % content_type) +if content_type not in ok_types: +continue +payload = part.get_payload(decode=True) +m = diffre.search(payload) +if m: +hgpatch = False +hgpatchheader = False +ignoretext = False -ui.debug('found patch at byte %d\n' % m.start(0)) -diffs_seen += 1 -cfp = stringio() -for line in payload[:m.start(0)].splitlines(): -if line.startswith('# HG changeset patch') and not hgpatch: -ui.debug('patch generated by hg export\n') -hgpatch = True -hgpatchheader = T
D3301: fix: use sysstrs for command template formatting
yuja requested changes to this revision. yuja added a comment. This revision now requires changes to proceed. sysstr -> sysbytes can't round trip, and `unicode.format(...=bytes)` wouldn't work as expected. Let me check if we can use templater here. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3301 To: durin42, #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
Re: [PATCH 2 of 4 V3] revset: disable compat with legacy compat for internal revsets API (API)
On 11/04/2018 17:23, Yuya Nishihara wrote: On Wed, 11 Apr 2018 11:50:15 +0200, Boris Feld wrote: # HG changeset patch # User Boris Feld # Date 1520412972 18000 # Wed Mar 07 03:56:12 2018 -0500 # Node ID 00090d394d4e0a7c2637f161493353530dcf739d # Parent 5c1a0d784a26d4e8659dcec80503c8764432a303 # EXP-Topic noname # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 00090d394d4e revset: disable compat with legacy compat for internal revsets API (API) In theory, there should be no user input going directly to "repo.revs" and "repo.set" so we can adjust the behavior in all cases. Avoiding name lookup during revsets parsing provide important speedup when some namespaces are slow to load. diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -832,7 +832,7 @@ class localrepository(object): that contains integer revisions. ''' expr = revsetlang.formatspec(expr, *args) -m = revset.match(None, expr) +m = revset.match(None, expr, legacycompat=False) return m(self) -def matchany(ui, specs, repo=None, localalias=None): +def matchany(ui, specs, repo=None, localalias=None, legacycompat=True): """Create a matcher that will include any revisions matching one of the given specs @@ -2191,7 +2191,7 @@ def matchany(ui, specs, repo=None, local if not all(specs): raise error.ParseError(_("empty query")) lookup = None -if repo: +if repo and legacycompat: lookup = lookupfn(repo) So legacycompat=False is identical to repo=None? Almost, there is also the `posttreebuilthook` hook that takes repo as a parameter. It is (was?) used at least by the directaccess extension. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 2 of 4 V3] revset: disable compat with legacy compat for internal revsets API (API)
On Fri, 13 Apr 2018 14:53:57 +0200, Feld Boris wrote: > On 11/04/2018 17:23, Yuya Nishihara wrote: > > On Wed, 11 Apr 2018 11:50:15 +0200, Boris Feld wrote: > >> # HG changeset patch > >> # User Boris Feld > >> # Date 1520412972 18000 > >> # Wed Mar 07 03:56:12 2018 -0500 > >> # Node ID 00090d394d4e0a7c2637f161493353530dcf739d > >> # Parent 5c1a0d784a26d4e8659dcec80503c8764432a303 > >> # EXP-Topic noname > >> # Available At https://bitbucket.org/octobus/mercurial-devel/ > >> # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r > >> 00090d394d4e > >> revset: disable compat with legacy compat for internal revsets API (API) > >> > >> In theory, there should be no user input going directly to "repo.revs" and > >> "repo.set" so we can adjust the behavior in all cases. Avoiding name lookup > >> during revsets parsing provide important speedup when some namespaces are > >> slow > >> to load. > >> > >> diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py > >> --- a/mercurial/localrepo.py > >> +++ b/mercurial/localrepo.py > >> @@ -832,7 +832,7 @@ class localrepository(object): > >> that contains integer revisions. > >> ''' > >> expr = revsetlang.formatspec(expr, *args) > >> -m = revset.match(None, expr) > >> +m = revset.match(None, expr, legacycompat=False) > >> return m(self) > >> -def matchany(ui, specs, repo=None, localalias=None): > >> +def matchany(ui, specs, repo=None, localalias=None, legacycompat=True): > >> """Create a matcher that will include any revisions matching one of > >> the > >> given specs > >> > >> @@ -2191,7 +2191,7 @@ def matchany(ui, specs, repo=None, local > >> if not all(specs): > >> raise error.ParseError(_("empty query")) > >> lookup = None > >> -if repo: > >> +if repo and legacycompat: > >> lookup = lookupfn(repo) > > So legacycompat=False is identical to repo=None? > > Almost, there is also the `posttreebuilthook` hook that takes repo as a > parameter. Got it. Then, can you rephrase the commit message? The legacy lookup is disabled for internal API from the beginning. > It is (was?) used at least by the directaccess extension. Pulkit, do we still need the posttreebuildhook? I think the only user was the directaccess, and it's in core. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] dispatch: add a whitelist map of commands to implicitly loaded extensions
On Thu, 12 Apr 2018 13:35:50 -0400, Matt Harbison wrote: > # HG changeset patch > # User Matt Harbison > # Date 1523332699 14400 > # Mon Apr 09 23:58:19 2018 -0400 > # Node ID 986b51f15e9bce19b2f67573ff76612540320d1b > # Parent 2e0e61312a257708a70201427b31deba964e9b05 > dispatch: add a whitelist map of commands to implicitly loaded extensions > > This was Augie's idea[1] (or at least my interpretation of it). The goal is > to > be able to load the lfs extension without user intervention where it is both > harmless, and provides a user convenience. The referenced thread discusses > the > clone command and the cryptic error around that in some cases. But the > obvious > benefit is to be able transparently support cloning an lfs repository, or the > sudden transition to lfs on a pull, without bothering the user. The choice to > use the lfs extension in the repository has already been made in both cases at > that point, and neither command can accidentally introduce the feature. > > The implementation is perhaps a bit more than imagined in the thread, because > I > think there may be room to allow extensions like 'share' to force lfs on too, > in > order to make the extension usage more invisible. The largefiles extension > could probably be given the same treatment. For simplicity, I didn't bother > allowing external extensions to be loadable like this. The mechanism used > here > was pilfered from acl.ensureenabled(). > > [1] > https://www.mercurial-scm.org/pipermail/mercurial-devel/2018-January/109851.html > > diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py > --- a/mercurial/dispatch.py > +++ b/mercurial/dispatch.py > @@ -48,6 +48,15 @@ from .utils import ( > > unrecoverablewrite = registrar.command.unrecoverablewrite > > +# Map a command to a list of extensions that are forced on prior to running > the > +# command (unless it has been explicitly disabled.) The idea is to help the > +# user by enabling extensions implicitly when requirements may be added by a > +# remote exchange, instead of erroring out after a partial exchange. > +extensionwhitelist = { > +commands.clone: ['lfs'], > +commands.pull: ['lfs'], > +} Perhaps we can't use a command function as key since it may be wrapped by extensions. > class request(object): > def __init__(self, args, ui=None, repo=None, fin=None, fout=None, > ferr=None, prereposetups=None): > @@ -843,6 +852,20 @@ def _dispatch(req): > fullargs = args > cmd, func, args, options, cmdoptions = _parse(lui, args) > > +# resolve aliases back to the core function > +entrypoint = func > +if isinstance(entrypoint, cmdalias): > +entrypoint = entrypoint.fn > + > +if entrypoint in extensionwhitelist: > +configured = [ext for (ext, _path) in > ui.configitems("extensions")] > +missing = [ext for ext in extensionwhitelist[entrypoint] > + if ext not in configured] > +for ext in missing: > +ui.setconfig('extensions', ext, '', source='internal') > +if missing: > +extensions.loadall(ui, missing) I'm -1 on this because loading extra Python module isn't always instant, and the loaded extension persists forever, which can be a problem for command-server process. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3279: py3: make sure decode() first argument is str
This revision was automatically updated to reflect the committed changes. Closed by commit rHGbfdd20d22a86: py3: make sure decode() first argument is str (authored by pulkit, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3279?vs=8046&id=8112 REVISION DETAIL https://phab.mercurial-scm.org/D3279 AFFECTED FILES hgext/convert/common.py CHANGE DETAILS diff --git a/hgext/convert/common.py b/hgext/convert/common.py --- a/hgext/convert/common.py +++ b/hgext/convert/common.py @@ -217,12 +217,13 @@ if isinstance(s, unicode): return s.encode("utf-8") try: -return s.decode(encoding).encode("utf-8") +return s.decode(pycompat.sysstr(encoding)).encode("utf-8") except UnicodeError: try: return s.decode("latin-1").encode("utf-8") except UnicodeError: -return s.decode(encoding, "replace").encode("utf-8") +return s.decode(pycompat.sysstr(encoding), +"replace").encode("utf-8") def getchangedfiles(self, rev, i): """Return the files changed by rev compared to parent[i]. To: pulkit, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] dispatch: add a whitelist map of commands to implicitly loaded extensions
> On Apr 13, 2018, at 9:11 AM, Yuya Nishihara wrote: > >> On Thu, 12 Apr 2018 13:35:50 -0400, Matt Harbison wrote: >> # HG changeset patch >> # User Matt Harbison >> # Date 1523332699 14400 >> # Mon Apr 09 23:58:19 2018 -0400 >> # Node ID 986b51f15e9bce19b2f67573ff76612540320d1b >> # Parent 2e0e61312a257708a70201427b31deba964e9b05 >> dispatch: add a whitelist map of commands to implicitly loaded extensions > >> class request(object): >> def __init__(self, args, ui=None, repo=None, fin=None, fout=None, >> ferr=None, prereposetups=None): >> @@ -843,6 +852,20 @@ def _dispatch(req): >> fullargs = args >> cmd, func, args, options, cmdoptions = _parse(lui, args) >> >> +# resolve aliases back to the core function >> +entrypoint = func >> +if isinstance(entrypoint, cmdalias): >> +entrypoint = entrypoint.fn >> + >> +if entrypoint in extensionwhitelist: >> +configured = [ext for (ext, _path) in >> ui.configitems("extensions")] >> +missing = [ext for ext in extensionwhitelist[entrypoint] >> + if ext not in configured] >> +for ext in missing: >> +ui.setconfig('extensions', ext, '', source='internal') >> +if missing: >> +extensions.loadall(ui, missing) > > I'm -1 on this because loading extra Python module isn't always instant, and > the loaded extension persists forever, which can be a problem for > command-server > process. Good point. Any alternate ideas? I wasn’t sure what the bundlepart idea would look like, because presumably this needs to be loaded pretty early in the startup process. One thing I like about this is the user doesn’t have to do anything to switch. I can send out an email saying “upgrade to 4.6”, convert on the server, and nothing changes for the developer. Otherwise, everyone has to enable the extension in their user settings, or alias clone and pull to enable it anyway. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 2 of 2 V2] revset: skip old style lookup if external whitespace are detected
On 12/04/2018 13:09, Yuya Nishihara wrote: On Thu, 12 Apr 2018 11:32:23 +0200, Feld Boris wrote: On 11/04/2018 17:16, Yuya Nishihara wrote: On Wed, 11 Apr 2018 11:36:05 +0200, Feld Boris wrote: The proposal here is to define a prefix for which we break backward compatibility. If we do so, people with a "" label will have to use: "":whatever to get a similar effect. IIRC x:y was the most important syntax that needed a strong BC guarantee, so this proposal doesn't sound good. Indeed, the `x:y` is important and we don't want to break the BC guarantee of it. The proposal is less impacting, only people using 'set' as labels and using it at the beginning of a revsetwould be impacted. This prefix has the advantage of being concise and coherent with whatfilesetuse. Doesn't '-r set:foo' look like a range? I don't like an idea of introducing another ambiguous syntax to resolve ambiguity, but furthermore "set:foo" seems confusing to humans. IIUC, we have "set:" for filesets only because that's the syntax to specify file patterns. If we really want to add something to force revset evaluation, I think it has to be compatible with the current syntax, such as "(EXPR)" or "revset(EXPR)". `(EXPR)` seemtoo likely to introduce a BC breakage, as people are more likely to have a tag that looks like `(xxx)` than a 'set' tag IMHO. Ugh, I never assumed that would actually happen, but since you face to more real users having various backgrounds than me, I might be wrong. In a previous discussion, you pointed out that `revset(EXPR)` would be painful because we would have to escape everything within EXPR. What makes you consider it again? Do you mean that if a revset has this form `revset((.*))`; we just evaluate the contents inside parenthesis? My proposal is 'revset(EXPR)', not 'revset("EXPR")'. It's an identity function similar to present(), but also disables the legacy lookup. I don't wanna add such magic, but 'revset(EXPR)' seems less bad than 'set:EXPR' or ' EXPR'. Agreed that `set:foo` looks like a range, maybe we need to use a less ambiguous operator? `=foo+bar` `#foo+bar` `#revset# foo+bar` (Nothing really stands out as pretty, but trying to extend our search area here.) Yeah, they look weird. FWIW, I prefer not reviewing this sort of patches just before the freeze. The patch itself is simple, but we have to carefully think about UX and syntactic consistency. We love the 'revset(EXPR)' idea and we have a v4 series that is ready to send. It is okay to send it before the freeze? If not, would it be possible to take the first two changesets of the V3 series? They will likely bring some speed improvements for big repositories where name lookups are slow and for commands that don't need to load them. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3307: context: set stack level for deprecation warning
martinvonz created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This patch makes the deprecation warning print the caller of repo.__getitem__. That's not quite correct, since there could also be other callers of changectx.__init__, but it seems like the most helpful stack level in practice. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3307 AFFECTED FILES mercurial/context.py CHANGE DETAILS diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -399,7 +399,7 @@ # * If "x" can be a mix of the above, you'll have to figure it out #yourself repo.ui.deprecwarn("changectx.__init__ is getting more limited, see source " - "for details", "4.6") + "for details", "4.6", stacklevel=4) class changectx(basectx): """A changecontext object makes access to data related to a particular To: martinvonz, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
mercurial@37493: 112 new changesets
112 new changesets in mercurial: https://www.mercurial-scm.org/repo/hg/rev/de9f9f888900 changeset: 37382:de9f9f888900 user:Augie Fackler date:Thu Apr 05 10:13:01 2018 -0400 summary: util: whitelist apfs for hardlink support ... 110 changesets not listed ... https://www.mercurial-scm.org/repo/hg/rev/f1413e4a54a6 changeset: 37493:f1413e4a54a6 bookmark:@ tag: tip user:Anton Shestakov date:Wed Apr 04 13:14:48 2018 +0800 summary: paper: make all source lines have the same minimum height -- Repository URL: https://www.mercurial-scm.org/repo/hg ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3267: repository: define new interface for running commands
indygreg planned changes to this revision. indygreg added a comment. I'll be making small revisions to the interface docs and implementation. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3267 To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 2 of 2 V2] revset: skip old style lookup if external whitespace are detected
On 13/04/2018 15:48, Feld Boris wrote: On 12/04/2018 13:09, Yuya Nishihara wrote: On Thu, 12 Apr 2018 11:32:23 +0200, Feld Boris wrote: On 11/04/2018 17:16, Yuya Nishihara wrote: On Wed, 11 Apr 2018 11:36:05 +0200, Feld Boris wrote: The proposal here is to define a prefix for which we break backward compatibility. If we do so, people with a "" label will have to use: "":whatever to get a similar effect. IIRC x:y was the most important syntax that needed a strong BC guarantee, so this proposal doesn't sound good. Indeed, the `x:y` is important and we don't want to break the BC guarantee of it. The proposal is less impacting, only people using 'set' as labels and using it at the beginning of a revsetwould be impacted. This prefix has the advantage of being concise and coherent with whatfilesetuse. Doesn't '-r set:foo' look like a range? I don't like an idea of introducing another ambiguous syntax to resolve ambiguity, but furthermore "set:foo" seems confusing to humans. IIUC, we have "set:" for filesets only because that's the syntax to specify file patterns. If we really want to add something to force revset evaluation, I think it has to be compatible with the current syntax, such as "(EXPR)" or "revset(EXPR)". `(EXPR)` seemtoo likely to introduce a BC breakage, as people are more likely to have a tag that looks like `(xxx)` than a 'set' tag IMHO. Ugh, I never assumed that would actually happen, but since you face to more real users having various backgrounds than me, I might be wrong. In a previous discussion, you pointed out that `revset(EXPR)` would be painful because we would have to escape everything within EXPR. What makes you consider it again? Do you mean that if a revset has this form `revset((.*))`; we just evaluate the contents inside parenthesis? My proposal is 'revset(EXPR)', not 'revset("EXPR")'. It's an identity function similar to present(), but also disables the legacy lookup. I don't wanna add such magic, but 'revset(EXPR)' seems less bad than 'set:EXPR' or ' EXPR'. Agreed that `set:foo` looks like a range, maybe we need to use a less ambiguous operator? `=foo+bar` `#foo+bar` `#revset# foo+bar` (Nothing really stands out as pretty, but trying to extend our search area here.) Yeah, they look weird. FWIW, I prefer not reviewing this sort of patches just before the freeze. The patch itself is simple, but we have to carefully think about UX and syntactic consistency. We love the 'revset(EXPR)' idea and we have a v4 series that is ready to send. It is okay to send it before the freeze? If not, would it be possible to take the first two changesets of the V3 series? They will likely bring some speed improvements for big repositories where name lookups are slow and for commands that don't need to load them. By re-reading your comments on the V3 series, I realized that the second changeset is not necessary since repo is never passed for internal lookup I will update the series and prepare a new V4. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3308: scmutil: document that isrevsymbol() raises on ambiguous node prefix
martinvonz 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/D3308 AFFECTED FILES mercurial/scmutil.py CHANGE DETAILS diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py --- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -444,6 +444,11 @@ return node def isrevsymbol(repo, symbol): +"""Checks if a symbol exists in the repo. + + See revsymbol() for details. Raises error.LookupError if the symbol is an + an ambiguous nodeid prefix. +""" try: revsymbol(repo, symbol) return True To: martinvonz, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3310: scmutil: use resolvepartialhexnodeid() from revsymbol()
martinvonz created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY I should have copied this from changectx.__init__ into in https://phab.mercurial-scm.org/rHG35b34202dd3b2effc6e5ff5a82f911825a9cf532 (context: handle partial nodeids in revsymbol(), 2018-04-08). REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3310 AFFECTED FILES mercurial/scmutil.py CHANGE DETAILS diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py --- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -503,7 +503,7 @@ except KeyError: pass -node = repo.unfiltered().changelog._partialmatch(symbol) +node = resolvehexnodeidprefix(repo, symbol) if node is not None: rev = repo.changelog.rev(node) return repo[rev] To: martinvonz, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3313: scmutil: make shortesthexnodeidprefix() use unfiltered repo
martinvonz created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Both callers were doing this, and resolvehexnodeidprefix() was also working on the unfiltered repo, so it makes more sense to have it all in one place. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3313 AFFECTED FILES hgext/show.py mercurial/scmutil.py mercurial/templatefuncs.py CHANGE DETAILS diff --git a/mercurial/templatefuncs.py b/mercurial/templatefuncs.py --- a/mercurial/templatefuncs.py +++ b/mercurial/templatefuncs.py @@ -587,11 +587,8 @@ # i18n: "shortest" is a keyword _("shortest() expects an integer minlength")) -# _partialmatch() of filtered changelog could take O(len(repo)) time, -# which would be unacceptably slow. so we look for hash collision in -# unfiltered space, which means some hashes may be slightly longer. repo = context.resource(mapping, 'ctx')._repo -return scmutil.shortesthexnodeidprefix(repo.unfiltered(), node, minlength) +return scmutil.shortesthexnodeidprefix(repo, node, minlength) @templatefunc('strip(text[, chars])') def strip(context, mapping, args): diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py --- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -436,16 +436,19 @@ def resolvehexnodeidprefix(repo, prefix): # Uses unfiltered repo because it's faster when prefix is ambiguous/ -# This matches the "shortest" template function. +# This matches the shortesthexnodeidprefix() function below. node = repo.unfiltered().changelog._partialmatch(prefix) if node is None: return repo.changelog.rev(node) # make sure node isn't filtered return node def shortesthexnodeidprefix(repo, hexnode, minlength=1): """Find the shortest unambiguous prefix that matches hexnode.""" -cl = repo.changelog +# _partialmatch() of filtered changelog could take O(len(repo)) time, +# which would be unacceptably slow. so we look for hash collision in +# unfiltered space, which means some hashes may be slightly longer. +cl = repo.unfiltered().changelog def isvalid(test): try: if cl._partialmatch(test) is None: diff --git a/hgext/show.py b/hgext/show.py --- a/hgext/show.py +++ b/hgext/show.py @@ -447,10 +447,8 @@ """ if not revs: return minlen -# don't use filtered repo because it's slow. see templater.shortest(). cl = repo.changelog -return max(len(scmutil.shortesthexnodeidprefix(repo.unfiltered(), - hex(cl.node(r)), +return max(len(scmutil.shortesthexnodeidprefix(repo, hex(cl.node(r)), minlen)) for r in revs) # Adjust the docstring of the show command so it shows all registered views. To: martinvonz, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3311: revset: use resolvehexnodeidprefix() in id() predicate (BC)
martinvonz created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY We now have a public method for this purpose, so we don't need to access the private revlog._partialmatch(). Also, I'll probably make some changes to resolvehexnodeidprefix() later, and I want those to be reflected by the id() predicate. Note that this breaks a test case, because we now resolve the prefix in the unfiltered repo and get an ambiguous lookup. The test case was already documented to be broken even though it wasn't. The new behavior is more consistent: now "hg co ffb" and "hg co 'id(ffb)'" behaves the same. We should fix both of them at once later instead. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3311 AFFECTED FILES mercurial/revset.py tests/test-revset.t CHANGE DETAILS diff --git a/tests/test-revset.t b/tests/test-revset.t --- a/tests/test-revset.t +++ b/tests/test-revset.t @@ -1878,7 +1878,8 @@ [255] BROKEN should be '2' (node lookup uses unfiltered repo since dc25ed84bee8) $ hg debugrevspec '0:wdir() & id(fffb)' - 2 + abort: 00changelog.i@fffb: ambiguous identifier! + [255] $ hg debugrevspec '0:wdir() & 8' 4 $ hg debugrevspec '0:wdir() & f' diff --git a/mercurial/revset.py b/mercurial/revset.py --- a/mercurial/revset.py +++ b/mercurial/revset.py @@ -1333,7 +1333,7 @@ else: rn = None try: -pm = repo.changelog._partialmatch(n) +pm = scmutil.resolvehexnodeidprefix(repo, n) if pm is not None: rn = repo.changelog.rev(pm) except error.WdirUnsupported: To: martinvonz, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3309: scmutil: rename resolvepartialhexnodeid() to resolvehexnodeidprefix()
martinvonz created this revision. Herald added a reviewer: durin42. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY I'm going to move revlog.shortest() to scmutil. I plan on calling it shortesthexnodeidprefix(). resolvehexnodeidprefix() matches that better. Also, "prefix" carries more information than "partial". REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3309 AFFECTED FILES hgext/eol.py hgext/histedit.py mercurial/context.py mercurial/scmutil.py CHANGE DETAILS diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py --- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -434,8 +434,8 @@ hexfunc = short return '%d:%s' % (rev, hexfunc(node)) -def resolvepartialhexnodeid(repo, prefix): -# Uses unfiltered repo because it's faster when then prefix is ambiguous/ +def resolvehexnodeidprefix(repo, prefix): +# Uses unfiltered repo because it's faster when prefix is ambiguous/ # This matches the "shortest" template function. node = repo.unfiltered().changelog._partialmatch(prefix) if node is None: diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -392,7 +392,7 @@ # * If you know that "x" is a branch or in some other namespace, #use the appropriate mechanism for that namespace # * If you know that "x" is a hex nodeid prefix, use -#repo[scmutil.resolvepartialhexnodeid(repo, x)] +#repo[scmutil.resolvehexnodeidprefix(repo, x)] # * If "x" is a string that can be any of the above, but you don't want #to allow general revsets (perhaps because "x" may come from a remote #user and the revset may be too costly), use scmutil.revsymbol(repo, x) @@ -476,7 +476,7 @@ except KeyError: pass -self._node = scmutil.resolvepartialhexnodeid(repo, changeid) +self._node = scmutil.resolvehexnodeidprefix(repo, changeid) if self._node is not None: self._rev = repo.changelog.rev(self._node) changectxdeprecwarn(repo) diff --git a/hgext/histedit.py b/hgext/histedit.py --- a/hgext/histedit.py +++ b/hgext/histedit.py @@ -443,7 +443,7 @@ """ Verifies semantic correctness of the rule""" repo = self.repo ha = node.hex(self.node) -self.node = scmutil.resolvepartialhexnodeid(repo, ha) +self.node = scmutil.resolvehexnodeidprefix(repo, ha) if self.node is None: raise error.ParseError(_('unknown changeset %s listed') % ha[:12]) self._verifynodeconstraints(prev, expected, seen) diff --git a/hgext/eol.py b/hgext/eol.py --- a/hgext/eol.py +++ b/hgext/eol.py @@ -300,7 +300,7 @@ hook = checkheadshook def preupdate(ui, repo, hooktype, parent1, parent2): -p1node = scmutil.resolvepartialhexnodeid(repo, parent1) +p1node = scmutil.resolvehexnodeidprefix(repo, parent1) repo.loadeol([p1node]) return False To: martinvonz, durin42, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3312: revlog: move shortest() to scmutil.shortesthexnodeidprefix() (API)
martinvonz created this revision. Herald added a reviewer: indygreg. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY I apparently moved this function from templater.py in https://phab.mercurial-scm.org/rHG448725a2ef7356524bcc9638a5c5eaaf59f263af (templater: extract shortest() logic from template function, 2017-09-15). Now we have scmutil.resolvehexnodeidprefix(), so it makes sense to have this method next to it. Note that the change in show.py also makes it so the conversion from revnum to nodeid is done on the filtered repo, but that should be inconsequential since the revs are all from the filtered repo anyway. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3312 AFFECTED FILES hgext/show.py mercurial/revlog.py mercurial/scmutil.py mercurial/templatefuncs.py CHANGE DETAILS diff --git a/mercurial/templatefuncs.py b/mercurial/templatefuncs.py --- a/mercurial/templatefuncs.py +++ b/mercurial/templatefuncs.py @@ -590,8 +590,8 @@ # _partialmatch() of filtered changelog could take O(len(repo)) time, # which would be unacceptably slow. so we look for hash collision in # unfiltered space, which means some hashes may be slightly longer. -cl = context.resource(mapping, 'ctx')._repo.unfiltered().changelog -return cl.shortest(node, minlength) +repo = context.resource(mapping, 'ctx')._repo +return scmutil.shortesthexnodeidprefix(repo.unfiltered(), node, minlength) @templatefunc('strip(text[, chars])') def strip(context, mapping, args): diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py --- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -443,6 +443,46 @@ repo.changelog.rev(node) # make sure node isn't filtered return node +def shortesthexnodeidprefix(repo, hexnode, minlength=1): +"""Find the shortest unambiguous prefix that matches hexnode.""" +cl = repo.changelog +def isvalid(test): +try: +if cl._partialmatch(test) is None: +return False + +try: +i = int(test) +# if we are a pure int, then starting with zero will not be +# confused as a rev; or, obviously, if the int is larger +# than the value of the tip rev +if test[0] == '0' or i > len(cl): +return True +return False +except ValueError: +return True +except error.RevlogError: +return False +except error.WdirUnsupported: +# single 'ff...' match +return True + +shortest = hexnode +startlength = max(6, minlength) +length = startlength +while True: +test = hexnode[:length] +if isvalid(test): +shortest = test +if length == minlength or length > startlength: +return shortest +length -= 1 +else: +length += 1 +if len(shortest) <= length: +return shortest + + def isrevsymbol(repo, symbol): """Checks if a symbol exists in the repo. diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -1500,44 +1500,6 @@ raise LookupError(id, self.indexfile, _('no match found')) -def shortest(self, hexnode, minlength=1): -"""Find the shortest unambiguous prefix that matches hexnode.""" -def isvalid(test): -try: -if self._partialmatch(test) is None: -return False - -try: -i = int(test) -# if we are a pure int, then starting with zero will not be -# confused as a rev; or, obviously, if the int is larger -# than the value of the tip rev -if test[0] == '0' or i > len(self): -return True -return False -except ValueError: -return True -except error.RevlogError: -return False -except error.WdirUnsupported: -# single 'ff...' match -return True - -shortest = hexnode -startlength = max(6, minlength) -length = startlength -while True: -test = hexnode[:length] -if isvalid(test): -shortest = test -if length == minlength or length > startlength: -return shortest -length -= 1 -else: -length += 1 -if len(shortest) <= length: -return shortest - def cmp(self, node, text): """compare text with a given file revision diff --git a/hgext/show.py b/hgext/show.py --- a/hgext/show.py +++ b/hgext/show.py @@ -45,6 +45,7 @@ registrar, revset, revset
Re: [PATCH 2 of 4 V3] revset: disable compat with legacy compat for internal revsets API (API)
On Fri, Apr 13, 2018 at 6:33 PM, Yuya Nishihara wrote: > > It is (was?) used at least by the directaccess extension. > > Pulkit, do we still need the posttreebuildhook? I think the only user was > the directaccess, and it's in core. > Nope, we don't need that as far as directaccess is concerned. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3312: revlog: move shortest() to scmutil.shortesthexnodeidprefix() (API)
indygreg added a comment. I haven't looked at the patch yet, but I also wrote something like this as part of storage refactors. IMO the shortest node logic has no business being on the generic revlog implementation. I never submitted that patch since removing filelog's inheritance of revlog worked around the problem for me. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3312 To: martinvonz, indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] dispatch: add a whitelist map of commands to implicitly loaded extensions
On Fri, Apr 13, 2018 at 6:35 AM, Matt Harbison wrote: > > > On Apr 13, 2018, at 9:11 AM, Yuya Nishihara wrote: > > > >> On Thu, 12 Apr 2018 13:35:50 -0400, Matt Harbison wrote: > >> # HG changeset patch > >> # User Matt Harbison > >> # Date 1523332699 14400 > >> # Mon Apr 09 23:58:19 2018 -0400 > >> # Node ID 986b51f15e9bce19b2f67573ff76612540320d1b > >> # Parent 2e0e61312a257708a70201427b31deba964e9b05 > >> dispatch: add a whitelist map of commands to implicitly loaded > extensions > > > >> class request(object): > >> def __init__(self, args, ui=None, repo=None, fin=None, fout=None, > >> ferr=None, prereposetups=None): > >> @@ -843,6 +852,20 @@ def _dispatch(req): > >> fullargs = args > >> cmd, func, args, options, cmdoptions = _parse(lui, args) > >> > >> +# resolve aliases back to the core function > >> +entrypoint = func > >> +if isinstance(entrypoint, cmdalias): > >> +entrypoint = entrypoint.fn > >> + > >> +if entrypoint in extensionwhitelist: > >> +configured = [ext for (ext, _path) in > ui.configitems("extensions")] > >> +missing = [ext for ext in extensionwhitelist[entrypoint] > >> + if ext not in configured] > >> +for ext in missing: > >> +ui.setconfig('extensions', ext, '', source='internal') > >> +if missing: > >> +extensions.loadall(ui, missing) > > > > I'm -1 on this because loading extra Python module isn't always instant, > and > > the loaded extension persists forever, which can be a problem for > command-server > > process. > > Good point. Any alternate ideas? I wasn’t sure what the bundlepart idea > would look like, because presumably this needs to be loaded pretty early in > the startup process. > > One thing I like about this is the user doesn’t have to do anything to > switch. I can send out an email saying “upgrade to 4.6”, convert on the > server, and nothing changes for the developer. Otherwise, everyone has to > enable the extension in their user settings, or alias clone and pull to > enable it anyway. > I want to add a "capabilities" mechanism to hg.peer() and hg.repository() that makes the caller declare what features the returned repository must have. This would allow callers to say things like "I only want a read-only repo," "I need to be able to speak wire protocol command X," etc. These capabilities would get passed down to the localrepo or peer layers for evaluation, where they may result in the constructed instance being a different type, having a different composition of methods, etc. This may seem orthogonal to command dispatch. However, I would eventually like @command's to self-declare what features they need. e.g. `log` would say it needs a read-only repository. `update` would say it needs a repository with a working directory. `commit` would say it needs a mutable repository. A command to update the narrow profile would require a repository type that can be narrowed. And the list goes on. For your immediate use case, getting the extension tie-ins could be difficult. Obviously the extension needs to be loaded in order for the extension to declare a "capability" for a requested repo/peer instance. What we may want instead is to key things off .hg/requires or a to-be-invented supplemental requirements-like file that declares soft features. localrepository.__init__ could then load trusted extensions at repo open time if a requirements/capability was present/requested. i.e. if you talk to an LFS server, write a semaphore somewhere into .hg/ and have something in core look for that and automatically load lfs if present. This would get you the "automatically enable LFS when talking to LFS servers" benefits. Alternatively, you could move the client pieces of LFS into core so no extension loading is needed. That may still require a semaphore somewhere. I think that's the best long-term behavior. But I think we should shore up the storage interfaces and figure out the narrow/partial clone integration story before we do that. Maybe much of this work has already been done? ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3267: repository: define new interface for running commands
indygreg updated this revision to Diff 8120. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3267?vs=8033&id=8120 REVISION DETAIL https://phab.mercurial-scm.org/D3267 AFFECTED FILES mercurial/repository.py CHANGE DETAILS diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -196,6 +196,88 @@ def changegroupsubset(bases, heads, kind): pass +class ipeercommandexecutor(zi.Interface): +"""Represents a mechanism to execute remote commands. + +This is the primary interface for requesting that wire protocol commands +be executed. Instances of this interface are active in a context manager +and have a well-defined lifetime. When the context manager exits, all +outstanding requests are waited on. +""" + +def callcommand(name, args): +"""Request that a named command be executed. + +Receives the command name and a dictionary of command arguments. + +Returns a ``concurrent.futures.Future`` that will resolve to the +result of that command request. That exact value is left up to +the implementation and possibly varies by command. + +Not all commands can coexist with other commands in an executor +instance: it depends on the underlying wire protocol transport being +used and the command itself. + +Implementations MAY call ``sendcommands()`` automatically if the +requested command can not coexist with other commands in this executor. + +Implementations MAY call ``sendcommands()`` automatically when the +future's ``result()`` is called. So, consumers using multiple +commands with an executor MUST ensure that ``result()`` is not called +until all command requests have been issued. +""" + +def sendcommands(): +"""Trigger submission of queued command requests. + +Not all transports submit commands as soon as they are requested to +run. When called, this method forces queued command requests to be +issued. It will no-op if all commands have already been sent. + +When called, no more new commands may be issued with this executor. +""" + +def close(): +"""Signal that this command request is finished. + +When called, no more new commands may be issued. All outstanding +commands that have previously been issued are waited on before +returning. This not only includes waiting for the futures to resolve, +but also waiting for all response data to arrive. In other words, +calling this waits for all on-wire state for issued command requests +to finish. + +When used as a context manager, this method is called when exiting the +context manager. + +This method may call ``sendcommands()`` if there are buffered commands. +""" + +class ipeerrequests(zi.Interface): +"""Interface for executing commands on a peer.""" + +def commandexecutor(): +"""A context manager that resolves to an ipeercommandexecutor. + +The object this resolves to can be used to issue command requests +to the peer. + +Callers should call its ``callcommand`` method to issue command +requests. + +A new executor should be obtained for each distinct set of commands +(possibly just a single command) that the consumer wants to execute +as part of a single operation or round trip. This is because some +peers are half-duplex and/or don't support persistent connections. +e.g. in the case of HTTP peers, commands sent to an executor represent +a single HTTP request. While some peers may support multiple command +sends over the wire per executor, consumers need to code to the least +capable peer. So it should be assumed that command executors buffer +called commands until they are told to send them and that each +command executor could result in a new connection or wire-level request +being issued. +""" + class ipeerbase(ipeerconnection, ipeercapabilities, ipeercommands): """Unified interface for peer repositories. To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3270: largefiles: use command executor for batch operation
indygreg updated this revision to Diff 8123. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3270?vs=8036&id=8123 REVISION DETAIL https://phab.mercurial-scm.org/D3270 AFFECTED FILES hgext/largefiles/wirestore.py tests/test-largefiles-wireproto.t CHANGE DETAILS diff --git a/tests/test-largefiles-wireproto.t b/tests/test-largefiles-wireproto.t --- a/tests/test-largefiles-wireproto.t +++ b/tests/test-largefiles-wireproto.t @@ -312,7 +312,7 @@ getting changed largefiles using http://localhost:$HGPORT2/ sending capabilities command - sending batch command + sending statlfile command getting largefiles: 0/1 files (0.00%) getting f1:02a439e5c31c526465ab1a0ca1f431f76b827b90 sending getlfile command @@ -400,7 +400,7 @@ searching 3 changesets for largefiles verified existence of 3 revisions of 3 largefiles $ tail -1 access.log - $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=statlfile+sha%3Dc8559c3c9cfb42131794b7d8009230403b9b454c x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob) + $LOCALIP - - [$LOGDATE$] "GET /?cmd=statlfile HTTP/1.1" 200 - x-hgarg-1:sha=c8559c3c9cfb42131794b7d8009230403b9b454c x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob) $ killdaemons.py diff --git a/hgext/largefiles/wirestore.py b/hgext/largefiles/wirestore.py --- a/hgext/largefiles/wirestore.py +++ b/hgext/largefiles/wirestore.py @@ -32,8 +32,12 @@ '''For each hash, return 0 if it is available, other values if not. It is usually 2 if the largefile is missing, but might be 1 the server has a corrupted copy.''' -batch = self.remote.iterbatch() -for hash in hashes: -batch.statlfile(hash) -batch.submit() -return dict(zip(hashes, batch.results())) + +with self.remote.commandexecutor() as e: +fs = [] +for hash in hashes: +fs.append((hash, e.callcommand('statlfile', { +'sha': hash, +}))) + +return {hash: f.result() for hash, f in fs} To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3272: treediscovery: switch to command executor interface
indygreg updated this revision to Diff 8125. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3272?vs=8039&id=8125 REVISION DETAIL https://phab.mercurial-scm.org/D3272 AFFECTED FILES mercurial/treediscovery.py CHANGE DETAILS diff --git a/mercurial/treediscovery.py b/mercurial/treediscovery.py --- a/mercurial/treediscovery.py +++ b/mercurial/treediscovery.py @@ -36,7 +36,8 @@ base = set() if not heads: -heads = remote.heads() +with remote.commandexecutor() as e: +heads = e.callcommand('heads', {}).result() if repo.changelog.tip() == nullid: base.add(nullid) @@ -65,7 +66,10 @@ # a 'branch' here is a linear segment of history, with four parts: # head, root, first parent, second parent # (a branch always has two parents (or none) by definition) -unknown = collections.deque(remote.branches(unknown)) +with remote.commandexecutor() as e: +branches = e.callcommand('branches', {'nodes': unknown}).result() + +unknown = collections.deque(branches) while unknown: r = [] while unknown: @@ -107,7 +111,12 @@ repo.ui.debug("request %d: %s\n" % (reqcnt, " ".join(map(short, r for p in xrange(0, len(r), 10): -for b in remote.branches(r[p:p + 10]): +with remote.commandexecutor() as e: +branches = e.callcommand('branches', { +'nodes': r[p:p + 10], +}).result() + +for b in branches: repo.ui.debug("received %s:%s\n" % (short(b[0]), short(b[1]))) unknown.append(b) @@ -117,7 +126,11 @@ newsearch = [] reqcnt += 1 repo.ui.progress(_('searching'), reqcnt, unit=_('queries')) -for n, l in zip(search, remote.between(search)): + +with remote.commandexecutor() as e: +between = e.callcommand('between', {'pairs': search}).result() + +for n, l in zip(search, between): l.append(n[1]) p = n[0] f = 1 To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3269: wireproto: implement batching on peer executor interface
indygreg updated this revision to Diff 8122. indygreg edited the summary of this revision. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3269?vs=8035&id=8122 REVISION DETAIL https://phab.mercurial-scm.org/D3269 AFFECTED FILES mercurial/setdiscovery.py mercurial/wireprotov1peer.py CHANGE DETAILS diff --git a/mercurial/wireprotov1peer.py b/mercurial/wireprotov1peer.py --- a/mercurial/wireprotov1peer.py +++ b/mercurial/wireprotov1peer.py @@ -9,6 +9,7 @@ import hashlib import sys +import weakref from .i18n import _ from .node import ( @@ -180,13 +181,36 @@ return ';'.join(cmds) +class unsentfuture(pycompat.futures.Future): +"""A Future variation to represent an unsent command. + +Because we buffer commands and don't submit them immediately, calling +``result()`` on an unsent future could deadlock. Futures for buffered +commands are represented by this type, which wraps ``result()`` to +call ``sendcommands()``. +""" + +def result(self, timeout=None): +if self.done(): +return pycompat.futures.Future.result(self, timeout) + +self._peerexecutor.sendcommands() + +# This looks like it will infinitely recurse. However, +# sendcommands() should modify __class__. This call serves as a check +# on that. +return self.result(timeout) + @zi.implementer(repository.ipeercommandexecutor) class peerexecutor(object): def __init__(self, peer): self._peer = peer self._sent = False self._closed = False self._calls = [] +self._futures = weakref.WeakSet() +self._responseexecutor = None +self._responsef = None def __enter__(self): return self @@ -214,20 +238,35 @@ # Commands are either batchable or they aren't. If a command # isn't batchable, we send it immediately because the executor # can no longer accept new commands after a non-batchable command. -# If a command is batchable, we queue it for later. +# If a command is batchable, we queue it for later. But we have +# to account for the case of a non-batchable command arriving after +# a batchable one and refuse to service it. + +def addcall(): +f = pycompat.futures.Future() +self._futures.add(f) +self._calls.append((command, args, fn, f)) +return f if getattr(fn, 'batchable', False): -pass +f = addcall() + +# But since we don't issue it immediately, we wrap its result() +# to trigger sending so we avoid deadlocks. +f.__class__ = unsentfuture +f._peerexecutor = self else: if self._calls: raise error.ProgrammingError( '%s is not batchable and cannot be called on a command ' 'executor along with other commands' % command) -# We don't support batching yet. So resolve it immediately. -f = pycompat.futures.Future() -self._calls.append((command, args, fn, f)) -self.sendcommands() +f = addcall() + +# Non-batchable commands can never coexist with another command +# in this executor. So send the command immediately. +self.sendcommands() + return f def sendcommands(self): @@ -239,10 +278,18 @@ self._sent = True +# Unhack any future types so caller seens a clean type and to break +# cycle between us and futures. +for f in self._futures: +if isinstance(f, unsentfuture): +f.__class__ = pycompat.futures.Future +f._peerexecutor = None + calls = self._calls # Mainly to destroy references to futures. self._calls = None +# Simple case of a single command. We call it synchronously. if len(calls) == 1: command, args, fn, f = calls[0] @@ -259,14 +306,99 @@ return -raise error.ProgrammingError('support for multiple commands not ' - 'yet implemented') +# Batch commands are a bit harder. First, we have to deal with the +# @batchable coroutine. That's a bit annoying. Furthermore, we also +# need to preserve streaming. i.e. it should be possible for the +# futures to resolve as data is coming in off the wire without having +# to wait for the final byte of the final response. We do this by +# spinning up a thread to read the responses. + +requests = [] +states = [] + +for command, args, fn, f in calls: +# Future was cancelled. Ignore it. +if not f.set_running_or_notify_cancel(): +continue + +try: +batchable = fn.batchable(fn.__self__, +
D3288: discovery: use command executor interface
indygreg updated this revision to Diff 8127. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3288?vs=8081&id=8127 REVISION DETAIL https://phab.mercurial-scm.org/D3288 AFFECTED FILES mercurial/discovery.py CHANGE DETAILS diff --git a/mercurial/discovery.py b/mercurial/discovery.py --- a/mercurial/discovery.py +++ b/mercurial/discovery.py @@ -203,7 +203,10 @@ headssum = {} # A. Create set of branches involved in the push. branches = set(repo[n].branch() for n in outgoing.missing) -remotemap = remote.branchmap() + +with remote.commandexecutor() as e: +remotemap = e.callcommand('branchmap', {}).result() + newbranches = branches - set(remotemap) branches.difference_update(newbranches) @@ -287,7 +290,12 @@ repo = pushop.repo.unfiltered() remote = pushop.remote localbookmarks = repo._bookmarks -remotebookmarks = remote.listkeys('bookmarks') + +with remote.commandexecutor() as e: +remotebookmarks = e.callcommand('listkeys', { +'namespace': 'bookmarks', +}).result() + bookmarkedheads = set() # internal config: bookmarks.pushing To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3290: logexchange: use command executor for wire protocol commands
indygreg updated this revision to Diff 8129. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3290?vs=8083&id=8129 REVISION DETAIL https://phab.mercurial-scm.org/D3290 AFFECTED FILES mercurial/logexchange.py CHANGE DETAILS diff --git a/mercurial/logexchange.py b/mercurial/logexchange.py --- a/mercurial/logexchange.py +++ b/mercurial/logexchange.py @@ -127,14 +127,23 @@ remoterepo is the peer instance """ remotepath = activepath(localrepo, remoterepo) -bookmarks = remoterepo.listkeys('bookmarks') + +with remoterepo.commandexecutor() as e: +bookmarks = e.callcommand('listkeys', { +'namespace': 'bookmarks', +}).result() + # on a push, we don't want to keep obsolete heads since # they won't show up as heads on the next pull, so we # remove them here otherwise we would require the user # to issue a pull to refresh the storage bmap = {} repo = localrepo.unfiltered() -for branch, nodes in remoterepo.branchmap().iteritems(): + +with remoterepo.commandexecutor() as e: +branchmap = e.callcommand('branchmap', {}).result() + +for branch, nodes in branchmap.iteritems(): bmap[branch] = [] for node in nodes: if node in repo and not repo[node].obsolete(): To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3289: streamclone: use command executor for wire protocol commands
indygreg updated this revision to Diff 8128. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3289?vs=8082&id=8128 REVISION DETAIL https://phab.mercurial-scm.org/D3289 AFFECTED FILES mercurial/streamclone.py CHANGE DETAILS diff --git a/mercurial/streamclone.py b/mercurial/streamclone.py --- a/mercurial/streamclone.py +++ b/mercurial/streamclone.py @@ -126,11 +126,18 @@ # creation. rbranchmap = None if remote.capable('branchmap'): -rbranchmap = remote.branchmap() +with remote.commandexecutor() as e: +rbranchmap = e.callcommand('branchmap', {}).result() repo.ui.status(_('streaming all changes\n')) -fp = remote.stream_out() +with remote.commandexecutor() as e: +fp = e.callcommand('stream_out', {}).result() + +# TODO strictly speaking, this code should all be inside the context +# manager because the context manager is supposed to ensure all wire state +# is flushed when exiting. But the legacy peers don't do this, so it +# doesn't matter. l = fp.readline() try: resp = int(l) To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3268: wireproto: implement command executor interface for version 1 peers
indygreg updated this revision to Diff 8121. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3268?vs=8034&id=8121 REVISION DETAIL https://phab.mercurial-scm.org/D3268 AFFECTED FILES mercurial/localrepo.py mercurial/repository.py mercurial/setdiscovery.py mercurial/wireprotov1peer.py tests/test-check-interfaces.py CHANGE DETAILS diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py --- a/tests/test-check-interfaces.py +++ b/tests/test-check-interfaces.py @@ -23,6 +23,7 @@ vfs as vfsmod, wireprotoserver, wireprototypes, +wireprotov1peer, wireprotov2server, ) @@ -102,6 +103,14 @@ localrepo.localpeer) checkzobject(localrepo.localpeer(dummyrepo())) +ziverify.verifyClass(repository.ipeercommandexecutor, + localrepo.localcommandexecutor) +checkzobject(localrepo.localcommandexecutor(None)) + +ziverify.verifyClass(repository.ipeercommandexecutor, + wireprotov1peer.peerexecutor) +checkzobject(wireprotov1peer.peerexecutor(None)) + ziverify.verifyClass(repository.ipeerbaselegacycommands, sshpeer.sshv1peer) checkzobject(sshpeer.sshv1peer(ui, 'ssh://localhost/foo', None, dummypipe(), diff --git a/mercurial/wireprotov1peer.py b/mercurial/wireprotov1peer.py --- a/mercurial/wireprotov1peer.py +++ b/mercurial/wireprotov1peer.py @@ -8,12 +8,15 @@ from __future__ import absolute_import import hashlib +import sys from .i18n import _ from .node import ( bin, ) - +from .thirdparty.zope import ( +interface as zi, +) from . import ( bundle2, changegroup as changegroupmod, @@ -177,14 +180,104 @@ return ';'.join(cmds) +@zi.implementer(repository.ipeercommandexecutor) +class peerexecutor(object): +def __init__(self, peer): +self._peer = peer +self._sent = False +self._closed = False +self._calls = [] + +def __enter__(self): +return self + +def __exit__(self, exctype, excvalee, exctb): +self.close() + +def callcommand(self, command, args): +if self._sent: +raise error.ProgrammingError('callcommand() cannot be used ' + 'after commands are sent') + +if self._closed: +raise error.ProgrammingError('callcommand() cannot be used ' + 'after close()') + +# Commands are dispatched through methods on the peer. +fn = getattr(self._peer, pycompat.sysstr(command), None) + +if not fn: +raise error.ProgrammingError( +'cannot call command %s: method of same name not available ' +'on peer' % command) + +# Commands are either batchable or they aren't. If a command +# isn't batchable, we send it immediately because the executor +# can no longer accept new commands after a non-batchable command. +# If a command is batchable, we queue it for later. + +if getattr(fn, 'batchable', False): +pass +else: +if self._calls: +raise error.ProgrammingError( +'%s is not batchable and cannot be called on a command ' +'executor along with other commands' % command) + +# We don't support batching yet. So resolve it immediately. +f = pycompat.futures.Future() +self._calls.append((command, args, fn, f)) +self.sendcommands() +return f + +def sendcommands(self): +if self._sent: +return + +if not self._calls: +return + +self._sent = True + +calls = self._calls +# Mainly to destroy references to futures. +self._calls = None + +if len(calls) == 1: +command, args, fn, f = calls[0] + +# Future was cancelled. Ignore it. +if not f.set_running_or_notify_cancel(): +return + +try: +result = fn(**pycompat.strkwargs(args)) +except Exception: +f.set_exception_info(*sys.exc_info()[1:]) +else: +f.set_result(result) + +return + +raise error.ProgrammingError('support for multiple commands not ' + 'yet implemented') + +def close(self): +self.sendcommands() + +self._closed = True + class wirepeer(repository.legacypeer): """Client-side interface for communicating with a peer repository. Methods commonly call wire protocol commands of the same name. See also httppeer.py and sshpeer.py for protocol-specific implementations of this interface. """ +def commandexecutor(self): +return peerexecutor(self) + # Begin of ipeercommands interface. def iterbatch(self): diff --gi
D3292: bookmarks: use command executor for wire protocol commands
indygreg updated this revision to Diff 8131. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3292?vs=8085&id=8131 REVISION DETAIL https://phab.mercurial-scm.org/D3292 AFFECTED FILES mercurial/bookmarks.py CHANGE DETAILS diff --git a/mercurial/bookmarks.py b/mercurial/bookmarks.py --- a/mercurial/bookmarks.py +++ b/mercurial/bookmarks.py @@ -646,12 +646,16 @@ writer(msg) localmarks.applychanges(repo, tr, changes) -def incoming(ui, repo, other): +def incoming(ui, repo, peer): '''Show bookmarks incoming from other to repo ''' ui.status(_("searching for changed bookmarks\n")) -remotemarks = unhexlifybookmarks(other.listkeys('bookmarks')) +with peer.commandexecutor() as e: +remotemarks = unhexlifybookmarks(e.callcommand('listkeys', { +'namespace': 'bookmarks', +}).result()) + r = comparebookmarks(repo, remotemarks, repo._bookmarks) addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r @@ -733,12 +737,16 @@ return 0 -def summary(repo, other): +def summary(repo, peer): '''Compare bookmarks between repo and other for "hg summary" output This returns "(# of incoming, # of outgoing)" tuple. ''' -remotemarks = unhexlifybookmarks(other.listkeys('bookmarks')) +with peer.commandexecutor() as e: +remotemarks = unhexlifybookmarks(e.callcommand('listkeys', { +'namespace': 'bookmarks', +}).result()) + r = comparebookmarks(repo, remotemarks, repo._bookmarks) addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r return (len(addsrc), len(adddst)) To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3291: hg: use command executor for wire protocol commands
indygreg updated this revision to Diff 8130. indygreg edited the summary of this revision. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3291?vs=8084&id=8130 REVISION DETAIL https://phab.mercurial-scm.org/D3291 AFFECTED FILES mercurial/hg.py CHANGE DETAILS diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -81,7 +81,9 @@ raise error.Abort(_("remote branch lookup not supported")) revs.append(hashbranch) return revs, revs[0] -branchmap = peer.branchmap() + +with peer.commandexecutor() as e: +branchmap = e.callcommand('branchmap', {}).result() def primary(branch): if branch == '.': @@ -421,7 +423,15 @@ raise error.Abort(_("src repository does not support " "revision lookup and so doesn't " "support clone by revision")) -revs = [srcpeer.lookup(r) for r in rev] + +# TODO this is batchable. +remoterevs = [] +for r in rev: +with srcpeer.commandexecutor() as e: +remoterevs.append(e.callcommand('lookup', { +'key': r, +}).result()) +revs = remoterevs # Obtain a lock before checking for or cloning the pooled repo otherwise # 2 clients may race creating or populating it. @@ -567,7 +577,11 @@ # raises RepoLookupError if revision 0 is filtered or otherwise # not available. If we fail to resolve, sharing is not enabled. try: -rootnode = srcpeer.lookup('0') +with srcpeer.commandexecutor() as e: +rootnode = e.callcommand('lookup', { +'key': '0', +}).result() + if rootnode != node.nullid: sharepath = os.path.join(sharepool, node.hex(rootnode)) else: @@ -663,7 +677,16 @@ raise error.Abort(_("src repository does not support " "revision lookup and so doesn't " "support clone by revision")) -revs = [srcpeer.lookup(r) for r in revs] + +# TODO this is batchable. +remoterevs = [] +for rev in revs: +with srcpeer.commandexecutor() as e: +remoterevs.append(e.callcommand('lookup', { +'key': rev, +}).result()) +revs = remoterevs + checkout = revs[0] else: revs = None @@ -705,7 +728,11 @@ if update: if update is not True: -checkout = srcpeer.lookup(update) +with srcpeer.commandexecutor() as e: +checkout = e.callcommand('lookup', { +'key': update, +}).result() + uprev = None status = None if checkout is not None: To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3293: bundlerepo: rename "other" to "peer"
indygreg updated this revision to Diff 8132. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3293?vs=8086&id=8132 REVISION DETAIL https://phab.mercurial-scm.org/D3293 AFFECTED FILES mercurial/bundlerepo.py CHANGE DETAILS diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py +++ b/mercurial/bundlerepo.py @@ -492,9 +492,9 @@ def release(self): raise NotImplementedError -def getremotechanges(ui, repo, other, onlyheads=None, bundlename=None, +def getremotechanges(ui, repo, peer, onlyheads=None, bundlename=None, force=False): -'''obtains a bundle of changes incoming from other +'''obtains a bundle of changes incoming from peer "onlyheads" restricts the returned changes to those reachable from the specified heads. @@ -507,62 +507,62 @@ "local" is a local repo from which to obtain the actual incoming changesets; it is a bundlerepo for the obtained bundle when the - original "other" is remote. + original "peer" is remote. "csets" lists the incoming changeset node ids. "cleanupfn" must be called without arguments when you're done processing - the changes; it closes both the original "other" and the one returned + the changes; it closes both the original "peer" and the one returned here. ''' -tmp = discovery.findcommonincoming(repo, other, heads=onlyheads, +tmp = discovery.findcommonincoming(repo, peer, heads=onlyheads, force=force) common, incoming, rheads = tmp if not incoming: try: if bundlename: os.unlink(bundlename) except OSError: pass -return repo, [], other.close +return repo, [], peer.close commonset = set(common) rheads = [x for x in rheads if x not in commonset] bundle = None bundlerepo = None -localrepo = other.local() +localrepo = peer.local() if bundlename or not localrepo: -# create a bundle (uncompressed if other repo is not local) +# create a bundle (uncompressed if peer repo is not local) # developer config: devel.legacy.exchange legexc = ui.configlist('devel', 'legacy.exchange') forcebundle1 = 'bundle2' not in legexc and 'bundle1' in legexc canbundle2 = (not forcebundle1 - and other.capable('getbundle') - and other.capable('bundle2')) + and peer.capable('getbundle') + and peer.capable('bundle2')) if canbundle2: kwargs = {} kwargs[r'common'] = common kwargs[r'heads'] = rheads kwargs[r'bundlecaps'] = exchange.caps20to10(repo, role='client') kwargs[r'cg'] = True -b2 = other.getbundle('incoming', **kwargs) +b2 = peer.getbundle('incoming', **kwargs) fname = bundle = changegroup.writechunks(ui, b2._forwardchunks(), bundlename) else: -if other.capable('getbundle'): -cg = other.getbundle('incoming', common=common, heads=rheads) -elif onlyheads is None and not other.capable('changegroupsubset'): +if peer.capable('getbundle'): +cg = peer.getbundle('incoming', common=common, heads=rheads) +elif onlyheads is None and not peer.capable('changegroupsubset'): # compat with older servers when pulling all remote heads -with other.commandexecutor() as e: +with peer.commandexecutor() as e: cg = e.callcommand('changegroup', { 'nodes': incoming, 'source': 'incoming', }).result() rheads = None else: -with other.commandexecutor() as e: +with peer.commandexecutor() as e: cg = e.callcommand('changegroupsubset', { 'bases': incoming, 'heads': rheads, @@ -582,7 +582,7 @@ # use the created uncompressed bundlerepo localrepo = bundlerepo = bundlerepository(repo.baseui, repo.root, fname) -# this repo contains local and other now, so filter out local again +# this repo contains local and peer now, so filter out local again common = repo.heads() if localrepo: # Part of common may be remotely filtered @@ -594,17 +594,17 @@ if bundlerepo: reponodes = [ctx.node() for ctx in bundlerepo[bundlerepo.firstnewrev:]] -remotephases = other.listkeys('phases') +remotephases = peer.listkeys('phases') -pullop = exchange.pulloperation(bundlerepo, othe
D3294: bundlerepo: use command executor for wire protocol commands
indygreg updated this revision to Diff 8133. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3294?vs=8087&id=8133 REVISION DETAIL https://phab.mercurial-scm.org/D3294 AFFECTED FILES mercurial/bundlerepo.py CHANGE DETAILS diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py +++ b/mercurial/bundlerepo.py @@ -540,17 +540,26 @@ and peer.capable('getbundle') and peer.capable('bundle2')) if canbundle2: -kwargs = {} -kwargs[r'common'] = common -kwargs[r'heads'] = rheads -kwargs[r'bundlecaps'] = exchange.caps20to10(repo, role='client') -kwargs[r'cg'] = True -b2 = peer.getbundle('incoming', **kwargs) -fname = bundle = changegroup.writechunks(ui, b2._forwardchunks(), - bundlename) +with peer.commandexecutor() as e: +b2 = e.callcommand('getbundle', { +'source': 'incoming', +'common': common, +'heads': rheads, +'bundlecaps': exchange.caps20to10(repo, role='client'), +'cg': True, +}).result() + +fname = bundle = changegroup.writechunks(ui, + b2._forwardchunks(), + bundlename) else: if peer.capable('getbundle'): -cg = peer.getbundle('incoming', common=common, heads=rheads) +with peer.commandexecutor() as e: +cg = e.callcommand('getbundle', { +'source': 'incoming', +'common': common, +'heads': rheads, +}).result() elif onlyheads is None and not peer.capable('changegroupsubset'): # compat with older servers when pulling all remote heads @@ -594,7 +603,11 @@ if bundlerepo: reponodes = [ctx.node() for ctx in bundlerepo[bundlerepo.firstnewrev:]] -remotephases = peer.listkeys('phases') + +with peer.commandexecutor() as e: +remotephases = e.callcommand('listkeys', { +'namespace': 'phases', +}).result() pullop = exchange.pulloperation(bundlerepo, peer, heads=reponodes) pullop.trmanager = bundletransactionmanager() To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3271: wireproto: remove iterbatch() from peer interface (API)
indygreg updated this revision to Diff 8124. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3271?vs=8037&id=8124 REVISION DETAIL https://phab.mercurial-scm.org/D3271 AFFECTED FILES mercurial/localrepo.py mercurial/repository.py mercurial/wireprotov1peer.py tests/test-batching.py tests/test-batching.py.out tests/test-wireproto.py CHANGE DETAILS diff --git a/tests/test-wireproto.py b/tests/test-wireproto.py --- a/tests/test-wireproto.py +++ b/tests/test-wireproto.py @@ -93,7 +93,9 @@ clt = clientpeer(srv, uimod.ui()) print(clt.greet(b"Foobar")) -b = clt.iterbatch() -list(map(b.greet, (b'Fo, =;:https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3316: exchange: use command executor for getbundle
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY The code consuming the bundle has been moved to inside the context manager, as that is supposed to be part of the API. (Although it doesn't matter for version 1 peers.) REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3316 AFFECTED FILES mercurial/exchange.py CHANGE DETAILS diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -1648,17 +1648,22 @@ kwargs['obsmarkers'] = True pullop.stepsdone.add('obsmarkers') _pullbundle2extraprepare(pullop, kwargs) -bundle = pullop.remote.getbundle('pull', **pycompat.strkwargs(kwargs)) -try: -op = bundle2.bundleoperation(pullop.repo, pullop.gettransaction, - source='pull') -op.modes['bookmarks'] = 'records' -bundle2.processbundle(pullop.repo, bundle, op=op) -except bundle2.AbortFromPart as exc: -pullop.repo.ui.status(_('remote: abort: %s\n') % exc) -raise error.Abort(_('pull failed on remote'), hint=exc.hint) -except error.BundleValueError as exc: -raise error.Abort(_('missing support for %s') % exc) + +with pullop.remote.commandexecutor() as e: +args = dict(kwargs) +args['source'] = 'pull' +bundle = e.callcommand('getbundle', args).result() + +try: +op = bundle2.bundleoperation(pullop.repo, pullop.gettransaction, + source='pull') +op.modes['bookmarks'] = 'records' +bundle2.processbundle(pullop.repo, bundle, op=op) +except bundle2.AbortFromPart as exc: +pullop.repo.ui.status(_('remote: abort: %s\n') % exc) +raise error.Abort(_('pull failed on remote'), hint=exc.hint) +except error.BundleValueError as exc: +raise error.Abort(_('missing support for %s') % exc) if pullop.fetch: pullop.cgresult = bundle2.combinechangegroupresults(op) To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3273: wireproto: convert legacy commands to command executor
indygreg updated this revision to Diff 8126. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3273?vs=8040&id=8126 REVISION DETAIL https://phab.mercurial-scm.org/D3273 AFFECTED FILES mercurial/bundlerepo.py mercurial/exchange.py mercurial/localrepo.py mercurial/repository.py mercurial/wireprotov1peer.py tests/test-check-interfaces.py CHANGE DETAILS diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py --- a/tests/test-check-interfaces.py +++ b/tests/test-check-interfaces.py @@ -89,8 +89,7 @@ checkzobject(badpeer()) -ziverify.verifyClass(repository.ipeerbaselegacycommands, - httppeer.httppeer) +ziverify.verifyClass(repository.ipeerbase, httppeer.httppeer) checkzobject(httppeer.httppeer(None, None, None, dummyopener(), None, None)) ziverify.verifyClass(repository.ipeerconnection, @@ -111,13 +110,11 @@ wireprotov1peer.peerexecutor) checkzobject(wireprotov1peer.peerexecutor(None)) -ziverify.verifyClass(repository.ipeerbaselegacycommands, - sshpeer.sshv1peer) +ziverify.verifyClass(repository.ipeerbase, sshpeer.sshv1peer) checkzobject(sshpeer.sshv1peer(ui, 'ssh://localhost/foo', None, dummypipe(), dummypipe(), None, None)) -ziverify.verifyClass(repository.ipeerbaselegacycommands, - sshpeer.sshv2peer) +ziverify.verifyClass(repository.ipeerbase, sshpeer.sshv2peer) checkzobject(sshpeer.sshv2peer(ui, 'ssh://localhost/foo', None, dummypipe(), dummypipe(), None, None)) diff --git a/mercurial/wireprotov1peer.py b/mercurial/wireprotov1peer.py --- a/mercurial/wireprotov1peer.py +++ b/mercurial/wireprotov1peer.py @@ -308,7 +308,8 @@ else: f.set_result(result) -class wirepeer(repository.legacypeer): +@zi.implementer(repository.ipeerlegacycommands) +class wirepeer(repository.peer): """Client-side interface for communicating with a peer repository. Methods commonly call wire protocol commands of the same name. @@ -502,12 +503,12 @@ self._abort(error.ResponseError(_("unexpected response:"), d)) return r -def changegroup(self, nodes, kind): +def changegroup(self, nodes, source): n = wireprototypes.encodelist(nodes) f = self._callcompressable("changegroup", roots=n) return changegroupmod.cg1unpacker(f, 'UN') -def changegroupsubset(self, bases, heads, kind): +def changegroupsubset(self, bases, heads, source): self.requirecap('changegroupsubset', _('look up remote changes')) bases = wireprototypes.encodelist(bases) heads = wireprototypes.encodelist(heads) diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -190,10 +190,10 @@ Returns an iterable of iterables with the resolved values for each node. """ -def changegroup(nodes, kind): +def changegroup(nodes, source): """Obtain a changegroup with data for descendants of specified nodes.""" -def changegroupsubset(bases, heads, kind): +def changegroupsubset(bases, heads, source): pass class ipeercommandexecutor(zi.Interface): @@ -285,9 +285,6 @@ All peer instances must conform to this interface. """ -class ipeerbaselegacycommands(ipeerbase, ipeerlegacycommands): -"""Unified peer interface that supports legacy commands.""" - @zi.implementer(ipeerbase) class peer(object): """Base class for peer repositories.""" @@ -312,10 +309,6 @@ _('cannot %s; remote repository does not support the %r ' 'capability') % (purpose, name)) -@zi.implementer(ipeerbaselegacycommands) -class legacypeer(peer): -"""peer but with support for legacy wire protocol commands.""" - class ifilerevisionssequence(zi.Interface): """Contains index data for all revisions of a file. diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -320,7 +320,8 @@ # End of peer interface. -class locallegacypeer(repository.legacypeer, localpeer): +@zi.implementer(repository.ipeerlegacycommands) +class locallegacypeer(localpeer): '''peer extension which implements legacy methods too; used for tests with restricted capabilities''' @@ -335,8 +336,8 @@ def branches(self, nodes): return self._repo.branches(nodes) -def changegroup(self, basenodes, source): -outgoing = discovery.outgoing(self._repo, missingroots=basenodes, +def changegroup(self, nodes, source): +outgoing = discovery.outgoing(self._repo, missingroots=nodes, missingheads=self._repo.heads()) return changegroup.makechangegroup(self._repo, outgoing, '01', sou
D3314: wireproto: use command executor for unbundle
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This also required unifying the name of the argument because the new API always passes arguments by keyword. I decided to change implementations to "bundle" instead of the interface to "cg" because "bundle" is more appropriate in a modern world. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3314 AFFECTED FILES mercurial/exchange.py mercurial/localrepo.py mercurial/wireprotov1peer.py CHANGE DETAILS diff --git a/mercurial/wireprotov1peer.py b/mercurial/wireprotov1peer.py --- a/mercurial/wireprotov1peer.py +++ b/mercurial/wireprotov1peer.py @@ -436,7 +436,7 @@ else: return changegroupmod.cg1unpacker(f, 'UN') -def unbundle(self, cg, heads, url): +def unbundle(self, bundle, heads, url): '''Send cg (a readable file-like object representing the changegroup to push, typically a chunkbuffer object) to the remote server as a bundle. @@ -456,9 +456,9 @@ else: heads = wireprototypes.encodelist(heads) -if util.safehasattr(cg, 'deltaheader'): +if util.safehasattr(bundle, 'deltaheader'): # this a bundle10, do the old style call sequence -ret, output = self._callpush("unbundle", cg, heads=heads) +ret, output = self._callpush("unbundle", bundle, heads=heads) if ret == "": raise error.ResponseError( _('push failed:'), output) @@ -472,7 +472,7 @@ self.ui.status(_('remote: '), l) else: # bundle2 push. Send a stream, fetch a stream. -stream = self._calltwowaystream('unbundle', cg, heads=heads) +stream = self._calltwowaystream('unbundle', bundle, heads=heads) ret = bundle2.getunbundler(self.ui, stream) return ret diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -275,14 +275,14 @@ raise error.Abort(_('cannot perform stream clone against local ' 'peer')) -def unbundle(self, cg, heads, url): +def unbundle(self, bundle, heads, url): """apply a bundle on a repo This function handles the repo locking itself.""" try: try: -cg = exchange.readbundle(self.ui, cg, None) -ret = exchange.unbundle(self._repo, cg, heads, 'push', url) +bundle = exchange.readbundle(self.ui, bundle, None) +ret = exchange.unbundle(self._repo, bundle, heads, 'push', url) if util.safehasattr(ret, 'getchunks'): # This is a bundle20 object, turn it into an unbundler. # This little dance should be dropped eventually when the diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -1096,8 +1096,12 @@ stream = util.chunkbuffer(bundler.getchunks()) try: try: -reply = pushop.remote.unbundle( -stream, ['force'], pushop.remote.url()) +with pushop.remote.commandexecutor() as e: +reply = e.callcommand('unbundle', { +'bundle': stream, +'heads': ['force'], +'url': pushop.remote.url(), +}).result() except error.BundleValueError as exc: raise error.Abort(_('missing support for %s') % exc) try: To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3318: repository: remove ipeercommands from ipeerbase
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY AFAICT all callers in core have moved to the commandexecutor interface for invoking wire protocol commands. Or at least they aren't using the named methods on ipeercommands to invoke them. This means we can drop ipeercommands from the ipeerbase interface. As far as interface based programming goes, it is now illegal to call an ipeercommands method for issuing wire protocol commands. However, the methods are still there, so they will still work. At some point we will want to break that API... REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3318 AFFECTED FILES mercurial/localrepo.py mercurial/repository.py mercurial/wireprotov1peer.py CHANGE DETAILS diff --git a/mercurial/wireprotov1peer.py b/mercurial/wireprotov1peer.py --- a/mercurial/wireprotov1peer.py +++ b/mercurial/wireprotov1peer.py @@ -308,7 +308,7 @@ else: f.set_result(result) -@zi.implementer(repository.ipeerlegacycommands) +@zi.implementer(repository.ipeercommands, repository.ipeerlegacycommands) class wirepeer(repository.peer): """Client-side interface for communicating with a peer repository. diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -284,8 +284,7 @@ being issued. """ -class ipeerbase(ipeerconnection, ipeercapabilities, ipeercommands, -ipeerrequests): +class ipeerbase(ipeerconnection, ipeercapabilities, ipeerrequests): """Unified interface for peer repositories. All peer instances must conform to this interface. diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -196,6 +196,7 @@ def close(self): self._closed = True +@zi.implementer(repository.ipeercommands) class localpeer(repository.peer): '''peer for a local repo; reflects only the most recent API''' To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3315: exchange: use command executor for pushkey
indygreg 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/D3315 AFFECTED FILES mercurial/debugcommands.py mercurial/exchange.py CHANGE DETAILS diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -1212,10 +1212,14 @@ outdated = [c for c in outdated if c.node() not in pheads] # fallback to independent pushkey command for newremotehead in outdated: -r = pushop.remote.pushkey('phases', - newremotehead.hex(), - ('%d' % phases.draft), - ('%d' % phases.public)) +with pushop.remote.commandexecutor() as e: +r = e.callcommand('pushkey', { +'namespace': 'phases', +'key': newremotehead.hex(), +'old': '%d' % phases.draft, +'new': '%d' % phases.public +}).result() + if not r: pushop.ui.warn(_('updating %s to public failed!\n') % newremotehead) @@ -1270,7 +1274,16 @@ action = 'export' elif not new: action = 'delete' -if remote.pushkey('bookmarks', b, old, new): + +with remote.commandexecutor() as e: +r = e.callcommand('pushkey', { +'namespace': 'bookmarks', +'key': b, +'old': old, +'new': new, +}).result() + +if r: ui.status(bookmsgmap[action][0] % b) else: ui.warn(bookmsgmap[action][1] % b) diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py --- a/mercurial/debugcommands.py +++ b/mercurial/debugcommands.py @@ -1832,7 +1832,14 @@ target = hg.peer(ui, {}, repopath) if keyinfo: key, old, new = keyinfo -r = target.pushkey(namespace, key, old, new) +with target.commandexecutor() as e: +r = e.callcommand('pushkey', { +'namespace': namespace, +'key': key, +'old': old, +'new': new, +}).result() + ui.status(pycompat.bytestr(r) + '\n') return not r else: To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3317: wireproto: properly call clonebundles command
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY We should not be using _call() to make wire protocol calls because it isn't part of the peer API. But clonebundles wasn't part of the supported commands in the peer API! So this commit defines that command in the commands interface, implements it, and teaches the one caller in core to call it using the command executor interface. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3317 AFFECTED FILES mercurial/exchange.py mercurial/localrepo.py mercurial/repository.py mercurial/wireprotov1peer.py CHANGE DETAILS diff --git a/mercurial/wireprotov1peer.py b/mercurial/wireprotov1peer.py --- a/mercurial/wireprotov1peer.py +++ b/mercurial/wireprotov1peer.py @@ -322,6 +322,10 @@ # Begin of ipeercommands interface. +def clonebundles(self): +self.requirecap('clonebundles', _('clone bundles')) +return self._call('clonebundles') + @batchable def lookup(self, key): self.requirecap('lookup', _('look up remote revision')) diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -101,6 +101,12 @@ Returns a set of string capabilities. """ +def clonebundles(): +"""Obtains the clone bundles manifest for the repo. + +Returns the manifest as unparsed bytes. +""" + def debugwireargs(one, two, three=None, four=None, five=None): """Used to facilitate debugging of arguments passed over the wire.""" diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -235,6 +235,9 @@ def capabilities(self): return self._caps +def clonebundles(self): +return self._repo.tryread('clonebundles.manifest') + def debugwireargs(self, one, two, three=None, four=None, five=None): """Used to test argument passing over the wire""" return "%s %s %s %s %s" % (one, two, pycompat.bytestr(three), diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -2170,7 +2170,8 @@ if not remote.capable('clonebundles'): return -res = remote._call('clonebundles') +with remote.commandexecutor() as e: +res = e.callcommand('clonebundles', {}).result() # If we call the wire protocol command, that's good enough to record the # attempt. To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3267: repository: define new interface for running commands
indygreg added a comment. We now send non-batchable commands immediately and wrap queued futures so `result()` will trigger submission. This improves the end-user experience from: with peer.commandexecutor() as e: f = e.callcommand(...) res = f.result() to with peer.commandexecutor() as e: res = e.callcommand(...).result() And in order to preserve streaming on batch responses: with peer.commandexecutor() as e: fs = [] for x in ...: fs.append(...) e.sendcommands() for f in fs: result = f.result() to with peer.commandexecutor() as e: fs = [] for x in ...: fs.append(e.callcommand(...)) for f in fs: result = f.result() This later improvement is because the first `result()` call on any returned future will trigger submission of all queued commands. This also means we can iterate or access the futures in any order. Of course, wire protocol version 1 will resolve them in the order they were issued. But this isn't necessarily true about wire protocol version 2 :) REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3267 To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3297: httppeer: implement command executor for version 2 peer
indygreg planned changes to this revision. indygreg added a comment. This needs some revisions to adapt to the tweaked command executor interface semantics. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3297 To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3303: cborutil: implement support for indefinite length CBOR types
indygreg added inline comments. INLINE COMMENTS > yuja wrote in cborutil.py:73 > I don't think yielding `encoder.encode` would make much sense > because an array item can also be a nested indefinite array, in > which case, we can't use `writeitem()`. Indeed. Proper support for nesting will likely require a whole new high-level encoder API. Because state of the nesting needs to be tracked somewhere. FWIW, the more I'm looking at the CBOR code, the more I'm thinking we will end up having to reinvent the full wheel. Not-yet-submitted commits to add wire protocol commands to do CBOR things are spending a *ton* of time in cbor2. The reason appears to be primarily driven by cbor2's insistence on using `write()`. There are a few places where we need to emit a generator of chunks. And the overhead from instantiating `io.BytesIO` instances to handle the `write()` from cbor2 only to call `getvalue()` to retrieve the data is non-trivial. The next version of this may just invent a whole new CBOR encoder with only limited support for types. Or at least I'll change the API so a streaming array doesn't require an encoder be passed in. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3303 To: indygreg, #hg-reviewers Cc: yuja, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3319: py3: use b"%d" instead of str() to convert integers to bytes
pulkit 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/D3319 AFFECTED FILES hgext/convert/__init__.py hgext/convert/cvs.py hgext/convert/cvsps.py mercurial/lock.py CHANGE DETAILS diff --git a/mercurial/lock.py b/mercurial/lock.py --- a/mercurial/lock.py +++ b/mercurial/lock.py @@ -348,7 +348,7 @@ if self._parentheld: lockname = self.parentlock else: -lockname = '%s:%s' % (lock._host, self.pid) +lockname = b'%s:%d' % (lock._host, self.pid) self._inherited = True try: yield lockname diff --git a/hgext/convert/cvsps.py b/hgext/convert/cvsps.py --- a/hgext/convert/cvsps.py +++ b/hgext/convert/cvsps.py @@ -919,7 +919,7 @@ if opts["parents"] and cs.parents: if len(cs.parents) > 1: ui.write(('Parents: %s\n' % - (','.join([str(p.id) for p in cs.parents] + (','.join([(b"%d" % p.id) for p in cs.parents] else: ui.write(('Parent: %d\n' % cs.parents[0].id)) @@ -941,18 +941,18 @@ fn = fn[len(opts["prefix"]):] ui.write('\t%s:%s->%s%s \n' % ( fn, '.'.join([str(x) for x in f.parent]) or 'INITIAL', -'.'.join([str(x) for x in f.revision]), +'.'.join([(b"%d" % x) for x in f.revision]), ['', '(DEAD)'][f.dead])) ui.write('\n') # have we seen the start tag? if revisions and off: -if revisions[0] == str(cs.id) or \ +if revisions[0] == (b"%d" % cs.id) or \ revisions[0] in cs.tags: off = False # see if we reached the end tag if len(revisions) > 1 and not off: -if revisions[1] == str(cs.id) or \ +if revisions[1] == (b"%d" % cs.id) or \ revisions[1] in cs.tags: break diff --git a/hgext/convert/cvs.py b/hgext/convert/cvs.py --- a/hgext/convert/cvs.py +++ b/hgext/convert/cvs.py @@ -91,7 +91,7 @@ for cs in db: if maxrev and cs.id > maxrev: break -id = str(cs.id) +id = (b"%d" % cs.id) cs.author = self.recode(cs.author) self.lastbranch[cs.branch] = id cs.comment = self.recode(cs.comment) @@ -102,13 +102,13 @@ files = {} for f in cs.entries: -files[f.file] = "%s%s" % ('.'.join([str(x) +files[f.file] = "%s%s" % ('.'.join([(b"%d" % x) for x in f.revision]), ['', '(DEAD)'][f.dead]) # add current commit to set c = commit(author=cs.author, date=date, - parents=[str(p.id) for p in cs.parents], + parents=[(b"%d" % p.id) for p in cs.parents], desc=cs.comment, branch=cs.branch or '') self.changeset[id] = c self.files[id] = files diff --git a/hgext/convert/__init__.py b/hgext/convert/__init__.py --- a/hgext/convert/__init__.py +++ b/hgext/convert/__init__.py @@ -482,7 +482,7 @@ rev = ctx.extra().get('convert_revision', '') if rev.startswith('svn:'): if name == 'svnrev': -return str(subversion.revsplit(rev)[2]) +return (b"%d" % subversion.revsplit(rev)[2]) elif name == 'svnpath': return subversion.revsplit(rev)[1] elif name == 'svnuuid': To: pulkit, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3320: lock: don't use 'file' as a variable name
pulkit 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/D3320 AFFECTED FILES mercurial/lock.py CHANGE DETAILS diff --git a/mercurial/lock.py b/mercurial/lock.py --- a/mercurial/lock.py +++ b/mercurial/lock.py @@ -175,11 +175,11 @@ _host = None -def __init__(self, vfs, file, timeout=-1, releasefn=None, acquirefn=None, +def __init__(self, vfs, fname, timeout=-1, releasefn=None, acquirefn=None, desc=None, inheritchecker=None, parentlock=None, dolock=True): self.vfs = vfs -self.f = file +self.f = fname self.held = 0 self.timeout = timeout self.releasefn = releasefn To: pulkit, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3321: py3: add b'' prefixes to tests/test-status-inprocess.py
pulkit created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY 1. skip-blame because just b'' prefixes REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3321 AFFECTED FILES tests/test-status-inprocess.py CHANGE DETAILS diff --git a/tests/test-status-inprocess.py b/tests/test-status-inprocess.py --- a/tests/test-status-inprocess.py +++ b/tests/test-status-inprocess.py @@ -10,17 +10,17 @@ u = uimod.ui.load() print('% creating repo') -repo = localrepo.localrepository(u, '.', create=True) +repo = localrepo.localrepository(u, b'.', create=True) f = open('test.py', 'w') try: f.write('foo\n') finally: f.close print('% add and commit') -commands.add(u, repo, 'test.py') -commands.commit(u, repo, message='*') +commands.add(u, repo, b'test.py') +commands.commit(u, repo, message=b'*') commands.status(u, repo, clean=True) To: pulkit, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3322: py3: use urllib.parse.unquote_plus instead of urllib.unquote_plus
pulkit created this revision. Herald added a reviewer: durin42. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY The later is not present in Python 3. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3322 AFFECTED FILES tests/test-narrow-clone-non-narrow-server.t CHANGE DETAILS diff --git a/tests/test-narrow-clone-non-narrow-server.t b/tests/test-narrow-clone-non-narrow-server.t --- a/tests/test-narrow-clone-non-narrow-server.t +++ b/tests/test-narrow-clone-non-narrow-server.t @@ -18,8 +18,20 @@ $ cat hg.pid >> "$DAEMON_PIDS" Verify that narrow is advertised in the bundle2 capabilities: + + $ cat >> unquote.py < from __future__ import print_function + > import sys + > if sys.version[0] == '3': + > import urllib.parse as up + > unquote = up.unquote_plus + > else: + > import urllib + > unquote = urllib.unquote_plus + > print(unquote(list(sys.stdin)[1])) + > EOF $ echo hello | hg -R . serve --stdio | \ - > $PYTHON -c "from __future__ import print_function; import sys, urllib; print(urllib.unquote_plus(list(sys.stdin)[1]))" | grep narrow + > $PYTHON unquote.py | grep narrow narrow=v0 $ cd .. To: pulkit, durin42, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3323: py3: iterate over a copy of dict while changing it
pulkit 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/D3323 AFFECTED FILES mercurial/color.py CHANGE DETAILS diff --git a/mercurial/color.py b/mercurial/color.py --- a/mercurial/color.py +++ b/mercurial/color.py @@ -168,7 +168,7 @@ ui._terminfoparams.clear() return -for key, (b, e, c) in ui._terminfoparams.items(): +for key, (b, e, c) in ui._terminfoparams.copy().items(): if not b: continue if not c and not curses.tigetstr(e): To: pulkit, #hg-reviewers Cc: mercurial-devel, spectral ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3324: py3: use stringutil.forcebytestr() instead of str()
pulkit created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY We need to convert errors to bytes using stringutil.forcebytestr() REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3324 AFFECTED FILES mercurial/exchange.py CHANGE DETAILS diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -2257,7 +2257,7 @@ continue except error.InvalidBundleSpecification as e: -repo.ui.debug(str(e) + '\n') +repo.ui.debug(stringutil.forcebytestr(e) + '\n') continue except error.UnsupportedBundleSpecification as e: repo.ui.debug('filtering %s because unsupported bundle ' To: pulkit, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3325: py3: make sure curses.tigetstr() first argument is a str
pulkit 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/D3325 AFFECTED FILES mercurial/color.py CHANGE DETAILS diff --git a/mercurial/color.py b/mercurial/color.py --- a/mercurial/color.py +++ b/mercurial/color.py @@ -171,12 +171,12 @@ for key, (b, e, c) in ui._terminfoparams.copy().items(): if not b: continue -if not c and not curses.tigetstr(e): +if not c and not curses.tigetstr(pycompat.sysstr(e)): # Most terminals don't support dim, invis, etc, so don't be # noisy and use ui.debug(). ui.debug("no terminfo entry for %s\n" % e) del ui._terminfoparams[key] -if not curses.tigetstr('setaf') or not curses.tigetstr('setab'): +if not curses.tigetstr(r'setaf') or not curses.tigetstr(r'setab'): # Only warn about missing terminfo entries if we explicitly asked for # terminfo mode and we're in a formatted terminal. if mode == "terminfo" and formatted: @@ -325,11 +325,11 @@ if termcode: return termcode else: -return curses.tigetstr(val) +return curses.tigetstr(pycompat.sysstr(val)) elif bg: -return curses.tparm(curses.tigetstr('setab'), val) +return curses.tparm(curses.tigetstr(r'setab'), val) else: -return curses.tparm(curses.tigetstr('setaf'), val) +return curses.tparm(curses.tigetstr(r'setaf'), val) def _mergeeffects(text, start, stop): """Insert start sequence at every occurrence of stop sequence To: pulkit, #hg-reviewers Cc: mercurial-devel, spectral ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3326: py3: use str variables to check keys in request header
pulkit created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY The values in header are of str type. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3326 AFFECTED FILES mercurial/keepalive.py CHANGE DETAILS diff --git a/mercurial/keepalive.py b/mercurial/keepalive.py --- a/mercurial/keepalive.py +++ b/mercurial/keepalive.py @@ -318,24 +318,24 @@ headers.update(sorted(req.unredirected_hdrs.items())) headers = util.sortdict((n.lower(), v) for n, v in headers.items()) skipheaders = {} -for n in ('host', 'accept-encoding'): +for n in (r'host', r'accept-encoding'): if n in headers: -skipheaders['skip_' + n.replace('-', '_')] = 1 +skipheaders[r'skip_' + n.replace(r'-', r'_')] = 1 try: if urllibcompat.hasdata(req): data = urllibcompat.getdata(req) h.putrequest( req.get_method(), urllibcompat.getselector(req), -**pycompat.strkwargs(skipheaders)) +skipheaders) if r'content-type' not in headers: h.putheader(r'Content-type', r'application/x-www-form-urlencoded') if r'content-length' not in headers: h.putheader(r'Content-length', r'%d' % len(data)) else: h.putrequest( req.get_method(), urllibcompat.getselector(req), -**pycompat.strkwargs(skipheaders)) +skipheaders) except socket.error as err: raise urlerr.urlerror(err) for k, v in headers.items(): To: pulkit, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3327: py3: add b'' prefixes to make values bytes
pulkit created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY 1. skip-blame because just b'' prefixes REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3327 AFFECTED FILES mercurial/sslutil.py CHANGE DETAILS diff --git a/mercurial/sslutil.py b/mercurial/sslutil.py --- a/mercurial/sslutil.py +++ b/mercurial/sslutil.py @@ -621,13 +621,13 @@ pats.append(re.escape(leftmost)) else: # Otherwise, '*' matches any dotless string, e.g. www* -pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) +pats.append(re.escape(leftmost).replace(br'\*', '[^.]*')) # add the remaining fragments, ignore any wildcards for frag in remainder: pats.append(re.escape(frag)) -pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) +pat = re.compile(br'\A' + br'\.'.join(pats) + br'\Z', re.IGNORECASE) return pat.match(hostname) is not None def _verifycert(cert, hostname): To: pulkit, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3328: py3: use b"%d" instead of str() to convert int to bytes
pulkit created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY While I was here, I added 'and None' to suppress return values of .write() calls. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3328 AFFECTED FILES tests/test-clone-uncompressed.t CHANGE DETAILS diff --git a/tests/test-clone-uncompressed.t b/tests/test-clone-uncompressed.t --- a/tests/test-clone-uncompressed.t +++ b/tests/test-clone-uncompressed.t @@ -18,7 +18,7 @@ $ hg -q commit -A -m initial >>> for i in range(1024): ... with open(str(i), 'wb') as fh: - ... fh.write(str(i)) + ... fh.write(b"%d" % i) and None $ hg -q commit -A -m 'add a lot of files' $ hg st $ hg --config server.uncompressed=false serve -p $HGPORT -d --pid-file=hg.pid To: pulkit, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3329: py3: add b'' prefixes to make values bytes
pulkit created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY 1. skip-blame beacuse just b'' prefixes REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3329 AFFECTED FILES tests/test-clone-cgi.t tests/test-clone.t CHANGE DETAILS diff --git a/tests/test-clone.t b/tests/test-clone.t --- a/tests/test-clone.t +++ b/tests/test-clone.t @@ -559,8 +559,8 @@ $ cat < simpleclone.py > from mercurial import ui, hg > myui = ui.ui.load() - > repo = hg.repository(myui, 'a') - > hg.clone(myui, {}, repo, dest="ua") + > repo = hg.repository(myui, b'a') + > hg.clone(myui, {}, repo, dest=b"ua") > EOF $ $PYTHON simpleclone.py @@ -573,8 +573,8 @@ > from mercurial import ui, hg, extensions > myui = ui.ui.load() > extensions.loadall(myui) - > repo = hg.repository(myui, 'a') - > hg.clone(myui, {}, repo, dest="ua", branch=["stable",]) + > repo = hg.repository(myui, b'a') + > hg.clone(myui, {}, repo, dest=b"ua", branch=[b"stable",]) > EOF $ $PYTHON branchclone.py diff --git a/tests/test-clone-cgi.t b/tests/test-clone-cgi.t --- a/tests/test-clone-cgi.t +++ b/tests/test-clone-cgi.t @@ -17,7 +17,7 @@ > from mercurial import demandimport; demandimport.enable() > from mercurial.hgweb import hgweb > from mercurial.hgweb import wsgicgi - > application = hgweb("test", "Empty test repository") + > application = hgweb(b"test", b"Empty test repository") > wsgicgi.launch(application) > HGWEB $ chmod 755 hgweb.cgi To: pulkit, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3330: py3: make values bytes before passing into server.runservice()
pulkit created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY The values of opts dict still needed to be converted to bytes. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3330 AFFECTED FILES tests/dumbhttp.py CHANGE DETAILS diff --git a/tests/dumbhttp.py b/tests/dumbhttp.py --- a/tests/dumbhttp.py +++ b/tests/dumbhttp.py @@ -13,6 +13,7 @@ import sys from mercurial import ( +pycompat, server, util, ) @@ -63,10 +64,12 @@ if options.foreground and options.pid: parser.error("options --pid and --foreground are mutually exclusive") -opts = {'pid_file': options.pid, -'daemon': not options.foreground, -'daemon_postexec': options.daemon_postexec} +opts = {b'pid_file': options.pid, +b'daemon': not options.foreground, +b'daemon_postexec': options.daemon_postexec} service = simplehttpservice(options.host, options.port) +runargs = [sys.executable, __file__] + sys.argv[1:] +runargs = [pycompat.fsencode(a) for a in runargs] server.runservice(opts, initfn=service.init, runfn=service.run, logfile=options.logfile, - runargs=[sys.executable, __file__] + sys.argv[1:]) + runargs=runargs) To: pulkit, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3303: cborutil: implement support for streaming encoding, bytestring decoding
indygreg updated this revision to Diff 8152. indygreg edited the summary of this revision. indygreg retitled this revision from "cborutil: implement support for indefinite length CBOR types" to "cborutil: implement support for streaming encoding, bytestring decoding". REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3303?vs=8104&id=8152 REVISION DETAIL https://phab.mercurial-scm.org/D3303 AFFECTED FILES contrib/import-checker.py mercurial/utils/cborutil.py tests/test-cbor.py CHANGE DETAILS diff --git a/tests/test-cbor.py b/tests/test-cbor.py new file mode 100644 --- /dev/null +++ b/tests/test-cbor.py @@ -0,0 +1,182 @@ +from __future__ import absolute_import + +import io +import unittest + +from mercurial.thirdparty import ( +cbor, +) +from mercurial.utils import ( +cborutil, +) + +class BytestringTests(unittest.TestCase): +def testsimple(self): +self.assertEqual( +list(cborutil.streamencode(b'foobar')), +[b'\x46', b'foobar']) + +self.assertEqual( +cbor.loads(b''.join(cborutil.streamencode(b'foobar'))), +b'foobar') + +def testlong(self): +source = b'x' * 1048576 + +self.assertEqual( +cbor.loads(b''.join(cborutil.streamencode(source))), +source) + +def testfromiter(self): +# This is the example from RFC 7049 Section 2.2.2. +source = [b'\xaa\xbb\xcc\xdd', b'\xee\xff\x99'] + +self.assertEqual( +list(cborutil.streamencodebytestringfromiter(source)), +[ +b'\x5f', +b'\x44', +b'\xaa\xbb\xcc\xdd', +b'\x43', +b'\xee\xff\x99', +b'\xff', +]) + +dest = b''.join(cborutil.streamencodebytestringfromiter(source)) +self.assertEqual(cbor.loads(dest), b''.join(source)) + +def testfromiterlarge(self): +source = [b'a' * 16, b'b' * 128, b'c' * 1024, b'd' * 1048576] + +dest = b''.join(cborutil.streamencodebytestringfromiter(source)) + +self.assertEqual(cbor.loads(dest), b''.join(source)) + +def testindefinite(self): +source = b'\x00\x01\x02\x03' + b'\xff' * 16384 + +it = cborutil.streamencodeindefinitebytestring(source, chunksize=2) + +self.assertEqual(next(it), b'\x5f') +self.assertEqual(next(it), b'\x42') +self.assertEqual(next(it), b'\x00\x01') +self.assertEqual(next(it), b'\x42') +self.assertEqual(next(it), b'\x02\x03') +self.assertEqual(next(it), b'\x42') +self.assertEqual(next(it), b'\xff\xff') + +dest = b''.join(cborutil.streamencodeindefinitebytestring( +source, chunksize=42)) +self.assertEqual(cbor.loads(dest), b''.join(source)) + +def testreadtoiter(self): +source = io.BytesIO(b'\x5f\x44\xaa\xbb\xcc\xdd\x43\xee\xff\x99\xff') + +it = cborutil.readindefinitebytestringtoiter(source) +self.assertEqual(next(it), b'\xaa\xbb\xcc\xdd') +self.assertEqual(next(it), b'\xee\xff\x99') + +with self.assertRaises(StopIteration): +next(it) + +class IntTests(unittest.TestCase): +def testsmall(self): +self.assertEqual(list(cborutil.streamencode(0)), [b'\x00']) +self.assertEqual(list(cborutil.streamencode(1)), [b'\x01']) +self.assertEqual(list(cborutil.streamencode(2)), [b'\x02']) +self.assertEqual(list(cborutil.streamencode(3)), [b'\x03']) +self.assertEqual(list(cborutil.streamencode(4)), [b'\x04']) + +def testnegativesmall(self): +self.assertEqual(list(cborutil.streamencode(-1)), [b'\x20']) +self.assertEqual(list(cborutil.streamencode(-2)), [b'\x21']) +self.assertEqual(list(cborutil.streamencode(-3)), [b'\x22']) +self.assertEqual(list(cborutil.streamencode(-4)), [b'\x23']) +self.assertEqual(list(cborutil.streamencode(-5)), [b'\x24']) + +def testrange(self): +for i in range(-7, 7, 10): +self.assertEqual( +b''.join(cborutil.streamencode(i)), +cbor.dumps(i)) + +class ArrayTests(unittest.TestCase): +def testempty(self): +self.assertEqual(list(cborutil.streamencode([])), [b'\x80']) +self.assertEqual(cbor.loads(b''.join(cborutil.streamencode([]))), []) + +def testbasic(self): +source = [b'foo', b'bar', 1, -10] + +self.assertEqual(list(cborutil.streamencode(source)), [ +b'\x84', b'\x43', b'foo', b'\x43', b'bar', b'\x01', b'\x29']) + +def testemptyfromiter(self): +self.assertEqual(b''.join(cborutil.streamencodearrayfromiter([])), + b'\x9f\xff') + +def testfromiter1(self): +source = [b'foo'] + +self.assertEqual(list(cborutil.streamencodearrayfromiter(source)), [ +b'\x9f', +b'\x43', b'foo', +b'\xff', +]) + + +dest
D3331: util: set correct stack level on deprecation warnings
martinvonz created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Without this patch, you'll get something like this: /mercurial/util.py:3784: DeprecationWarning: 'util.hgexecutable' is deprecated, use 'utils.procutil.hgexecutable' (but on one line) REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3331 AFFECTED FILES mercurial/util.py CHANGE DETAILS diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -3781,7 +3781,7 @@ fn = pycompat.sysbytes(func.__name__) mn = modname or pycompat.sysbytes(func.__module__)[len('mercurial.'):] msg = "'util.%s' is deprecated, use '%s.%s'" % (fn, mn, fn) -nouideprecwarn(msg, version) +nouideprecwarn(msg, version, stacklevel=2) return func(*args, **kwargs) wrapped.__name__ = func.__name__ return wrapped To: martinvonz, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3303: cborutil: implement support for streaming encoding, bytestring decoding
indygreg added a comment. I ended up implementing my own CBOR encoder for a starting subset of types. Profiling the wire protocol server inspired me to do this. I have a future wire protocol command that emits the fulltext data of every file in a revision. It was taking ~45s CPU to run. After plugging in the new generator based CBOR encoder, that drops to ~31s. All pure Python still too. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3303 To: indygreg, #hg-reviewers Cc: yuja, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3297: httppeer: implement command executor for version 2 peer
indygreg updated this revision to Diff 8153. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3297?vs=8102&id=8153 REVISION DETAIL https://phab.mercurial-scm.org/D3297 AFFECTED FILES mercurial/httppeer.py CHANGE DETAILS diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py --- a/mercurial/httppeer.py +++ b/mercurial/httppeer.py @@ -13,7 +13,9 @@ import os import socket import struct +import sys import tempfile +import weakref from .i18n import _ from .thirdparty import ( @@ -31,7 +33,6 @@ statichttprepo, url as urlmod, util, -wireproto, wireprotoframing, wireprototypes, wireprotov1peer, @@ -517,8 +518,262 @@ def _abort(self, exception): raise exception +def sendv2request(ui, opener, requestbuilder, apiurl, permission, requests): +reactor = wireprotoframing.clientreactor(hasmultiplesend=False, + buffersends=True) + +url = '%s/%s' % (apiurl, permission) + +if len(requests) > 1: +url += '/multirequest' +else: +url += '/%s' % requests[0][0] + +# Request ID to (request, future) +requestmap = {} + +for command, args, f in requests: +request, action, meta = reactor.callcommand(command, args) +assert action == 'noop' + +requestmap[request.requestid] = (request, f) + +action, meta = reactor.flushcommands() +assert action == 'sendframes' + +# TODO stream this. +body = b''.join(map(bytes, meta['framegen'])) + +# TODO modify user-agent to reflect v2 +headers = { +r'Accept': wireprotov2server.FRAMINGTYPE, +r'Content-Type': wireprotov2server.FRAMINGTYPE, +} + +req = requestbuilder(pycompat.strurl(url), body, headers) +req.add_unredirected_header(r'Content-Length', r'%d' % len(body)) + +try: +res = opener.open(req) +except urlerr.httperror as e: +if e.code == 401: +raise error.Abort(_('authorization failed')) + +raise +except httplib.HTTPException as e: +ui.traceback() +raise IOError(None, e) + +return reactor, requestmap, res + +class queuedcommandfuture(pycompat.futures.Future): +"""Wraps result() on command futures to trigger submission on call.""" + +def result(self, timeout=None): +if self.done(): +return pycompat.futures.Future.result(self, timeout) + +self._peerexecutor.sendcommands() + +# sendcommands() will restore the original __class__ and self.result +# will resolve to Future.result. +return self.result(timeout) + +@zi.implementer(repository.ipeercommandexecutor) +class httpv2executor(object): +def __init__(self, ui, opener, requestbuilder, apiurl, descriptor): +self._ui = ui +self._opener = opener +self._requestbuilder = requestbuilder +self._apiurl = apiurl +self._descriptor = descriptor +self._sent = False +self._closed = False +self._neededpermissions = set() +self._calls = [] +self._futures = weakref.WeakSet() +self._responseexecutor = None +self._responsef = None + +def __enter__(self): +return self + +def __exit__(self, exctype, excvalue, exctb): +self.close() + +def callcommand(self, command, args): +if self._sent: +raise error.ProgrammingError('callcommand() cannot be used after ' + 'commands are sent') + +if self._closed: +raise error.ProgrammingError('callcommand() cannot be used after ' + 'close()') + +# The service advertises which commands are available. So if we attempt +# to call an unknown command or pass an unknown argument, we can screen +# for this. +if command not in self._descriptor['commands']: +raise error.ProgrammingError( +'wire protocol command %s is not available' % command) + +cmdinfo = self._descriptor['commands'][command] +unknownargs = set(args.keys()) - set(cmdinfo.get('args', {})) + +if unknownargs: +raise error.ProgrammingError( +'wire protocol command %s does not accept argument: %s' % ( +command, ', '.join(sorted(unknownargs + +self._neededpermissions |= set(cmdinfo['permissions']) + +# TODO we /could/ also validate types here, since the API descriptor +# includes types... + +f = pycompat.futures.Future() + +# Monkeypatch it so result() triggers sendcommands(), otherwise result() +# could deadlock. +f.__class__ = queuedcommandfuture +f._peerexecutor = self + +self._futures.add(f) +self._calls.append((command, args, f)) + +return f + +def sendcommands(self): +if self._sent: +return + +
D3332: httppeer: handle error response from client reactor
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY With this in place, we're now seeing useful errors when running tests with the new wire protocol enabled! REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3332 AFFECTED FILES mercurial/httppeer.py CHANGE DETAILS diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py --- a/mercurial/httppeer.py +++ b/mercurial/httppeer.py @@ -763,6 +763,14 @@ f.set_result(result) del results[request.requestid] +elif action == 'error': +e = error.RepoError(meta['message']) + +if f: +f.set_exception(e) +else: +raise e + else: e = error.ProgrammingError('unhandled action: %s' % action) To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3298: debugcommands: use command executor for invoking commands
indygreg updated this revision to Diff 8154. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3298?vs=8091&id=8154 REVISION DETAIL https://phab.mercurial-scm.org/D3298 AFFECTED FILES mercurial/debugcommands.py tests/test-http-protocol.t tests/test-ssh-proto.t CHANGE DETAILS diff --git a/tests/test-ssh-proto.t b/tests/test-ssh-proto.t --- a/tests/test-ssh-proto.t +++ b/tests/test-ssh-proto.t @@ -1367,7 +1367,7 @@ o> bookmarks\t\n o> namespaces\t\n o> phases\t - response: b'bookmarks\t\nnamespaces\t\nphases\t' + response: {b'bookmarks': b'', b'namespaces': b'', b'phases': b''} testing ssh2 creating ssh peer from handshake results @@ -1398,7 +1398,7 @@ o> bookmarks\t\n o> namespaces\t\n o> phases\t - response: b'bookmarks\t\nnamespaces\t\nphases\t' + response: {b'bookmarks': b'', b'namespaces': b'', b'phases': b''} $ cd .. @@ -1443,7 +1443,7 @@ i> flush() -> None o> bufferedreadline() -> 2: o> 0\n - response: b'' + response: {} testing ssh2 creating ssh peer from handshake results @@ -1470,7 +1470,7 @@ i> flush() -> None o> bufferedreadline() -> 2: o> 0\n - response: b'' + response: {} With a single bookmark set @@ -1505,7 +1505,7 @@ o> bufferedreadline() -> 3: o> 46\n o> bufferedread(46) -> 46: bookA\t68986213bd4485ea51533535e3fc9e78007a711f - response: b'bookA\t68986213bd4485ea51533535e3fc9e78007a711f' + response: {b'bookA': b'68986213bd4485ea51533535e3fc9e78007a711f'} testing ssh2 creating ssh peer from handshake results @@ -1533,7 +1533,7 @@ o> bufferedreadline() -> 3: o> 46\n o> bufferedread(46) -> 46: bookA\t68986213bd4485ea51533535e3fc9e78007a711f - response: b'bookA\t68986213bd4485ea51533535e3fc9e78007a711f' + response: {b'bookA': b'68986213bd4485ea51533535e3fc9e78007a711f'} With multiple bookmarks set @@ -1570,7 +1570,7 @@ o> bufferedread(93) -> 93: o> bookA\t68986213bd4485ea51533535e3fc9e78007a711f\n o> bookB\t1880f3755e2e52e3199e0ee5638128b08642f34d - response: b'bookA\t68986213bd4485ea51533535e3fc9e78007a711f\nbookB\t1880f3755e2e52e3199e0ee5638128b08642f34d' + response: {b'bookA': b'68986213bd4485ea51533535e3fc9e78007a711f', b'bookB': b'1880f3755e2e52e3199e0ee5638128b08642f34d'} testing ssh2 creating ssh peer from handshake results @@ -1600,7 +1600,7 @@ o> bufferedread(93) -> 93: o> bookA\t68986213bd4485ea51533535e3fc9e78007a711f\n o> bookB\t1880f3755e2e52e3199e0ee5638128b08642f34d - response: b'bookA\t68986213bd4485ea51533535e3fc9e78007a711f\nbookB\t1880f3755e2e52e3199e0ee5638128b08642f34d' + response: {b'bookA': b'68986213bd4485ea51533535e3fc9e78007a711f', b'bookB': b'1880f3755e2e52e3199e0ee5638128b08642f34d'} Test pushkey for bookmarks @@ -1646,7 +1646,7 @@ o> 2\n o> bufferedread(2) -> 2: o> 1\n - response: b'1\n' + response: True testing ssh2 creating ssh peer from handshake results @@ -1683,7 +1683,7 @@ o> 2\n o> bufferedread(2) -> 2: o> 1\n - response: b'1\n' + response: True $ hg bookmarks bookA 0:68986213bd44 @@ -1729,7 +1729,7 @@ o> bufferedreadline() -> 3: o> 15\n o> bufferedread(15) -> 15: publishing\tTrue - response: b'publishing\tTrue' + response: {b'publishing': b'True'} testing ssh2 creating ssh peer from handshake results @@ -1757,7 +1757,7 @@ o> bufferedreadline() -> 3: o> 15\n o> bufferedread(15) -> 15: publishing\tTrue - response: b'publishing\tTrue' + response: {b'publishing': b'True'} Create some commits @@ -1811,7 +1811,7 @@ o> 20b8a89289d80036e6c4e87c2083e3bea1586637\t1\n o> c4750011d906c18ea2f0527419cbc1a544435150\t1\n o> publishing\tTrue - response: b'20b8a89289d80036e6c4e87c2083e3bea1586637\t1\nc4750011d906c18ea2f0527419cbc1a544435150\t1\npublishing\tTrue' + response: {b'20b8a89289d80036e6c4e87c2083e3bea1586637': b'1', b'c4750011d906c18ea2f0527419cbc1a544435150': b'1', b'publishing': b'True'} testing ssh2 creating ssh peer from handshake results @@ -1842,7 +1842,7 @@ o> 20b8a89289d80036e6c4e87c2083e3bea1586637\t1\n o> c4750011d906c18ea2f0527419cbc1a544435150\t1\n o> publishing\tTrue - response: b'20b8a89289d80036e6c4e87c2083e3bea1586637\t1\nc4750011d906c18ea2f0527419cbc1a544435150\t1\npublishing\tTrue' + response: {b'20b8a89289d80036e6c4e87c2083e3bea1586637': b'1', b'c4750011d906c18ea2f0527419cbc1a544435150': b'1', b'publishing': b'True'} Single draft head @@ -1879,7 +1879,7 @@ o> bufferedread(58) -> 58: o> c4750011d906c18ea2f0527419cbc1a544435150\t1\n o> publishing\tTrue - response: b'c4750011d906c18ea2f0527419cbc1a544435150\t1\npublishing\tTrue' + response: {b'c4750011d906c18ea2f0527419cbc1a544435150': b'1', b'publishing': b'True'} testing ssh2 creating ssh peer from handshake results @@ -1909,7 +1
D3333: wireprotoframing: use value passed into function
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Oops. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D AFFECTED FILES mercurial/wireprotoframing.py CHANGE DETAILS diff --git a/mercurial/wireprotoframing.py b/mercurial/wireprotoframing.py --- a/mercurial/wireprotoframing.py +++ b/mercurial/wireprotoframing.py @@ -464,7 +464,7 @@ def __init__(self, streamid, active=False): self.streamid = streamid -self._active = False +self._active = active def makeframe(self, requestid, typeid, flags, payload): """Create a frame to be sent out over this stream. To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3334: wireprotoframing: record when new stream is encountered
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Without this, we choke after receiving the 2nd frame in a stream. Not sure how we made it this far without finding this bug. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3334 AFFECTED FILES mercurial/wireprotoframing.py tests/test-wireproto-clientreactor.py CHANGE DETAILS diff --git a/tests/test-wireproto-clientreactor.py b/tests/test-wireproto-clientreactor.py --- a/tests/test-wireproto-clientreactor.py +++ b/tests/test-wireproto-clientreactor.py @@ -105,6 +105,26 @@ 'unhandled frame type'): sendframe(reactor, ffs(b'1 0 stream-begin text-output 0 foo')) +class StreamTests(unittest.TestCase): +def testmultipleresponseframes(self): +reactor = framing.clientreactor(buffersends=False) + +request, action, meta = reactor.callcommand(b'foo', {}) + +self.assertEqual(action, 'sendframes') +for f in meta['framegen']: +pass + +action, meta = sendframe( +reactor, +ffs(b'%d 0 stream-begin 4 0 foo' % request.requestid)) +self.assertEqual(action, 'responsedata') + +action, meta = sendframe( +reactor, +ffs(b'%d 0 0 4 eos bar' % request.requestid)) +self.assertEqual(action, 'responsedata') + if __name__ == '__main__': import silenttestrunner silenttestrunner.main(__name__) diff --git a/mercurial/wireprotoframing.py b/mercurial/wireprotoframing.py --- a/mercurial/wireprotoframing.py +++ b/mercurial/wireprotoframing.py @@ -1029,6 +1029,8 @@ 'without beginning of stream flag set'), } +self._incomingstreams[frame.streamid] = stream(frame.streamid) + if frame.streamflags & STREAM_FLAG_ENCODING_APPLIED: raise error.ProgrammingError('support for decoding stream ' 'payloads not yet implemneted') To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3335: wireproto: expose repository formats via capabilities
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Servers need to expose their set of repository storage requirements in order to facilitate streaming clones (clients need to know if they are capable of reading the raw storage files that the server exposes). REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3335 AFFECTED FILES mercurial/help/internals/wireprotocol.txt mercurial/wireprotov2server.py tests/test-http-api-httpv2.t tests/test-http-protocol.t tests/test-wireproto-command-branchmap.t tests/test-wireproto-command-capabilities.t tests/test-wireproto-command-known.t tests/test-wireproto-command-listkeys.t tests/test-wireproto-command-lookup.t tests/test-wireproto-command-pushkey.t CHANGE DETAILS diff --git a/tests/test-wireproto-command-pushkey.t b/tests/test-wireproto-command-pushkey.t --- a/tests/test-wireproto-command-pushkey.t +++ b/tests/test-wireproto-command-pushkey.t @@ -45,7 +45,7 @@ s> Content-Type: application/mercurial-cbor\r\n s> Content-Length: *\r\n (glob) s> \r\n - s> \xa3Dapis\xa1Pexp-http-v2-0001\xa3Hcommands\xa7Eheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyCnewCnewColdColdInamespaceBnsKpermissions\x81DpushHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullKcompression\x82\xa1DnameDzstd\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0003GapibaseDapi/Nv1capabilitiesY\x01\xcabatch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash + s> \xa3Dapis\xa1Pexp-http-v2-0001\xa4Hcommands\xa7Eheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyCnewCnewColdColdInamespaceBnsKpermissions\x81DpushHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullKcompression\x82\xa1DnameDzstd\xa1DnameDzlibNrawrepoformats\x82LgeneraldeltaHrevlogv1Qframingmediatypes\x81X&application/mercurial-exp-framing-0003GapibaseDapi/Nv1capabilitiesY\x01\xcabatch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash sending pushkey command s> *\r\n (glob) s> Accept-Encoding: identity\r\n @@ -93,7 +93,7 @@ s> Content-Type: application/mercurial-cbor\r\n s> Content-Length: *\r\n (glob) s> \r\n - s> \xa3Dapis\xa1Pexp-http-v2-0001\xa3Hcommands\xa7Eheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyCnewCnewColdColdInamespaceBnsKpermissions\x81DpushHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullKcompression\x82\xa1DnameDzstd\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0003GapibaseDapi/Nv1capabilitiesY\x01\xcabatch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash + s> \xa3Dapis\xa1Pexp-http-v2-0001\xa4Hcommands\xa7Eheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyCnewCnewColdColdInamespaceBnsKpermissions\x81DpushHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullKcompression\x82\xa1DnameDzstd\xa1DnameDzlibNrawrepoformats\x82LgeneraldeltaHrevlogv1Qframingmediatypes\x81X&application/mercurial-exp-framing-0003GapibaseDapi/Nv1capabilitiesY\x01\xcabatch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash sending listkeys command s> POST /api/exp-http-v2-0001/ro/listkeys HTTP/1.1\r\n s> Accept-Encoding: identity\r\n diff --git a/tests/t
D3261: thirdparty: vendor futures 3.2.0
This revision was automatically updated to reflect the committed changes. Closed by commit rHGeb687c28a915: thirdparty: vendor futures 3.2.0 (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3261?vs=8027&id=8159 REVISION DETAIL https://phab.mercurial-scm.org/D3261 AFFECTED FILES mercurial/thirdparty/concurrent/LICENSE mercurial/thirdparty/concurrent/__init__.py mercurial/thirdparty/concurrent/futures/__init__.py mercurial/thirdparty/concurrent/futures/_base.py mercurial/thirdparty/concurrent/futures/process.py mercurial/thirdparty/concurrent/futures/thread.py CHANGE DETAILS diff --git a/mercurial/thirdparty/concurrent/futures/thread.py b/mercurial/thirdparty/concurrent/futures/thread.py new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/concurrent/futures/thread.py @@ -0,0 +1,160 @@ +# Copyright 2009 Brian Quinlan. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""Implements ThreadPoolExecutor.""" + +import atexit +from concurrent.futures import _base +import itertools +import Queue as queue +import threading +import weakref +import sys + +try: +from multiprocessing import cpu_count +except ImportError: +# some platforms don't have multiprocessing +def cpu_count(): +return None + +__author__ = 'Brian Quinlan (br...@sweetapp.com)' + +# Workers are created as daemon threads. This is done to allow the interpreter +# to exit when there are still idle threads in a ThreadPoolExecutor's thread +# pool (i.e. shutdown() was not called). However, allowing workers to die with +# the interpreter has two undesirable properties: +# - The workers would still be running during interpretor shutdown, +# meaning that they would fail in unpredictable ways. +# - The workers could be killed while evaluating a work item, which could +# be bad if the callable being evaluated has external side-effects e.g. +# writing to a file. +# +# To work around this problem, an exit handler is installed which tells the +# workers to exit when their work queues are empty and then waits until the +# threads finish. + +_threads_queues = weakref.WeakKeyDictionary() +_shutdown = False + +def _python_exit(): +global _shutdown +_shutdown = True +items = list(_threads_queues.items()) if _threads_queues else () +for t, q in items: +q.put(None) +for t, q in items: +t.join(sys.maxint) + +atexit.register(_python_exit) + +class _WorkItem(object): +def __init__(self, future, fn, args, kwargs): +self.future = future +self.fn = fn +self.args = args +self.kwargs = kwargs + +def run(self): +if not self.future.set_running_or_notify_cancel(): +return + +try: +result = self.fn(*self.args, **self.kwargs) +except: +e, tb = sys.exc_info()[1:] +self.future.set_exception_info(e, tb) +else: +self.future.set_result(result) + +def _worker(executor_reference, work_queue): +try: +while True: +work_item = work_queue.get(block=True) +if work_item is not None: +work_item.run() +# Delete references to object. See issue16284 +del work_item +continue +executor = executor_reference() +# Exit if: +# - The interpreter is shutting down OR +# - The executor that owns the worker has been collected OR +# - The executor that owns the worker has been shutdown. +if _shutdown or executor is None or executor._shutdown: +# Notice other workers +work_queue.put(None) +return +del executor +except: +_base.LOGGER.critical('Exception in worker', exc_info=True) + + +class ThreadPoolExecutor(_base.Executor): + +# Used to assign unique thread names when thread_name_prefix is not supplied. +_counter = itertools.count().next + +def __init__(self, max_workers=None, thread_name_prefix=''): +"""Initializes a new ThreadPoolExecutor instance. + +Args: +max_workers: The maximum number of threads that can be used to +execute the given calls. +thread_name_prefix: An optional name prefix to give our threads. +""" +if max_workers is None: +# Use this number because ThreadPoolExecutor is often +# used to overlap I/O instead of CPU work. +max_workers = (cpu_count() or 1) * 5 +if max_workers <= 0: +raise ValueError("max_workers must be greater than 0") + +self._max_workers = max_workers +self._work_queue = queue.Queue() +self._threads = set() +self._shutdown = False +self._shutdown_lock = threading.Lock() +self._thread_name_prefix = (thread_name_prefi
D3264: futures: switch to absolute and relative imports
This revision was automatically updated to reflect the committed changes. Closed by commit rHG0a9c0d3480b2: futures: switch to absolute and relative imports (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3264?vs=8030&id=8162 REVISION DETAIL https://phab.mercurial-scm.org/D3264 AFFECTED FILES mercurial/thirdparty/concurrent/futures/__init__.py mercurial/thirdparty/concurrent/futures/_base.py mercurial/thirdparty/concurrent/futures/process.py mercurial/thirdparty/concurrent/futures/thread.py CHANGE DETAILS diff --git a/mercurial/thirdparty/concurrent/futures/thread.py b/mercurial/thirdparty/concurrent/futures/thread.py --- a/mercurial/thirdparty/concurrent/futures/thread.py +++ b/mercurial/thirdparty/concurrent/futures/thread.py @@ -3,8 +3,10 @@ """Implements ThreadPoolExecutor.""" +from __future__ import absolute_import + import atexit -from concurrent.futures import _base +from . import _base import itertools import Queue as queue import threading diff --git a/mercurial/thirdparty/concurrent/futures/process.py b/mercurial/thirdparty/concurrent/futures/process.py --- a/mercurial/thirdparty/concurrent/futures/process.py +++ b/mercurial/thirdparty/concurrent/futures/process.py @@ -43,8 +43,10 @@ _ResultItems in "Request Q" """ +from __future__ import absolute_import + import atexit -from concurrent.futures import _base +from . import _base import Queue as queue import multiprocessing import threading diff --git a/mercurial/thirdparty/concurrent/futures/_base.py b/mercurial/thirdparty/concurrent/futures/_base.py --- a/mercurial/thirdparty/concurrent/futures/_base.py +++ b/mercurial/thirdparty/concurrent/futures/_base.py @@ -1,6 +1,8 @@ # Copyright 2009 Brian Quinlan. All Rights Reserved. # Licensed to PSF under a Contributor Agreement. +from __future__ import absolute_import + import collections import logging import threading diff --git a/mercurial/thirdparty/concurrent/futures/__init__.py b/mercurial/thirdparty/concurrent/futures/__init__.py --- a/mercurial/thirdparty/concurrent/futures/__init__.py +++ b/mercurial/thirdparty/concurrent/futures/__init__.py @@ -3,21 +3,25 @@ """Execute computations asynchronously using threads or processes.""" +from __future__ import absolute_import + __author__ = 'Brian Quinlan (br...@sweetapp.com)' -from concurrent.futures._base import (FIRST_COMPLETED, - FIRST_EXCEPTION, - ALL_COMPLETED, - CancelledError, - TimeoutError, - Future, - Executor, - wait, - as_completed) -from concurrent.futures.thread import ThreadPoolExecutor +from ._base import ( +FIRST_COMPLETED, +FIRST_EXCEPTION, +ALL_COMPLETED, +CancelledError, +TimeoutError, +Future, +Executor, +wait, +as_completed, +) +from .thread import ThreadPoolExecutor try: -from concurrent.futures.process import ProcessPoolExecutor +from .process import ProcessPoolExecutor except ImportError: # some platforms don't have multiprocessing pass To: indygreg, #hg-reviewers, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3262: futures: get rid of extend_path
This revision was automatically updated to reflect the committed changes. Closed by commit rHG33db69b6b58b: futures: get rid of extend_path (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3262?vs=8028&id=8160 REVISION DETAIL https://phab.mercurial-scm.org/D3262 AFFECTED FILES mercurial/thirdparty/concurrent/__init__.py CHANGE DETAILS diff --git a/mercurial/thirdparty/concurrent/__init__.py b/mercurial/thirdparty/concurrent/__init__.py --- a/mercurial/thirdparty/concurrent/__init__.py +++ b/mercurial/thirdparty/concurrent/__init__.py @@ -1,3 +0,0 @@ -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) To: indygreg, #hg-reviewers, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3263: tests: silence pyflakes for thirdparty/concurrent
This revision was automatically updated to reflect the committed changes. Closed by commit rHG3ccaf995f549: tests: silence pyflakes for thirdparty/concurrent (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3263?vs=8029&id=8161 REVISION DETAIL https://phab.mercurial-scm.org/D3263 AFFECTED FILES tests/test-check-pyflakes.t CHANGE DETAILS diff --git a/tests/test-check-pyflakes.t b/tests/test-check-pyflakes.t --- a/tests/test-check-pyflakes.t +++ b/tests/test-check-pyflakes.t @@ -17,6 +17,7 @@ > -X hgext/fsmonitor/pywatchman \ > -X mercurial/pycompat.py -X contrib/python-zstandard \ > -X mercurial/thirdparty/cbor \ + > -X mercurial/thirdparty/concurrent \ > -X mercurial/thirdparty/zope \ > 2>/dev/null \ > | xargs pyflakes 2>/dev/null | "$TESTDIR/filterpyflakes.py" To: indygreg, #hg-reviewers, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3266: pycompat: export a handle on concurrent.futures
This revision was automatically updated to reflect the committed changes. Closed by commit rHG8da30ceae88f: pycompat: export a handle on concurrent.futures (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3266?vs=8032&id=8164 REVISION DETAIL https://phab.mercurial-scm.org/D3266 AFFECTED FILES mercurial/pycompat.py CHANGE DETAILS diff --git a/mercurial/pycompat.py b/mercurial/pycompat.py --- a/mercurial/pycompat.py +++ b/mercurial/pycompat.py @@ -26,7 +26,10 @@ import Queue as _queue import SocketServer as socketserver import xmlrpclib + +from .thirdparty.concurrent import futures else: +import concurrent.futures as futures import http.cookiejar as cookielib import http.client as httplib import pickle To: indygreg, #hg-reviewers, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3267: repository: define new interface for running commands
This revision was automatically updated to reflect the committed changes. Closed by commit rHGfa0382088993: repository: define new interface for running commands (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3267?vs=8120&id=8165 REVISION DETAIL https://phab.mercurial-scm.org/D3267 AFFECTED FILES mercurial/repository.py CHANGE DETAILS diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -196,6 +196,88 @@ def changegroupsubset(bases, heads, kind): pass +class ipeercommandexecutor(zi.Interface): +"""Represents a mechanism to execute remote commands. + +This is the primary interface for requesting that wire protocol commands +be executed. Instances of this interface are active in a context manager +and have a well-defined lifetime. When the context manager exits, all +outstanding requests are waited on. +""" + +def callcommand(name, args): +"""Request that a named command be executed. + +Receives the command name and a dictionary of command arguments. + +Returns a ``concurrent.futures.Future`` that will resolve to the +result of that command request. That exact value is left up to +the implementation and possibly varies by command. + +Not all commands can coexist with other commands in an executor +instance: it depends on the underlying wire protocol transport being +used and the command itself. + +Implementations MAY call ``sendcommands()`` automatically if the +requested command can not coexist with other commands in this executor. + +Implementations MAY call ``sendcommands()`` automatically when the +future's ``result()`` is called. So, consumers using multiple +commands with an executor MUST ensure that ``result()`` is not called +until all command requests have been issued. +""" + +def sendcommands(): +"""Trigger submission of queued command requests. + +Not all transports submit commands as soon as they are requested to +run. When called, this method forces queued command requests to be +issued. It will no-op if all commands have already been sent. + +When called, no more new commands may be issued with this executor. +""" + +def close(): +"""Signal that this command request is finished. + +When called, no more new commands may be issued. All outstanding +commands that have previously been issued are waited on before +returning. This not only includes waiting for the futures to resolve, +but also waiting for all response data to arrive. In other words, +calling this waits for all on-wire state for issued command requests +to finish. + +When used as a context manager, this method is called when exiting the +context manager. + +This method may call ``sendcommands()`` if there are buffered commands. +""" + +class ipeerrequests(zi.Interface): +"""Interface for executing commands on a peer.""" + +def commandexecutor(): +"""A context manager that resolves to an ipeercommandexecutor. + +The object this resolves to can be used to issue command requests +to the peer. + +Callers should call its ``callcommand`` method to issue command +requests. + +A new executor should be obtained for each distinct set of commands +(possibly just a single command) that the consumer wants to execute +as part of a single operation or round trip. This is because some +peers are half-duplex and/or don't support persistent connections. +e.g. in the case of HTTP peers, commands sent to an executor represent +a single HTTP request. While some peers may support multiple command +sends over the wire per executor, consumers need to code to the least +capable peer. So it should be assumed that command executors buffer +called commands until they are told to send them and that each +command executor could result in a new connection or wire-level request +being issued. +""" + class ipeerbase(ipeerconnection, ipeercapabilities, ipeercommands): """Unified interface for peer repositories. To: indygreg, #hg-reviewers, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3265: setup: add packages for concurrent.futures
This revision was automatically updated to reflect the committed changes. Closed by commit rHGcfb32979abcd: setup: add packages for concurrent.futures (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3265?vs=8031&id=8163 REVISION DETAIL https://phab.mercurial-scm.org/D3265 AFFECTED FILES setup.py CHANGE DETAILS diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -812,6 +812,8 @@ 'mercurial.thirdparty.attr', 'mercurial.thirdparty.cbor', 'mercurial.thirdparty.cbor.cbor2', +'mercurial.thirdparty.concurrent', +'mercurial.thirdparty.concurrent.futures', 'mercurial.thirdparty.zope', 'mercurial.thirdparty.zope.interface', 'mercurial.utils', To: indygreg, #hg-reviewers, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3270: largefiles: use command executor for batch operation
This revision was automatically updated to reflect the committed changes. Closed by commit rHG6c55ce51d6c3: largefiles: use command executor for batch operation (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3270?vs=8123&id=8168 REVISION DETAIL https://phab.mercurial-scm.org/D3270 AFFECTED FILES hgext/largefiles/wirestore.py tests/test-largefiles-wireproto.t CHANGE DETAILS diff --git a/tests/test-largefiles-wireproto.t b/tests/test-largefiles-wireproto.t --- a/tests/test-largefiles-wireproto.t +++ b/tests/test-largefiles-wireproto.t @@ -312,7 +312,7 @@ getting changed largefiles using http://localhost:$HGPORT2/ sending capabilities command - sending batch command + sending statlfile command getting largefiles: 0/1 files (0.00%) getting f1:02a439e5c31c526465ab1a0ca1f431f76b827b90 sending getlfile command @@ -400,7 +400,7 @@ searching 3 changesets for largefiles verified existence of 3 revisions of 3 largefiles $ tail -1 access.log - $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=statlfile+sha%3Dc8559c3c9cfb42131794b7d8009230403b9b454c x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob) + $LOCALIP - - [$LOGDATE$] "GET /?cmd=statlfile HTTP/1.1" 200 - x-hgarg-1:sha=c8559c3c9cfb42131794b7d8009230403b9b454c x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob) $ killdaemons.py diff --git a/hgext/largefiles/wirestore.py b/hgext/largefiles/wirestore.py --- a/hgext/largefiles/wirestore.py +++ b/hgext/largefiles/wirestore.py @@ -32,8 +32,12 @@ '''For each hash, return 0 if it is available, other values if not. It is usually 2 if the largefile is missing, but might be 1 the server has a corrupted copy.''' -batch = self.remote.iterbatch() -for hash in hashes: -batch.statlfile(hash) -batch.submit() -return dict(zip(hashes, batch.results())) + +with self.remote.commandexecutor() as e: +fs = [] +for hash in hashes: +fs.append((hash, e.callcommand('statlfile', { +'sha': hash, +}))) + +return {hash: f.result() for hash, f in fs} To: indygreg, #hg-reviewers, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3269: wireproto: implement batching on peer executor interface
This revision was automatically updated to reflect the committed changes. Closed by commit rHG2f626233859b: wireproto: implement batching on peer executor interface (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3269?vs=8122&id=8166 REVISION DETAIL https://phab.mercurial-scm.org/D3269 AFFECTED FILES mercurial/setdiscovery.py mercurial/wireprotov1peer.py CHANGE DETAILS diff --git a/mercurial/wireprotov1peer.py b/mercurial/wireprotov1peer.py --- a/mercurial/wireprotov1peer.py +++ b/mercurial/wireprotov1peer.py @@ -9,6 +9,7 @@ import hashlib import sys +import weakref from .i18n import _ from .node import ( @@ -180,13 +181,36 @@ return ';'.join(cmds) +class unsentfuture(pycompat.futures.Future): +"""A Future variation to represent an unsent command. + +Because we buffer commands and don't submit them immediately, calling +``result()`` on an unsent future could deadlock. Futures for buffered +commands are represented by this type, which wraps ``result()`` to +call ``sendcommands()``. +""" + +def result(self, timeout=None): +if self.done(): +return pycompat.futures.Future.result(self, timeout) + +self._peerexecutor.sendcommands() + +# This looks like it will infinitely recurse. However, +# sendcommands() should modify __class__. This call serves as a check +# on that. +return self.result(timeout) + @zi.implementer(repository.ipeercommandexecutor) class peerexecutor(object): def __init__(self, peer): self._peer = peer self._sent = False self._closed = False self._calls = [] +self._futures = weakref.WeakSet() +self._responseexecutor = None +self._responsef = None def __enter__(self): return self @@ -214,20 +238,35 @@ # Commands are either batchable or they aren't. If a command # isn't batchable, we send it immediately because the executor # can no longer accept new commands after a non-batchable command. -# If a command is batchable, we queue it for later. +# If a command is batchable, we queue it for later. But we have +# to account for the case of a non-batchable command arriving after +# a batchable one and refuse to service it. + +def addcall(): +f = pycompat.futures.Future() +self._futures.add(f) +self._calls.append((command, args, fn, f)) +return f if getattr(fn, 'batchable', False): -pass +f = addcall() + +# But since we don't issue it immediately, we wrap its result() +# to trigger sending so we avoid deadlocks. +f.__class__ = unsentfuture +f._peerexecutor = self else: if self._calls: raise error.ProgrammingError( '%s is not batchable and cannot be called on a command ' 'executor along with other commands' % command) -# We don't support batching yet. So resolve it immediately. -f = pycompat.futures.Future() -self._calls.append((command, args, fn, f)) -self.sendcommands() +f = addcall() + +# Non-batchable commands can never coexist with another command +# in this executor. So send the command immediately. +self.sendcommands() + return f def sendcommands(self): @@ -239,10 +278,18 @@ self._sent = True +# Unhack any future types so caller seens a clean type and to break +# cycle between us and futures. +for f in self._futures: +if isinstance(f, unsentfuture): +f.__class__ = pycompat.futures.Future +f._peerexecutor = None + calls = self._calls # Mainly to destroy references to futures. self._calls = None +# Simple case of a single command. We call it synchronously. if len(calls) == 1: command, args, fn, f = calls[0] @@ -259,14 +306,99 @@ return -raise error.ProgrammingError('support for multiple commands not ' - 'yet implemented') +# Batch commands are a bit harder. First, we have to deal with the +# @batchable coroutine. That's a bit annoying. Furthermore, we also +# need to preserve streaming. i.e. it should be possible for the +# futures to resolve as data is coming in off the wire without having +# to wait for the final byte of the final response. We do this by +# spinning up a thread to read the responses. + +requests = [] +states = [] + +for command, args, fn, f in calls: +# Future was cancelled. Ignore it. +if not f.set_running_or_notify_cancel(): +c
D3268: wireproto: implement command executor interface for version 1 peers
This revision was automatically updated to reflect the committed changes. Closed by commit rHGe1b32dc4646c: wireproto: implement command executor interface for version 1 peers (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3268?vs=8121&id=8167 REVISION DETAIL https://phab.mercurial-scm.org/D3268 AFFECTED FILES mercurial/localrepo.py mercurial/repository.py mercurial/setdiscovery.py mercurial/wireprotov1peer.py tests/test-check-interfaces.py CHANGE DETAILS diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py --- a/tests/test-check-interfaces.py +++ b/tests/test-check-interfaces.py @@ -23,6 +23,7 @@ vfs as vfsmod, wireprotoserver, wireprototypes, +wireprotov1peer, wireprotov2server, ) @@ -102,6 +103,14 @@ localrepo.localpeer) checkzobject(localrepo.localpeer(dummyrepo())) +ziverify.verifyClass(repository.ipeercommandexecutor, + localrepo.localcommandexecutor) +checkzobject(localrepo.localcommandexecutor(None)) + +ziverify.verifyClass(repository.ipeercommandexecutor, + wireprotov1peer.peerexecutor) +checkzobject(wireprotov1peer.peerexecutor(None)) + ziverify.verifyClass(repository.ipeerbaselegacycommands, sshpeer.sshv1peer) checkzobject(sshpeer.sshv1peer(ui, 'ssh://localhost/foo', None, dummypipe(), diff --git a/mercurial/wireprotov1peer.py b/mercurial/wireprotov1peer.py --- a/mercurial/wireprotov1peer.py +++ b/mercurial/wireprotov1peer.py @@ -8,12 +8,15 @@ from __future__ import absolute_import import hashlib +import sys from .i18n import _ from .node import ( bin, ) - +from .thirdparty.zope import ( +interface as zi, +) from . import ( bundle2, changegroup as changegroupmod, @@ -177,14 +180,104 @@ return ';'.join(cmds) +@zi.implementer(repository.ipeercommandexecutor) +class peerexecutor(object): +def __init__(self, peer): +self._peer = peer +self._sent = False +self._closed = False +self._calls = [] + +def __enter__(self): +return self + +def __exit__(self, exctype, excvalee, exctb): +self.close() + +def callcommand(self, command, args): +if self._sent: +raise error.ProgrammingError('callcommand() cannot be used ' + 'after commands are sent') + +if self._closed: +raise error.ProgrammingError('callcommand() cannot be used ' + 'after close()') + +# Commands are dispatched through methods on the peer. +fn = getattr(self._peer, pycompat.sysstr(command), None) + +if not fn: +raise error.ProgrammingError( +'cannot call command %s: method of same name not available ' +'on peer' % command) + +# Commands are either batchable or they aren't. If a command +# isn't batchable, we send it immediately because the executor +# can no longer accept new commands after a non-batchable command. +# If a command is batchable, we queue it for later. + +if getattr(fn, 'batchable', False): +pass +else: +if self._calls: +raise error.ProgrammingError( +'%s is not batchable and cannot be called on a command ' +'executor along with other commands' % command) + +# We don't support batching yet. So resolve it immediately. +f = pycompat.futures.Future() +self._calls.append((command, args, fn, f)) +self.sendcommands() +return f + +def sendcommands(self): +if self._sent: +return + +if not self._calls: +return + +self._sent = True + +calls = self._calls +# Mainly to destroy references to futures. +self._calls = None + +if len(calls) == 1: +command, args, fn, f = calls[0] + +# Future was cancelled. Ignore it. +if not f.set_running_or_notify_cancel(): +return + +try: +result = fn(**pycompat.strkwargs(args)) +except Exception: +f.set_exception_info(*sys.exc_info()[1:]) +else: +f.set_result(result) + +return + +raise error.ProgrammingError('support for multiple commands not ' + 'yet implemented') + +def close(self): +self.sendcommands() + +self._closed = True + class wirepeer(repository.legacypeer): """Client-side interface for communicating with a peer repository. Methods commonly call wire protocol commands of the same name. See also httppeer.py and sshpeer.py for protocol-specific implementations of t
D3271: wireproto: remove iterbatch() from peer interface (API)
This revision was automatically updated to reflect the committed changes. Closed by commit rHG33a6eee08db2: wireproto: remove iterbatch() from peer interface (API) (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3271?vs=8124&id=8169 REVISION DETAIL https://phab.mercurial-scm.org/D3271 AFFECTED FILES mercurial/localrepo.py mercurial/repository.py mercurial/wireprotov1peer.py tests/test-batching.py tests/test-batching.py.out tests/test-wireproto.py CHANGE DETAILS diff --git a/tests/test-wireproto.py b/tests/test-wireproto.py --- a/tests/test-wireproto.py +++ b/tests/test-wireproto.py @@ -93,7 +93,9 @@ clt = clientpeer(srv, uimod.ui()) print(clt.greet(b"Foobar")) -b = clt.iterbatch() -list(map(b.greet, (b'Fo, =;:https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3292: bookmarks: use command executor for wire protocol commands
durin42 added a comment. btw, I don't love how this looks, it feels like we could probably revisit letting "normal" function calls work for one-shot requests like this, but let's do it later once we map the new wireproto world a little more REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3292 To: indygreg, #hg-reviewers Cc: durin42, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3318: repository: remove ipeercommands from ipeerbase
durin42 added a comment. I think we should probably avoid breaking that API until after we get remotefilelog in core? That'd at least be nice for me, because RFL is pretty invasive proto-wise. :( REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3318 To: indygreg, #hg-reviewers Cc: durin42, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 2 of 5] lfs: log information about Internal Server Errors reported in the Batch API
# HG changeset patch # User Matt Harbison # Date 1523637594 14400 # Fri Apr 13 12:39:54 2018 -0400 # Node ID 54c1ab20ed7fbf415d087e6e94ca273d172046e8 # Parent 1d394ac0efd4aa4f61f428fbac140fe57398f0b8 lfs: log information about Internal Server Errors reported in the Batch API Reporting a 500 and then not leaving any traces on the server seems like a receipe for frustration. The log writing was cargoculted from do_POST() in hgweb.server. That doesn't write directly to the wsgi.errors object, so it doesn't seem worth trying to refactor. It does seem like earlier stack frames are missing for some reason. diff --git a/hgext/lfs/wireprotolfsserver.py b/hgext/lfs/wireprotolfsserver.py --- a/hgext/lfs/wireprotolfsserver.py +++ b/hgext/lfs/wireprotolfsserver.py @@ -10,6 +10,7 @@ from __future__ import absolute_import import datetime import errno import json +import traceback from mercurial.hgweb import ( common as hgwebcommon, @@ -63,6 +64,23 @@ def _sethttperror(res, code, message=Non res.headers[b'Content-Type'] = b'text/plain; charset=utf-8' res.setbodybytes(b'') +def _logexception(req): +"""Write information about the current exception to wsgi.errors.""" +tb = traceback.format_exc() +# We need a native-string newline to poke in the log +# message, because we won't get a newline when using an +# r-string. This is the easy way out. +newline = chr(10) +errorlog = req.rawenv[r'wsgi.errors'] + +uri = '' +if req.apppath: +uri += req.apppath +uri += b'/' + req.dispatchpath + +errorlog.write(r"Exception happened while processing request " + r"'%s':%s%s" % (uri.decode('latin-1'), newline, tb)) + def _processbatchrequest(repo, req, res): """Handle a request for the Batch API, which is the gateway to granting file access. @@ -179,6 +197,8 @@ def _batchresponseobjects(req, objects, verifies = store.verify(oid) except IOError as inst: if inst.errno != errno.ENOENT: +_logexception(req) + rsp['error'] = { 'code': 500, 'message': inst.strerror or 'Internal Server Server' diff --git a/tests/test-lfs-serve-access.t b/tests/test-lfs-serve-access.t --- a/tests/test-lfs-serve-access.t +++ b/tests/test-lfs-serve-access.t @@ -291,6 +291,18 @@ Test a checksum failure during the proce $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d HTTP/1.1" 500 - (glob) $ grep -v ' File "' $TESTTMP/errors.log + $LOCALIP - - [$ERRDATE$] HG error: Exception happened while processing request '/.git/info/lfs/objects/batch': (glob) + $LOCALIP - - [$ERRDATE$] HG error: Traceback (most recent call last): (glob) + $LOCALIP - - [$ERRDATE$] HG error: verifies = store.verify(oid) (glob) + $LOCALIP - - [$ERRDATE$] HG error: raise IOError(errno.EIO, '%s: I/O error' % oid) (glob) + $LOCALIP - - [$ERRDATE$] HG error: IOError: [Errno 5] f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e: I/O error (glob) + $LOCALIP - - [$ERRDATE$] HG error: (glob) + $LOCALIP - - [$ERRDATE$] HG error: Exception happened while processing request '/.git/info/lfs/objects/batch': (glob) + $LOCALIP - - [$ERRDATE$] HG error: Traceback (most recent call last): (glob) + $LOCALIP - - [$ERRDATE$] HG error: verifies = store.verify(oid) (glob) + $LOCALIP - - [$ERRDATE$] HG error: raise IOError(errno.EIO, '%s: I/O error' % oid) (glob) + $LOCALIP - - [$ERRDATE$] HG error: IOError: [Errno 5] b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c: I/O error (glob) + $LOCALIP - - [$ERRDATE$] HG error: (glob) $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/.hg/lfs/objects/b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c': (glob) Traceback (most recent call last): self.do_write() ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 3 of 5] lfs: fix the inferred remote store path when using a --prefix
# HG changeset patch # User Matt Harbison # Date 1523643390 14400 # Fri Apr 13 14:16:30 2018 -0400 # Node ID a4c12789ef4bac6e736681ef8a08ccbe71fb5c41 # Parent 54c1ab20ed7fbf415d087e6e94ca273d172046e8 lfs: fix the inferred remote store path when using a --prefix This wasn't appending the '.git/info/lfs' path in this case. diff --git a/hgext/lfs/blobstore.py b/hgext/lfs/blobstore.py --- a/hgext/lfs/blobstore.py +++ b/hgext/lfs/blobstore.py @@ -561,7 +561,7 @@ def remote(repo, remote=None): if defaulturl.scheme in (b'http', b'https'): if defaulturl.path and defaulturl.path[:-1] != b'/': defaulturl.path += b'/' -defaulturl.path = defaulturl.path or b'' + b'.git/info/lfs' +defaulturl.path = (defaulturl.path or b'') + b'.git/info/lfs' url = util.url(bytes(defaulturl)) repo.ui.note(_('lfs: assuming remote store: %s\n') % url) diff --git a/tests/test-lfs-serve-access.t b/tests/test-lfs-serve-access.t --- a/tests/test-lfs-serve-access.t +++ b/tests/test-lfs-serve-access.t @@ -73,8 +73,7 @@ Blob URIs are correct when --prefix is u >-A $TESTTMP/access.log -E $TESTTMP/errors.log $ cat hg.pid >> $DAEMON_PIDS - $ hg --config lfs.url=http://localhost:$HGPORT/subdir/mount/point/.git/info/lfs \ - >clone --debug http://localhost:$HGPORT/subdir/mount/point cloned2 + $ hg clone --debug http://localhost:$HGPORT/subdir/mount/point cloned2 using http://localhost:$HGPORT/subdir/mount/point sending capabilities command query 1; heads @@ -104,6 +103,7 @@ Blob URIs are correct when --prefix is u resolving manifests branchmerge: False, force: False, partial: False ancestor: , local: +, remote: 525251863cad + lfs: assuming remote store: http://localhost:$HGPORT/subdir/mount/point/.git/info/lfs Status: 200 Content-Length: 371 Content-Type: application/vnd.git-lfs+json ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 4 of 5] lfs: gracefully handle aborts on the server when corrupt blobs are detected
# HG changeset patch # User Matt Harbison # Date 1519585633 18000 # Sun Feb 25 14:07:13 2018 -0500 # Node ID b795b3f8eca0aa917b10612dc95d46dee8ee7972 # Parent a4c12789ef4bac6e736681ef8a08ccbe71fb5c41 lfs: gracefully handle aborts on the server when corrupt blobs are detected The aborts weren't killing the server, but this seems cleaner. I'm not sure if it matters to handle the remaining IOError in the test like this, for consistency. The error code still feels wrong (especially if the client is trying to download a corrupt blob) but I don't see anything better in the RFCs, and this is already used elsewhere because the Batch API spec specifically mentioned this as a "Validation Error". diff --git a/hgext/lfs/blobstore.py b/hgext/lfs/blobstore.py --- a/hgext/lfs/blobstore.py +++ b/hgext/lfs/blobstore.py @@ -152,7 +152,8 @@ class local(object): realoid = sha256.hexdigest() if realoid != oid: -raise error.Abort(_('corrupt remote lfs object: %s') % oid) +raise LfsCorruptionError(_('corrupt remote lfs object: %s') + % oid) self._linktousercache(oid) @@ -526,8 +527,8 @@ def _deduplicate(pointers): def _verify(oid, content): realoid = hashlib.sha256(content).hexdigest() if realoid != oid: -raise error.Abort(_('detected corrupt lfs object: %s') % oid, - hint=_('run hg verify')) +raise LfsCorruptionError(_('detected corrupt lfs object: %s') % oid, + hint=_('run hg verify')) def remote(repo, remote=None): """remotestore factory. return a store in _storemap depending on config @@ -573,3 +574,8 @@ def remote(repo, remote=None): class LfsRemoteError(error.RevlogError): pass + +class LfsCorruptionError(error.Abort): +"""Raised when a corrupt blob is detected, aborting an operation + +It exists to allow specialized handling on the server side.""" diff --git a/hgext/lfs/wireprotolfsserver.py b/hgext/lfs/wireprotolfsserver.py --- a/hgext/lfs/wireprotolfsserver.py +++ b/hgext/lfs/wireprotolfsserver.py @@ -20,6 +20,8 @@ from mercurial import ( pycompat, ) +from . import blobstore + HTTP_OK = hgwebcommon.HTTP_OK HTTP_CREATED = hgwebcommon.HTTP_CREATED HTTP_BAD_REQUEST = hgwebcommon.HTTP_BAD_REQUEST @@ -280,13 +282,15 @@ def _processbasictransfer(repo, req, res # Content-Length, but what happens if a client sends less than it # says it will? -# TODO: download() will abort if the checksum fails. It should raise -# something checksum specific that can be caught here, and turned -# into an http code. -localstore.download(oid, req.bodyfh) +statusmessage = hgwebcommon.statusmessage +try: +localstore.download(oid, req.bodyfh) +res.status = statusmessage(HTTP_OK if existed else HTTP_CREATED) +except blobstore.LfsCorruptionError: +_logexception(req) -statusmessage = hgwebcommon.statusmessage -res.status = statusmessage(HTTP_OK if existed else HTTP_CREATED) +# XXX: Is this the right code? +res.status = statusmessage(422, b'corrupt blob') # There's no payload here, but this is the header that lfs-test-server # sends back. This eliminates some gratuitous test output conditionals. @@ -300,9 +304,18 @@ def _processbasictransfer(repo, req, res res.status = hgwebcommon.statusmessage(HTTP_OK) res.headers[b'Content-Type'] = b'application/octet-stream' -# TODO: figure out how to send back the file in chunks, instead of -# reading the whole thing. -res.setbodybytes(localstore.read(oid)) +try: +# TODO: figure out how to send back the file in chunks, instead of +# reading the whole thing. (Also figure out how to send back +# an error status if an IOError occurs after a partial write +# in that case. Here, everything is read before starting.) +res.setbodybytes(localstore.read(oid)) +except blobstore.LfsCorruptionError: +_logexception(req) + +# XXX: Is this the right code? +res.status = hgwebcommon.statusmessage(422, b'corrupt blob') +res.setbodybytes(b'') return True else: diff --git a/tests/test-lfs-serve-access.t b/tests/test-lfs-serve-access.t --- a/tests/test-lfs-serve-access.t +++ b/tests/test-lfs-serve-access.t @@ -236,7 +236,7 @@ Test a bad checksum sent by the client i $ hg -R client push http://localhost:$HGPORT1 pushing to http://localhost:$HGPORT1/ searching for changes - abort: HTTP error: HTTP Error 500: Internal Server Error (oid=b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c, action=upload)! + abort: HTTP error: HTTP Error 422: corrupt blob (oid=b5bb9d8014a0f9b
[PATCH 5 of 5] lfs: update the HTTP status codes in error cases
# HG changeset patch # User Matt Harbison # Date 1523651553 14400 # Fri Apr 13 16:32:33 2018 -0400 # Node ID f7f3443324c9ebcb065bebedfd54d4167eb673d4 # Parent b795b3f8eca0aa917b10612dc95d46dee8ee7972 lfs: update the HTTP status codes in error cases I'm not bothering with validating PUT requests (for now), because the spec doesn't explicitly call out a Content-Type (though the example transcript does use the sensible 'application/octet-stream'). diff --git a/hgext/lfs/wireprotolfsserver.py b/hgext/lfs/wireprotolfsserver.py --- a/hgext/lfs/wireprotolfsserver.py +++ b/hgext/lfs/wireprotolfsserver.py @@ -26,6 +26,9 @@ HTTP_OK = hgwebcommon.HTTP_OK HTTP_CREATED = hgwebcommon.HTTP_CREATED HTTP_BAD_REQUEST = hgwebcommon.HTTP_BAD_REQUEST HTTP_NOT_FOUND = hgwebcommon.HTTP_NOT_FOUND +HTTP_METHOD_NOT_ALLOWED = hgwebcommon.HTTP_METHOD_NOT_ALLOWED +HTTP_NOT_ACCEPTABLE = hgwebcommon.HTTP_NOT_ACCEPTABLE +HTTP_UNSUPPORTED_MEDIA_TYPE = hgwebcommon.HTTP_UNSUPPORTED_MEDIA_TYPE def handlewsgirequest(orig, rctx, req, res, checkperm): """Wrap wireprotoserver.handlewsgirequest() to possibly process an LFS @@ -109,12 +112,16 @@ def _processbatchrequest(repo, req, res) # "operation": "upload" # } -if (req.method != b'POST' -or req.headers[b'Content-Type'] != b'application/vnd.git-lfs+json' -or req.headers[b'Accept'] != b'application/vnd.git-lfs+json'): -# TODO: figure out what the proper handling for a bad request to the -# Batch API is. -_sethttperror(res, HTTP_BAD_REQUEST, b'Invalid Batch API request') +if req.method != b'POST': +_sethttperror(res, HTTP_METHOD_NOT_ALLOWED) +return True + +if req.headers[b'Content-Type'] != b'application/vnd.git-lfs+json': +_sethttperror(res, HTTP_UNSUPPORTED_MEDIA_TYPE) +return True + +if req.headers[b'Accept'] != b'application/vnd.git-lfs+json': +_sethttperror(res, HTTP_NOT_ACCEPTABLE) return True # XXX: specify an encoding? @@ -319,6 +326,6 @@ def _processbasictransfer(repo, req, res return True else: -_sethttperror(res, HTTP_BAD_REQUEST, +_sethttperror(res, HTTP_METHOD_NOT_ALLOWED, message=b'Unsupported LFS transfer method: %s' % method) return True diff --git a/mercurial/hgweb/common.py b/mercurial/hgweb/common.py --- a/mercurial/hgweb/common.py +++ b/mercurial/hgweb/common.py @@ -30,6 +30,8 @@ HTTP_UNAUTHORIZED = 401 HTTP_FORBIDDEN = 403 HTTP_NOT_FOUND = 404 HTTP_METHOD_NOT_ALLOWED = 405 +HTTP_NOT_ACCEPTABLE = 406 +HTTP_UNSUPPORTED_MEDIA_TYPE = 415 HTTP_SERVER_ERROR = 500 ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 5] test-lfs: add tests to force server error path coverage
# HG changeset patch # User Matt Harbison # Date 1523119701 14400 # Sat Apr 07 12:48:21 2018 -0400 # Node ID 1d394ac0efd4aa4f61f428fbac140fe57398f0b8 # Parent bfdd20d22a86edc318493b4da84a1d7ff4ef98f2 test-lfs: add tests to force server error path coverage The tests are somewhat fragile in that the extension that forces the errors is counting how many times some of the functions are being called, so it depends heavily on the content of the repo. Maybe we can do something clever like load an extension on the client, and have it send over instructions in the HTTP header how to fail. (I'm trying to avoid killing and restarting the server, because Windows seems to have issues with doing that a lot.) But I'd rather fix issues than polish tests before the freeze. diff --git a/tests/test-lfs-serve-access.t b/tests/test-lfs-serve-access.t --- a/tests/test-lfs-serve-access.t +++ b/tests/test-lfs-serve-access.t @@ -4,7 +4,6 @@ > [extensions] > lfs= > [lfs] - > url=http://localhost:$HGPORT/.git/info/lfs > track=all() > [web] > push_ssl = False @@ -149,3 +148,189 @@ Blob URIs are correct when --prefix is u $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=&heads=525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob) $LOCALIP - - [$LOGDATE$] "POST /subdir/mount/point/.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob) $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point/.hg/lfs/objects/f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e HTTP/1.1" 200 - (glob) + + $ cat >> $TESTTMP/lfsstoreerror.py < import errno + > from hgext.lfs import blobstore + > + > _numverifies = 0 + > _readerr = True + > + > def reposetup(ui, repo): + > # Nothing to do with a remote repo + > if not repo.local(): + > return + > + > store = repo.svfs.lfslocalblobstore + > class badstore(store.__class__): + > def download(self, oid, src): + > '''Called in the server to handle reading from the client in a + > PUT request.''' + > origread = src.read + > def _badread(nbytes): + > # Simulate bad data/checksum failure from the client + > return b'0' * len(origread(nbytes)) + > src.read = _badread + > super(badstore, self).download(oid, src) + > + > def _read(self, vfs, oid, verify): + > '''Called in the server to read data for a GET request, and then + > calls self._verify() on it before returning.''' + > global _readerr + > # One time simulation of a read error + > if _readerr: + > _readerr = False + > raise IOError(errno.EIO, '%s: I/O error' % oid) + > # Simulate corrupt content on client download + > blobstore._verify(oid, 'dummy content') + > + > def verify(self, oid): + > '''Called in the server to populate the Batch API response, + > letting the client re-upload if the file is corrupt.''' + > # Fail verify in Batch API for one clone command and one push + > # command with an IOError. Then let it through to access other + > # functions. Checksum failure is tested elsewhere. + > global _numverifies + > _numverifies += 1 + > if _numverifies <= 2: + > raise IOError(errno.EIO, '%s: I/O error' % oid) + > return super(badstore, self).verify(oid) + > + > store.__class__ = badstore + > EOF + + $ rm -rf `hg config lfs.usercache` + $ rm -f $TESTTMP/access.log $TESTTMP/errors.log + $ hg --config "lfs.usercache=$TESTTMP/servercache" \ + >--config extensions.lfsstoreerror=$TESTTMP/lfsstoreerror.py \ + >-R server serve -d \ + >-p $HGPORT1 --pid-file=hg.pid -A $TESTTMP/access.log -E $TESTTMP/errors.log + $ cat hg.pid >> $DAEMON_PIDS + +Test an I/O error in localstore.verify() (Batch API) with GET + + $ hg clone http://localhost:$HGPORT1 httpclone2 + requesting all changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + new changesets 525251863cad + updating to branch default + abort: LFS server error for "lfs.bin": Internal server error! + [255] + +Test an I/O error in localstore.verify() (Batch API) with PUT + + $ echo foo > client/lfs.bin + $ hg -R client ci -
D3318: repository: remove ipeercommands from ipeerbase
indygreg added a comment. In https://phab.mercurial-scm.org/D3318#53227, @durin42 wrote: > I think we should probably avoid breaking that API until after we get remotefilelog in core? That'd at least be nice for me, because RFL is pretty invasive proto-wise. :( I have little desire to purge this until at least next release. Minimal gains from it. Wins come from core and new peers being able to use new API. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3318 To: indygreg, #hg-reviewers, durin42 Cc: durin42, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3288: discovery: use command executor interface
This revision was automatically updated to reflect the committed changes. Closed by commit rHG1964d2d1f421: discovery: use command executor interface (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3288?vs=8127&id=8173 REVISION DETAIL https://phab.mercurial-scm.org/D3288 AFFECTED FILES mercurial/discovery.py CHANGE DETAILS diff --git a/mercurial/discovery.py b/mercurial/discovery.py --- a/mercurial/discovery.py +++ b/mercurial/discovery.py @@ -203,7 +203,10 @@ headssum = {} # A. Create set of branches involved in the push. branches = set(repo[n].branch() for n in outgoing.missing) -remotemap = remote.branchmap() + +with remote.commandexecutor() as e: +remotemap = e.callcommand('branchmap', {}).result() + newbranches = branches - set(remotemap) branches.difference_update(newbranches) @@ -287,7 +290,12 @@ repo = pushop.repo.unfiltered() remote = pushop.remote localbookmarks = repo._bookmarks -remotebookmarks = remote.listkeys('bookmarks') + +with remote.commandexecutor() as e: +remotebookmarks = e.callcommand('listkeys', { +'namespace': 'bookmarks', +}).result() + bookmarkedheads = set() # internal config: bookmarks.pushing To: indygreg, #hg-reviewers, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3272: treediscovery: switch to command executor interface
This revision was automatically updated to reflect the committed changes. Closed by commit rHG0ed11f9368fd: treediscovery: switch to command executor interface (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3272?vs=8125&id=8170 REVISION DETAIL https://phab.mercurial-scm.org/D3272 AFFECTED FILES mercurial/treediscovery.py CHANGE DETAILS diff --git a/mercurial/treediscovery.py b/mercurial/treediscovery.py --- a/mercurial/treediscovery.py +++ b/mercurial/treediscovery.py @@ -36,7 +36,8 @@ base = set() if not heads: -heads = remote.heads() +with remote.commandexecutor() as e: +heads = e.callcommand('heads', {}).result() if repo.changelog.tip() == nullid: base.add(nullid) @@ -65,7 +66,10 @@ # a 'branch' here is a linear segment of history, with four parts: # head, root, first parent, second parent # (a branch always has two parents (or none) by definition) -unknown = collections.deque(remote.branches(unknown)) +with remote.commandexecutor() as e: +branches = e.callcommand('branches', {'nodes': unknown}).result() + +unknown = collections.deque(branches) while unknown: r = [] while unknown: @@ -107,7 +111,12 @@ repo.ui.debug("request %d: %s\n" % (reqcnt, " ".join(map(short, r for p in xrange(0, len(r), 10): -for b in remote.branches(r[p:p + 10]): +with remote.commandexecutor() as e: +branches = e.callcommand('branches', { +'nodes': r[p:p + 10], +}).result() + +for b in branches: repo.ui.debug("received %s:%s\n" % (short(b[0]), short(b[1]))) unknown.append(b) @@ -117,7 +126,11 @@ newsearch = [] reqcnt += 1 repo.ui.progress(_('searching'), reqcnt, unit=_('queries')) -for n, l in zip(search, remote.between(search)): + +with remote.commandexecutor() as e: +between = e.callcommand('between', {'pairs': search}).result() + +for n, l in zip(search, between): l.append(n[1]) p = n[0] f = 1 To: indygreg, #hg-reviewers, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3289: streamclone: use command executor for wire protocol commands
This revision was automatically updated to reflect the committed changes. Closed by commit rHG65b86ee69383: streamclone: use command executor for wire protocol commands (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3289?vs=8128&id=8174 REVISION DETAIL https://phab.mercurial-scm.org/D3289 AFFECTED FILES mercurial/streamclone.py CHANGE DETAILS diff --git a/mercurial/streamclone.py b/mercurial/streamclone.py --- a/mercurial/streamclone.py +++ b/mercurial/streamclone.py @@ -126,11 +126,18 @@ # creation. rbranchmap = None if remote.capable('branchmap'): -rbranchmap = remote.branchmap() +with remote.commandexecutor() as e: +rbranchmap = e.callcommand('branchmap', {}).result() repo.ui.status(_('streaming all changes\n')) -fp = remote.stream_out() +with remote.commandexecutor() as e: +fp = e.callcommand('stream_out', {}).result() + +# TODO strictly speaking, this code should all be inside the context +# manager because the context manager is supposed to ensure all wire state +# is flushed when exiting. But the legacy peers don't do this, so it +# doesn't matter. l = fp.readline() try: resp = int(l) To: indygreg, #hg-reviewers, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3290: logexchange: use command executor for wire protocol commands
This revision was automatically updated to reflect the committed changes. Closed by commit rHG0e50dda7e9c1: logexchange: use command executor for wire protocol commands (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3290?vs=8129&id=8175 REVISION DETAIL https://phab.mercurial-scm.org/D3290 AFFECTED FILES mercurial/logexchange.py CHANGE DETAILS diff --git a/mercurial/logexchange.py b/mercurial/logexchange.py --- a/mercurial/logexchange.py +++ b/mercurial/logexchange.py @@ -127,14 +127,23 @@ remoterepo is the peer instance """ remotepath = activepath(localrepo, remoterepo) -bookmarks = remoterepo.listkeys('bookmarks') + +with remoterepo.commandexecutor() as e: +bookmarks = e.callcommand('listkeys', { +'namespace': 'bookmarks', +}).result() + # on a push, we don't want to keep obsolete heads since # they won't show up as heads on the next pull, so we # remove them here otherwise we would require the user # to issue a pull to refresh the storage bmap = {} repo = localrepo.unfiltered() -for branch, nodes in remoterepo.branchmap().iteritems(): + +with remoterepo.commandexecutor() as e: +branchmap = e.callcommand('branchmap', {}).result() + +for branch, nodes in branchmap.iteritems(): bmap[branch] = [] for node in nodes: if node in repo and not repo[node].obsolete(): To: indygreg, #hg-reviewers, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3291: hg: use command executor for wire protocol commands
This revision was automatically updated to reflect the committed changes. Closed by commit rHGce8828217369: hg: use command executor for wire protocol commands (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3291?vs=8130&id=8176 REVISION DETAIL https://phab.mercurial-scm.org/D3291 AFFECTED FILES mercurial/hg.py CHANGE DETAILS diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -81,7 +81,9 @@ raise error.Abort(_("remote branch lookup not supported")) revs.append(hashbranch) return revs, revs[0] -branchmap = peer.branchmap() + +with peer.commandexecutor() as e: +branchmap = e.callcommand('branchmap', {}).result() def primary(branch): if branch == '.': @@ -421,7 +423,15 @@ raise error.Abort(_("src repository does not support " "revision lookup and so doesn't " "support clone by revision")) -revs = [srcpeer.lookup(r) for r in rev] + +# TODO this is batchable. +remoterevs = [] +for r in rev: +with srcpeer.commandexecutor() as e: +remoterevs.append(e.callcommand('lookup', { +'key': r, +}).result()) +revs = remoterevs # Obtain a lock before checking for or cloning the pooled repo otherwise # 2 clients may race creating or populating it. @@ -567,7 +577,11 @@ # raises RepoLookupError if revision 0 is filtered or otherwise # not available. If we fail to resolve, sharing is not enabled. try: -rootnode = srcpeer.lookup('0') +with srcpeer.commandexecutor() as e: +rootnode = e.callcommand('lookup', { +'key': '0', +}).result() + if rootnode != node.nullid: sharepath = os.path.join(sharepool, node.hex(rootnode)) else: @@ -663,7 +677,16 @@ raise error.Abort(_("src repository does not support " "revision lookup and so doesn't " "support clone by revision")) -revs = [srcpeer.lookup(r) for r in revs] + +# TODO this is batchable. +remoterevs = [] +for rev in revs: +with srcpeer.commandexecutor() as e: +remoterevs.append(e.callcommand('lookup', { +'key': rev, +}).result()) +revs = remoterevs + checkout = revs[0] else: revs = None @@ -705,7 +728,11 @@ if update: if update is not True: -checkout = srcpeer.lookup(update) +with srcpeer.commandexecutor() as e: +checkout = e.callcommand('lookup', { +'key': update, +}).result() + uprev = None status = None if checkout is not None: To: indygreg, #hg-reviewers, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3287: discovery: don't redundantly call branchmap
This revision was automatically updated to reflect the committed changes. Closed by commit rHG330ada7e8ea5: discovery: don't redundantly call branchmap (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3287?vs=8080&id=8172 REVISION DETAIL https://phab.mercurial-scm.org/D3287 AFFECTED FILES mercurial/discovery.py tests/test-http-bundle1.t tests/test-http.t tests/test-treediscovery.t CHANGE DETAILS diff --git a/tests/test-treediscovery.t b/tests/test-treediscovery.t --- a/tests/test-treediscovery.t +++ b/tests/test-treediscovery.t @@ -537,7 +537,6 @@ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull - "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=686173686564+1827a5bb63e602382eb89dd58f2ac9f3b007ad91* (glob) "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull diff --git a/tests/test-http.t b/tests/test-http.t --- a/tests/test-http.t +++ b/tests/test-http.t @@ -296,11 +296,6 @@ devel-peer-request: Vary X-HgProto-1 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull devel-peer-request: finished in *. seconds (200) (glob) - sending branchmap command - devel-peer-request: GET http://localhost:$HGPORT2/?cmd=branchmap - devel-peer-request: Vary X-HgProto-1 - devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull - devel-peer-request: finished in *. seconds (200) (glob) preparing listkeys for "bookmarks" sending listkeys command devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys @@ -395,7 +390,6 @@ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull - "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365* (glob) "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull @@ -405,7 +399,6 @@ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull - "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull diff --git a/tests/test-http-bundle1.t b/tests/test-http-bundle1.t --- a/tests/test-http-bundle1.t +++ b/tests/test-http-bundle1.t @@ -316,7 +316,6 @@ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull - "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=686173686564+5eb5abfefeea63c80dd7553bcc3783f37e0c5524* (glob) "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull diff --git a/mercurial/discovery.py b/mercurial/discovery.py --- a/mercurial/discovery.py +++ b/mercurial/discovery.py @@ -209,7 +209,7 @@
D3294: bundlerepo: use command executor for wire protocol commands
This revision was automatically updated to reflect the committed changes. Closed by commit rHG1aa4d646d0de: bundlerepo: use command executor for wire protocol commands (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3294?vs=8133&id=8179 REVISION DETAIL https://phab.mercurial-scm.org/D3294 AFFECTED FILES mercurial/bundlerepo.py CHANGE DETAILS diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py +++ b/mercurial/bundlerepo.py @@ -540,17 +540,26 @@ and peer.capable('getbundle') and peer.capable('bundle2')) if canbundle2: -kwargs = {} -kwargs[r'common'] = common -kwargs[r'heads'] = rheads -kwargs[r'bundlecaps'] = exchange.caps20to10(repo, role='client') -kwargs[r'cg'] = True -b2 = peer.getbundle('incoming', **kwargs) -fname = bundle = changegroup.writechunks(ui, b2._forwardchunks(), - bundlename) +with peer.commandexecutor() as e: +b2 = e.callcommand('getbundle', { +'source': 'incoming', +'common': common, +'heads': rheads, +'bundlecaps': exchange.caps20to10(repo, role='client'), +'cg': True, +}).result() + +fname = bundle = changegroup.writechunks(ui, + b2._forwardchunks(), + bundlename) else: if peer.capable('getbundle'): -cg = peer.getbundle('incoming', common=common, heads=rheads) +with peer.commandexecutor() as e: +cg = e.callcommand('getbundle', { +'source': 'incoming', +'common': common, +'heads': rheads, +}).result() elif onlyheads is None and not peer.capable('changegroupsubset'): # compat with older servers when pulling all remote heads @@ -594,7 +603,11 @@ if bundlerepo: reponodes = [ctx.node() for ctx in bundlerepo[bundlerepo.firstnewrev:]] -remotephases = peer.listkeys('phases') + +with peer.commandexecutor() as e: +remotephases = e.callcommand('listkeys', { +'namespace': 'phases', +}).result() pullop = exchange.pulloperation(bundlerepo, peer, heads=reponodes) pullop.trmanager = bundletransactionmanager() To: indygreg, #hg-reviewers, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3293: bundlerepo: rename "other" to "peer"
This revision was automatically updated to reflect the committed changes. Closed by commit rHGd959277ff1b5: bundlerepo: rename "other" to "peer" (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3293?vs=8132&id=8178 REVISION DETAIL https://phab.mercurial-scm.org/D3293 AFFECTED FILES mercurial/bundlerepo.py CHANGE DETAILS diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py +++ b/mercurial/bundlerepo.py @@ -492,9 +492,9 @@ def release(self): raise NotImplementedError -def getremotechanges(ui, repo, other, onlyheads=None, bundlename=None, +def getremotechanges(ui, repo, peer, onlyheads=None, bundlename=None, force=False): -'''obtains a bundle of changes incoming from other +'''obtains a bundle of changes incoming from peer "onlyheads" restricts the returned changes to those reachable from the specified heads. @@ -507,62 +507,62 @@ "local" is a local repo from which to obtain the actual incoming changesets; it is a bundlerepo for the obtained bundle when the - original "other" is remote. + original "peer" is remote. "csets" lists the incoming changeset node ids. "cleanupfn" must be called without arguments when you're done processing - the changes; it closes both the original "other" and the one returned + the changes; it closes both the original "peer" and the one returned here. ''' -tmp = discovery.findcommonincoming(repo, other, heads=onlyheads, +tmp = discovery.findcommonincoming(repo, peer, heads=onlyheads, force=force) common, incoming, rheads = tmp if not incoming: try: if bundlename: os.unlink(bundlename) except OSError: pass -return repo, [], other.close +return repo, [], peer.close commonset = set(common) rheads = [x for x in rheads if x not in commonset] bundle = None bundlerepo = None -localrepo = other.local() +localrepo = peer.local() if bundlename or not localrepo: -# create a bundle (uncompressed if other repo is not local) +# create a bundle (uncompressed if peer repo is not local) # developer config: devel.legacy.exchange legexc = ui.configlist('devel', 'legacy.exchange') forcebundle1 = 'bundle2' not in legexc and 'bundle1' in legexc canbundle2 = (not forcebundle1 - and other.capable('getbundle') - and other.capable('bundle2')) + and peer.capable('getbundle') + and peer.capable('bundle2')) if canbundle2: kwargs = {} kwargs[r'common'] = common kwargs[r'heads'] = rheads kwargs[r'bundlecaps'] = exchange.caps20to10(repo, role='client') kwargs[r'cg'] = True -b2 = other.getbundle('incoming', **kwargs) +b2 = peer.getbundle('incoming', **kwargs) fname = bundle = changegroup.writechunks(ui, b2._forwardchunks(), bundlename) else: -if other.capable('getbundle'): -cg = other.getbundle('incoming', common=common, heads=rheads) -elif onlyheads is None and not other.capable('changegroupsubset'): +if peer.capable('getbundle'): +cg = peer.getbundle('incoming', common=common, heads=rheads) +elif onlyheads is None and not peer.capable('changegroupsubset'): # compat with older servers when pulling all remote heads -with other.commandexecutor() as e: +with peer.commandexecutor() as e: cg = e.callcommand('changegroup', { 'nodes': incoming, 'source': 'incoming', }).result() rheads = None else: -with other.commandexecutor() as e: +with peer.commandexecutor() as e: cg = e.callcommand('changegroupsubset', { 'bases': incoming, 'heads': rheads, @@ -582,7 +582,7 @@ # use the created uncompressed bundlerepo localrepo = bundlerepo = bundlerepository(repo.baseui, repo.root, fname) -# this repo contains local and other now, so filter out local again +# this repo contains local and peer now, so filter out local again common = repo.heads() if localrepo: # Part of common may be remotely filtered @@ -594,17 +594,17 @@ if bundlerepo: reponodes = [ctx.node() for ctx in bundlerepo[bundlerepo.firstnewrev:]] -remotep
D3273: wireproto: convert legacy commands to command executor
This revision was automatically updated to reflect the committed changes. Closed by commit rHGcc8c06835097: wireproto: convert legacy commands to command executor (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3273?vs=8126&id=8171 REVISION DETAIL https://phab.mercurial-scm.org/D3273 AFFECTED FILES mercurial/bundlerepo.py mercurial/exchange.py mercurial/localrepo.py mercurial/repository.py mercurial/wireprotov1peer.py tests/test-check-interfaces.py CHANGE DETAILS diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py --- a/tests/test-check-interfaces.py +++ b/tests/test-check-interfaces.py @@ -89,8 +89,7 @@ checkzobject(badpeer()) -ziverify.verifyClass(repository.ipeerbaselegacycommands, - httppeer.httppeer) +ziverify.verifyClass(repository.ipeerbase, httppeer.httppeer) checkzobject(httppeer.httppeer(None, None, None, dummyopener(), None, None)) ziverify.verifyClass(repository.ipeerconnection, @@ -111,13 +110,11 @@ wireprotov1peer.peerexecutor) checkzobject(wireprotov1peer.peerexecutor(None)) -ziverify.verifyClass(repository.ipeerbaselegacycommands, - sshpeer.sshv1peer) +ziverify.verifyClass(repository.ipeerbase, sshpeer.sshv1peer) checkzobject(sshpeer.sshv1peer(ui, 'ssh://localhost/foo', None, dummypipe(), dummypipe(), None, None)) -ziverify.verifyClass(repository.ipeerbaselegacycommands, - sshpeer.sshv2peer) +ziverify.verifyClass(repository.ipeerbase, sshpeer.sshv2peer) checkzobject(sshpeer.sshv2peer(ui, 'ssh://localhost/foo', None, dummypipe(), dummypipe(), None, None)) diff --git a/mercurial/wireprotov1peer.py b/mercurial/wireprotov1peer.py --- a/mercurial/wireprotov1peer.py +++ b/mercurial/wireprotov1peer.py @@ -308,7 +308,8 @@ else: f.set_result(result) -class wirepeer(repository.legacypeer): +@zi.implementer(repository.ipeerlegacycommands) +class wirepeer(repository.peer): """Client-side interface for communicating with a peer repository. Methods commonly call wire protocol commands of the same name. @@ -502,12 +503,12 @@ self._abort(error.ResponseError(_("unexpected response:"), d)) return r -def changegroup(self, nodes, kind): +def changegroup(self, nodes, source): n = wireprototypes.encodelist(nodes) f = self._callcompressable("changegroup", roots=n) return changegroupmod.cg1unpacker(f, 'UN') -def changegroupsubset(self, bases, heads, kind): +def changegroupsubset(self, bases, heads, source): self.requirecap('changegroupsubset', _('look up remote changes')) bases = wireprototypes.encodelist(bases) heads = wireprototypes.encodelist(heads) diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -190,10 +190,10 @@ Returns an iterable of iterables with the resolved values for each node. """ -def changegroup(nodes, kind): +def changegroup(nodes, source): """Obtain a changegroup with data for descendants of specified nodes.""" -def changegroupsubset(bases, heads, kind): +def changegroupsubset(bases, heads, source): pass class ipeercommandexecutor(zi.Interface): @@ -285,9 +285,6 @@ All peer instances must conform to this interface. """ -class ipeerbaselegacycommands(ipeerbase, ipeerlegacycommands): -"""Unified peer interface that supports legacy commands.""" - @zi.implementer(ipeerbase) class peer(object): """Base class for peer repositories.""" @@ -312,10 +309,6 @@ _('cannot %s; remote repository does not support the %r ' 'capability') % (purpose, name)) -@zi.implementer(ipeerbaselegacycommands) -class legacypeer(peer): -"""peer but with support for legacy wire protocol commands.""" - class ifilerevisionssequence(zi.Interface): """Contains index data for all revisions of a file. diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -320,7 +320,8 @@ # End of peer interface. -class locallegacypeer(repository.legacypeer, localpeer): +@zi.implementer(repository.ipeerlegacycommands) +class locallegacypeer(localpeer): '''peer extension which implements legacy methods too; used for tests with restricted capabilities''' @@ -335,8 +336,8 @@ def branches(self, nodes): return self._repo.branches(nodes) -def changegroup(self, basenodes, source): -outgoing = discovery.outgoing(self._repo, missingroots=basenodes, +def changegroup(self, nodes, source): +outgoing = discovery.outgoing(self._repo, missingro
D3292: bookmarks: use command executor for wire protocol commands
This revision was automatically updated to reflect the committed changes. Closed by commit rHGadd129811176: bookmarks: use command executor for wire protocol commands (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D3292?vs=8131&id=8177 REVISION DETAIL https://phab.mercurial-scm.org/D3292 AFFECTED FILES mercurial/bookmarks.py CHANGE DETAILS diff --git a/mercurial/bookmarks.py b/mercurial/bookmarks.py --- a/mercurial/bookmarks.py +++ b/mercurial/bookmarks.py @@ -646,12 +646,16 @@ writer(msg) localmarks.applychanges(repo, tr, changes) -def incoming(ui, repo, other): +def incoming(ui, repo, peer): '''Show bookmarks incoming from other to repo ''' ui.status(_("searching for changed bookmarks\n")) -remotemarks = unhexlifybookmarks(other.listkeys('bookmarks')) +with peer.commandexecutor() as e: +remotemarks = unhexlifybookmarks(e.callcommand('listkeys', { +'namespace': 'bookmarks', +}).result()) + r = comparebookmarks(repo, remotemarks, repo._bookmarks) addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r @@ -733,12 +737,16 @@ return 0 -def summary(repo, other): +def summary(repo, peer): '''Compare bookmarks between repo and other for "hg summary" output This returns "(# of incoming, # of outgoing)" tuple. ''' -remotemarks = unhexlifybookmarks(other.listkeys('bookmarks')) +with peer.commandexecutor() as e: +remotemarks = unhexlifybookmarks(e.callcommand('listkeys', { +'namespace': 'bookmarks', +}).result()) + r = comparebookmarks(repo, remotemarks, repo._bookmarks) addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r return (len(addsrc), len(adddst)) To: indygreg, #hg-reviewers, durin42 Cc: durin42, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D3292: bookmarks: use command executor for wire protocol commands
indygreg added a comment. In https://phab.mercurial-scm.org/D3292#53207, @durin42 wrote: > btw, I don't love how this looks, it feels like we could probably revisit letting "normal" function calls work for one-shot requests like this, but let's do it later once we map the new wireproto world a little more I’m not opposed to providing a helper method that wraps and makes the consumer simpler. Where my mind is at is that proto v2 commands will be smaller and we’ll issue more of them. I wanted to make batching and transfer semantics for half-duplex connections explicit. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3292 To: indygreg, #hg-reviewers, durin42 Cc: durin42, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel