##// END OF EJS Templates
interfaces: convert `repository.ipeerconnection` from zope `Attribute` attrs...
Matt Harbison -
r53360:0cfcc276 default
parent child Browse files
Show More
@@ -1,2164 +1,2181
1 1 # repository.py - Interfaces and base classes for repositories and peers.
2 2 # coding: utf-8
3 3 #
4 4 # Copyright 2017 Gregory Szorc <gregory.szorc@gmail.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 from __future__ import annotations
10 10
11 import typing
12
11 13 from typing import (
14 Any,
12 15 Protocol,
13 16 )
14 17
15 18 from ..i18n import _
16 19 from .. import error
17 20 from . import util as interfaceutil
18 21
22 if typing.TYPE_CHECKING:
23 # Almost all mercurial modules are only imported in the type checking phase
24 # to avoid circular imports
25 from ..utils import (
26 urlutil,
27 )
28
29 # TODO: create a Protocol class, since importing uimod here causes a cycle
30 # that confuses pytype.
31 Ui = Any
32
19 33 # Local repository feature string.
20 34
21 35 # Revlogs are being used for file storage.
22 36 REPO_FEATURE_REVLOG_FILE_STORAGE = b'revlogfilestorage'
23 37 # The storage part of the repository is shared from an external source.
24 38 REPO_FEATURE_SHARED_STORAGE = b'sharedstore'
25 39 # LFS supported for backing file storage.
26 40 REPO_FEATURE_LFS = b'lfs'
27 41 # Repository supports being stream cloned.
28 42 REPO_FEATURE_STREAM_CLONE = b'streamclone'
29 43 # Repository supports (at least) some sidedata to be stored
30 44 REPO_FEATURE_SIDE_DATA = b'side-data'
31 45 # Files storage may lack data for all ancestors.
32 46 REPO_FEATURE_SHALLOW_FILE_STORAGE = b'shallowfilestorage'
33 47
34 48 REVISION_FLAG_CENSORED = 1 << 15
35 49 REVISION_FLAG_ELLIPSIS = 1 << 14
36 50 REVISION_FLAG_EXTSTORED = 1 << 13
37 51 REVISION_FLAG_HASCOPIESINFO = 1 << 12
38 52
39 53 REVISION_FLAGS_KNOWN = (
40 54 REVISION_FLAG_CENSORED
41 55 | REVISION_FLAG_ELLIPSIS
42 56 | REVISION_FLAG_EXTSTORED
43 57 | REVISION_FLAG_HASCOPIESINFO
44 58 )
45 59
46 60 CG_DELTAMODE_STD = b'default'
47 61 CG_DELTAMODE_PREV = b'previous'
48 62 CG_DELTAMODE_FULL = b'fulltext'
49 63 CG_DELTAMODE_P1 = b'p1'
50 64
51 65
52 66 ## Cache related constants:
53 67 #
54 68 # Used to control which cache should be warmed in a repo.updatecaches(…) call.
55 69
56 70 # Warm branchmaps of all known repoview's filter-level
57 71 CACHE_BRANCHMAP_ALL = b"branchmap-all"
58 72 # Warm branchmaps of repoview's filter-level used by server
59 73 CACHE_BRANCHMAP_SERVED = b"branchmap-served"
60 74 # Warm internal changelog cache (eg: persistent nodemap)
61 75 CACHE_CHANGELOG_CACHE = b"changelog-cache"
62 76 # check of a branchmap can use the "pure topo" mode
63 77 CACHE_BRANCHMAP_DETECT_PURE_TOPO = b"branchmap-detect-pure-topo"
64 78 # Warm full manifest cache
65 79 CACHE_FULL_MANIFEST = b"full-manifest"
66 80 # Warm file-node-tags cache
67 81 CACHE_FILE_NODE_TAGS = b"file-node-tags"
68 82 # Warm internal manifestlog cache (eg: persistent nodemap)
69 83 CACHE_MANIFESTLOG_CACHE = b"manifestlog-cache"
70 84 # Warn rev branch cache
71 85 CACHE_REV_BRANCH = b"rev-branch-cache"
72 86 # Warm tags' cache for default repoview'
73 87 CACHE_TAGS_DEFAULT = b"tags-default"
74 88 # Warm tags' cache for repoview's filter-level used by server
75 89 CACHE_TAGS_SERVED = b"tags-served"
76 90
77 91 # the cache to warm by default after a simple transaction
78 92 # (this is a mutable set to let extension update it)
79 93 CACHES_DEFAULT = {
80 94 CACHE_BRANCHMAP_SERVED,
81 95 }
82 96
83 97 # the caches to warm when warming all of them
84 98 # (this is a mutable set to let extension update it)
85 99 CACHES_ALL = {
86 100 CACHE_BRANCHMAP_SERVED,
87 101 CACHE_BRANCHMAP_ALL,
88 102 CACHE_BRANCHMAP_DETECT_PURE_TOPO,
89 103 CACHE_REV_BRANCH,
90 104 CACHE_CHANGELOG_CACHE,
91 105 CACHE_FILE_NODE_TAGS,
92 106 CACHE_FULL_MANIFEST,
93 107 CACHE_MANIFESTLOG_CACHE,
94 108 CACHE_TAGS_DEFAULT,
95 109 CACHE_TAGS_SERVED,
96 110 }
97 111
98 112 # the cache to warm by default on simple call
99 113 # (this is a mutable set to let extension update it)
100 114 CACHES_POST_CLONE = CACHES_ALL.copy()
101 115 CACHES_POST_CLONE.discard(CACHE_FILE_NODE_TAGS)
102 116 CACHES_POST_CLONE.discard(CACHE_REV_BRANCH)
103 117
104 118
105 119 class ipeerconnection(Protocol):
106 120 """Represents a "connection" to a repository.
107 121
108 122 This is the base interface for representing a connection to a repository.
109 123 It holds basic properties and methods applicable to all peer types.
110 124
111 125 This is not a complete interface definition and should not be used
112 126 outside of this module.
113 127 """
114 128
115 ui = interfaceutil.Attribute("""ui.ui instance""")
116 path = interfaceutil.Attribute("""a urlutil.path instance or None""")
129 ui: Ui
130 """ui.ui instance"""
131
132 path: urlutil.path | None
133 """a urlutil.path instance or None"""
117 134
118 135 def url(self):
119 136 """Returns a URL string representing this peer.
120 137
121 138 Currently, implementations expose the raw URL used to construct the
122 139 instance. It may contain credentials as part of the URL. The
123 140 expectations of the value aren't well-defined and this could lead to
124 141 data leakage.
125 142
126 143 TODO audit/clean consumers and more clearly define the contents of this
127 144 value.
128 145 """
129 146
130 147 def local(self):
131 148 """Returns a local repository instance.
132 149
133 150 If the peer represents a local repository, returns an object that
134 151 can be used to interface with it. Otherwise returns ``None``.
135 152 """
136 153
137 154 def canpush(self):
138 155 """Returns a boolean indicating if this peer can be pushed to."""
139 156
140 157 def close(self):
141 158 """Close the connection to this peer.
142 159
143 160 This is called when the peer will no longer be used. Resources
144 161 associated with the peer should be cleaned up.
145 162 """
146 163
147 164
148 165 class ipeercapabilities(Protocol):
149 166 """Peer sub-interface related to capabilities."""
150 167
151 168 def capable(self, name):
152 169 """Determine support for a named capability.
153 170
154 171 Returns ``False`` if capability not supported.
155 172
156 173 Returns ``True`` if boolean capability is supported. Returns a string
157 174 if capability support is non-boolean.
158 175
159 176 Capability strings may or may not map to wire protocol capabilities.
160 177 """
161 178
162 179 def requirecap(self, name, purpose):
163 180 """Require a capability to be present.
164 181
165 182 Raises a ``CapabilityError`` if the capability isn't present.
166 183 """
167 184
168 185
169 186 class ipeercommands(Protocol):
170 187 """Client-side interface for communicating over the wire protocol.
171 188
172 189 This interface is used as a gateway to the Mercurial wire protocol.
173 190 methods commonly call wire protocol commands of the same name.
174 191 """
175 192
176 193 def branchmap(self):
177 194 """Obtain heads in named branches.
178 195
179 196 Returns a dict mapping branch name to an iterable of nodes that are
180 197 heads on that branch.
181 198 """
182 199
183 200 def capabilities(self):
184 201 """Obtain capabilities of the peer.
185 202
186 203 Returns a set of string capabilities.
187 204 """
188 205
189 206 def get_cached_bundle_inline(self, path):
190 207 """Retrieve a clonebundle across the wire.
191 208
192 209 Returns a chunkbuffer
193 210 """
194 211
195 212 def clonebundles(self):
196 213 """Obtains the clone bundles manifest for the repo.
197 214
198 215 Returns the manifest as unparsed bytes.
199 216 """
200 217
201 218 def debugwireargs(self, one, two, three=None, four=None, five=None):
202 219 """Used to facilitate debugging of arguments passed over the wire."""
203 220
204 221 def getbundle(self, source, **kwargs):
205 222 """Obtain remote repository data as a bundle.
206 223
207 224 This command is how the bulk of repository data is transferred from
208 225 the peer to the local repository
209 226
210 227 Returns a generator of bundle data.
211 228 """
212 229
213 230 def heads(self):
214 231 """Determine all known head revisions in the peer.
215 232
216 233 Returns an iterable of binary nodes.
217 234 """
218 235
219 236 def known(self, nodes):
220 237 """Determine whether multiple nodes are known.
221 238
222 239 Accepts an iterable of nodes whose presence to check for.
223 240
224 241 Returns an iterable of booleans indicating of the corresponding node
225 242 at that index is known to the peer.
226 243 """
227 244
228 245 def listkeys(self, namespace):
229 246 """Obtain all keys in a pushkey namespace.
230 247
231 248 Returns an iterable of key names.
232 249 """
233 250
234 251 def lookup(self, key):
235 252 """Resolve a value to a known revision.
236 253
237 254 Returns a binary node of the resolved revision on success.
238 255 """
239 256
240 257 def pushkey(self, namespace, key, old, new):
241 258 """Set a value using the ``pushkey`` protocol.
242 259
243 260 Arguments correspond to the pushkey namespace and key to operate on and
244 261 the old and new values for that key.
245 262
246 263 Returns a string with the peer result. The value inside varies by the
247 264 namespace.
248 265 """
249 266
250 267 def stream_out(self):
251 268 """Obtain streaming clone data.
252 269
253 270 Successful result should be a generator of data chunks.
254 271 """
255 272
256 273 def unbundle(self, bundle, heads, url):
257 274 """Transfer repository data to the peer.
258 275
259 276 This is how the bulk of data during a push is transferred.
260 277
261 278 Returns the integer number of heads added to the peer.
262 279 """
263 280
264 281
265 282 class ipeerlegacycommands(Protocol):
266 283 """Interface for implementing support for legacy wire protocol commands.
267 284
268 285 Wire protocol commands transition to legacy status when they are no longer
269 286 used by modern clients. To facilitate identifying which commands are
270 287 legacy, the interfaces are split.
271 288 """
272 289
273 290 def between(self, pairs):
274 291 """Obtain nodes between pairs of nodes.
275 292
276 293 ``pairs`` is an iterable of node pairs.
277 294
278 295 Returns an iterable of iterables of nodes corresponding to each
279 296 requested pair.
280 297 """
281 298
282 299 def branches(self, nodes):
283 300 """Obtain ancestor changesets of specific nodes back to a branch point.
284 301
285 302 For each requested node, the peer finds the first ancestor node that is
286 303 a DAG root or is a merge.
287 304
288 305 Returns an iterable of iterables with the resolved values for each node.
289 306 """
290 307
291 308 def changegroup(self, nodes, source):
292 309 """Obtain a changegroup with data for descendants of specified nodes."""
293 310
294 311 def changegroupsubset(self, bases, heads, source):
295 312 pass
296 313
297 314
298 315 class ipeercommandexecutor(Protocol):
299 316 """Represents a mechanism to execute remote commands.
300 317
301 318 This is the primary interface for requesting that wire protocol commands
302 319 be executed. Instances of this interface are active in a context manager
303 320 and have a well-defined lifetime. When the context manager exits, all
304 321 outstanding requests are waited on.
305 322 """
306 323
307 324 def callcommand(self, name, args):
308 325 """Request that a named command be executed.
309 326
310 327 Receives the command name and a dictionary of command arguments.
311 328
312 329 Returns a ``concurrent.futures.Future`` that will resolve to the
313 330 result of that command request. That exact value is left up to
314 331 the implementation and possibly varies by command.
315 332
316 333 Not all commands can coexist with other commands in an executor
317 334 instance: it depends on the underlying wire protocol transport being
318 335 used and the command itself.
319 336
320 337 Implementations MAY call ``sendcommands()`` automatically if the
321 338 requested command can not coexist with other commands in this executor.
322 339
323 340 Implementations MAY call ``sendcommands()`` automatically when the
324 341 future's ``result()`` is called. So, consumers using multiple
325 342 commands with an executor MUST ensure that ``result()`` is not called
326 343 until all command requests have been issued.
327 344 """
328 345
329 346 def sendcommands(self):
330 347 """Trigger submission of queued command requests.
331 348
332 349 Not all transports submit commands as soon as they are requested to
333 350 run. When called, this method forces queued command requests to be
334 351 issued. It will no-op if all commands have already been sent.
335 352
336 353 When called, no more new commands may be issued with this executor.
337 354 """
338 355
339 356 def close(self):
340 357 """Signal that this command request is finished.
341 358
342 359 When called, no more new commands may be issued. All outstanding
343 360 commands that have previously been issued are waited on before
344 361 returning. This not only includes waiting for the futures to resolve,
345 362 but also waiting for all response data to arrive. In other words,
346 363 calling this waits for all on-wire state for issued command requests
347 364 to finish.
348 365
349 366 When used as a context manager, this method is called when exiting the
350 367 context manager.
351 368
352 369 This method may call ``sendcommands()`` if there are buffered commands.
353 370 """
354 371
355 372
356 373 class ipeerrequests(Protocol):
357 374 """Interface for executing commands on a peer."""
358 375
359 376 limitedarguments = interfaceutil.Attribute(
360 377 """True if the peer cannot receive large argument value for commands."""
361 378 )
362 379
363 380 def commandexecutor(self):
364 381 """A context manager that resolves to an ipeercommandexecutor.
365 382
366 383 The object this resolves to can be used to issue command requests
367 384 to the peer.
368 385
369 386 Callers should call its ``callcommand`` method to issue command
370 387 requests.
371 388
372 389 A new executor should be obtained for each distinct set of commands
373 390 (possibly just a single command) that the consumer wants to execute
374 391 as part of a single operation or round trip. This is because some
375 392 peers are half-duplex and/or don't support persistent connections.
376 393 e.g. in the case of HTTP peers, commands sent to an executor represent
377 394 a single HTTP request. While some peers may support multiple command
378 395 sends over the wire per executor, consumers need to code to the least
379 396 capable peer. So it should be assumed that command executors buffer
380 397 called commands until they are told to send them and that each
381 398 command executor could result in a new connection or wire-level request
382 399 being issued.
383 400 """
384 401
385 402
386 403 class ipeerbase(ipeerconnection, ipeercapabilities, ipeerrequests):
387 404 """Unified interface for peer repositories.
388 405
389 406 All peer instances must conform to this interface.
390 407 """
391 408
392 409
393 410 @interfaceutil.implementer(ipeerbase)
394 411 class peer:
395 412 """Base class for peer repositories."""
396 413
397 414 limitedarguments = False
398 415
399 416 def __init__(self, ui, path=None, remotehidden=False):
400 417 self.ui = ui
401 418 self.path = path
402 419
403 420 def capable(self, name):
404 421 # TODO: this class should maybe subclass ipeercommands too, otherwise it
405 422 # is assuming whatever uses this as a mixin also has this interface.
406 423 caps = self.capabilities() # pytype: disable=attribute-error
407 424 if name in caps:
408 425 return True
409 426
410 427 name = b'%s=' % name
411 428 for cap in caps:
412 429 if cap.startswith(name):
413 430 return cap[len(name) :]
414 431
415 432 return False
416 433
417 434 def requirecap(self, name, purpose):
418 435 if self.capable(name):
419 436 return
420 437
421 438 raise error.CapabilityError(
422 439 _(
423 440 b'cannot %s; remote repository does not support the '
424 441 b'\'%s\' capability'
425 442 )
426 443 % (purpose, name)
427 444 )
428 445
429 446
430 447 class iverifyproblem(Protocol):
431 448 """Represents a problem with the integrity of the repository.
432 449
433 450 Instances of this interface are emitted to describe an integrity issue
434 451 with a repository (e.g. corrupt storage, missing data, etc).
435 452
436 453 Instances are essentially messages associated with severity.
437 454 """
438 455
439 456 warning = interfaceutil.Attribute(
440 457 """Message indicating a non-fatal problem."""
441 458 )
442 459
443 460 error = interfaceutil.Attribute("""Message indicating a fatal problem.""")
444 461
445 462 node = interfaceutil.Attribute(
446 463 """Revision encountering the problem.
447 464
448 465 ``None`` means the problem doesn't apply to a single revision.
449 466 """
450 467 )
451 468
452 469
453 470 class irevisiondelta(Protocol):
454 471 """Represents a delta between one revision and another.
455 472
456 473 Instances convey enough information to allow a revision to be exchanged
457 474 with another repository.
458 475
459 476 Instances represent the fulltext revision data or a delta against
460 477 another revision. Therefore the ``revision`` and ``delta`` attributes
461 478 are mutually exclusive.
462 479
463 480 Typically used for changegroup generation.
464 481 """
465 482
466 483 node = interfaceutil.Attribute("""20 byte node of this revision.""")
467 484
468 485 p1node = interfaceutil.Attribute(
469 486 """20 byte node of 1st parent of this revision."""
470 487 )
471 488
472 489 p2node = interfaceutil.Attribute(
473 490 """20 byte node of 2nd parent of this revision."""
474 491 )
475 492
476 493 linknode = interfaceutil.Attribute(
477 494 """20 byte node of the changelog revision this node is linked to."""
478 495 )
479 496
480 497 flags = interfaceutil.Attribute(
481 498 """2 bytes of integer flags that apply to this revision.
482 499
483 500 This is a bitwise composition of the ``REVISION_FLAG_*`` constants.
484 501 """
485 502 )
486 503
487 504 basenode = interfaceutil.Attribute(
488 505 """20 byte node of the revision this data is a delta against.
489 506
490 507 ``nullid`` indicates that the revision is a full revision and not
491 508 a delta.
492 509 """
493 510 )
494 511
495 512 baserevisionsize = interfaceutil.Attribute(
496 513 """Size of base revision this delta is against.
497 514
498 515 May be ``None`` if ``basenode`` is ``nullid``.
499 516 """
500 517 )
501 518
502 519 revision = interfaceutil.Attribute(
503 520 """Raw fulltext of revision data for this node."""
504 521 )
505 522
506 523 delta = interfaceutil.Attribute(
507 524 """Delta between ``basenode`` and ``node``.
508 525
509 526 Stored in the bdiff delta format.
510 527 """
511 528 )
512 529
513 530 sidedata = interfaceutil.Attribute(
514 531 """Raw sidedata bytes for the given revision."""
515 532 )
516 533
517 534 protocol_flags = interfaceutil.Attribute(
518 535 """Single byte of integer flags that can influence the protocol.
519 536
520 537 This is a bitwise composition of the ``storageutil.CG_FLAG*`` constants.
521 538 """
522 539 )
523 540
524 541
525 542 class ifilerevisionssequence(Protocol):
526 543 """Contains index data for all revisions of a file.
527 544
528 545 Types implementing this behave like lists of tuples. The index
529 546 in the list corresponds to the revision number. The values contain
530 547 index metadata.
531 548
532 549 The *null* revision (revision number -1) is always the last item
533 550 in the index.
534 551 """
535 552
536 553 def __len__(self):
537 554 """The total number of revisions."""
538 555
539 556 def __getitem__(self, rev):
540 557 """Returns the object having a specific revision number.
541 558
542 559 Returns an 8-tuple with the following fields:
543 560
544 561 offset+flags
545 562 Contains the offset and flags for the revision. 64-bit unsigned
546 563 integer where first 6 bytes are the offset and the next 2 bytes
547 564 are flags. The offset can be 0 if it is not used by the store.
548 565 compressed size
549 566 Size of the revision data in the store. It can be 0 if it isn't
550 567 needed by the store.
551 568 uncompressed size
552 569 Fulltext size. It can be 0 if it isn't needed by the store.
553 570 base revision
554 571 Revision number of revision the delta for storage is encoded
555 572 against. -1 indicates not encoded against a base revision.
556 573 link revision
557 574 Revision number of changelog revision this entry is related to.
558 575 p1 revision
559 576 Revision number of 1st parent. -1 if no 1st parent.
560 577 p2 revision
561 578 Revision number of 2nd parent. -1 if no 1st parent.
562 579 node
563 580 Binary node value for this revision number.
564 581
565 582 Negative values should index off the end of the sequence. ``-1``
566 583 should return the null revision. ``-2`` should return the most
567 584 recent revision.
568 585 """
569 586
570 587 def __contains__(self, rev):
571 588 """Whether a revision number exists."""
572 589
573 590 def insert(self, i, entry):
574 591 """Add an item to the index at specific revision."""
575 592
576 593
577 594 class ifileindex(Protocol):
578 595 """Storage interface for index data of a single file.
579 596
580 597 File storage data is divided into index metadata and data storage.
581 598 This interface defines the index portion of the interface.
582 599
583 600 The index logically consists of:
584 601
585 602 * A mapping between revision numbers and nodes.
586 603 * DAG data (storing and querying the relationship between nodes).
587 604 * Metadata to facilitate storage.
588 605 """
589 606
590 607 nullid = interfaceutil.Attribute(
591 608 """node for the null revision for use as delta base."""
592 609 )
593 610
594 611 def __len__(self):
595 612 """Obtain the number of revisions stored for this file."""
596 613
597 614 def __iter__(self):
598 615 """Iterate over revision numbers for this file."""
599 616
600 617 def hasnode(self, node):
601 618 """Returns a bool indicating if a node is known to this store.
602 619
603 620 Implementations must only return True for full, binary node values:
604 621 hex nodes, revision numbers, and partial node matches must be
605 622 rejected.
606 623
607 624 The null node is never present.
608 625 """
609 626
610 627 def revs(self, start=0, stop=None):
611 628 """Iterate over revision numbers for this file, with control."""
612 629
613 630 def parents(self, node):
614 631 """Returns a 2-tuple of parent nodes for a revision.
615 632
616 633 Values will be ``nullid`` if the parent is empty.
617 634 """
618 635
619 636 def parentrevs(self, rev):
620 637 """Like parents() but operates on revision numbers."""
621 638
622 639 def rev(self, node):
623 640 """Obtain the revision number given a node.
624 641
625 642 Raises ``error.LookupError`` if the node is not known.
626 643 """
627 644
628 645 def node(self, rev):
629 646 """Obtain the node value given a revision number.
630 647
631 648 Raises ``IndexError`` if the node is not known.
632 649 """
633 650
634 651 def lookup(self, node):
635 652 """Attempt to resolve a value to a node.
636 653
637 654 Value can be a binary node, hex node, revision number, or a string
638 655 that can be converted to an integer.
639 656
640 657 Raises ``error.LookupError`` if a node could not be resolved.
641 658 """
642 659
643 660 def linkrev(self, rev):
644 661 """Obtain the changeset revision number a revision is linked to."""
645 662
646 663 def iscensored(self, rev):
647 664 """Return whether a revision's content has been censored."""
648 665
649 666 def commonancestorsheads(self, node1, node2):
650 667 """Obtain an iterable of nodes containing heads of common ancestors.
651 668
652 669 See ``ancestor.commonancestorsheads()``.
653 670 """
654 671
655 672 def descendants(self, revs):
656 673 """Obtain descendant revision numbers for a set of revision numbers.
657 674
658 675 If ``nullrev`` is in the set, this is equivalent to ``revs()``.
659 676 """
660 677
661 678 def heads(self, start=None, stop=None):
662 679 """Obtain a list of nodes that are DAG heads, with control.
663 680
664 681 The set of revisions examined can be limited by specifying
665 682 ``start`` and ``stop``. ``start`` is a node. ``stop`` is an
666 683 iterable of nodes. DAG traversal starts at earlier revision
667 684 ``start`` and iterates forward until any node in ``stop`` is
668 685 encountered.
669 686 """
670 687
671 688 def children(self, node):
672 689 """Obtain nodes that are children of a node.
673 690
674 691 Returns a list of nodes.
675 692 """
676 693
677 694
678 695 class ifiledata(Protocol):
679 696 """Storage interface for data storage of a specific file.
680 697
681 698 This complements ``ifileindex`` and provides an interface for accessing
682 699 data for a tracked file.
683 700 """
684 701
685 702 def size(self, rev):
686 703 """Obtain the fulltext size of file data.
687 704
688 705 Any metadata is excluded from size measurements.
689 706 """
690 707
691 708 def revision(self, node):
692 709 """Obtain fulltext data for a node.
693 710
694 711 By default, any storage transformations are applied before the data
695 712 is returned. If ``raw`` is True, non-raw storage transformations
696 713 are not applied.
697 714
698 715 The fulltext data may contain a header containing metadata. Most
699 716 consumers should use ``read()`` to obtain the actual file data.
700 717 """
701 718
702 719 def rawdata(self, node):
703 720 """Obtain raw data for a node."""
704 721
705 722 def read(self, node):
706 723 """Resolve file fulltext data.
707 724
708 725 This is similar to ``revision()`` except any metadata in the data
709 726 headers is stripped.
710 727 """
711 728
712 729 def renamed(self, node):
713 730 """Obtain copy metadata for a node.
714 731
715 732 Returns ``False`` if no copy metadata is stored or a 2-tuple of
716 733 (path, node) from which this revision was copied.
717 734 """
718 735
719 736 def cmp(self, node, fulltext):
720 737 """Compare fulltext to another revision.
721 738
722 739 Returns True if the fulltext is different from what is stored.
723 740
724 741 This takes copy metadata into account.
725 742
726 743 TODO better document the copy metadata and censoring logic.
727 744 """
728 745
729 746 def emitrevisions(
730 747 self,
731 748 nodes,
732 749 nodesorder=None,
733 750 revisiondata=False,
734 751 assumehaveparentrevisions=False,
735 752 deltamode=CG_DELTAMODE_STD,
736 753 ):
737 754 """Produce ``irevisiondelta`` for revisions.
738 755
739 756 Given an iterable of nodes, emits objects conforming to the
740 757 ``irevisiondelta`` interface that describe revisions in storage.
741 758
742 759 This method is a generator.
743 760
744 761 The input nodes may be unordered. Implementations must ensure that a
745 762 node's parents are emitted before the node itself. Transitively, this
746 763 means that a node may only be emitted once all its ancestors in
747 764 ``nodes`` have also been emitted.
748 765
749 766 By default, emits "index" data (the ``node``, ``p1node``, and
750 767 ``p2node`` attributes). If ``revisiondata`` is set, revision data
751 768 will also be present on the emitted objects.
752 769
753 770 With default argument values, implementations can choose to emit
754 771 either fulltext revision data or a delta. When emitting deltas,
755 772 implementations must consider whether the delta's base revision
756 773 fulltext is available to the receiver.
757 774
758 775 The base revision fulltext is guaranteed to be available if any of
759 776 the following are met:
760 777
761 778 * Its fulltext revision was emitted by this method call.
762 779 * A delta for that revision was emitted by this method call.
763 780 * ``assumehaveparentrevisions`` is True and the base revision is a
764 781 parent of the node.
765 782
766 783 ``nodesorder`` can be used to control the order that revisions are
767 784 emitted. By default, revisions can be reordered as long as they are
768 785 in DAG topological order (see above). If the value is ``nodes``,
769 786 the iteration order from ``nodes`` should be used. If the value is
770 787 ``storage``, then the native order from the backing storage layer
771 788 is used. (Not all storage layers will have strong ordering and behavior
772 789 of this mode is storage-dependent.) ``nodes`` ordering can force
773 790 revisions to be emitted before their ancestors, so consumers should
774 791 use it with care.
775 792
776 793 The ``linknode`` attribute on the returned ``irevisiondelta`` may not
777 794 be set and it is the caller's responsibility to resolve it, if needed.
778 795
779 796 If ``deltamode`` is CG_DELTAMODE_PREV and revision data is requested,
780 797 all revision data should be emitted as deltas against the revision
781 798 emitted just prior. The initial revision should be a delta against its
782 799 1st parent.
783 800 """
784 801
785 802
786 803 class ifilemutation(Protocol):
787 804 """Storage interface for mutation events of a tracked file."""
788 805
789 806 def add(self, filedata, meta, transaction, linkrev, p1, p2):
790 807 """Add a new revision to the store.
791 808
792 809 Takes file data, dictionary of metadata, a transaction, linkrev,
793 810 and parent nodes.
794 811
795 812 Returns the node that was added.
796 813
797 814 May no-op if a revision matching the supplied data is already stored.
798 815 """
799 816
800 817 def addrevision(
801 818 self,
802 819 revisiondata,
803 820 transaction,
804 821 linkrev,
805 822 p1,
806 823 p2,
807 824 node=None,
808 825 flags=0,
809 826 cachedelta=None,
810 827 ):
811 828 """Add a new revision to the store and return its number.
812 829
813 830 This is similar to ``add()`` except it operates at a lower level.
814 831
815 832 The data passed in already contains a metadata header, if any.
816 833
817 834 ``node`` and ``flags`` can be used to define the expected node and
818 835 the flags to use with storage. ``flags`` is a bitwise value composed
819 836 of the various ``REVISION_FLAG_*`` constants.
820 837
821 838 ``add()`` is usually called when adding files from e.g. the working
822 839 directory. ``addrevision()`` is often called by ``add()`` and for
823 840 scenarios where revision data has already been computed, such as when
824 841 applying raw data from a peer repo.
825 842 """
826 843
827 844 def addgroup(
828 845 self,
829 846 deltas,
830 847 linkmapper,
831 848 transaction,
832 849 addrevisioncb=None,
833 850 duplicaterevisioncb=None,
834 851 maybemissingparents=False,
835 852 ):
836 853 """Process a series of deltas for storage.
837 854
838 855 ``deltas`` is an iterable of 7-tuples of
839 856 (node, p1, p2, linknode, deltabase, delta, flags) defining revisions
840 857 to add.
841 858
842 859 The ``delta`` field contains ``mpatch`` data to apply to a base
843 860 revision, identified by ``deltabase``. The base node can be
844 861 ``nullid``, in which case the header from the delta can be ignored
845 862 and the delta used as the fulltext.
846 863
847 864 ``alwayscache`` instructs the lower layers to cache the content of the
848 865 newly added revision, even if it needs to be explicitly computed.
849 866 This used to be the default when ``addrevisioncb`` was provided up to
850 867 Mercurial 5.8.
851 868
852 869 ``addrevisioncb`` should be called for each new rev as it is committed.
853 870 ``duplicaterevisioncb`` should be called for all revs with a
854 871 pre-existing node.
855 872
856 873 ``maybemissingparents`` is a bool indicating whether the incoming
857 874 data may reference parents/ancestor revisions that aren't present.
858 875 This flag is set when receiving data into a "shallow" store that
859 876 doesn't hold all history.
860 877
861 878 Returns a list of nodes that were processed. A node will be in the list
862 879 even if it existed in the store previously.
863 880 """
864 881
865 882 def censorrevision(self, tr, node, tombstone=b''):
866 883 """Remove the content of a single revision.
867 884
868 885 The specified ``node`` will have its content purged from storage.
869 886 Future attempts to access the revision data for this node will
870 887 result in failure.
871 888
872 889 A ``tombstone`` message can optionally be stored. This message may be
873 890 displayed to users when they attempt to access the missing revision
874 891 data.
875 892
876 893 Storage backends may have stored deltas against the previous content
877 894 in this revision. As part of censoring a revision, these storage
878 895 backends are expected to rewrite any internally stored deltas such
879 896 that they no longer reference the deleted content.
880 897 """
881 898
882 899 def getstrippoint(self, minlink):
883 900 """Find the minimum revision that must be stripped to strip a linkrev.
884 901
885 902 Returns a 2-tuple containing the minimum revision number and a set
886 903 of all revisions numbers that would be broken by this strip.
887 904
888 905 TODO this is highly revlog centric and should be abstracted into
889 906 a higher-level deletion API. ``repair.strip()`` relies on this.
890 907 """
891 908
892 909 def strip(self, minlink, transaction):
893 910 """Remove storage of items starting at a linkrev.
894 911
895 912 This uses ``getstrippoint()`` to determine the first node to remove.
896 913 Then it effectively truncates storage for all revisions after that.
897 914
898 915 TODO this is highly revlog centric and should be abstracted into a
899 916 higher-level deletion API.
900 917 """
901 918
902 919
903 920 class ifilestorage(ifileindex, ifiledata, ifilemutation):
904 921 """Complete storage interface for a single tracked file."""
905 922
906 923 def files(self):
907 924 """Obtain paths that are backing storage for this file.
908 925
909 926 TODO this is used heavily by verify code and there should probably
910 927 be a better API for that.
911 928 """
912 929
913 930 def storageinfo(
914 931 self,
915 932 exclusivefiles=False,
916 933 sharedfiles=False,
917 934 revisionscount=False,
918 935 trackedsize=False,
919 936 storedsize=False,
920 937 ):
921 938 """Obtain information about storage for this file's data.
922 939
923 940 Returns a dict describing storage for this tracked path. The keys
924 941 in the dict map to arguments of the same. The arguments are bools
925 942 indicating whether to calculate and obtain that data.
926 943
927 944 exclusivefiles
928 945 Iterable of (vfs, path) describing files that are exclusively
929 946 used to back storage for this tracked path.
930 947
931 948 sharedfiles
932 949 Iterable of (vfs, path) describing files that are used to back
933 950 storage for this tracked path. Those files may also provide storage
934 951 for other stored entities.
935 952
936 953 revisionscount
937 954 Number of revisions available for retrieval.
938 955
939 956 trackedsize
940 957 Total size in bytes of all tracked revisions. This is a sum of the
941 958 length of the fulltext of all revisions.
942 959
943 960 storedsize
944 961 Total size in bytes used to store data for all tracked revisions.
945 962 This is commonly less than ``trackedsize`` due to internal usage
946 963 of deltas rather than fulltext revisions.
947 964
948 965 Not all storage backends may support all queries are have a reasonable
949 966 value to use. In that case, the value should be set to ``None`` and
950 967 callers are expected to handle this special value.
951 968 """
952 969
953 970 def verifyintegrity(self, state):
954 971 """Verifies the integrity of file storage.
955 972
956 973 ``state`` is a dict holding state of the verifier process. It can be
957 974 used to communicate data between invocations of multiple storage
958 975 primitives.
959 976
960 977 If individual revisions cannot have their revision content resolved,
961 978 the method is expected to set the ``skipread`` key to a set of nodes
962 979 that encountered problems. If set, the method can also add the node(s)
963 980 to ``safe_renamed`` in order to indicate nodes that may perform the
964 981 rename checks with currently accessible data.
965 982
966 983 The method yields objects conforming to the ``iverifyproblem``
967 984 interface.
968 985 """
969 986
970 987
971 988 class idirs(Protocol):
972 989 """Interface representing a collection of directories from paths.
973 990
974 991 This interface is essentially a derived data structure representing
975 992 directories from a collection of paths.
976 993 """
977 994
978 995 def addpath(self, path):
979 996 """Add a path to the collection.
980 997
981 998 All directories in the path will be added to the collection.
982 999 """
983 1000
984 1001 def delpath(self, path):
985 1002 """Remove a path from the collection.
986 1003
987 1004 If the removal was the last path in a particular directory, the
988 1005 directory is removed from the collection.
989 1006 """
990 1007
991 1008 def __iter__(self):
992 1009 """Iterate over the directories in this collection of paths."""
993 1010
994 1011 def __contains__(self, path):
995 1012 """Whether a specific directory is in this collection."""
996 1013
997 1014
998 1015 class imanifestdict(Protocol):
999 1016 """Interface representing a manifest data structure.
1000 1017
1001 1018 A manifest is effectively a dict mapping paths to entries. Each entry
1002 1019 consists of a binary node and extra flags affecting that entry.
1003 1020 """
1004 1021
1005 1022 def __getitem__(self, path):
1006 1023 """Returns the binary node value for a path in the manifest.
1007 1024
1008 1025 Raises ``KeyError`` if the path does not exist in the manifest.
1009 1026
1010 1027 Equivalent to ``self.find(path)[0]``.
1011 1028 """
1012 1029
1013 1030 def find(self, path):
1014 1031 """Returns the entry for a path in the manifest.
1015 1032
1016 1033 Returns a 2-tuple of (node, flags).
1017 1034
1018 1035 Raises ``KeyError`` if the path does not exist in the manifest.
1019 1036 """
1020 1037
1021 1038 def __len__(self):
1022 1039 """Return the number of entries in the manifest."""
1023 1040
1024 1041 def __nonzero__(self):
1025 1042 """Returns True if the manifest has entries, False otherwise."""
1026 1043
1027 1044 __bool__ = __nonzero__
1028 1045
1029 1046 def set(self, path, node, flags):
1030 1047 """Define the node value and flags for a path in the manifest.
1031 1048
1032 1049 Equivalent to __setitem__ followed by setflag, but can be more efficient.
1033 1050 """
1034 1051
1035 1052 def __setitem__(self, path, node):
1036 1053 """Define the node value for a path in the manifest.
1037 1054
1038 1055 If the path is already in the manifest, its flags will be copied to
1039 1056 the new entry.
1040 1057 """
1041 1058
1042 1059 def __contains__(self, path):
1043 1060 """Whether a path exists in the manifest."""
1044 1061
1045 1062 def __delitem__(self, path):
1046 1063 """Remove a path from the manifest.
1047 1064
1048 1065 Raises ``KeyError`` if the path is not in the manifest.
1049 1066 """
1050 1067
1051 1068 def __iter__(self):
1052 1069 """Iterate over paths in the manifest."""
1053 1070
1054 1071 def iterkeys(self):
1055 1072 """Iterate over paths in the manifest."""
1056 1073
1057 1074 def keys(self):
1058 1075 """Obtain a list of paths in the manifest."""
1059 1076
1060 1077 def filesnotin(self, other, match=None):
1061 1078 """Obtain the set of paths in this manifest but not in another.
1062 1079
1063 1080 ``match`` is an optional matcher function to be applied to both
1064 1081 manifests.
1065 1082
1066 1083 Returns a set of paths.
1067 1084 """
1068 1085
1069 1086 def dirs(self):
1070 1087 """Returns an object implementing the ``idirs`` interface."""
1071 1088
1072 1089 def hasdir(self, dir):
1073 1090 """Returns a bool indicating if a directory is in this manifest."""
1074 1091
1075 1092 def walk(self, match):
1076 1093 """Generator of paths in manifest satisfying a matcher.
1077 1094
1078 1095 If the matcher has explicit files listed and they don't exist in
1079 1096 the manifest, ``match.bad()`` is called for each missing file.
1080 1097 """
1081 1098
1082 1099 def diff(self, other, match=None, clean=False):
1083 1100 """Find differences between this manifest and another.
1084 1101
1085 1102 This manifest is compared to ``other``.
1086 1103
1087 1104 If ``match`` is provided, the two manifests are filtered against this
1088 1105 matcher and only entries satisfying the matcher are compared.
1089 1106
1090 1107 If ``clean`` is True, unchanged files are included in the returned
1091 1108 object.
1092 1109
1093 1110 Returns a dict with paths as keys and values of 2-tuples of 2-tuples of
1094 1111 the form ``((node1, flag1), (node2, flag2))`` where ``(node1, flag1)``
1095 1112 represents the node and flags for this manifest and ``(node2, flag2)``
1096 1113 are the same for the other manifest.
1097 1114 """
1098 1115
1099 1116 def setflag(self, path, flag):
1100 1117 """Set the flag value for a given path.
1101 1118
1102 1119 Raises ``KeyError`` if the path is not already in the manifest.
1103 1120 """
1104 1121
1105 1122 def get(self, path, default=None):
1106 1123 """Obtain the node value for a path or a default value if missing."""
1107 1124
1108 1125 def flags(self, path):
1109 1126 """Return the flags value for a path (default: empty bytestring)."""
1110 1127
1111 1128 def copy(self):
1112 1129 """Return a copy of this manifest."""
1113 1130
1114 1131 def items(self):
1115 1132 """Returns an iterable of (path, node) for items in this manifest."""
1116 1133
1117 1134 def iteritems(self):
1118 1135 """Identical to items()."""
1119 1136
1120 1137 def iterentries(self):
1121 1138 """Returns an iterable of (path, node, flags) for this manifest.
1122 1139
1123 1140 Similar to ``iteritems()`` except items are a 3-tuple and include
1124 1141 flags.
1125 1142 """
1126 1143
1127 1144 def text(self):
1128 1145 """Obtain the raw data representation for this manifest.
1129 1146
1130 1147 Result is used to create a manifest revision.
1131 1148 """
1132 1149
1133 1150 def fastdelta(self, base, changes):
1134 1151 """Obtain a delta between this manifest and another given changes.
1135 1152
1136 1153 ``base`` in the raw data representation for another manifest.
1137 1154
1138 1155 ``changes`` is an iterable of ``(path, to_delete)``.
1139 1156
1140 1157 Returns a 2-tuple containing ``bytearray(self.text())`` and the
1141 1158 delta between ``base`` and this manifest.
1142 1159
1143 1160 If this manifest implementation can't support ``fastdelta()``,
1144 1161 raise ``mercurial.manifest.FastdeltaUnavailable``.
1145 1162 """
1146 1163
1147 1164
1148 1165 class imanifestrevisionbase(Protocol):
1149 1166 """Base interface representing a single revision of a manifest.
1150 1167
1151 1168 Should not be used as a primary interface: should always be inherited
1152 1169 as part of a larger interface.
1153 1170 """
1154 1171
1155 1172 def copy(self):
1156 1173 """Obtain a copy of this manifest instance.
1157 1174
1158 1175 Returns an object conforming to the ``imanifestrevisionwritable``
1159 1176 interface. The instance will be associated with the same
1160 1177 ``imanifestlog`` collection as this instance.
1161 1178 """
1162 1179
1163 1180 def read(self):
1164 1181 """Obtain the parsed manifest data structure.
1165 1182
1166 1183 The returned object conforms to the ``imanifestdict`` interface.
1167 1184 """
1168 1185
1169 1186
1170 1187 class imanifestrevisionstored(imanifestrevisionbase):
1171 1188 """Interface representing a manifest revision committed to storage."""
1172 1189
1173 1190 def node(self):
1174 1191 """The binary node for this manifest."""
1175 1192
1176 1193 parents = interfaceutil.Attribute(
1177 1194 """List of binary nodes that are parents for this manifest revision."""
1178 1195 )
1179 1196
1180 1197 def readdelta(self, shallow=False):
1181 1198 """Obtain the manifest data structure representing changes from parent.
1182 1199
1183 1200 This manifest is compared to its 1st parent. A new manifest
1184 1201 representing those differences is constructed.
1185 1202
1186 1203 If `shallow` is True, this will read the delta for this directory,
1187 1204 without recursively reading subdirectory manifests. Instead, any
1188 1205 subdirectory entry will be reported as it appears in the manifest, i.e.
1189 1206 the subdirectory will be reported among files and distinguished only by
1190 1207 its 't' flag. This only apply if the underlying manifest support it.
1191 1208
1192 1209 The returned object conforms to the ``imanifestdict`` interface.
1193 1210 """
1194 1211
1195 1212 def read_any_fast_delta(self, valid_bases=None, *, shallow=False):
1196 1213 """read some manifest information as fast if possible
1197 1214
1198 1215 This might return a "delta", a manifest object containing only file
1199 1216 changed compared to another revisions. The `valid_bases` argument
1200 1217 control the set of revision that might be used as a base.
1201 1218
1202 1219 If no delta can be retrieved quickly, a full read of the manifest will
1203 1220 be performed instead.
1204 1221
1205 1222 The function return a tuple with two elements. The first one is the
1206 1223 delta base used (or None if we did a full read), the second one is the
1207 1224 manifest information.
1208 1225
1209 1226 If `shallow` is True, this will read the delta for this directory,
1210 1227 without recursively reading subdirectory manifests. Instead, any
1211 1228 subdirectory entry will be reported as it appears in the manifest, i.e.
1212 1229 the subdirectory will be reported among files and distinguished only by
1213 1230 its 't' flag. This only apply if the underlying manifest support it.
1214 1231
1215 1232 The returned object conforms to the ``imanifestdict`` interface.
1216 1233 """
1217 1234
1218 1235 def read_delta_parents(self, *, shallow=False, exact=True):
1219 1236 """return a diff from this revision against both parents.
1220 1237
1221 1238 If `exact` is False, this might return a superset of the diff, containing
1222 1239 files that are actually present as is in one of the parents.
1223 1240
1224 1241 If `shallow` is True, this will read the delta for this directory,
1225 1242 without recursively reading subdirectory manifests. Instead, any
1226 1243 subdirectory entry will be reported as it appears in the manifest, i.e.
1227 1244 the subdirectory will be reported among files and distinguished only by
1228 1245 its 't' flag. This only apply if the underlying manifest support it.
1229 1246
1230 1247 The returned object conforms to the ``imanifestdict`` interface."""
1231 1248
1232 1249 def read_delta_new_entries(self, *, shallow=False):
1233 1250 """Return a manifest containing just the entries that might be new to
1234 1251 the repository.
1235 1252
1236 1253 This is often equivalent to a diff against both parents, but without
1237 1254 garantee. For performance reason, It might contains more files in some cases.
1238 1255
1239 1256 If `shallow` is True, this will read the delta for this directory,
1240 1257 without recursively reading subdirectory manifests. Instead, any
1241 1258 subdirectory entry will be reported as it appears in the manifest, i.e.
1242 1259 the subdirectory will be reported among files and distinguished only by
1243 1260 its 't' flag. This only apply if the underlying manifest support it.
1244 1261
1245 1262 The returned object conforms to the ``imanifestdict`` interface."""
1246 1263
1247 1264 def readfast(self, shallow=False):
1248 1265 """Calls either ``read()`` or ``readdelta()``.
1249 1266
1250 1267 The faster of the two options is called.
1251 1268 """
1252 1269
1253 1270 def find(self, key):
1254 1271 """Calls self.read().find(key)``.
1255 1272
1256 1273 Returns a 2-tuple of ``(node, flags)`` or raises ``KeyError``.
1257 1274 """
1258 1275
1259 1276
1260 1277 class imanifestrevisionwritable(imanifestrevisionbase):
1261 1278 """Interface representing a manifest revision that can be committed."""
1262 1279
1263 1280 def write(
1264 1281 self, transaction, linkrev, p1node, p2node, added, removed, match=None
1265 1282 ):
1266 1283 """Add this revision to storage.
1267 1284
1268 1285 Takes a transaction object, the changeset revision number it will
1269 1286 be associated with, its parent nodes, and lists of added and
1270 1287 removed paths.
1271 1288
1272 1289 If match is provided, storage can choose not to inspect or write out
1273 1290 items that do not match. Storage is still required to be able to provide
1274 1291 the full manifest in the future for any directories written (these
1275 1292 manifests should not be "narrowed on disk").
1276 1293
1277 1294 Returns the binary node of the created revision.
1278 1295 """
1279 1296
1280 1297
1281 1298 class imanifeststorage(Protocol):
1282 1299 """Storage interface for manifest data."""
1283 1300
1284 1301 nodeconstants = interfaceutil.Attribute(
1285 1302 """nodeconstants used by the current repository."""
1286 1303 )
1287 1304
1288 1305 tree = interfaceutil.Attribute(
1289 1306 """The path to the directory this manifest tracks.
1290 1307
1291 1308 The empty bytestring represents the root manifest.
1292 1309 """
1293 1310 )
1294 1311
1295 1312 index = interfaceutil.Attribute(
1296 1313 """An ``ifilerevisionssequence`` instance."""
1297 1314 )
1298 1315
1299 1316 opener = interfaceutil.Attribute(
1300 1317 """VFS opener to use to access underlying files used for storage.
1301 1318
1302 1319 TODO this is revlog specific and should not be exposed.
1303 1320 """
1304 1321 )
1305 1322
1306 1323 fulltextcache = interfaceutil.Attribute(
1307 1324 """Dict with cache of fulltexts.
1308 1325
1309 1326 TODO this doesn't feel appropriate for the storage interface.
1310 1327 """
1311 1328 )
1312 1329
1313 1330 def __len__(self):
1314 1331 """Obtain the number of revisions stored for this manifest."""
1315 1332
1316 1333 def __iter__(self):
1317 1334 """Iterate over revision numbers for this manifest."""
1318 1335
1319 1336 def rev(self, node):
1320 1337 """Obtain the revision number given a binary node.
1321 1338
1322 1339 Raises ``error.LookupError`` if the node is not known.
1323 1340 """
1324 1341
1325 1342 def node(self, rev):
1326 1343 """Obtain the node value given a revision number.
1327 1344
1328 1345 Raises ``error.LookupError`` if the revision is not known.
1329 1346 """
1330 1347
1331 1348 def lookup(self, value):
1332 1349 """Attempt to resolve a value to a node.
1333 1350
1334 1351 Value can be a binary node, hex node, revision number, or a bytes
1335 1352 that can be converted to an integer.
1336 1353
1337 1354 Raises ``error.LookupError`` if a ndoe could not be resolved.
1338 1355 """
1339 1356
1340 1357 def parents(self, node):
1341 1358 """Returns a 2-tuple of parent nodes for a node.
1342 1359
1343 1360 Values will be ``nullid`` if the parent is empty.
1344 1361 """
1345 1362
1346 1363 def parentrevs(self, rev):
1347 1364 """Like parents() but operates on revision numbers."""
1348 1365
1349 1366 def linkrev(self, rev):
1350 1367 """Obtain the changeset revision number a revision is linked to."""
1351 1368
1352 1369 def revision(self, node):
1353 1370 """Obtain fulltext data for a node."""
1354 1371
1355 1372 def rawdata(self, node):
1356 1373 """Obtain raw data for a node."""
1357 1374
1358 1375 def revdiff(self, rev1, rev2):
1359 1376 """Obtain a delta between two revision numbers.
1360 1377
1361 1378 The returned data is the result of ``bdiff.bdiff()`` on the raw
1362 1379 revision data.
1363 1380 """
1364 1381
1365 1382 def cmp(self, node, fulltext):
1366 1383 """Compare fulltext to another revision.
1367 1384
1368 1385 Returns True if the fulltext is different from what is stored.
1369 1386 """
1370 1387
1371 1388 def emitrevisions(
1372 1389 self,
1373 1390 nodes,
1374 1391 nodesorder=None,
1375 1392 revisiondata=False,
1376 1393 assumehaveparentrevisions=False,
1377 1394 ):
1378 1395 """Produce ``irevisiondelta`` describing revisions.
1379 1396
1380 1397 See the documentation for ``ifiledata`` for more.
1381 1398 """
1382 1399
1383 1400 def addgroup(
1384 1401 self,
1385 1402 deltas,
1386 1403 linkmapper,
1387 1404 transaction,
1388 1405 addrevisioncb=None,
1389 1406 duplicaterevisioncb=None,
1390 1407 ):
1391 1408 """Process a series of deltas for storage.
1392 1409
1393 1410 See the documentation in ``ifilemutation`` for more.
1394 1411 """
1395 1412
1396 1413 def rawsize(self, rev):
1397 1414 """Obtain the size of tracked data.
1398 1415
1399 1416 Is equivalent to ``len(m.rawdata(node))``.
1400 1417
1401 1418 TODO this method is only used by upgrade code and may be removed.
1402 1419 """
1403 1420
1404 1421 def getstrippoint(self, minlink):
1405 1422 """Find minimum revision that must be stripped to strip a linkrev.
1406 1423
1407 1424 See the documentation in ``ifilemutation`` for more.
1408 1425 """
1409 1426
1410 1427 def strip(self, minlink, transaction):
1411 1428 """Remove storage of items starting at a linkrev.
1412 1429
1413 1430 See the documentation in ``ifilemutation`` for more.
1414 1431 """
1415 1432
1416 1433 def checksize(self):
1417 1434 """Obtain the expected sizes of backing files.
1418 1435
1419 1436 TODO this is used by verify and it should not be part of the interface.
1420 1437 """
1421 1438
1422 1439 def files(self):
1423 1440 """Obtain paths that are backing storage for this manifest.
1424 1441
1425 1442 TODO this is used by verify and there should probably be a better API
1426 1443 for this functionality.
1427 1444 """
1428 1445
1429 1446 def deltaparent(self, rev):
1430 1447 """Obtain the revision that a revision is delta'd against.
1431 1448
1432 1449 TODO delta encoding is an implementation detail of storage and should
1433 1450 not be exposed to the storage interface.
1434 1451 """
1435 1452
1436 1453 def clone(self, tr, dest, **kwargs):
1437 1454 """Clone this instance to another."""
1438 1455
1439 1456 def clearcaches(self, clear_persisted_data=False):
1440 1457 """Clear any caches associated with this instance."""
1441 1458
1442 1459 def dirlog(self, d):
1443 1460 """Obtain a manifest storage instance for a tree."""
1444 1461
1445 1462 def add(
1446 1463 self,
1447 1464 m,
1448 1465 transaction,
1449 1466 link,
1450 1467 p1,
1451 1468 p2,
1452 1469 added,
1453 1470 removed,
1454 1471 readtree=None,
1455 1472 match=None,
1456 1473 ):
1457 1474 """Add a revision to storage.
1458 1475
1459 1476 ``m`` is an object conforming to ``imanifestdict``.
1460 1477
1461 1478 ``link`` is the linkrev revision number.
1462 1479
1463 1480 ``p1`` and ``p2`` are the parent revision numbers.
1464 1481
1465 1482 ``added`` and ``removed`` are iterables of added and removed paths,
1466 1483 respectively.
1467 1484
1468 1485 ``readtree`` is a function that can be used to read the child tree(s)
1469 1486 when recursively writing the full tree structure when using
1470 1487 treemanifets.
1471 1488
1472 1489 ``match`` is a matcher that can be used to hint to storage that not all
1473 1490 paths must be inspected; this is an optimization and can be safely
1474 1491 ignored. Note that the storage must still be able to reproduce a full
1475 1492 manifest including files that did not match.
1476 1493 """
1477 1494
1478 1495 def storageinfo(
1479 1496 self,
1480 1497 exclusivefiles=False,
1481 1498 sharedfiles=False,
1482 1499 revisionscount=False,
1483 1500 trackedsize=False,
1484 1501 storedsize=False,
1485 1502 ):
1486 1503 """Obtain information about storage for this manifest's data.
1487 1504
1488 1505 See ``ifilestorage.storageinfo()`` for a description of this method.
1489 1506 This one behaves the same way, except for manifest data.
1490 1507 """
1491 1508
1492 1509 def get_revlog(self):
1493 1510 """return an actual revlog instance if any
1494 1511
1495 1512 This exist because a lot of code leverage the fact the underlying
1496 1513 storage is a revlog for optimization, so giving simple way to access
1497 1514 the revlog instance helps such code.
1498 1515 """
1499 1516
1500 1517
1501 1518 class imanifestlog(Protocol):
1502 1519 """Interface representing a collection of manifest snapshots.
1503 1520
1504 1521 Represents the root manifest in a repository.
1505 1522
1506 1523 Also serves as a means to access nested tree manifests and to cache
1507 1524 tree manifests.
1508 1525 """
1509 1526
1510 1527 nodeconstants = interfaceutil.Attribute(
1511 1528 """nodeconstants used by the current repository."""
1512 1529 )
1513 1530
1514 1531 narrowed = interfaceutil.Attribute(
1515 1532 """True, is the manifest is narrowed by a matcher"""
1516 1533 )
1517 1534
1518 1535 def __getitem__(self, node):
1519 1536 """Obtain a manifest instance for a given binary node.
1520 1537
1521 1538 Equivalent to calling ``self.get('', node)``.
1522 1539
1523 1540 The returned object conforms to the ``imanifestrevisionstored``
1524 1541 interface.
1525 1542 """
1526 1543
1527 1544 def get(self, tree, node, verify=True):
1528 1545 """Retrieve the manifest instance for a given directory and binary node.
1529 1546
1530 1547 ``node`` always refers to the node of the root manifest (which will be
1531 1548 the only manifest if flat manifests are being used).
1532 1549
1533 1550 If ``tree`` is the empty string, the root manifest is returned.
1534 1551 Otherwise the manifest for the specified directory will be returned
1535 1552 (requires tree manifests).
1536 1553
1537 1554 If ``verify`` is True, ``LookupError`` is raised if the node is not
1538 1555 known.
1539 1556
1540 1557 The returned object conforms to the ``imanifestrevisionstored``
1541 1558 interface.
1542 1559 """
1543 1560
1544 1561 def getstorage(self, tree):
1545 1562 """Retrieve an interface to storage for a particular tree.
1546 1563
1547 1564 If ``tree`` is the empty bytestring, storage for the root manifest will
1548 1565 be returned. Otherwise storage for a tree manifest is returned.
1549 1566
1550 1567 TODO formalize interface for returned object.
1551 1568 """
1552 1569
1553 1570 def clearcaches(self, clear_persisted_data: bool = False) -> None:
1554 1571 """Clear caches associated with this collection."""
1555 1572
1556 1573 def rev(self, node):
1557 1574 """Obtain the revision number for a binary node.
1558 1575
1559 1576 Raises ``error.LookupError`` if the node is not known.
1560 1577 """
1561 1578
1562 1579 def update_caches(self, transaction):
1563 1580 """update whatever cache are relevant for the used storage."""
1564 1581
1565 1582
1566 1583 class ilocalrepositoryfilestorage(Protocol):
1567 1584 """Local repository sub-interface providing access to tracked file storage.
1568 1585
1569 1586 This interface defines how a repository accesses storage for a single
1570 1587 tracked file path.
1571 1588 """
1572 1589
1573 1590 def file(self, f):
1574 1591 """Obtain a filelog for a tracked path.
1575 1592
1576 1593 The returned type conforms to the ``ifilestorage`` interface.
1577 1594 """
1578 1595
1579 1596
1580 1597 class ilocalrepositorymain(Protocol):
1581 1598 """Main interface for local repositories.
1582 1599
1583 1600 This currently captures the reality of things - not how things should be.
1584 1601 """
1585 1602
1586 1603 nodeconstants = interfaceutil.Attribute(
1587 1604 """Constant nodes matching the hash function used by the repository."""
1588 1605 )
1589 1606 nullid = interfaceutil.Attribute(
1590 1607 """null revision for the hash function used by the repository."""
1591 1608 )
1592 1609
1593 1610 supported = interfaceutil.Attribute(
1594 1611 """Set of requirements that this repo is capable of opening."""
1595 1612 )
1596 1613
1597 1614 requirements = interfaceutil.Attribute(
1598 1615 """Set of requirements this repo uses."""
1599 1616 )
1600 1617
1601 1618 features = interfaceutil.Attribute(
1602 1619 """Set of "features" this repository supports.
1603 1620
1604 1621 A "feature" is a loosely-defined term. It can refer to a feature
1605 1622 in the classical sense or can describe an implementation detail
1606 1623 of the repository. For example, a ``readonly`` feature may denote
1607 1624 the repository as read-only. Or a ``revlogfilestore`` feature may
1608 1625 denote that the repository is using revlogs for file storage.
1609 1626
1610 1627 The intent of features is to provide a machine-queryable mechanism
1611 1628 for repo consumers to test for various repository characteristics.
1612 1629
1613 1630 Features are similar to ``requirements``. The main difference is that
1614 1631 requirements are stored on-disk and represent requirements to open the
1615 1632 repository. Features are more run-time capabilities of the repository
1616 1633 and more granular capabilities (which may be derived from requirements).
1617 1634 """
1618 1635 )
1619 1636
1620 1637 filtername = interfaceutil.Attribute(
1621 1638 """Name of the repoview that is active on this repo."""
1622 1639 )
1623 1640
1624 1641 vfs_map = interfaceutil.Attribute(
1625 1642 """a bytes-key β†’ vfs mapping used by transaction and others"""
1626 1643 )
1627 1644
1628 1645 wvfs = interfaceutil.Attribute(
1629 1646 """VFS used to access the working directory."""
1630 1647 )
1631 1648
1632 1649 vfs = interfaceutil.Attribute(
1633 1650 """VFS rooted at the .hg directory.
1634 1651
1635 1652 Used to access repository data not in the store.
1636 1653 """
1637 1654 )
1638 1655
1639 1656 svfs = interfaceutil.Attribute(
1640 1657 """VFS rooted at the store.
1641 1658
1642 1659 Used to access repository data in the store. Typically .hg/store.
1643 1660 But can point elsewhere if the store is shared.
1644 1661 """
1645 1662 )
1646 1663
1647 1664 root = interfaceutil.Attribute(
1648 1665 """Path to the root of the working directory."""
1649 1666 )
1650 1667
1651 1668 path = interfaceutil.Attribute("""Path to the .hg directory.""")
1652 1669
1653 1670 origroot = interfaceutil.Attribute(
1654 1671 """The filesystem path that was used to construct the repo."""
1655 1672 )
1656 1673
1657 1674 auditor = interfaceutil.Attribute(
1658 1675 """A pathauditor for the working directory.
1659 1676
1660 1677 This checks if a path refers to a nested repository.
1661 1678
1662 1679 Operates on the filesystem.
1663 1680 """
1664 1681 )
1665 1682
1666 1683 nofsauditor = interfaceutil.Attribute(
1667 1684 """A pathauditor for the working directory.
1668 1685
1669 1686 This is like ``auditor`` except it doesn't do filesystem checks.
1670 1687 """
1671 1688 )
1672 1689
1673 1690 baseui = interfaceutil.Attribute(
1674 1691 """Original ui instance passed into constructor."""
1675 1692 )
1676 1693
1677 1694 ui = interfaceutil.Attribute("""Main ui instance for this instance.""")
1678 1695
1679 1696 sharedpath = interfaceutil.Attribute(
1680 1697 """Path to the .hg directory of the repo this repo was shared from."""
1681 1698 )
1682 1699
1683 1700 store = interfaceutil.Attribute("""A store instance.""")
1684 1701
1685 1702 spath = interfaceutil.Attribute("""Path to the store.""")
1686 1703
1687 1704 sjoin = interfaceutil.Attribute("""Alias to self.store.join.""")
1688 1705
1689 1706 cachevfs = interfaceutil.Attribute(
1690 1707 """A VFS used to access the cache directory.
1691 1708
1692 1709 Typically .hg/cache.
1693 1710 """
1694 1711 )
1695 1712
1696 1713 wcachevfs = interfaceutil.Attribute(
1697 1714 """A VFS used to access the cache directory dedicated to working copy
1698 1715
1699 1716 Typically .hg/wcache.
1700 1717 """
1701 1718 )
1702 1719
1703 1720 filteredrevcache = interfaceutil.Attribute(
1704 1721 """Holds sets of revisions to be filtered."""
1705 1722 )
1706 1723
1707 1724 names = interfaceutil.Attribute("""A ``namespaces`` instance.""")
1708 1725
1709 1726 filecopiesmode = interfaceutil.Attribute(
1710 1727 """The way files copies should be dealt with in this repo."""
1711 1728 )
1712 1729
1713 1730 def close(self):
1714 1731 """Close the handle on this repository."""
1715 1732
1716 1733 def peer(self, path=None):
1717 1734 """Obtain an object conforming to the ``peer`` interface."""
1718 1735
1719 1736 def unfiltered(self):
1720 1737 """Obtain an unfiltered/raw view of this repo."""
1721 1738
1722 1739 def filtered(self, name, visibilityexceptions=None):
1723 1740 """Obtain a named view of this repository."""
1724 1741
1725 1742 obsstore = interfaceutil.Attribute("""A store of obsolescence data.""")
1726 1743
1727 1744 changelog = interfaceutil.Attribute("""A handle on the changelog revlog.""")
1728 1745
1729 1746 manifestlog = interfaceutil.Attribute(
1730 1747 """An instance conforming to the ``imanifestlog`` interface.
1731 1748
1732 1749 Provides access to manifests for the repository.
1733 1750 """
1734 1751 )
1735 1752
1736 1753 dirstate = interfaceutil.Attribute("""Working directory state.""")
1737 1754
1738 1755 narrowpats = interfaceutil.Attribute(
1739 1756 """Matcher patterns for this repository's narrowspec."""
1740 1757 )
1741 1758
1742 1759 def narrowmatch(self, match=None, includeexact=False):
1743 1760 """Obtain a matcher for the narrowspec."""
1744 1761
1745 1762 def setnarrowpats(self, newincludes, newexcludes):
1746 1763 """Define the narrowspec for this repository."""
1747 1764
1748 1765 def __getitem__(self, changeid):
1749 1766 """Try to resolve a changectx."""
1750 1767
1751 1768 def __contains__(self, changeid):
1752 1769 """Whether a changeset exists."""
1753 1770
1754 1771 def __nonzero__(self):
1755 1772 """Always returns True."""
1756 1773 return True
1757 1774
1758 1775 __bool__ = __nonzero__
1759 1776
1760 1777 def __len__(self):
1761 1778 """Returns the number of changesets in the repo."""
1762 1779
1763 1780 def __iter__(self):
1764 1781 """Iterate over revisions in the changelog."""
1765 1782
1766 1783 def revs(self, expr, *args):
1767 1784 """Evaluate a revset.
1768 1785
1769 1786 Emits revisions.
1770 1787 """
1771 1788
1772 1789 def set(self, expr, *args):
1773 1790 """Evaluate a revset.
1774 1791
1775 1792 Emits changectx instances.
1776 1793 """
1777 1794
1778 1795 def anyrevs(self, specs, user=False, localalias=None):
1779 1796 """Find revisions matching one of the given revsets."""
1780 1797
1781 1798 def url(self):
1782 1799 """Returns a string representing the location of this repo."""
1783 1800
1784 1801 def hook(self, name, throw=False, **args):
1785 1802 """Call a hook."""
1786 1803
1787 1804 def tags(self):
1788 1805 """Return a mapping of tag to node."""
1789 1806
1790 1807 def tagtype(self, tagname):
1791 1808 """Return the type of a given tag."""
1792 1809
1793 1810 def tagslist(self):
1794 1811 """Return a list of tags ordered by revision."""
1795 1812
1796 1813 def nodetags(self, node):
1797 1814 """Return the tags associated with a node."""
1798 1815
1799 1816 def nodebookmarks(self, node):
1800 1817 """Return the list of bookmarks pointing to the specified node."""
1801 1818
1802 1819 def branchmap(self):
1803 1820 """Return a mapping of branch to heads in that branch."""
1804 1821
1805 1822 def revbranchcache(self):
1806 1823 pass
1807 1824
1808 1825 def register_changeset(self, rev, changelogrevision):
1809 1826 """Extension point for caches for new nodes.
1810 1827
1811 1828 Multiple consumers are expected to need parts of the changelogrevision,
1812 1829 so it is provided as optimization to avoid duplicate lookups. A simple
1813 1830 cache would be fragile when other revisions are accessed, too."""
1814 1831 pass
1815 1832
1816 1833 def branchtip(self, branchtip, ignoremissing=False):
1817 1834 """Return the tip node for a given branch."""
1818 1835
1819 1836 def lookup(self, key):
1820 1837 """Resolve the node for a revision."""
1821 1838
1822 1839 def lookupbranch(self, key):
1823 1840 """Look up the branch name of the given revision or branch name."""
1824 1841
1825 1842 def known(self, nodes):
1826 1843 """Determine whether a series of nodes is known.
1827 1844
1828 1845 Returns a list of bools.
1829 1846 """
1830 1847
1831 1848 def local(self):
1832 1849 """Whether the repository is local."""
1833 1850 return True
1834 1851
1835 1852 def publishing(self):
1836 1853 """Whether the repository is a publishing repository."""
1837 1854
1838 1855 def cancopy(self):
1839 1856 pass
1840 1857
1841 1858 def shared(self):
1842 1859 """The type of shared repository or None."""
1843 1860
1844 1861 def wjoin(self, f, *insidef):
1845 1862 """Calls self.vfs.reljoin(self.root, f, *insidef)"""
1846 1863
1847 1864 def setparents(self, p1, p2):
1848 1865 """Set the parent nodes of the working directory."""
1849 1866
1850 1867 def filectx(self, path, changeid=None, fileid=None):
1851 1868 """Obtain a filectx for the given file revision."""
1852 1869
1853 1870 def getcwd(self):
1854 1871 """Obtain the current working directory from the dirstate."""
1855 1872
1856 1873 def pathto(self, f, cwd=None):
1857 1874 """Obtain the relative path to a file."""
1858 1875
1859 1876 def adddatafilter(self, name, fltr):
1860 1877 pass
1861 1878
1862 1879 def wread(self, filename):
1863 1880 """Read a file from wvfs, using data filters."""
1864 1881
1865 1882 def wwrite(self, filename, data, flags, backgroundclose=False, **kwargs):
1866 1883 """Write data to a file in the wvfs, using data filters."""
1867 1884
1868 1885 def wwritedata(self, filename, data):
1869 1886 """Resolve data for writing to the wvfs, using data filters."""
1870 1887
1871 1888 def currenttransaction(self):
1872 1889 """Obtain the current transaction instance or None."""
1873 1890
1874 1891 def transaction(self, desc, report=None):
1875 1892 """Open a new transaction to write to the repository."""
1876 1893
1877 1894 def undofiles(self):
1878 1895 """Returns a list of (vfs, path) for files to undo transactions."""
1879 1896
1880 1897 def recover(self):
1881 1898 """Roll back an interrupted transaction."""
1882 1899
1883 1900 def rollback(self, dryrun=False, force=False):
1884 1901 """Undo the last transaction.
1885 1902
1886 1903 DANGEROUS.
1887 1904 """
1888 1905
1889 1906 def updatecaches(self, tr=None, full=False, caches=None):
1890 1907 """Warm repo caches."""
1891 1908
1892 1909 def invalidatecaches(self):
1893 1910 """Invalidate cached data due to the repository mutating."""
1894 1911
1895 1912 def invalidatevolatilesets(self):
1896 1913 pass
1897 1914
1898 1915 def invalidatedirstate(self):
1899 1916 """Invalidate the dirstate."""
1900 1917
1901 1918 def invalidate(self, clearfilecache=False):
1902 1919 pass
1903 1920
1904 1921 def invalidateall(self):
1905 1922 pass
1906 1923
1907 1924 def lock(self, wait=True):
1908 1925 """Lock the repository store and return a lock instance."""
1909 1926
1910 1927 def currentlock(self):
1911 1928 """Return the lock if it's held or None."""
1912 1929
1913 1930 def wlock(self, wait=True):
1914 1931 """Lock the non-store parts of the repository."""
1915 1932
1916 1933 def currentwlock(self):
1917 1934 """Return the wlock if it's held or None."""
1918 1935
1919 1936 def checkcommitpatterns(self, wctx, match, status, fail):
1920 1937 pass
1921 1938
1922 1939 def commit(
1923 1940 self,
1924 1941 text=b'',
1925 1942 user=None,
1926 1943 date=None,
1927 1944 match=None,
1928 1945 force=False,
1929 1946 editor=False,
1930 1947 extra=None,
1931 1948 ):
1932 1949 """Add a new revision to the repository."""
1933 1950
1934 1951 def commitctx(self, ctx, error=False, origctx=None):
1935 1952 """Commit a commitctx instance to the repository."""
1936 1953
1937 1954 def destroying(self):
1938 1955 """Inform the repository that nodes are about to be destroyed."""
1939 1956
1940 1957 def destroyed(self):
1941 1958 """Inform the repository that nodes have been destroyed."""
1942 1959
1943 1960 def status(
1944 1961 self,
1945 1962 node1=b'.',
1946 1963 node2=None,
1947 1964 match=None,
1948 1965 ignored=False,
1949 1966 clean=False,
1950 1967 unknown=False,
1951 1968 listsubrepos=False,
1952 1969 ):
1953 1970 """Convenience method to call repo[x].status()."""
1954 1971
1955 1972 def addpostdsstatus(self, ps):
1956 1973 pass
1957 1974
1958 1975 def postdsstatus(self):
1959 1976 pass
1960 1977
1961 1978 def clearpostdsstatus(self):
1962 1979 pass
1963 1980
1964 1981 def heads(self, start=None):
1965 1982 """Obtain list of nodes that are DAG heads."""
1966 1983
1967 1984 def branchheads(self, branch=None, start=None, closed=False):
1968 1985 pass
1969 1986
1970 1987 def branches(self, nodes):
1971 1988 pass
1972 1989
1973 1990 def between(self, pairs):
1974 1991 pass
1975 1992
1976 1993 def checkpush(self, pushop):
1977 1994 pass
1978 1995
1979 1996 prepushoutgoinghooks = interfaceutil.Attribute("""util.hooks instance.""")
1980 1997
1981 1998 def pushkey(self, namespace, key, old, new):
1982 1999 pass
1983 2000
1984 2001 def listkeys(self, namespace):
1985 2002 pass
1986 2003
1987 2004 def debugwireargs(self, one, two, three=None, four=None, five=None):
1988 2005 pass
1989 2006
1990 2007 def savecommitmessage(self, text):
1991 2008 pass
1992 2009
1993 2010 def register_sidedata_computer(
1994 2011 self, kind, category, keys, computer, flags, replace=False
1995 2012 ):
1996 2013 pass
1997 2014
1998 2015 def register_wanted_sidedata(self, category):
1999 2016 pass
2000 2017
2001 2018
2002 2019 class completelocalrepository(
2003 2020 ilocalrepositorymain, ilocalrepositoryfilestorage
2004 2021 ):
2005 2022 """Complete interface for a local repository."""
2006 2023
2007 2024
2008 2025 class iwireprotocolcommandcacher(Protocol):
2009 2026 """Represents a caching backend for wire protocol commands.
2010 2027
2011 2028 Wire protocol version 2 supports transparent caching of many commands.
2012 2029 To leverage this caching, servers can activate objects that cache
2013 2030 command responses. Objects handle both cache writing and reading.
2014 2031 This interface defines how that response caching mechanism works.
2015 2032
2016 2033 Wire protocol version 2 commands emit a series of objects that are
2017 2034 serialized and sent to the client. The caching layer exists between
2018 2035 the invocation of the command function and the sending of its output
2019 2036 objects to an output layer.
2020 2037
2021 2038 Instances of this interface represent a binding to a cache that
2022 2039 can serve a response (in place of calling a command function) and/or
2023 2040 write responses to a cache for subsequent use.
2024 2041
2025 2042 When a command request arrives, the following happens with regards
2026 2043 to this interface:
2027 2044
2028 2045 1. The server determines whether the command request is cacheable.
2029 2046 2. If it is, an instance of this interface is spawned.
2030 2047 3. The cacher is activated in a context manager (``__enter__`` is called).
2031 2048 4. A cache *key* for that request is derived. This will call the
2032 2049 instance's ``adjustcachekeystate()`` method so the derivation
2033 2050 can be influenced.
2034 2051 5. The cacher is informed of the derived cache key via a call to
2035 2052 ``setcachekey()``.
2036 2053 6. The cacher's ``lookup()`` method is called to test for presence of
2037 2054 the derived key in the cache.
2038 2055 7. If ``lookup()`` returns a hit, that cached result is used in place
2039 2056 of invoking the command function. ``__exit__`` is called and the instance
2040 2057 is discarded.
2041 2058 8. The command function is invoked.
2042 2059 9. ``onobject()`` is called for each object emitted by the command
2043 2060 function.
2044 2061 10. After the final object is seen, ``onfinished()`` is called.
2045 2062 11. ``__exit__`` is called to signal the end of use of the instance.
2046 2063
2047 2064 Cache *key* derivation can be influenced by the instance.
2048 2065
2049 2066 Cache keys are initially derived by a deterministic representation of
2050 2067 the command request. This includes the command name, arguments, protocol
2051 2068 version, etc. This initial key derivation is performed by CBOR-encoding a
2052 2069 data structure and feeding that output into a hasher.
2053 2070
2054 2071 Instances of this interface can influence this initial key derivation
2055 2072 via ``adjustcachekeystate()``.
2056 2073
2057 2074 The instance is informed of the derived cache key via a call to
2058 2075 ``setcachekey()``. The instance must store the key locally so it can
2059 2076 be consulted on subsequent operations that may require it.
2060 2077
2061 2078 When constructed, the instance has access to a callable that can be used
2062 2079 for encoding response objects. This callable receives as its single
2063 2080 argument an object emitted by a command function. It returns an iterable
2064 2081 of bytes chunks representing the encoded object. Unless the cacher is
2065 2082 caching native Python objects in memory or has a way of reconstructing
2066 2083 the original Python objects, implementations typically call this function
2067 2084 to produce bytes from the output objects and then store those bytes in
2068 2085 the cache. When it comes time to re-emit those bytes, they are wrapped
2069 2086 in a ``wireprototypes.encodedresponse`` instance to tell the output
2070 2087 layer that they are pre-encoded.
2071 2088
2072 2089 When receiving the objects emitted by the command function, instances
2073 2090 can choose what to do with those objects. The simplest thing to do is
2074 2091 re-emit the original objects. They will be forwarded to the output
2075 2092 layer and will be processed as if the cacher did not exist.
2076 2093
2077 2094 Implementations could also choose to not emit objects - instead locally
2078 2095 buffering objects or their encoded representation. They could then emit
2079 2096 a single "coalesced" object when ``onfinished()`` is called. In
2080 2097 this way, the implementation would function as a filtering layer of
2081 2098 sorts.
2082 2099
2083 2100 When caching objects, typically the encoded form of the object will
2084 2101 be stored. Keep in mind that if the original object is forwarded to
2085 2102 the output layer, it will need to be encoded there as well. For large
2086 2103 output, this redundant encoding could add overhead. Implementations
2087 2104 could wrap the encoded object data in ``wireprototypes.encodedresponse``
2088 2105 instances to avoid this overhead.
2089 2106 """
2090 2107
2091 2108 def __enter__(self):
2092 2109 """Marks the instance as active.
2093 2110
2094 2111 Should return self.
2095 2112 """
2096 2113
2097 2114 def __exit__(self, exctype, excvalue, exctb):
2098 2115 """Called when cacher is no longer used.
2099 2116
2100 2117 This can be used by implementations to perform cleanup actions (e.g.
2101 2118 disconnecting network sockets, aborting a partially cached response.
2102 2119 """
2103 2120
2104 2121 def adjustcachekeystate(self, state):
2105 2122 """Influences cache key derivation by adjusting state to derive key.
2106 2123
2107 2124 A dict defining the state used to derive the cache key is passed.
2108 2125
2109 2126 Implementations can modify this dict to record additional state that
2110 2127 is wanted to influence key derivation.
2111 2128
2112 2129 Implementations are *highly* encouraged to not modify or delete
2113 2130 existing keys.
2114 2131 """
2115 2132
2116 2133 def setcachekey(self, key):
2117 2134 """Record the derived cache key for this request.
2118 2135
2119 2136 Instances may mutate the key for internal usage, as desired. e.g.
2120 2137 instances may wish to prepend the repo name, introduce path
2121 2138 components for filesystem or URL addressing, etc. Behavior is up to
2122 2139 the cache.
2123 2140
2124 2141 Returns a bool indicating if the request is cacheable by this
2125 2142 instance.
2126 2143 """
2127 2144
2128 2145 def lookup(self):
2129 2146 """Attempt to resolve an entry in the cache.
2130 2147
2131 2148 The instance is instructed to look for the cache key that it was
2132 2149 informed about via the call to ``setcachekey()``.
2133 2150
2134 2151 If there's no cache hit or the cacher doesn't wish to use the cached
2135 2152 entry, ``None`` should be returned.
2136 2153
2137 2154 Else, a dict defining the cached result should be returned. The
2138 2155 dict may have the following keys:
2139 2156
2140 2157 objs
2141 2158 An iterable of objects that should be sent to the client. That
2142 2159 iterable of objects is expected to be what the command function
2143 2160 would return if invoked or an equivalent representation thereof.
2144 2161 """
2145 2162
2146 2163 def onobject(self, obj):
2147 2164 """Called when a new object is emitted from the command function.
2148 2165
2149 2166 Receives as its argument the object that was emitted from the
2150 2167 command function.
2151 2168
2152 2169 This method returns an iterator of objects to forward to the output
2153 2170 layer. The easiest implementation is a generator that just
2154 2171 ``yield obj``.
2155 2172 """
2156 2173
2157 2174 def onfinished(self):
2158 2175 """Called after all objects have been emitted from the command function.
2159 2176
2160 2177 Implementations should return an iterator of objects to forward to
2161 2178 the output layer.
2162 2179
2163 2180 This method can be a generator.
2164 2181 """
General Comments 0
You need to be logged in to leave comments. Login now