##// END OF EJS Templates
repository: define interface for local repositories...
repository: define interface for local repositories Per discussions on the mailing list and at the 4.4 and 4.6 sprints, we want to start defining interfaces for local repository primitives so that we a) have a better idea of what the formal interface for various types is b) can more easily introduce alternate implementations of various components (e.g. in Rust). We have previously implemented interfaces that declare the peer and wire protocol APIs using the abc module. This commit introduces a monolithic interface for the localrepository class. It uses zope.interface - not abc - for defining and declaring the interface. The newly defined "completelocalrepository" interface is objectively horrible. It is based on what is actually in localrepository and doesn't represent a reasonable interface definition IMO. There's lots of... unwanted garbage in the interface. In other words, it reflects the horrible state of the localrepository "god object." But this is fine: a goal of this commit is to get the interface defined so that we have an interface. Future commits can refactor the interface into sub-interfaces, remove unwanted public attributes, etc. I attempted to define reasonable docstrings for the various interface members. But there are so many of them and I didn't know what some are used for. So I was lazy in a number of places and didn't write docstrings or detailed usage docs. Also, the members of the interface are defined in the order they are declared in localrepo.py. This revealed that the grouping of things in localrepo.py is... odd. The localrepository class now declares that it implements our newly defined interface. Unlike abc, zope.interface doesn't check interface conformance at type creation time (abc uses __metaclass__ magic to validate interface conformance when a type is created - usually at module import time). It does provide some functions for validating class and object conformance with declared interfaces. We add these checks to test-check-interfaces.py. We /could/ validate at run-time. But we hold off for now. (I'm a bit scared of doing that because of the various ways extensions monkeypatch repo instances.) After this commit, test-check-interfaces.py will fail if the set of public attributes on the localrepository class or instances change without corresponding updates to the interface. This is by design. Differential Revision: https://phab.mercurial-scm.org/D2933

File last commit:

r37121:be3f33f5 default
r37198:0dfb5672 default
Show More
webutil.py
703 lines | 22.0 KiB | text/x-python | PythonLexer
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 # hgweb/webutil.py - utility library for the web interface.
#
# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Yuya Nishihara
hgweb: use absolute_import
r27046 from __future__ import absolute_import
import copy
import difflib
import os
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162 import re
Yuya Nishihara
hgweb: use absolute_import
r27046
from ..i18n import _
from ..node import hex, nullid, short
from .common import (
ErrorResponse,
Denis Laxalde
hgweb: handle a "linerange" request parameter in filelog command...
r31665 HTTP_BAD_REQUEST,
Yuya Nishihara
hgweb: use absolute_import
r27046 HTTP_NOT_FOUND,
paritygen,
)
from .. import (
context,
error,
match,
Denis Laxalde
mdiff: add a hunkinrange helper function...
r31808 mdiff,
av6
hgweb: explain instabilities of unstable changesets
r36973 obsutil,
Yuya Nishihara
hgweb: use absolute_import
r27046 patch,
pathutil,
Augie Fackler
webutil: use pycompat.bytestr() instead of str()...
r34808 pycompat,
Yuya Nishihara
hgweb: use absolute_import
r27046 templatefilters,
av6
hgweb: display fate of obsolete changesets...
r35501 templatekw,
Yuya Nishihara
hgweb: use absolute_import
r27046 ui as uimod,
util,
)
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 from ..utils import (
stringutil,
)
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393 def up(p):
Augie Fackler
hgweb: fix up trailing slash detection on Python 3...
r36731 if p[0:1] != "/":
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393 p = "/" + p
Augie Fackler
hgweb: fix up trailing slash detection on Python 3...
r36731 if p[-1:] == "/":
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393 p = p[:-1]
up = os.path.dirname(p)
if up == "/":
return "/"
return up + "/"
Pierre-Yves David
hgweb: better names for _navseq arguments...
r18391 def _navseq(step, firststep=None):
if firststep:
yield firststep
if firststep >= 20 and firststep <= 40:
Pierre-Yves David
hgweb: ensure _navseq yield strictly increasing numbers...
r18392 firststep = 50
yield firststep
assert step > 0
assert firststep > 0
while step <= firststep:
step *= 10
Pierre-Yves David
hgweb: drop recursivity in _navseq...
r18390 while True:
Pierre-Yves David
hgweb: better names for _navseq arguments...
r18391 yield 1 * step
yield 3 * step
step *= 10
Pierre-Yves David
hgweb: move the `seq` function out of the revnavgen scope...
r18389
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 class revnav(object):
Pierre-Yves David
hgweb: document the revnavgen function
r18320
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409 def __init__(self, repo):
Pierre-Yves David
hgweb: pass nodefunc to the revnav object...
r18404 """Navigation generation object
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409 :repo: repo object we generate nav for
Pierre-Yves David
hgweb: pass nodefunc to the revnav object...
r18404 """
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409 # used for hex generation
self._revlog = repo.changelog
Pierre-Yves David
hgweb: pass nodefunc to the revnav object...
r18404
Pierre-Yves David
hgweb: simplify the handling of empty repo...
r18406 def __nonzero__(self):
"""return True if any revision to navigate over"""
Pierre-Yves David
hgweb: handle filtered "0" rev in navigation...
r19094 return self._first() is not None
Gregory Szorc
py3: add __bool__ to every class defining __nonzero__...
r31476 __bool__ = __nonzero__
Pierre-Yves David
hgweb: handle filtered "0" rev in navigation...
r19094 def _first(self):
"""return the minimum non-filtered changeset or None"""
try:
timeless
py3: convert to next() function...
r29216 return next(iter(self._revlog))
Pierre-Yves David
hgweb: handle filtered "0" rev in navigation...
r19094 except StopIteration:
return None
Pierre-Yves David
hgweb: simplify the handling of empty repo...
r18406
Pierre-Yves David
hgweb: move hex creation into an object method...
r18405 def hex(self, rev):
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409 return hex(self._revlog.node(rev))
Pierre-Yves David
hgweb: move hex creation into an object method...
r18405
Pierre-Yves David
hgweb: pass nodefunc to the revnav object...
r18404 def gen(self, pos, pagelen, limit):
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 """computes label and revision id for navigation link
Pierre-Yves David
hgweb: document the revnavgen function
r18320
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 :pos: is the revision relative to which we generate navigation.
:pagelen: the size of each navigation page
:limit: how far shall we link
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 The return is:
- a single element tuple
- containing a dictionary with a `before` and `after` key
- values are generator functions taking arbitrary number of kwargs
- yield items are dictionaries with `label` and `node` keys
"""
Pierre-Yves David
hgweb: simplify the handling of empty repo...
r18406 if not self:
# empty repo
return ({'before': (), 'after': ()},)
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 targets = []
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 for f in _navseq(1, pagelen):
if f > limit:
break
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 targets.append(pos + f)
targets.append(pos - f)
targets.sort()
Pierre-Yves David
hgweb: handle filtered "0" rev in navigation...
r19094 first = self._first()
navbefore = [("(%i)" % first, self.hex(first))]
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 navafter = []
for rev in targets:
Pierre-Yves David
hgweb: ignore filtered revision in revnav...
r18426 if rev not in self._revlog:
continue
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 if pos < rev < limit:
Pierre-Yves David
hgweb: fix navigation label (issue3792)...
r18503 navafter.append(("+%d" % abs(rev - pos), self.hex(rev)))
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 if 0 < rev < pos:
Pierre-Yves David
hgweb: fix navigation label (issue3792)...
r18503 navbefore.append(("-%d" % abs(rev - pos), self.hex(rev)))
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425
Nicolas Dumazet
hgweb: changenav: separate pages before and after the current position...
r10254
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 navafter.append(("tip", "tip"))
data = lambda i: {"label": i[0], "node": i[1]}
return ({'before': lambda **map: (data(i) for i in navbefore),
'after': lambda **map: (data(i) for i in navafter)},)
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393
Pierre-Yves David
hgweb: introduction a filerevnav subclass...
r18408 class filerevnav(revnav):
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409
def __init__(self, repo, path):
"""Navigation generation object
:repo: repo object we generate nav for
:path: path of the file we generate nav for
"""
# used for iteration
self._changelog = repo.unfiltered().changelog
# used for hex generation
self._revlog = repo.file(path)
def hex(self, rev):
return hex(self._changelog.node(self._revlog.linkrev(rev)))
av6
webutil: make _siblings into an object with __iter__ and __len__...
r27023 class _siblings(object):
Gregory Szorc
hgweb: don't use mutable default argument value
r31391 def __init__(self, siblings=None, hiderev=None):
Pierre-Yves David
hgweb: explicitly tests for None in webutil...
r31434 if siblings is None:
siblings = []
self.siblings = [s for s in siblings if s.node() != nullid]
av6
webutil: make _siblings into an object with __iter__ and __len__...
r27023 if len(self.siblings) == 1 and self.siblings[0].rev() == hiderev:
self.siblings = []
Pierre-Yves David
hgweb: introduction a filerevnav subclass...
r18408
av6
webutil: make _siblings into an object with __iter__ and __len__...
r27023 def __iter__(self):
for s in self.siblings:
d = {
'node': s.hex(),
'rev': s.rev(),
'user': s.user(),
'date': s.date(),
'description': s.description(),
'branch': s.branch(),
}
if util.safehasattr(s, 'path'):
d['file'] = s.path()
yield d
def __len__(self):
return len(self.siblings)
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Gregory Szorc
hgweb: query string arguments to control whitespace for annotate...
r34391 def difffeatureopts(req, ui, section):
Jun Wu
hgweb: make fctx.annotate a separated function so it could be wrapped...
r30081 diffopts = patch.difffeatureopts(ui, untrusted=True,
Gregory Szorc
hgweb: query string arguments to control whitespace for annotate...
r34391 section=section, whitespace=True)
for k in ('ignorews', 'ignorewsamount', 'ignorewseol', 'ignoreblanklines'):
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 v = req.qsparams.get(k)
Gregory Szorc
hgweb: query string arguments to control whitespace for annotate...
r34391 if v is not None:
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 v = stringutil.parsebool(v)
Gregory Szorc
hgweb: use parsebool for parsing diff query string options...
r34404 setattr(diffopts, k, v if v is not None else True)
Gregory Szorc
hgweb: query string arguments to control whitespace for annotate...
r34391
return diffopts
def annotate(req, fctx, ui):
diffopts = difffeatureopts(req, ui, 'annotate')
Yuya Nishihara
annotate: drop linenumber flag from fctx.annotate() (API)...
r37083 return fctx.annotate(follow=True, diffopts=diffopts)
Jun Wu
hgweb: make fctx.annotate a separated function so it could be wrapped...
r30081
Dirkjan Ochtman
hgweb: simplify parents/children generation code
r7671 def parents(ctx, hide=None):
Anton Shestakov
hgweb: use introrev() for finding parents (issue4506)...
r24136 if isinstance(ctx, context.basefilectx):
introrev = ctx.introrev()
if ctx.changectx().rev() != introrev:
Matt Harbison
hgweb: replace 'ctx._repo' with 'ctx.repo()'
r24340 return _siblings([ctx.repo()[introrev]], hide)
Dirkjan Ochtman
hgweb: simplify parents/children generation code
r7671 return _siblings(ctx.parents(), hide)
def children(ctx, hide=None):
return _siblings(ctx.children(), hide)
Matt Mackall
hgweb: minor improvements for new web style...
r6434 def renamelink(fctx):
Matt Mackall
hgweb: fix merge breakage
r6437 r = fctx.renamed()
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 if r:
Augie Fackler
webutil: move from dict() construction to {} literals...
r20681 return [{'file': r[0], 'node': hex(r[1])}]
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 return []
def nodetagsdict(repo, node):
return [{"name": i} for i in repo.nodetags(node)]
Alexander Solovyov
hgweb: add display of bookmarks for changelog and changeset
r13596 def nodebookmarksdict(repo, node):
return [{"name": i} for i in repo.nodebookmarks(node)]
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 def nodebranchdict(repo, ctx):
branches = []
branch = ctx.branch()
# If this is an empty repo, ctx.node() == nullid,
Brodie Rao
localrepo: add branchtip() method for faster single-branch lookups...
r16719 # ctx.branch() == 'default'.
try:
branchnode = repo.branchtip(branch)
except error.RepoLookupError:
branchnode = None
if branchnode == ctx.node():
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 branches.append({"name": branch})
return branches
def nodeinbranch(repo, ctx):
branches = []
branch = ctx.branch()
Brodie Rao
localrepo: add branchtip() method for faster single-branch lookups...
r16719 try:
branchnode = repo.branchtip(branch)
except error.RepoLookupError:
branchnode = None
if branch != 'default' and branchnode != ctx.node():
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 branches.append({"name": branch})
return branches
def nodebranchnodefault(ctx):
branches = []
branch = ctx.branch()
if branch != 'default':
branches.append({"name": branch})
return branches
def showtag(repo, tmpl, t1, node=nullid, **args):
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 args = pycompat.byteskwargs(args)
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 for t in repo.nodetags(node):
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 lm = args.copy()
lm['tag'] = t
yield tmpl.generate(t1, lm)
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Alexander Solovyov
hgweb: add display of bookmarks for changelog and changeset
r13596 def showbookmark(repo, tmpl, t1, node=nullid, **args):
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 args = pycompat.byteskwargs(args)
Alexander Solovyov
hgweb: add display of bookmarks for changelog and changeset
r13596 for t in repo.nodebookmarks(node):
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 lm = args.copy()
lm['bookmark'] = t
yield tmpl.generate(t1, lm)
Alexander Solovyov
hgweb: add display of bookmarks for changelog and changeset
r13596
av6
hgweb: move branchentries code from webcommands to webutil
r26129 def branchentries(repo, stripecount, limit=0):
tips = []
heads = repo.heads()
parity = paritygen(stripecount)
sortkey = lambda item: (not item[1], item[0].rev())
def entries(**map):
count = 0
if not tips:
for tag, hs, tip, closed in repo.branchmap().iterbranches():
tips.append((repo[tip], closed))
for ctx, closed in sorted(tips, key=sortkey, reverse=True):
if limit > 0 and count >= limit:
return
count += 1
if closed:
status = 'closed'
elif ctx.node() not in heads:
status = 'inactive'
else:
status = 'open'
yield {
timeless
py3: convert to next() function...
r29216 'parity': next(parity),
av6
hgweb: move branchentries code from webcommands to webutil
r26129 'branch': ctx.branch(),
'status': status,
'node': ctx.hex(),
'date': ctx.date()
}
return entries
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 def cleanpath(repo, path):
path = path.lstrip('/')
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 return pathutil.canonpath(repo.root, '', path)
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
av6
style: adjust whitespaces in webutil.py...
r25999 def changeidctx(repo, changeid):
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 try:
Matt Mackall
use repo[changeid] to get a changectx
r6747 ctx = repo[changeid]
Matt Mackall
error: move repo errors...
r7637 except error.RepoError:
Durham Goode
manifest: remove last uses of repo.manifest...
r30375 man = repo.manifestlog._revlog
Matt Mackall
linkrev: take a revision number rather than a hash
r7361 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
return ctx
av6
style: adjust whitespaces in webutil.py...
r25999 def changectx(repo, req):
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 changeid = "tip"
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 if 'node' in req.qsparams:
changeid = req.qsparams['node']
av6
style: adjust whitespaces in webutil.py...
r25999 ipos = changeid.find(':')
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 if ipos != -1:
changeid = changeid[(ipos + 1):]
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 elif 'manifest' in req.qsparams:
changeid = req.qsparams['manifest']
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991
return changeidctx(repo, changeid)
def basechangectx(repo, req):
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 if 'node' in req.qsparams:
changeid = req.qsparams['node']
av6
style: adjust whitespaces in webutil.py...
r25999 ipos = changeid.find(':')
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 if ipos != -1:
changeid = changeid[:ipos]
return changeidctx(repo, changeid)
return None
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 def filectx(repo, req):
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 if 'file' not in req.qsparams:
Ross Lagerwall
hgweb: avoid traceback when file or node parameters are missing...
r17289 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 path = cleanpath(repo, req.qsparams['file'])
if 'node' in req.qsparams:
changeid = req.qsparams['node']
elif 'filenode' in req.qsparams:
changeid = req.qsparams['filenode']
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 else:
Ross Lagerwall
hgweb: avoid traceback when file or node parameters are missing...
r17289 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 try:
Matt Mackall
use repo[changeid] to get a changectx
r6747 fctx = repo[changeid][path]
Matt Mackall
error: move repo errors...
r7637 except error.RepoError:
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 fctx = repo.filectx(path, fileid=changeid)
return fctx
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310
Denis Laxalde
hgweb: handle a "linerange" request parameter in filelog command...
r31665 def linerange(req):
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 linerange = req.qsparams.getall('linerange')
Gregory Szorc
hgweb: perform all parameter lookup via qsparams...
r36881 if not linerange:
Denis Laxalde
hgweb: handle a "linerange" request parameter in filelog command...
r31665 return None
if len(linerange) > 1:
raise ErrorResponse(HTTP_BAD_REQUEST,
'redundant linerange parameter')
try:
fromline, toline = map(int, linerange[0].split(':', 1))
except ValueError:
raise ErrorResponse(HTTP_BAD_REQUEST,
'invalid linerange parameter')
try:
return util.processlinerange(fromline, toline)
except error.ParseError as exc:
Augie Fackler
py3: get bytes-repr of network errors portably...
r36272 raise ErrorResponse(HTTP_BAD_REQUEST, pycompat.bytestr(exc))
Denis Laxalde
hgweb: handle a "linerange" request parameter in filelog command...
r31665
def formatlinerange(fromline, toline):
return '%d:%d' % (fromline + 1, toline)
Yuya Nishihara
templatekw: switch obsfate-related template keywords to new API
r36612 def succsandmarkers(context, mapping):
repo = context.resource(mapping, 'repo')
for item in templatekw.showsuccsandmarkers(context, mapping):
av6
hgweb: link to successors of obsoleted changesets...
r35502 item['successors'] = _siblings(repo[successor]
for successor in item['successors'])
yield item
av6
hgweb: display fate of obsolete changesets...
r35501
Yuya Nishihara
templatekw: switch obsfate-related template keywords to new API
r36612 # teach templater succsandmarkers is switched to (context, mapping) API
Yuya Nishihara
templatekw: stop using _showlist() which is about to be deprecated...
r37087 succsandmarkers._requires = {'repo', 'ctx'}
Yuya Nishihara
templatekw: switch obsfate-related template keywords to new API
r36612
av6
hgweb: explain instabilities of unstable changesets
r36973 def whyunstable(context, mapping):
repo = context.resource(mapping, 'repo')
ctx = context.resource(mapping, 'ctx')
entries = obsutil.whyunstable(repo, ctx)
for entry in entries:
if entry.get('divergentnodes'):
entry['divergentnodes'] = _siblings(entry['divergentnodes'])
yield entry
Yuya Nishihara
templatekw: stop using _showlist() which is about to be deprecated...
r37087 whyunstable._requires = {'repo', 'ctx'}
av6
hgweb: explain instabilities of unstable changesets
r36973
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 def commonentry(repo, ctx):
node = ctx.node()
return {
Yuya Nishihara
hgweb: make templater mostly compatible with log templates...
r36535 # TODO: perhaps ctx.changectx() should be assigned if ctx is a
# filectx, but I'm not pretty sure if that would always work because
# fctx.parents() != fctx.changectx.parents() for example.
'ctx': ctx,
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 'rev': ctx.rev(),
'node': hex(node),
'author': ctx.user(),
'desc': ctx.description(),
'date': ctx.date(),
'extra': ctx.extra(),
'phase': ctx.phasestr(),
av6
context: add obsolete() method to basefilectx...
r35087 'obsolete': ctx.obsolete(),
Yuya Nishihara
hgweb: make templater mostly compatible with log templates...
r36535 'succsandmarkers': succsandmarkers,
av6
hgweb: rename the main attribute of instabilities...
r35129 'instabilities': [{"instability": i} for i in ctx.instabilities()],
av6
hgweb: explain instabilities of unstable changesets
r36973 'whyunstable': whyunstable,
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 'branch': nodebranchnodefault(ctx),
'inbranch': nodeinbranch(repo, ctx),
'branches': nodebranchdict(repo, ctx),
'tags': nodetagsdict(repo, node),
'bookmarks': nodebookmarksdict(repo, node),
'parent': lambda **x: parents(ctx),
'child': lambda **x: children(ctx),
}
Gregory Szorc
hgweb: don't redundantly pass templater with requestcontext (API)...
r36901 def changelistentry(web, ctx):
Gregory Szorc
hgweb: extract changelist entry generation into own function...
r23745 '''Obtain a dictionary to be used for entries in a changelist.
This function is called when producing items for the "entries" list passed
to the "shortlog" and "changelog" templates.
'''
repo = web.repo
rev = ctx.rev()
n = ctx.node()
Gregory Szorc
hgweb: don't redundantly pass templater with requestcontext (API)...
r36901 showtags = showtag(repo, web.tmpl, 'changelogtag', n)
files = listfilediffs(web.tmpl, ctx.files(), n, web.maxfiles)
Gregory Szorc
hgweb: extract changelist entry generation into own function...
r23745
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 entry = commonentry(repo, ctx)
entry.update(
av6
hgweb: add parents to json-log (issue5074)...
r28709 allparents=lambda **x: parents(ctx),
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 parent=lambda **x: parents(ctx, rev - 1),
child=lambda **x: children(ctx, rev + 1),
changelogtag=showtags,
files=files,
)
return entry
Gregory Szorc
hgweb: extract changelist entry generation into own function...
r23745
av6
hgweb: provide symrev (symbolic revision) property to the templates...
r25602 def symrevorshortnode(req, ctx):
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 if 'node' in req.qsparams:
return templatefilters.revescape(req.qsparams['node'])
av6
hgweb: provide symrev (symbolic revision) property to the templates...
r25602 else:
return short(ctx.node())
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 def changesetentry(web, ctx):
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 '''Obtain a dictionary to be used to render the "changeset" template.'''
Gregory Szorc
hgweb: don't redundantly pass templater with requestcontext (API)...
r36901 showtags = showtag(web.repo, web.tmpl, 'changesettag', ctx.node())
showbookmarks = showbookmark(web.repo, web.tmpl, 'changesetbookmark',
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 ctx.node())
showbranch = nodebranchnodefault(ctx)
files = []
parity = paritygen(web.stripecount)
for blockno, f in enumerate(ctx.files()):
av6
hgweb: rewrite `template = A and B or C` to be a proper ternary operator
r35315 template = 'filenodelink' if f in ctx else 'filenolink'
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 files.append(web.tmpl.generate(template, {
'node': ctx.hex(),
'file': f,
'blockno': blockno + 1,
'parity': next(parity),
}))
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 basectx = basechangectx(web.repo, web.req)
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 if basectx is None:
basectx = ctx.p1()
Boris Feld
configitems: register the 'web.style' config
r34243 style = web.config('web', 'style')
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 if 'style' in web.req.qsparams:
style = web.req.qsparams['style']
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177
Gregory Szorc
hgweb: don't redundantly pass templater with requestcontext (API)...
r36901 diff = diffs(web, ctx, basectx, None, style)
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177
parity = paritygen(web.stripecount)
diffstatsgen = diffstatgen(ctx, basectx)
Gregory Szorc
hgweb: don't redundantly pass templater with requestcontext (API)...
r36901 diffstats = diffstat(web.tmpl, ctx, diffstatsgen, parity)
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177
return dict(
diff=diff,
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 symrev=symrevorshortnode(web.req, ctx),
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 basenode=basectx.hex(),
changesettag=showtags,
changesetbookmark=showbookmarks,
changesetbranch=showbranch,
files=files,
diffsummary=lambda **x: diffsummary(diffstatsgen),
diffstat=diffstats,
archives=web.archivelist(ctx.hex()),
Pulkit Goyal
py3: use pycompat.strkwargs to convert kwargs keys to str...
r36452 **pycompat.strkwargs(commonentry(web.repo, ctx)))
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177
Dirkjan Ochtman
hgweb: move another utility function into the webutil module
r7311 def listfilediffs(tmpl, files, node, max):
for f in files[:max]:
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 yield tmpl.generate('filedifflink', {'node': hex(node), 'file': f})
Dirkjan Ochtman
hgweb: move another utility function into the webutil module
r7311 if len(files) > max:
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 yield tmpl.generate('fileellipses', {})
Dirkjan Ochtman
hgweb: move another utility function into the webutil module
r7311
Gregory Szorc
hgweb: don't redundantly pass templater with requestcontext (API)...
r36901 def diffs(web, ctx, basectx, files, style, linerange=None,
Denis Laxalde
hgweb: prefix line id by ctx shortnode in filelog when patches are shown...
r31727 lineidprefix=''):
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310
Denis Laxalde
hgweb: use patch.diffhunks in webutil.diffs to simplify the algorithm...
r31276 def prettyprintlines(lines, blockno):
for lineno, l in enumerate(lines, 1):
Denis Laxalde
hgweb: start enumerate at 1 in webutil.diffs's inner function prettyprintlines
r31275 difflineno = "%d.%d" % (blockno, lineno)
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310 if l.startswith('+'):
ltype = "difflineplus"
elif l.startswith('-'):
ltype = "difflineminus"
elif l.startswith('@'):
ltype = "difflineat"
else:
ltype = "diffline"
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 yield web.tmpl.generate(ltype, {
'line': l,
'lineno': lineno,
'lineid': lineidprefix + "l%s" % difflineno,
'linenumber': "% 8s" % difflineno,
})
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310
Denis Laxalde
hgweb: handle "parity" internally in webutil.diffs()...
r31660 repo = web.repo
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310 if files:
m = match.exact(repo.root, repo.getcwd(), files)
else:
m = match.always(repo.root, repo.getcwd())
diffopts = patch.diffopts(repo.ui, untrusted=True)
Denis Laxalde
hgweb: explictly pass basectx in webutil.diffs...
r31082 node1 = basectx.node()
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310 node2 = ctx.node()
Denis Laxalde
hgweb: handle "parity" internally in webutil.diffs()...
r31660 parity = paritygen(web.stripecount)
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310
Denis Laxalde
hgweb: use patch.diffhunks in webutil.diffs to simplify the algorithm...
r31276 diffhunks = patch.diffhunks(repo, node1, node2, m, opts=diffopts)
Denis Laxalde
diff: also yield file context objects in patch.trydiff() (API)...
r34856 for blockno, (fctx1, fctx2, header, hunks) in enumerate(diffhunks, 1):
Denis Laxalde
hgweb: use patch.diffhunks in webutil.diffs to simplify the algorithm...
r31276 if style != 'raw':
header = header[1:]
lines = [h + '\n' for h in header]
for hunkrange, hunklines in hunks:
Denis Laxalde
hgweb: add a 'linerange' parameter to webutil.diffs()...
r31666 if linerange is not None and hunkrange is not None:
s1, l1, s2, l2 = hunkrange
Denis Laxalde
mdiff: add a hunkinrange helper function...
r31808 if not mdiff.hunkinrange((s2, l2), linerange):
Denis Laxalde
hgweb: add a 'linerange' parameter to webutil.diffs()...
r31666 continue
Denis Laxalde
hgweb: use patch.diffhunks in webutil.diffs to simplify the algorithm...
r31276 lines.extend(hunklines)
if lines:
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 yield web.tmpl.generate('diffblock', {
'parity': next(parity),
'blockno': blockno,
'lines': prettyprintlines(lines, blockno),
})
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345
wujek srujek
hgweb: fixes traceback for invalid files by removing top-level template...
r17302 def compare(tmpl, context, leftlines, rightlines):
wujek srujek
hgweb: side-by-side comparison functionality...
r17202 '''Generator function that provides side-by-side comparison data.'''
def compline(type, leftlineno, leftline, rightlineno, rightline):
Augie Fackler
webutil: some %d instead of %s love on ints...
r36728 lineid = leftlineno and ("l%d" % leftlineno) or ''
lineid += rightlineno and ("r%d" % rightlineno) or ''
llno = '%d' % leftlineno if leftlineno else ''
rlno = '%d' % rightlineno if rightlineno else ''
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 return tmpl.generate('comparisonline', {
'type': type,
'lineid': lineid,
'leftlineno': leftlineno,
'leftlinenumber': "% 6s" % llno,
'leftline': leftline or '',
'rightlineno': rightlineno,
'rightlinenumber': "% 6s" % rlno,
'rightline': rightline or '',
})
wujek srujek
hgweb: side-by-side comparison functionality...
r17202
def getblock(opcodes):
for type, llo, lhi, rlo, rhi in opcodes:
len1 = lhi - llo
len2 = rhi - rlo
count = min(len1, len2)
for i in xrange(count):
yield compline(type=type,
leftlineno=llo + i + 1,
leftline=leftlines[llo + i],
rightlineno=rlo + i + 1,
rightline=rightlines[rlo + i])
if len1 > len2:
for i in xrange(llo + count, lhi):
yield compline(type=type,
leftlineno=i + 1,
leftline=leftlines[i],
rightlineno=None,
rightline=None)
elif len2 > len1:
for i in xrange(rlo + count, rhi):
yield compline(type=type,
leftlineno=None,
leftline=None,
rightlineno=i + 1,
rightline=rightlines[i])
s = difflib.SequenceMatcher(None, leftlines, rightlines)
if context < 0:
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 yield tmpl.generate('comparisonblock',
{'lines': getblock(s.get_opcodes())})
wujek srujek
hgweb: side-by-side comparison functionality...
r17202 else:
wujek srujek
hgweb: fixes traceback for invalid files by removing top-level template...
r17302 for oc in s.get_grouped_opcodes(n=context):
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 yield tmpl.generate('comparisonblock', {'lines': getblock(oc)})
wujek srujek
hgweb: side-by-side comparison functionality...
r17202
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 def diffstatgen(ctx, basectx):
Steven Brown
web: provide diff summary to the changeset page...
r14570 '''Generator function that provides the diffstat data.'''
Steven Brown
web: provide diffstat to the changeset page...
r14490
Yuya Nishihara
hgweb: disable diff.noprefix option for diffstat...
r35445 stats = patch.diffstatdata(
util.iterlines(ctx.diff(basectx, noprefix=False)))
Steven Brown
web: provide diffstat to the changeset page...
r14490 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
Steven Brown
web: provide diff summary to the changeset page...
r14570 while True:
yield stats, maxname, maxtotal, addtotal, removetotal, binary
def diffsummary(statgen):
'''Return a short summary of the diff.'''
timeless
py3: convert to next() function...
r29216 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
Steven Brown
web: provide diff summary to the changeset page...
r14570 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
len(stats), addtotal, removetotal)
def diffstat(tmpl, ctx, statgen, parity):
'''Return a diffstat template for each file in the diff.'''
timeless
py3: convert to next() function...
r29216 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
Steven Brown
web: include all files in the diffstat...
r14561 files = ctx.files()
Steven Brown
web: provide diffstat to the changeset page...
r14490
Steven Brown
web: include all files in the diffstat...
r14561 def pct(i):
if maxtotal == 0:
return 0
return (float(i) / maxtotal) * 100
Steven Brown
web: provide diffstat to the changeset page...
r14490
Steven Brown
web: provide the file number to the diffstat templates...
r14562 fileno = 0
Steven Brown
web: include all files in the diffstat...
r14561 for filename, adds, removes, isbinary in stats:
av6
hgweb: rewrite `template = A and B or C` to be a proper ternary operator
r35315 template = 'diffstatlink' if filename in files else 'diffstatnolink'
Steven Brown
web: include all files in the diffstat...
r14561 total = adds + removes
Steven Brown
web: provide the file number to the diffstat templates...
r14562 fileno += 1
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 yield tmpl.generate(template, {
'node': ctx.hex(),
'file': filename,
'fileno': fileno,
'total': total,
'addpct': pct(adds),
'removepct': pct(removes),
'parity': next(parity),
})
Steven Brown
web: provide diffstat to the changeset page...
r14490
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345 class sessionvars(object):
def __init__(self, vars, start='?'):
self.start = start
self.vars = vars
def __getitem__(self, key):
return self.vars[key]
def __setitem__(self, key, value):
self.vars[key] = value
def __copy__(self):
return sessionvars(copy.copy(self.vars), self.start)
def __iter__(self):
separator = self.start
Mads Kiilerich
hgweb: generate query strings with parameters sorted by key
r18367 for key, value in sorted(self.vars.iteritems()):
Augie Fackler
webutil: use pycompat.bytestr() instead of str()...
r34808 yield {'name': key,
'value': pycompat.bytestr(value),
'separator': separator,
}
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345 separator = '&'
Augie Fackler
hgweb: fix hgweb_mod as well as hgwebdir_mod
r12691
Yuya Nishihara
hgweb: alias ui module as uimod...
r27007 class wsgiui(uimod.ui):
Augie Fackler
hgweb: fix hgweb_mod as well as hgwebdir_mod
r12691 # default termwidth breaks under mod_wsgi
def termwidth(self):
return 80
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162
def getwebsubs(repo):
websubtable = []
websubdefs = repo.ui.configitems('websub')
# we must maintain interhg backwards compatibility
websubdefs += repo.ui.configitems('interhg')
for key, pattern in websubdefs:
# grab the delimiter from the character after the "s"
Pulkit Goyal
py3: slice over bytes to prevent getting ascii values...
r36198 unesc = pattern[1:2]
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162 delim = re.escape(unesc)
# identify portions of the pattern, taking care to avoid escaped
# delimiters. the replace format and flags are optional, but
# delimiters are required.
match = re.match(
Pulkit Goyal
py3: add b'' to make sure regex pattern are bytes in hgweb/webutil.py...
r36201 br'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162 % (delim, delim, delim), pattern)
if not match:
repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
% (key, pattern))
continue
# we need to unescape the delimiter for regexp and format
Pulkit Goyal
py3: add b'' to make sure regex pattern are bytes in hgweb/webutil.py...
r36201 delim_re = re.compile(br'(?<!\\)\\%s' % delim)
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162 regexp = delim_re.sub(unesc, match.group(1))
format = delim_re.sub(unesc, match.group(2))
# the pattern allows for 6 regexp flags, so set them if necessary
flagin = match.group(3)
flags = 0
if flagin:
for flag in flagin.upper():
flags |= re.__dict__[flag]
try:
regexp = re.compile(regexp, flags)
websubtable.append((regexp, format))
except re.error:
repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
% (key, regexp))
return websubtable