comparison mercurial/interfaces/dirstate.py @ 52508:2ac368d0a5b6

interfaces: make `dirstate` Protocol class methods abstract Now all known Protocol methods that should be implemented by the subclass are abstract. See cdd4bc69bfc1 for details. Note that this will break the `git` extension more, because there are a bunch of methods that aren't implemented that should be, in favor of some very old methods that won't be called (like `add()` and `drop()`). It's already broken, so I'm not taking the time to figure out how to modernize it right now. It's not detected by pytype because the only instantiation of `gitdirstate` is in `git/__init__.py`, which was already excluded from pytype checking for some other reason. AT least with this, it 1) doesn't get forgotten about, and 2) will require changing the interface if/when the core dirstate class evolves.
author Matt Harbison <matt_harbison@yahoo.com>
date Wed, 11 Dec 2024 21:09:15 -0500
parents 8820c991aee4
children 5c48fd4c0e68
comparison
equal deleted inserted replaced
52507:8820c991aee4 52508:2ac368d0a5b6
97 @property 97 @property
98 def _ignore(self) -> matchmod.basematcher: 98 def _ignore(self) -> matchmod.basematcher:
99 """Matcher for ignored files.""" 99 """Matcher for ignored files."""
100 100
101 @property 101 @property
102 @abc.abstractmethod
102 def is_changing_any(self) -> bool: 103 def is_changing_any(self) -> bool:
103 """True if any changes in progress.""" 104 """True if any changes in progress."""
104 105
105 @property 106 @property
107 @abc.abstractmethod
106 def is_changing_parents(self) -> bool: 108 def is_changing_parents(self) -> bool:
107 """True if parents changes in progress.""" 109 """True if parents changes in progress."""
108 110
109 @property 111 @property
112 @abc.abstractmethod
110 def is_changing_files(self) -> bool: 113 def is_changing_files(self) -> bool:
111 """True if file tracking changes in progress.""" 114 """True if file tracking changes in progress."""
112 115
116 @abc.abstractmethod
113 def _ignorefileandline(self, f: bytes) -> IgnoreFileAndLineT: 117 def _ignorefileandline(self, f: bytes) -> IgnoreFileAndLineT:
114 """Given a file `f`, return the ignore file and line that ignores it.""" 118 """Given a file `f`, return the ignore file and line that ignores it."""
115 119
116 # TODO: decorate with `@util.propertycache` like dirstate class? 120 # TODO: decorate with `@util.propertycache` like dirstate class?
117 # (can't because circular import) 121 # (can't because circular import)
118 @property 122 @property
123 @abc.abstractmethod
119 def _checklink(self) -> bool: 124 def _checklink(self) -> bool:
120 """Callable for checking symlinks.""" # TODO: this comment looks stale 125 """Callable for checking symlinks.""" # TODO: this comment looks stale
121 126
122 # TODO: decorate with `@util.propertycache` like dirstate class? 127 # TODO: decorate with `@util.propertycache` like dirstate class?
123 # (can't because circular import) 128 # (can't because circular import)
124 @property 129 @property
130 @abc.abstractmethod
125 def _checkexec(self) -> bool: 131 def _checkexec(self) -> bool:
126 """Callable for checking exec bits.""" # TODO: this comment looks stale 132 """Callable for checking exec bits.""" # TODO: this comment looks stale
127 133
128 @contextlib.contextmanager 134 @contextlib.contextmanager
129 @abc.abstractmethod 135 @abc.abstractmethod
143 If an exception occurs in the scope of the context manager, 149 If an exception occurs in the scope of the context manager,
144 the incoherent dirstate won't be written when wlock is 150 the incoherent dirstate won't be written when wlock is
145 released. 151 released.
146 """ 152 """
147 153
154 @abc.abstractmethod
148 def hasdir(self, d: bytes) -> bool: 155 def hasdir(self, d: bytes) -> bool:
149 pass 156 pass
150 157
158 @abc.abstractmethod
151 def flagfunc(self, buildfallback: FlagFuncFallbackT) -> FlagFuncReturnT: 159 def flagfunc(self, buildfallback: FlagFuncFallbackT) -> FlagFuncReturnT:
152 """build a callable that returns flags associated with a filename 160 """build a callable that returns flags associated with a filename
153 161
154 The information is extracted from three possible layers: 162 The information is extracted from three possible layers:
155 1. the file system if it supports the information 163 1. the file system if it supports the information
156 2. the "fallback" information stored in the dirstate if any 164 2. the "fallback" information stored in the dirstate if any
157 3. a more expensive mechanism inferring the flags from the parents. 165 3. a more expensive mechanism inferring the flags from the parents.
158 """ 166 """
159 167
168 @abc.abstractmethod
160 def getcwd(self) -> bytes: 169 def getcwd(self) -> bytes:
161 """Return the path from which a canonical path is calculated. 170 """Return the path from which a canonical path is calculated.
162 171
163 This path should be used to resolve file patterns or to convert 172 This path should be used to resolve file patterns or to convert
164 canonical paths back to file paths for display. It shouldn't be 173 canonical paths back to file paths for display. It shouldn't be
165 used to get real file paths. Use vfs functions instead. 174 used to get real file paths. Use vfs functions instead.
166 """ 175 """
167 176
177 @abc.abstractmethod
168 def pathto(self, f: bytes, cwd: Optional[bytes] = None) -> bytes: 178 def pathto(self, f: bytes, cwd: Optional[bytes] = None) -> bytes:
169 pass 179 pass
170 180
181 @abc.abstractmethod
171 def get_entry(self, path: bytes) -> DirstateItemT: 182 def get_entry(self, path: bytes) -> DirstateItemT:
172 """return a DirstateItem for the associated path""" 183 """return a DirstateItem for the associated path"""
173 184
185 @abc.abstractmethod
174 def __contains__(self, key: Any) -> bool: 186 def __contains__(self, key: Any) -> bool:
175 """Check if bytestring `key` is known to the dirstate.""" 187 """Check if bytestring `key` is known to the dirstate."""
176 188
189 @abc.abstractmethod
177 def __iter__(self) -> Iterator[bytes]: 190 def __iter__(self) -> Iterator[bytes]:
178 """Iterate the dirstate's contained filenames as bytestrings.""" 191 """Iterate the dirstate's contained filenames as bytestrings."""
179 192
193 @abc.abstractmethod
180 def items(self) -> Iterator[Tuple[bytes, DirstateItemT]]: 194 def items(self) -> Iterator[Tuple[bytes, DirstateItemT]]:
181 """Iterate the dirstate's entries as (filename, DirstateItem. 195 """Iterate the dirstate's entries as (filename, DirstateItem.
182 196
183 As usual, filename is a bytestring. 197 As usual, filename is a bytestring.
184 """ 198 """
185 199
186 iteritems = items 200 iteritems = items
187 201
202 @abc.abstractmethod
188 def parents(self) -> List[bytes]: 203 def parents(self) -> List[bytes]:
189 pass 204 pass
190 205
206 @abc.abstractmethod
191 def p1(self) -> bytes: 207 def p1(self) -> bytes:
192 pass 208 pass
193 209
210 @abc.abstractmethod
194 def p2(self) -> bytes: 211 def p2(self) -> bytes:
195 pass 212 pass
196 213
214 @abc.abstractmethod
197 def branch(self) -> bytes: 215 def branch(self) -> bytes:
198 pass 216 pass
199 217
200 # TODO: typehint the return. It's a copies Map of some sort. 218 # TODO: typehint the return. It's a copies Map of some sort.
219 @abc.abstractmethod
201 def setparents(self, p1: bytes, p2: Optional[bytes] = None): 220 def setparents(self, p1: bytes, p2: Optional[bytes] = None):
202 """Set dirstate parents to p1 and p2. 221 """Set dirstate parents to p1 and p2.
203 222
204 When moving from two parents to one, "merged" entries a 223 When moving from two parents to one, "merged" entries a
205 adjusted to normal and previous copy records discarded and 224 adjusted to normal and previous copy records discarded and
206 returned by the call. 225 returned by the call.
207 226
208 See localrepo.setparents() 227 See localrepo.setparents()
209 """ 228 """
210 229
230 @abc.abstractmethod
211 def setbranch( 231 def setbranch(
212 self, branch: bytes, transaction: Optional[TransactionT] 232 self, branch: bytes, transaction: Optional[TransactionT]
213 ) -> None: 233 ) -> None:
214 pass 234 pass
215 235
236 @abc.abstractmethod
216 def invalidate(self) -> None: 237 def invalidate(self) -> None:
217 """Causes the next access to reread the dirstate. 238 """Causes the next access to reread the dirstate.
218 239
219 This is different from localrepo.invalidatedirstate() because it always 240 This is different from localrepo.invalidatedirstate() because it always
220 rereads the dirstate. Use localrepo.invalidatedirstate() if you want to 241 rereads the dirstate. Use localrepo.invalidatedirstate() if you want to
221 check whether the dirstate has changed before rereading it.""" 242 check whether the dirstate has changed before rereading it."""
222 243
244 @abc.abstractmethod
223 def copy(self, source: Optional[bytes], dest: bytes) -> None: 245 def copy(self, source: Optional[bytes], dest: bytes) -> None:
224 """Mark dest as a copy of source. Unmark dest if source is None.""" 246 """Mark dest as a copy of source. Unmark dest if source is None."""
225 247
248 @abc.abstractmethod
226 def copied(self, file: bytes) -> Optional[bytes]: 249 def copied(self, file: bytes) -> Optional[bytes]:
227 pass 250 pass
228 251
252 @abc.abstractmethod
229 def copies(self) -> Dict[bytes, bytes]: 253 def copies(self) -> Dict[bytes, bytes]:
230 pass 254 pass
231 255
256 @abc.abstractmethod
232 def normalize( 257 def normalize(
233 self, path: bytes, isknown: bool = False, ignoremissing: bool = False 258 self, path: bytes, isknown: bool = False, ignoremissing: bool = False
234 ) -> bytes: 259 ) -> bytes:
235 """ 260 """
236 normalize the case of a pathname when on a casefolding filesystem 261 normalize the case of a pathname when on a casefolding filesystem
247 - version of name already stored in the dirstate 272 - version of name already stored in the dirstate
248 - version of name stored on disk 273 - version of name stored on disk
249 - version provided via command arguments 274 - version provided via command arguments
250 """ 275 """
251 276
277 @abc.abstractmethod
252 def clear(self) -> None: 278 def clear(self) -> None:
253 pass 279 pass
254 280
281 @abc.abstractmethod
255 def rebuild( 282 def rebuild(
256 self, 283 self,
257 parent: bytes, 284 parent: bytes,
258 allfiles: Iterable[bytes], # TODO: more than iterable? (uses len()) 285 allfiles: Iterable[bytes], # TODO: more than iterable? (uses len())
259 changedfiles: Optional[Iterable[bytes]] = None, 286 changedfiles: Optional[Iterable[bytes]] = None,
260 ) -> None: 287 ) -> None:
261 pass 288 pass
262 289
290 @abc.abstractmethod
263 def write(self, tr: Optional[TransactionT]) -> None: 291 def write(self, tr: Optional[TransactionT]) -> None:
264 pass 292 pass
265 293
294 @abc.abstractmethod
266 def addparentchangecallback( 295 def addparentchangecallback(
267 self, category: bytes, callback: AddParentChangeCallbackT 296 self, category: bytes, callback: AddParentChangeCallbackT
268 ) -> None: 297 ) -> None:
269 """add a callback to be called when the wd parents are changed 298 """add a callback to be called when the wd parents are changed
270 299
273 302
274 Category is a unique identifier to allow overwriting an old callback 303 Category is a unique identifier to allow overwriting an old callback
275 with a newer callback. 304 with a newer callback.
276 """ 305 """
277 306
307 @abc.abstractmethod
278 def walk( 308 def walk(
279 self, 309 self,
280 match: matchmod.basematcher, 310 match: matchmod.basematcher,
281 subrepos: Any, # TODO: figure out what this is 311 subrepos: Any, # TODO: figure out what this is
282 unknown: bool, 312 unknown: bool,
292 Return a dict mapping filename to stat-like object (either 322 Return a dict mapping filename to stat-like object (either
293 mercurial.osutil.stat instance or return value of os.stat()). 323 mercurial.osutil.stat instance or return value of os.stat()).
294 324
295 """ 325 """
296 326
327 @abc.abstractmethod
297 def status( 328 def status(
298 self, 329 self,
299 match: matchmod.basematcher, 330 match: matchmod.basematcher,
300 subrepos: bool, 331 subrepos: bool,
301 ignored: bool, 332 ignored: bool,
318 dirstate was written 349 dirstate was written
319 """ 350 """
320 351
321 # TODO: could return a list, except git.dirstate is a generator 352 # TODO: could return a list, except git.dirstate is a generator
322 353
354 @abc.abstractmethod
323 def matches(self, match: matchmod.basematcher) -> Iterable[bytes]: 355 def matches(self, match: matchmod.basematcher) -> Iterable[bytes]:
324 """ 356 """
325 return files in the dirstate (in whatever state) filtered by match 357 return files in the dirstate (in whatever state) filtered by match
326 """ 358 """
327 359
328 # TODO: finish adding typehints here, and to subclasses 360 # TODO: finish adding typehints here, and to subclasses
329 361
362 @abc.abstractmethod
330 def verify( 363 def verify(
331 self, m1, m2, p1: bytes, narrow_matcher: Optional[Any] = None 364 self, m1, m2, p1: bytes, narrow_matcher: Optional[Any] = None
332 ) -> Iterator[bytes]: 365 ) -> Iterator[bytes]:
333 """ 366 """
334 check the dirstate contents against the parent manifest and yield errors 367 check the dirstate contents against the parent manifest and yield errors