Mercurial > public > mercurial-scm > hg
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 |