packaging: fix setup.py and install as hgext3rd.topic
This changeset is doing two things (gasp):
- It fixes various errors in the setup.py
- It move the topic source and install into hgext3rd.topic.
This last part (code source move) use hgext3rd as namespace package to prevent
installation nightmare. This won't be officially supported until Mercurial 3.8,
but in the meantime, 3.7 user can enable it using the full package name:
[extensions]
hgext3rd.topic=
Thanks goes to Julien Cristau <julien.cristau@logilab.fr> for the initial
version of this.
import weakref
from mercurial import branchmap
from mercurial import error
from mercurial import exchange
from mercurial.i18n import _
from . import topicmap
def _headssummary(orig, repo, remote, outgoing):
publishing = ('phases' not in remote.listkeys('namespaces')
or bool(remote.listkeys('phases').get('publishing', False)))
if publishing:
return orig(repo, remote, outgoing)
oldgetitem = repo.__getitem__
oldrepo = repo.__class__
oldbranchcache = branchmap.branchcache
oldfilename = branchmap._filename
try:
class repocls(repo.__class__):
def __getitem__(self, key):
ctx = super(repocls, self).__getitem__(key)
oldbranch = ctx.branch
def branch():
branch = oldbranch()
topic = ctx.topic()
if topic:
branch = "%s:%s" % (branch, topic)
return branch
ctx.branch = branch
return ctx
repo.__class__ = repocls
branchmap.branchcache = topicmap.topiccache
branchmap._filename = topicmap._filename
summary = orig(repo, remote, outgoing)
for key, value in summary.iteritems():
if ':' in key: # This is a topic
if value[0] is None and value[1]:
summary[key] = ([value[1].pop(0)], ) + value[1:]
return summary
finally:
repo.__class__ = oldrepo
branchmap.branchcache = oldbranchcache
branchmap._filename = oldfilename
def wireprotobranchmap(orig, repo, proto):
oldrepo = repo.__class__
try:
class repocls(repo.__class__):
def branchmap(self):
usetopic = not self.publishing()
return super(repocls, self).branchmap(topic=usetopic)
repo.__class__ = repocls
return orig(repo, proto)
finally:
repo.__class__ = oldrepo
# Discovery have deficiency around phases, branch can get new heads with pure
# phases change. This happened with a changeset was allowed to be pushed
# because it had a topic, but it later become public and create a new branch
# head.
#
# Handle this by doing an extra check for new head creation server side
def _nbheads(repo):
data = {}
for b in repo.branchmap().iterbranches():
if ':' in b[0]:
continue
data[b[0]] = len(b[1])
return data
def handlecheckheads(orig, op, inpart):
orig(op, inpart)
if op.repo.publishing():
return
tr = op.gettransaction()
if tr.hookargs['source'] not in ('push', 'serve'): # not a push
return
tr._prepushheads = _nbheads(op.repo)
reporef = weakref.ref(op.repo)
oldvalidator = tr.validator
def validator(tr):
repo = reporef()
if repo is not None:
repo.invalidatecaches()
finalheads = _nbheads(repo)
for branch, oldnb in tr._prepushheads.iteritems():
newnb = finalheads.pop(branch, 0)
if oldnb < newnb:
msg = _('push create a new head on branch "%s"' % branch)
raise error.Abort(msg)
for branch, newnb in finalheads.iteritems():
if 1 < newnb:
msg = _('push create more than 1 head on new branch "%s"' % branch)
raise error.Abort(msg)
return oldvalidator(tr)
tr.validator = validator
handlecheckheads.params = frozenset()
def _pushb2phases(orig, pushop, bundler):
hascheck = any(p.type == 'check:heads' for p in bundler._parts)
if pushop.outdatedphases and not hascheck:
exchange._pushb2ctxcheckheads(pushop, bundler)
return orig(pushop, bundler)