Mercurial > public > mercurial-scm > hg
comparison mercurial/localrepo.py @ 51891:ee7e106b372b
typing: make the localrepo classes known to pytype
9d4ad05bc91c and 1b17309cdaab both mentioned making `bundlerepository` and
`unionrepository` subclass `localrepository` during the type checking phase, but
that didn't apply to pytype in practice. See bcaa5d408657 and friends for how
the zope interfaces confuse pytype, and end up converting the classes they
decorate into `Any`.
This commit is slightly more complex though, because `localrepository` has mixin
classes applied to it when it is instantiated. Specifically, `RevlogFileStorage`
is added, which adds `def file(f)` (which isn't defined on `localrepository`).
Therefore a list of `localrepository` superclasses is provided during type
checking to account for the mixins. Without this, the `bundlerepository` class
gets flagged when it attempts to call its superclass implementation of `file()`.
Note that pytype doesn't understand these mixin superclasses (it marks the
superclass of `localrepository` as `Any`, because they are zope interfaces it
doesn't understand), but that's enough to get it to not flag `bundlerepository`.
PyCharm also stops flagging it as a missing function, though it seems like it is
able to handle the zope interfaces.
author | Matt Harbison <matt_harbison@yahoo.com> |
---|---|
date | Fri, 20 Sep 2024 21:31:58 -0400 |
parents | f4733654f144 |
children | f0e07efc199f |
comparison
equal
deleted
inserted
replaced
51890:992fcf6b2473 | 51891:ee7e106b372b |
---|---|
12 import os | 12 import os |
13 import random | 13 import random |
14 import re | 14 import re |
15 import sys | 15 import sys |
16 import time | 16 import time |
17 import typing | |
17 import weakref | 18 import weakref |
18 | 19 |
19 from concurrent import futures | 20 from concurrent import futures |
20 from typing import ( | 21 from typing import ( |
21 Optional, | 22 Optional, |
253 b'unbundle', | 254 b'unbundle', |
254 } | 255 } |
255 legacycaps = moderncaps.union({b'changegroupsubset'}) | 256 legacycaps = moderncaps.union({b'changegroupsubset'}) |
256 | 257 |
257 | 258 |
258 @interfaceutil.implementer(repository.ipeercommandexecutor) | 259 class LocalCommandExecutor: |
259 class localcommandexecutor: | |
260 def __init__(self, peer): | 260 def __init__(self, peer): |
261 self._peer = peer | 261 self._peer = peer |
262 self._sent = False | 262 self._sent = False |
263 self._closed = False | 263 self._closed = False |
264 | 264 |
299 | 299 |
300 def close(self): | 300 def close(self): |
301 self._closed = True | 301 self._closed = True |
302 | 302 |
303 | 303 |
304 @interfaceutil.implementer(repository.ipeercommands) | 304 localcommandexecutor = interfaceutil.implementer( |
305 class localpeer(repository.peer): | 305 repository.ipeercommandexecutor |
306 )(LocalCommandExecutor) | |
307 | |
308 if typing.TYPE_CHECKING: | |
309 # Help pytype by hiding the interface stuff that confuses it. | |
310 localcommandexecutor = LocalCommandExecutor | |
311 | |
312 | |
313 class LocalPeer(repository.peer): | |
306 '''peer for a local repo; reflects only the most recent API''' | 314 '''peer for a local repo; reflects only the most recent API''' |
307 | 315 |
308 def __init__(self, repo, caps=None, path=None, remotehidden=False): | 316 def __init__(self, repo, caps=None, path=None, remotehidden=False): |
309 super(localpeer, self).__init__( | 317 super(LocalPeer, self).__init__( |
310 repo.ui, path=path, remotehidden=remotehidden | 318 repo.ui, path=path, remotehidden=remotehidden |
311 ) | 319 ) |
312 | 320 |
313 if caps is None: | 321 if caps is None: |
314 caps = moderncaps.copy() | 322 caps = moderncaps.copy() |
454 return localcommandexecutor(self) | 462 return localcommandexecutor(self) |
455 | 463 |
456 # End of peer interface. | 464 # End of peer interface. |
457 | 465 |
458 | 466 |
459 @interfaceutil.implementer(repository.ipeerlegacycommands) | 467 localpeer = interfaceutil.implementer(repository.ipeercommands)(LocalPeer) |
460 class locallegacypeer(localpeer): | 468 |
469 if typing.TYPE_CHECKING: | |
470 # Help pytype by hiding the interface stuff that confuses it. | |
471 localpeer = LocalPeer | |
472 | |
473 | |
474 class LocalLegacyPeer(localpeer): | |
461 """peer extension which implements legacy methods too; used for tests with | 475 """peer extension which implements legacy methods too; used for tests with |
462 restricted capabilities""" | 476 restricted capabilities""" |
463 | 477 |
464 def __init__(self, repo, path=None, remotehidden=False): | 478 def __init__(self, repo, path=None, remotehidden=False): |
465 super(locallegacypeer, self).__init__( | 479 super(LocalLegacyPeer, self).__init__( |
466 repo, caps=legacycaps, path=path, remotehidden=remotehidden | 480 repo, caps=legacycaps, path=path, remotehidden=remotehidden |
467 ) | 481 ) |
468 | 482 |
469 # Begin of baselegacywirecommands interface. | 483 # Begin of baselegacywirecommands interface. |
470 | 484 |
486 ) | 500 ) |
487 return changegroup.makechangegroup(self._repo, outgoing, b'01', source) | 501 return changegroup.makechangegroup(self._repo, outgoing, b'01', source) |
488 | 502 |
489 # End of baselegacywirecommands interface. | 503 # End of baselegacywirecommands interface. |
490 | 504 |
505 | |
506 locallegacypeer = interfaceutil.implementer(repository.ipeerlegacycommands)( | |
507 LocalLegacyPeer | |
508 ) | |
509 | |
510 if typing.TYPE_CHECKING: | |
511 # Help pytype by hiding the interface stuff that confuses it. | |
512 locallegacypeer = LocalLegacyPeer | |
491 | 513 |
492 # Functions receiving (ui, features) that extensions can register to impact | 514 # Functions receiving (ui, features) that extensions can register to impact |
493 # the ability to load repositories with custom requirements. Only | 515 # the ability to load repositories with custom requirements. Only |
494 # functions defined in loaded extensions are called. | 516 # functions defined in loaded extensions are called. |
495 # | 517 # |
1239 def makemain(**kwargs): | 1261 def makemain(**kwargs): |
1240 """Produce a type conforming to ``ilocalrepositorymain``.""" | 1262 """Produce a type conforming to ``ilocalrepositorymain``.""" |
1241 return localrepository | 1263 return localrepository |
1242 | 1264 |
1243 | 1265 |
1244 @interfaceutil.implementer(repository.ilocalrepositoryfilestorage) | 1266 class RevlogFileStorage: |
1245 class revlogfilestorage: | |
1246 """File storage when using revlogs.""" | 1267 """File storage when using revlogs.""" |
1247 | 1268 |
1248 def file(self, path): | 1269 def file(self, path): |
1249 if path.startswith(b'/'): | 1270 if path.startswith(b'/'): |
1250 path = path[1:] | 1271 path = path[1:] |
1255 ) | 1276 ) |
1256 | 1277 |
1257 return filelog.filelog(self.svfs, path, try_split=try_split) | 1278 return filelog.filelog(self.svfs, path, try_split=try_split) |
1258 | 1279 |
1259 | 1280 |
1260 @interfaceutil.implementer(repository.ilocalrepositoryfilestorage) | 1281 revlogfilestorage = interfaceutil.implementer( |
1261 class revlognarrowfilestorage: | 1282 repository.ilocalrepositoryfilestorage |
1283 )(RevlogFileStorage) | |
1284 | |
1285 if typing.TYPE_CHECKING: | |
1286 # Help pytype by hiding the interface stuff that confuses it. | |
1287 revlogfilestorage = RevlogFileStorage | |
1288 | |
1289 | |
1290 class RevlogNarrowFileStorage: | |
1262 """File storage when using revlogs and narrow files.""" | 1291 """File storage when using revlogs and narrow files.""" |
1263 | 1292 |
1264 def file(self, path): | 1293 def file(self, path): |
1265 if path.startswith(b'/'): | 1294 if path.startswith(b'/'): |
1266 path = path[1:] | 1295 path = path[1:] |
1270 or txnutil.mayhavepending(self.root) | 1299 or txnutil.mayhavepending(self.root) |
1271 ) | 1300 ) |
1272 return filelog.narrowfilelog( | 1301 return filelog.narrowfilelog( |
1273 self.svfs, path, self._storenarrowmatch, try_split=try_split | 1302 self.svfs, path, self._storenarrowmatch, try_split=try_split |
1274 ) | 1303 ) |
1304 | |
1305 | |
1306 revlognarrowfilestorage = interfaceutil.implementer( | |
1307 repository.ilocalrepositoryfilestorage | |
1308 )(RevlogNarrowFileStorage) | |
1309 | |
1310 if typing.TYPE_CHECKING: | |
1311 # Help pytype by hiding the interface stuff that confuses it. | |
1312 revlognarrowfilestorage = RevlogNarrowFileStorage | |
1275 | 1313 |
1276 | 1314 |
1277 def makefilestorage(requirements, features, **kwargs): | 1315 def makefilestorage(requirements, features, **kwargs): |
1278 """Produce a type conforming to ``ilocalrepositoryfilestorage``.""" | 1316 """Produce a type conforming to ``ilocalrepositoryfilestorage``.""" |
1279 features.add(repository.REPO_FEATURE_REVLOG_FILE_STORAGE) | 1317 features.add(repository.REPO_FEATURE_REVLOG_FILE_STORAGE) |
1293 REPO_INTERFACES = [ | 1331 REPO_INTERFACES = [ |
1294 (repository.ilocalrepositorymain, lambda: makemain), | 1332 (repository.ilocalrepositorymain, lambda: makemain), |
1295 (repository.ilocalrepositoryfilestorage, lambda: makefilestorage), | 1333 (repository.ilocalrepositoryfilestorage, lambda: makefilestorage), |
1296 ] | 1334 ] |
1297 | 1335 |
1298 | 1336 _localrepo_base_classes = object |
1299 @interfaceutil.implementer(repository.ilocalrepositorymain) | 1337 |
1300 class localrepository: | 1338 if typing.TYPE_CHECKING: |
1339 _localrepo_base_classes = [ | |
1340 repository.ilocalrepositorymain, | |
1341 repository.ilocalrepositoryfilestorage, | |
1342 ] | |
1343 | |
1344 | |
1345 class LocalRepository(_localrepo_base_classes): | |
1301 """Main class for representing local repositories. | 1346 """Main class for representing local repositories. |
1302 | 1347 |
1303 All local repositories are instances of this class. | 1348 All local repositories are instances of this class. |
1304 | 1349 |
1305 Constructed on its own, instances of this class are not usable as | 1350 Constructed on its own, instances of this class are not usable as |
3596 raise error.ProgrammingError(msg % category) | 3641 raise error.ProgrammingError(msg % category) |
3597 self._sidedata_computers.setdefault(kind, {}) | 3642 self._sidedata_computers.setdefault(kind, {}) |
3598 self._sidedata_computers[kind][category] = (keys, computer, flags) | 3643 self._sidedata_computers[kind][category] = (keys, computer, flags) |
3599 | 3644 |
3600 | 3645 |
3646 localrepository = interfaceutil.implementer(repository.ilocalrepositorymain)( | |
3647 LocalRepository | |
3648 ) | |
3649 | |
3650 if typing.TYPE_CHECKING: | |
3651 # Help pytype by hiding the interface stuff that confuses it. | |
3652 localrepository = LocalRepository | |
3653 | |
3654 | |
3601 def undoname(fn: bytes) -> bytes: | 3655 def undoname(fn: bytes) -> bytes: |
3602 base, name = os.path.split(fn) | 3656 base, name = os.path.split(fn) |
3603 assert name.startswith(b'journal') | 3657 assert name.startswith(b'journal') |
3604 return os.path.join(base, name.replace(b'journal', b'undo', 1)) | 3658 return os.path.join(base, name.replace(b'journal', b'undo', 1)) |
3605 | 3659 |