Mercurial > hg
view hgext/remotefilelog/metadatastore.py @ 51811:673b07a1db08 default tip
typing: create an @overload of `phasecache` ctor to handle the copy case
In `phasecache.copy()`, it calls `self.__class__(None, None, _load=False)`, but
the constuctor is typed to take a non-None repository. For the `_load=False`
case, all args are ignored (and the copy function itself populates the attrs on
the new object), so this isn't an error. For the default `_load=True` case, it
needs a non-None repository. This is the simplest way to handle that duality.
The reason this wasn't being detected is because pytype is confused by the
interface decorators on the `localrepository` class, and is inferring the whole
class as `Any`. (See 3e9a660b074a or c1d7ac70980b) Therefore, the type hint of
`localrepo.localrepository` here was also effectively `Any`, which disabled the
type checking entirely.
This is the first foray into using `typing_extensions` to unlock future typing
features. I think this is safe and reasonable because 1) it is only imported in
the type checking phase (so no need to vendor our own copy), and 2) pytype has
its own copy of `typing_extensions` bundled with it, so no need to alter the
test environment. When run with a version of python that supports the symbol(s)
natively, `typing_extensions` simply re-exports from `typing`, so there
shouldn't be any future headaches with this.
author | Matt Harbison <matt_harbison@yahoo.com> |
---|---|
date | Sat, 17 Aug 2024 18:43:23 -0400 |
parents | 642e31cb55f0 |
children |
line wrap: on
line source
from mercurial.node import ( hex, sha1nodeconstants, ) from . import ( basestore, shallowutil, ) class unionmetadatastore(basestore.baseunionstore): def __init__(self, *args, **kwargs): super(unionmetadatastore, self).__init__(*args, **kwargs) self.stores = args self.writestore = kwargs.get('writestore') # If allowincomplete==True then the union store can return partial # ancestor lists, otherwise it will throw a KeyError if a full # history can't be found. self.allowincomplete = kwargs.get('allowincomplete', False) def getancestors(self, name, node, known=None): """Returns as many ancestors as we're aware of. return value: { node: (p1, p2, linknode, copyfrom), ... } """ if known is None: known = set() if node in known: return [] ancestors = {} def traverse(curname, curnode): # TODO: this algorithm has the potential to traverse parts of # history twice. Ex: with A->B->C->F and A->B->D->F, both D and C # may be queued as missing, then B and A are traversed for both. queue = [(curname, curnode)] missing = [] seen = set() while queue: name, node = queue.pop() if (name, node) in seen: continue seen.add((name, node)) value = ancestors.get(node) if not value: missing.append((name, node)) continue p1, p2, linknode, copyfrom = value if p1 != sha1nodeconstants.nullid and p1 not in known: queue.append((copyfrom or curname, p1)) if p2 != sha1nodeconstants.nullid and p2 not in known: queue.append((curname, p2)) return missing missing = [(name, node)] while missing: curname, curnode = missing.pop() try: ancestors.update( self._getpartialancestors(curname, curnode, known=known) ) newmissing = traverse(curname, curnode) missing.extend(newmissing) except KeyError: # If we allow incomplete histories, don't throw. if not self.allowincomplete: raise # If the requested name+node doesn't exist, always throw. if (curname, curnode) == (name, node): raise # TODO: ancestors should probably be (name, node) -> (value) return ancestors @basestore.baseunionstore.retriable def _getpartialancestors(self, name, node, known=None): for store in self.stores: try: return store.getancestors(name, node, known=known) except KeyError: pass raise KeyError((name, hex(node))) @basestore.baseunionstore.retriable def getnodeinfo(self, name, node): for store in self.stores: try: return store.getnodeinfo(name, node) except KeyError: pass raise KeyError((name, hex(node))) def add(self, name, node, data): raise RuntimeError( b"cannot add content only to remotefilelog contentstore" ) def getmissing(self, keys): missing = keys for store in self.stores: if missing: missing = store.getmissing(missing) return missing def markledger(self, ledger, options=None): for store in self.stores: store.markledger(ledger, options) def getmetrics(self): metrics = [s.getmetrics() for s in self.stores] return shallowutil.sumdicts(*metrics) class remotefilelogmetadatastore(basestore.basestore): def getancestors(self, name, node, known=None): """Returns as many ancestors as we're aware of. return value: { node: (p1, p2, linknode, copyfrom), ... } """ data = self._getdata(name, node) ancestors = shallowutil.ancestormap(data) return ancestors def getnodeinfo(self, name, node): return self.getancestors(name, node)[node] def add(self, name, node, parents, linknode): raise RuntimeError( b"cannot add metadata only to remotefilelog metadatastore" ) class remotemetadatastore: def __init__(self, ui, fileservice, shared): self._fileservice = fileservice self._shared = shared def getancestors(self, name, node, known=None): self._fileservice.prefetch( [(name, hex(node))], force=True, fetchdata=False, fetchhistory=True ) return self._shared.getancestors(name, node, known=known) def getnodeinfo(self, name, node): return self.getancestors(name, node)[node] def add(self, name, node, data): raise RuntimeError(b"cannot add to a remote store") def getmissing(self, keys): return keys def markledger(self, ledger, options=None): pass