##// END OF EJS Templates
interfaces: mark a few dirstate methods abstract...
Matt Harbison -
r53328:2c8c46c3 default
parent child Browse files
Show More
@@ -1,334 +1,337
1 1 from __future__ import annotations
2 2
3 import abc
3 4 import contextlib
4 5 import os
5 6 import typing
6 7
7 8 from typing import (
8 9 Any,
9 10 Callable,
10 11 Dict,
11 12 Iterable,
12 13 Iterator,
13 14 List,
14 15 Optional,
15 16 Protocol,
16 17 Tuple,
17 18 )
18 19
19 20 if typing.TYPE_CHECKING:
20 21 # Almost all mercurial modules are only imported in the type checking phase
21 22 # to avoid circular imports
22 23 from .. import (
23 24 match as matchmod,
24 25 scmutil,
25 26 transaction as txnmod,
26 27 )
27 28
28 29 # TODO: finish adding type hints
29 30 AddParentChangeCallbackT = Callable[
30 31 ["idirstate", Tuple[Any, Any], Tuple[Any, Any]], Any
31 32 ]
32 33 """The callback type for dirstate.addparentchangecallback()."""
33 34
34 35 # TODO: add a Protocol for dirstatemap.DirStateItem? (It is
35 36 # conditionalized with python or rust implementations. Also,
36 37 # git.dirstate needs to yield non-None from ``items()``.)
37 38 DirstateItemT = Any # dirstatemap.DirstateItem
38 39
39 40 IgnoreFileAndLineT = Tuple[Optional[bytes], int, bytes]
40 41 """The return type of dirstate._ignorefileandline(), which holds
41 42 ``(file, lineno, originalline)``.
42 43 """
43 44
44 45 FlagFuncFallbackT = Callable[[], "FlagFuncReturnT"]
45 46 """The type for the dirstate.flagfunc() fallback function."""
46 47
47 48 FlagFuncReturnT = Callable[[bytes], bytes]
48 49 """The return type of dirstate.flagfunc()."""
49 50
50 51 # TODO: verify and complete this- it came from a pytype *.pyi file
51 52 StatusReturnT = Tuple[Any, scmutil.status, Any]
52 53 """The return type of dirstate.status()."""
53 54
54 55 # TODO: probably doesn't belong here.
55 56 TransactionT = txnmod.transaction
56 57 """The type for a transaction used with dirstate.
57 58
58 59 This is meant to help callers avoid having to remember to delay the import
59 60 of the transaction module.
60 61 """
61 62
62 63 # TODO: The value can also be mercurial.osutil.stat
63 64 WalkReturnT = Dict[bytes, Optional[os.stat_result]]
64 65 """The return type of dirstate.walk().
65 66
66 67 The matched files are keyed in the dictionary, mapped to a stat-like object
67 68 if the file exists.
68 69 """
69 70
70 71
71 72 class idirstate(Protocol):
72 73 # TODO: convert these constructor args to fields?
73 74 # def __init__(
74 75 # self,
75 76 # opener,
76 77 # ui,
77 78 # root,
78 79 # validate,
79 80 # sparsematchfn,
80 81 # nodeconstants,
81 82 # use_dirstate_v2,
82 83 # use_tracked_hint=False,
83 84 # ):
84 85 # """Create a new dirstate object.
85 86 #
86 87 # opener is an open()-like callable that can be used to open the
87 88 # dirstate file; root is the root of the directory tracked by
88 89 # the dirstate.
89 90 # """
90 91
91 92 # TODO: all these private methods and attributes should be made
92 93 # public or removed from the interface.
93 94
94 95 # TODO: decorate with `@rootcache(b'.hgignore')` like dirstate class?
95 96 @property
96 97 def _ignore(self) -> matchmod.basematcher:
97 98 """Matcher for ignored files."""
98 99
99 100 @property
100 101 def is_changing_any(self) -> bool:
101 102 """True if any changes in progress."""
102 103
103 104 @property
104 105 def is_changing_parents(self) -> bool:
105 106 """True if parents changes in progress."""
106 107
107 108 @property
108 109 def is_changing_files(self) -> bool:
109 110 """True if file tracking changes in progress."""
110 111
111 112 def _ignorefiles(self) -> List[bytes]:
112 113 """Return a list of files containing patterns to ignore."""
113 114
114 115 def _ignorefileandline(self, f: bytes) -> IgnoreFileAndLineT:
115 116 """Given a file `f`, return the ignore file and line that ignores it."""
116 117
117 118 # TODO: decorate with `@util.propertycache` like dirstate class?
118 119 # (can't because circular import)
119 120 @property
120 121 def _checklink(self) -> bool:
121 122 """Callable for checking symlinks.""" # TODO: this comment looks stale
122 123
123 124 # TODO: decorate with `@util.propertycache` like dirstate class?
124 125 # (can't because circular import)
125 126 @property
126 127 def _checkexec(self) -> bool:
127 128 """Callable for checking exec bits.""" # TODO: this comment looks stale
128 129
129 130 @contextlib.contextmanager
131 @abc.abstractmethod
130 132 def changing_parents(self, repo) -> Iterator: # TODO: typehint this
131 133 """Context manager for handling dirstate parents.
132 134
133 135 If an exception occurs in the scope of the context manager,
134 136 the incoherent dirstate won't be written when wlock is
135 137 released.
136 138 """
137 139
138 140 @contextlib.contextmanager
141 @abc.abstractmethod
139 142 def changing_files(self, repo) -> Iterator: # TODO: typehint this
140 143 """Context manager for handling dirstate files.
141 144
142 145 If an exception occurs in the scope of the context manager,
143 146 the incoherent dirstate won't be written when wlock is
144 147 released.
145 148 """
146 149
147 150 def hasdir(self, d: bytes) -> bool:
148 151 pass
149 152
150 153 def flagfunc(self, buildfallback: FlagFuncFallbackT) -> FlagFuncReturnT:
151 154 """build a callable that returns flags associated with a filename
152 155
153 156 The information is extracted from three possible layers:
154 157 1. the file system if it supports the information
155 158 2. the "fallback" information stored in the dirstate if any
156 159 3. a more expensive mechanism inferring the flags from the parents.
157 160 """
158 161
159 162 def getcwd(self) -> bytes:
160 163 """Return the path from which a canonical path is calculated.
161 164
162 165 This path should be used to resolve file patterns or to convert
163 166 canonical paths back to file paths for display. It shouldn't be
164 167 used to get real file paths. Use vfs functions instead.
165 168 """
166 169
167 170 def pathto(self, f: bytes, cwd: Optional[bytes] = None) -> bytes:
168 171 pass
169 172
170 173 def get_entry(self, path: bytes) -> DirstateItemT:
171 174 """return a DirstateItem for the associated path"""
172 175
173 176 def __contains__(self, key: Any) -> bool:
174 177 """Check if bytestring `key` is known to the dirstate."""
175 178
176 179 def __iter__(self) -> Iterator[bytes]:
177 180 """Iterate the dirstate's contained filenames as bytestrings."""
178 181
179 182 def items(self) -> Iterator[Tuple[bytes, DirstateItemT]]:
180 183 """Iterate the dirstate's entries as (filename, DirstateItem.
181 184
182 185 As usual, filename is a bytestring.
183 186 """
184 187
185 188 iteritems = items
186 189
187 190 def parents(self) -> List[bytes]:
188 191 pass
189 192
190 193 def p1(self) -> bytes:
191 194 pass
192 195
193 196 def p2(self) -> bytes:
194 197 pass
195 198
196 199 def branch(self) -> bytes:
197 200 pass
198 201
199 202 # TODO: typehint the return. It's a copies Map of some sort.
200 203 def setparents(self, p1: bytes, p2: Optional[bytes] = None):
201 204 """Set dirstate parents to p1 and p2.
202 205
203 206 When moving from two parents to one, "merged" entries a
204 207 adjusted to normal and previous copy records discarded and
205 208 returned by the call.
206 209
207 210 See localrepo.setparents()
208 211 """
209 212
210 213 def setbranch(
211 214 self, branch: bytes, transaction: Optional[TransactionT]
212 215 ) -> None:
213 216 pass
214 217
215 218 def invalidate(self) -> None:
216 219 """Causes the next access to reread the dirstate.
217 220
218 221 This is different from localrepo.invalidatedirstate() because it always
219 222 rereads the dirstate. Use localrepo.invalidatedirstate() if you want to
220 223 check whether the dirstate has changed before rereading it."""
221 224
222 225 def copy(self, source: Optional[bytes], dest: bytes) -> None:
223 226 """Mark dest as a copy of source. Unmark dest if source is None."""
224 227
225 228 def copied(self, file: bytes) -> Optional[bytes]:
226 229 pass
227 230
228 231 def copies(self) -> Dict[bytes, bytes]:
229 232 pass
230 233
231 234 def normalize(
232 235 self, path: bytes, isknown: bool = False, ignoremissing: bool = False
233 236 ) -> bytes:
234 237 """
235 238 normalize the case of a pathname when on a casefolding filesystem
236 239
237 240 isknown specifies whether the filename came from walking the
238 241 disk, to avoid extra filesystem access.
239 242
240 243 If ignoremissing is True, missing path are returned
241 244 unchanged. Otherwise, we try harder to normalize possibly
242 245 existing path components.
243 246
244 247 The normalized case is determined based on the following precedence:
245 248
246 249 - version of name already stored in the dirstate
247 250 - version of name stored on disk
248 251 - version provided via command arguments
249 252 """
250 253
251 254 def clear(self) -> None:
252 255 pass
253 256
254 257 def rebuild(
255 258 self,
256 259 parent: bytes,
257 260 allfiles: Iterable[bytes], # TODO: more than iterable? (uses len())
258 261 changedfiles: Optional[Iterable[bytes]] = None,
259 262 ) -> None:
260 263 pass
261 264
262 265 def write(self, tr: Optional[TransactionT]) -> None:
263 266 pass
264 267
265 268 def addparentchangecallback(
266 269 self, category: bytes, callback: AddParentChangeCallbackT
267 270 ) -> None:
268 271 """add a callback to be called when the wd parents are changed
269 272
270 273 Callback will be called with the following arguments:
271 274 dirstate, (oldp1, oldp2), (newp1, newp2)
272 275
273 276 Category is a unique identifier to allow overwriting an old callback
274 277 with a newer callback.
275 278 """
276 279
277 280 def walk(
278 281 self,
279 282 match: matchmod.basematcher,
280 283 subrepos: Any, # TODO: figure out what this is
281 284 unknown: bool,
282 285 ignored: bool,
283 286 full: bool = True,
284 287 ) -> WalkReturnT:
285 288 """
286 289 Walk recursively through the directory tree, finding all files
287 290 matched by match.
288 291
289 292 If full is False, maybe skip some known-clean files.
290 293
291 294 Return a dict mapping filename to stat-like object (either
292 295 mercurial.osutil.stat instance or return value of os.stat()).
293 296
294 297 """
295 298
296 299 def status(
297 300 self,
298 301 match: matchmod.basematcher,
299 302 subrepos: bool,
300 303 ignored: bool,
301 304 clean: bool,
302 305 unknown: bool,
303 306 ) -> StatusReturnT:
304 307 """Determine the status of the working copy relative to the
305 308 dirstate and return a pair of (unsure, status), where status is of type
306 309 scmutil.status and:
307 310
308 311 unsure:
309 312 files that might have been modified since the dirstate was
310 313 written, but need to be read to be sure (size is the same
311 314 but mtime differs)
312 315 status.modified:
313 316 files that have definitely been modified since the dirstate
314 317 was written (different size or mode)
315 318 status.clean:
316 319 files that have definitely not been modified since the
317 320 dirstate was written
318 321 """
319 322
320 323 # TODO: could return a list, except git.dirstate is a generator
321 324
322 325 def matches(self, match: matchmod.basematcher) -> Iterable[bytes]:
323 326 """
324 327 return files in the dirstate (in whatever state) filtered by match
325 328 """
326 329
327 330 # TODO: finish adding typehints here, and to subclasses
328 331
329 332 def verify(
330 333 self, m1, m2, p1: bytes, narrow_matcher: Optional[Any] = None
331 334 ) -> Iterator[bytes]:
332 335 """
333 336 check the dirstate contents against the parent manifest and yield errors
334 337 """
General Comments 0
You need to be logged in to leave comments. Login now