##// END OF EJS Templates
interfaces: make the `peer` mixin not a Protocol to fix Python 3.10 failures...
interfaces: make the `peer` mixin not a Protocol to fix Python 3.10 failures I can't find any documentation on this, but it appears that Protocol class attributes don't get inherited in subclasses that explicitly subclass a Protocol until Python 3.11, which caused a ton of failures in CI on macOS and Windows (which both test using Python 3.9). The problem started with 1df97507c6b8, and typically manifested as most tests failing to access `ui` on various `peer` classes. Here's a short proof of concept: from __future__ import annotations from typing import ( Protocol, ) class peer(Protocol): limitedarguments: bool = False def __init__(self, arg1, arg2, remotehidden: bool = False) -> None: self.arg1 = arg1 self.arg2 = arg2 class subclass(peer): def __init__(self, arg1, arg2): super(subclass, self).__init__(arg1, arg2, False) sub = subclass(1, 2) print("sub.arg1 is %r" % sub.arg1) When run with Python 3.8.10, 3.9.13, and 3.10.11, the result is: $ py -3.8 prot-test.py Traceback (most recent call last): File "prot-test.py", line 20, in <module> print("sub.arg1 is %r" % sub.arg1) AttributeError: 'subclass' object has no attribute 'arg1' On Python 3.11.9, 3.12.7, and 3.13.0, the result is: $ py -3.11 ../prot-test.py sub.arg1 is 1 Explicitly adding annotations to `peer` like `limitedarguments` didn't help.

File last commit:

r52756:f4733654 default
r53403:199b0e62 default
Show More
logexchange.py
168 lines | 4.9 KiB | text/x-python | PythonLexer
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 # logexchange.py
#
# Copyright 2017 Augie Fackler <raf@durin42.com>
# Copyright 2017 Sean Farley <sean@farley.io>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Matt Harbison
typing: add `from __future__ import annotations` to most files...
r52756 from __future__ import annotations
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348
from .node import hex
from . import (
Pulkit Goyal
logexchange: introduce helper function to get remote path name...
r36076 util,
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 vfs as vfsmod,
)
urlutil: extract `url` related code from `util` into the new module...
r47669 from .utils import (
urlutil,
)
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348
# directory name in .hg/ in which remotenames files will be present
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 remotenamedir = b'logexchange'
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348
Augie Fackler
formatting: blacken the codebase...
r43346
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 def readremotenamefile(repo, filename):
"""
reads a file from .hg/logexchange/ directory and yields it's content
filename: the file to be read
yield a tuple (node, remotepath, name)
"""
vfs = vfsmod.vfs(repo.vfs.join(remotenamedir))
if not vfs.exists(filename):
return
f = vfs(filename)
lineno = 0
for line in f:
line = line.strip()
if not line:
continue
# contains the version number
if lineno == 0:
lineno += 1
try:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 node, remote, rname = line.split(b'\0')
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 yield node, remote, rname
except ValueError:
pass
f.close()
Augie Fackler
formatting: blacken the codebase...
r43346
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 def readremotenames(repo):
"""
read the details about the remotenames stored in .hg/logexchange/ and
yields a tuple (node, remotepath, name). It does not yields information
about whether an entry yielded is branch or bookmark. To get that
information, call the respective functions.
"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 for bmentry in readremotenamefile(repo, b'bookmarks'):
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 yield bmentry
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 for branchentry in readremotenamefile(repo, b'branches'):
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 yield branchentry
Augie Fackler
formatting: blacken the codebase...
r43346
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 def writeremotenamefile(repo, remotepath, names, nametype):
vfs = vfsmod.vfs(repo.vfs.join(remotenamedir))
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 f = vfs(nametype, b'w', atomictemp=True)
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 # write the storage version info on top of file
# version '0' represents the very initial version of the storage format
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 f.write(b'0\n\n')
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348
olddata = set(readremotenamefile(repo, nametype))
# re-save the data from a different remote than this one.
for node, oldpath, rname in sorted(olddata):
if oldpath != remotepath:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 f.write(b'%s\0%s\0%s\n' % (node, oldpath, rname))
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348
Gregory Szorc
global: bulk replace simple pycompat.iteritems(x) with x.items()...
r49768 for name, node in sorted(names.items()):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if nametype == b"branches":
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 for n in node:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 f.write(b'%s\0%s\0%s\n' % (n, remotepath, name))
elif nametype == b"bookmarks":
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 if node:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 f.write(b'%s\0%s\0%s\n' % (node, remotepath, name))
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348
f.close()
Augie Fackler
formatting: blacken the codebase...
r43346
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 def saveremotenames(repo, remotepath, branches=None, bookmarks=None):
"""
save remotenames i.e. remotebookmarks and remotebranches in their
respective files under ".hg/logexchange/" directory.
"""
wlock = repo.wlock()
try:
if bookmarks:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 writeremotenamefile(repo, remotepath, bookmarks, b'bookmarks')
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 if branches:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 writeremotenamefile(repo, remotepath, branches, b'branches')
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 finally:
wlock.release()
Augie Fackler
formatting: blacken the codebase...
r43346
Pulkit Goyal
logexchange: introduce helper function to get remote path name...
r36076 def activepath(repo, remote):
"""returns remote path"""
# is the remote a local peer
local = remote.local()
# determine the remote path from the repo, if possible; else just
# use the string given to us
rpath = remote
if local:
Matt Harbison
logexchange: convert paths to unix when detecting the active path...
r40471 rpath = util.pconvert(remote._repo.root)
Pulkit Goyal
py3: use bytes instead of str in instance()...
r37383 elif not isinstance(remote, bytes):
logexchange: use the proper accessors to get the remote url...
r50655 rpath = remote.url()
Pulkit Goyal
logexchange: introduce helper function to get remote path name...
r36076
# represent the remotepath with user defined path name if exists
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 for path, url in repo.ui.configitems(b'paths'):
Pulkit Goyal
logexchange: introduce helper function to get remote path name...
r36076 # remove auth info from user defined url
urlutil: extract `url` related code from `util` into the new module...
r47669 noauthurl = urlutil.removeauth(url)
Matt Harbison
logexchange: convert paths to unix when detecting the active path...
r40471
# Standardize on unix style paths, otherwise some {remotenames} end up
# being an absolute path on Windows.
url = util.pconvert(bytes(url))
noauthurl = util.pconvert(noauthurl)
Pulkit Goyal
remotenames: check the remotepath with url containing user information too...
r38001 if url == rpath or noauthurl == rpath:
Pulkit Goyal
logexchange: introduce helper function to get remote path name...
r36076 rpath = path
break
return rpath
Augie Fackler
formatting: blacken the codebase...
r43346
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 def pullremotenames(localrepo, remoterepo):
"""
pulls bookmarks and branches information of the remote repo during a
pull or clone operation.
localrepo is our local repository
remoterepo is the peer instance
"""
Pulkit Goyal
logexchange: introduce helper function to get remote path name...
r36076 remotepath = activepath(localrepo, remoterepo)
Gregory Szorc
logexchange: use command executor for wire protocol commands...
r37657
with remoterepo.commandexecutor() as e:
Augie Fackler
formatting: blacken the codebase...
r43346 bookmarks = e.callcommand(
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 b'listkeys',
{
b'namespace': b'bookmarks',
},
Augie Fackler
formatting: blacken the codebase...
r43346 ).result()
Gregory Szorc
logexchange: use command executor for wire protocol commands...
r37657
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 # 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()
Gregory Szorc
logexchange: use command executor for wire protocol commands...
r37657
with remoterepo.commandexecutor() as e:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 branchmap = e.callcommand(b'branchmap', {}).result()
Gregory Szorc
logexchange: use command executor for wire protocol commands...
r37657
Gregory Szorc
global: bulk replace simple pycompat.iteritems(x) with x.items()...
r49768 for branch, nodes in branchmap.items():
Pulkit Goyal
remotenames: rename related file and storage dir to logexchange...
r35348 bmap[branch] = []
for node in nodes:
if node in repo and not repo[node].obsolete():
bmap[branch].append(hex(node))
saveremotenames(localrepo, remotepath, bmap, bookmarks)