Mercurial > public > mercurial-scm > hg
comparison rust/hg-cpython/src/revlog.rs @ 51243:41e19e8a6133
rust-index: stop using C index
We still keep its wrapper implementation in `hg-cpython::cindex`,
because we might want to recreate ancestors handling objects using
it for the case of REVLOGV2.
Also, we still instantiate it (from Python code) and store it as
attribute, for the likes of `get_cindex` and the caller that
relies on it, but that is soon to be removed, too.
author | Georges Racinet <georges.racinet@octobus.net> |
---|---|
date | Fri, 20 Oct 2023 09:48:53 +0200 |
parents | 0b81440e2a73 |
children | 8dbd985733ff |
comparison
equal
deleted
inserted
replaced
51242:0b81440e2a73 | 51243:41e19e8a6133 |
---|---|
12 PyRevision, | 12 PyRevision, |
13 }; | 13 }; |
14 use cpython::{ | 14 use cpython::{ |
15 buffer::{Element, PyBuffer}, | 15 buffer::{Element, PyBuffer}, |
16 exc::{IndexError, ValueError}, | 16 exc::{IndexError, ValueError}, |
17 ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyInt, PyList, | 17 ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyInt, PyList, PyModule, |
18 PyModule, PyObject, PyResult, PySet, PyString, PyTuple, Python, | 18 PyObject, PyResult, PySet, PyString, PyTuple, Python, PythonObject, |
19 PythonObject, ToPyObject, UnsafePyLeaked, | 19 ToPyObject, UnsafePyLeaked, |
20 }; | 20 }; |
21 use hg::{ | 21 use hg::{ |
22 errors::HgError, | 22 errors::HgError, |
23 index::{ | 23 index::{ |
24 IndexHeader, Phase, RevisionDataParams, SnapshotsCache, | 24 IndexHeader, Phase, RevisionDataParams, SnapshotsCache, |
121 /// Return Revision if found, raises a bare `error.RevlogError` | 121 /// Return Revision if found, raises a bare `error.RevlogError` |
122 /// in case of ambiguity, same as C version does | 122 /// in case of ambiguity, same as C version does |
123 def get_rev(&self, node: PyBytes) -> PyResult<Option<PyRevision>> { | 123 def get_rev(&self, node: PyBytes) -> PyResult<Option<PyRevision>> { |
124 let opt = self.get_nodetree(py)?.borrow(); | 124 let opt = self.get_nodetree(py)?.borrow(); |
125 let nt = opt.as_ref().unwrap(); | 125 let nt = opt.as_ref().unwrap(); |
126 let idx = &*self.cindex(py).borrow(); | |
127 let ridx = &*self.index(py).borrow(); | 126 let ridx = &*self.index(py).borrow(); |
128 let node = node_from_py_bytes(py, &node)?; | 127 let node = node_from_py_bytes(py, &node)?; |
129 let rust_rev = | 128 let rust_rev = |
130 nt.find_bin(ridx, node.into()).map_err(|e| nodemap_error(py, e))?; | 129 nt.find_bin(ridx, node.into()).map_err(|e| nodemap_error(py, e))?; |
131 let c_rev = | |
132 nt.find_bin(idx, node.into()).map_err(|e| nodemap_error(py, e))?; | |
133 assert_eq!(rust_rev, c_rev); | |
134 Ok(rust_rev.map(Into::into)) | 130 Ok(rust_rev.map(Into::into)) |
135 | 131 |
136 } | 132 } |
137 | 133 |
138 /// same as `get_rev()` but raises a bare `error.RevlogError` if node | 134 /// same as `get_rev()` but raises a bare `error.RevlogError` if node |
200 } | 196 } |
201 let node_bytes = tup.get_item(py, 7).extract(py)?; | 197 let node_bytes = tup.get_item(py, 7).extract(py)?; |
202 let node = node_from_py_object(py, &node_bytes)?; | 198 let node = node_from_py_object(py, &node_bytes)?; |
203 | 199 |
204 let rev = self.len(py)? as BaseRevision; | 200 let rev = self.len(py)? as BaseRevision; |
205 let mut idx = self.cindex(py).borrow_mut(); | |
206 | 201 |
207 // This is ok since we will just add the revision to the index | 202 // This is ok since we will just add the revision to the index |
208 let rev = Revision(rev); | 203 let rev = Revision(rev); |
209 idx.append(py, tup.clone_ref(py))?; | |
210 self.index(py) | 204 self.index(py) |
211 .borrow_mut() | 205 .borrow_mut() |
212 .append(py_tuple_to_revision_data_params(py, tup)?) | 206 .append(py_tuple_to_revision_data_params(py, tup)?) |
213 .unwrap(); | 207 .unwrap(); |
208 let idx = &*self.index(py).borrow(); | |
214 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap() | 209 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap() |
215 .insert(&*idx, &node, rev) | 210 .insert(idx, &node, rev) |
216 .map_err(|e| nodemap_error(py, e))?; | 211 .map_err(|e| nodemap_error(py, e))?; |
217 Ok(py.None()) | 212 Ok(py.None()) |
218 } | 213 } |
219 | 214 |
220 def __delitem__(&self, key: PyObject) -> PyResult<()> { | 215 def __delitem__(&self, key: PyObject) -> PyResult<()> { |
221 // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]` | 216 // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]` |
222 self.cindex(py).borrow().inner().del_item(py, &key)?; | |
223 let start = key.getattr(py, "start")?; | 217 let start = key.getattr(py, "start")?; |
224 let start = UncheckedRevision(start.extract(py)?); | 218 let start = UncheckedRevision(start.extract(py)?); |
225 let start = self.index(py) | 219 let start = self.index(py) |
226 .borrow() | 220 .borrow() |
227 .check_revision(start) | 221 .check_revision(start) |
235 self.fill_nodemap(py, nt)?; | 229 self.fill_nodemap(py, nt)?; |
236 Ok(()) | 230 Ok(()) |
237 } | 231 } |
238 | 232 |
239 // | 233 // |
240 // Reforwarded C index API | 234 // Index methods previously reforwarded to C index (tp_methods) |
235 // Same ordering as in revlog.c | |
241 // | 236 // |
242 | 237 |
243 // index_methods (tp_methods). Same ordering as in revlog.c | |
244 | |
245 /// return the gca set of the given revs | 238 /// return the gca set of the given revs |
246 def ancestors(&self, *args, **kw) -> PyResult<PyObject> { | 239 def ancestors(&self, *args, **_kw) -> PyResult<PyObject> { |
247 let rust_res = self.inner_ancestors(py, args)?; | 240 let rust_res = self.inner_ancestors(py, args)?; |
248 | |
249 let c_res = self.call_cindex(py, "ancestors", args, kw)?; | |
250 // the algorithm should always provide the results in reverse ordering | |
251 assert_py_eq(py, "ancestors", &rust_res, &c_res)?; | |
252 | |
253 Ok(rust_res) | 241 Ok(rust_res) |
254 } | 242 } |
255 | 243 |
256 /// return the heads of the common ancestors of the given revs | 244 /// return the heads of the common ancestors of the given revs |
257 def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> { | 245 def commonancestorsheads(&self, *args, **_kw) -> PyResult<PyObject> { |
258 let rust_res = self.inner_commonancestorsheads(py, args)?; | 246 let rust_res = self.inner_commonancestorsheads(py, args)?; |
259 | |
260 let c_res = self.call_cindex(py, "commonancestorsheads", args, kw)?; | |
261 // the algorithm should always provide the results in reverse ordering | |
262 assert_py_eq(py, "commonancestorsheads", &rust_res, &c_res)?; | |
263 | |
264 Ok(rust_res) | 247 Ok(rust_res) |
265 } | 248 } |
266 | 249 |
267 /// Clear the index caches and inner py_class data. | 250 /// Clear the index caches and inner py_class data. |
268 /// It is Python's responsibility to call `update_nodemap_data` again. | 251 /// It is Python's responsibility to call `update_nodemap_data` again. |
269 def clearcaches(&self, *args, **kw) -> PyResult<PyObject> { | 252 def clearcaches(&self) -> PyResult<PyObject> { |
270 self.nt(py).borrow_mut().take(); | 253 self.nt(py).borrow_mut().take(); |
271 self.docket(py).borrow_mut().take(); | 254 self.docket(py).borrow_mut().take(); |
272 self.nodemap_mmap(py).borrow_mut().take(); | 255 self.nodemap_mmap(py).borrow_mut().take(); |
273 self.index(py).borrow().clear_caches(); | 256 self.index(py).borrow().clear_caches(); |
274 self.call_cindex(py, "clearcaches", args, kw) | 257 Ok(py.None()) |
275 } | 258 } |
276 | 259 |
277 /// return the raw binary string representing a revision | 260 /// return the raw binary string representing a revision |
278 def entry_binary(&self, *args, **kw) -> PyResult<PyObject> { | 261 def entry_binary(&self, *args, **_kw) -> PyResult<PyObject> { |
279 let rindex = self.index(py).borrow(); | 262 let rindex = self.index(py).borrow(); |
280 let rev = UncheckedRevision(args.get_item(py, 0).extract(py)?); | 263 let rev = UncheckedRevision(args.get_item(py, 0).extract(py)?); |
281 let rust_bytes = rindex.check_revision(rev).and_then( | 264 let rust_bytes = rindex.check_revision(rev).and_then( |
282 |r| rindex.entry_binary(r)) | 265 |r| rindex.entry_binary(r)) |
283 .ok_or_else(|| rev_not_in_index(py, rev))?; | 266 .ok_or_else(|| rev_not_in_index(py, rev))?; |
284 let rust_res = PyBytes::new(py, rust_bytes).into_object(); | 267 let rust_res = PyBytes::new(py, rust_bytes).into_object(); |
285 | |
286 let c_res = self.call_cindex(py, "entry_binary", args, kw)?; | |
287 assert_py_eq(py, "entry_binary", &rust_res, &c_res)?; | |
288 Ok(rust_res) | 268 Ok(rust_res) |
289 } | 269 } |
290 | 270 |
291 /// return a binary packed version of the header | 271 /// return a binary packed version of the header |
292 def pack_header(&self, *args, **kw) -> PyResult<PyObject> { | 272 def pack_header(&self, *args, **_kw) -> PyResult<PyObject> { |
293 let rindex = self.index(py).borrow(); | 273 let rindex = self.index(py).borrow(); |
294 let packed = rindex.pack_header(args.get_item(py, 0).extract(py)?); | 274 let packed = rindex.pack_header(args.get_item(py, 0).extract(py)?); |
295 let rust_res = PyBytes::new(py, &packed).into_object(); | 275 let rust_res = PyBytes::new(py, &packed).into_object(); |
296 | |
297 let c_res = self.call_cindex(py, "pack_header", args, kw)?; | |
298 assert_py_eq(py, "pack_header", &rust_res, &c_res)?; | |
299 Ok(rust_res) | 276 Ok(rust_res) |
300 } | 277 } |
301 | 278 |
302 /// compute phases | 279 /// compute phases |
303 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> { | 280 def computephasesmapsets(&self, *args, **_kw) -> PyResult<PyObject> { |
304 let py_roots = args.get_item(py, 0).extract::<PyDict>(py)?; | 281 let py_roots = args.get_item(py, 0).extract::<PyDict>(py)?; |
305 let rust_res = self.inner_computephasesmapsets(py, py_roots)?; | 282 let rust_res = self.inner_computephasesmapsets(py, py_roots)?; |
306 | |
307 let c_res = self.call_cindex(py, "computephasesmapsets", args, kw)?; | |
308 assert_py_eq(py, "computephasesmapsets", &rust_res, &c_res)?; | |
309 Ok(rust_res) | 283 Ok(rust_res) |
310 } | 284 } |
311 | 285 |
312 /// reachableroots | 286 /// reachableroots |
313 def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> { | 287 def reachableroots2(&self, *args, **_kw) -> PyResult<PyObject> { |
314 let rust_res = self.inner_reachableroots2( | 288 let rust_res = self.inner_reachableroots2( |
315 py, | 289 py, |
316 UncheckedRevision(args.get_item(py, 0).extract(py)?), | 290 UncheckedRevision(args.get_item(py, 0).extract(py)?), |
317 args.get_item(py, 1), | 291 args.get_item(py, 1), |
318 args.get_item(py, 2), | 292 args.get_item(py, 2), |
319 args.get_item(py, 3).extract(py)?, | 293 args.get_item(py, 3).extract(py)?, |
320 )?; | 294 )?; |
321 | |
322 let c_res = self.call_cindex(py, "reachableroots2", args, kw)?; | |
323 // ordering of C result depends on how the computation went, and | |
324 // Rust result ordering is arbitrary. Hence we compare after | |
325 // sorting the results (in Python to avoid reconverting everything | |
326 // back to Rust structs). | |
327 assert_py_eq_normalized(py, "reachableroots2", &rust_res, &c_res, | |
328 |v| format!("sorted({})", v))?; | |
329 | |
330 Ok(rust_res) | 295 Ok(rust_res) |
331 } | 296 } |
332 | 297 |
333 /// get head revisions | 298 /// get head revisions |
334 def headrevs(&self, *args, **kw) -> PyResult<PyObject> { | 299 def headrevs(&self) -> PyResult<PyObject> { |
335 let rust_res = self.inner_headrevs(py)?; | 300 let rust_res = self.inner_headrevs(py)?; |
336 | |
337 let c_res = self.call_cindex(py, "headrevs", args, kw)?; | |
338 assert_py_eq(py, "headrevs", &rust_res, &c_res)?; | |
339 Ok(rust_res) | 301 Ok(rust_res) |
340 } | 302 } |
341 | 303 |
342 /// get filtered head revisions | 304 /// get filtered head revisions |
343 def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> { | 305 def headrevsfiltered(&self, *args, **_kw) -> PyResult<PyObject> { |
344 let rust_res = self.inner_headrevsfiltered(py, &args.get_item(py, 0))?; | 306 let rust_res = self.inner_headrevsfiltered(py, &args.get_item(py, 0))?; |
345 let c_res = self.call_cindex(py, "headrevsfiltered", args, kw)?; | |
346 | |
347 assert_py_eq(py, "headrevsfiltered", &rust_res, &c_res)?; | |
348 Ok(rust_res) | 307 Ok(rust_res) |
349 } | 308 } |
350 | 309 |
351 /// True if the object is a snapshot | 310 /// True if the object is a snapshot |
352 def issnapshot(&self, *args, **kw) -> PyResult<bool> { | 311 def issnapshot(&self, *args, **_kw) -> PyResult<bool> { |
353 let index = self.index(py).borrow(); | 312 let index = self.index(py).borrow(); |
354 let result = index | 313 let result = index |
355 .is_snapshot(UncheckedRevision(args.get_item(py, 0).extract(py)?)) | 314 .is_snapshot(UncheckedRevision(args.get_item(py, 0).extract(py)?)) |
356 .map_err(|e| { | 315 .map_err(|e| { |
357 PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string()) | 316 PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string()) |
358 })?; | 317 })?; |
359 let cresult = self.call_cindex(py, "issnapshot", args, kw)?; | |
360 assert_eq!(result, cresult.extract(py)?); | |
361 Ok(result) | 318 Ok(result) |
362 } | 319 } |
363 | 320 |
364 /// Gather snapshot data in a cache dict | 321 /// Gather snapshot data in a cache dict |
365 def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> { | 322 def findsnapshots(&self, *args, **_kw) -> PyResult<PyObject> { |
366 let index = self.index(py).borrow(); | 323 let index = self.index(py).borrow(); |
367 let cache: PyDict = args.get_item(py, 0).extract(py)?; | 324 let cache: PyDict = args.get_item(py, 0).extract(py)?; |
368 // this methods operates by setting new values in the cache, | 325 // this methods operates by setting new values in the cache, |
369 // hence we will compare results by letting the C implementation | 326 // hence we will compare results by letting the C implementation |
370 // operate over a deepcopy of the cache, and finally compare both | 327 // operate over a deepcopy of the cache, and finally compare both |
380 index.find_snapshots( | 337 index.find_snapshots( |
381 start_rev, | 338 start_rev, |
382 end_rev, | 339 end_rev, |
383 &mut cache_wrapper, | 340 &mut cache_wrapper, |
384 ).map_err(|_| revlog_error(py))?; | 341 ).map_err(|_| revlog_error(py))?; |
385 | |
386 let c_args = PyTuple::new( | |
387 py, | |
388 &[ | |
389 c_cache.clone_ref(py).into_object(), | |
390 args.get_item(py, 1), | |
391 args.get_item(py, 2) | |
392 ] | |
393 ); | |
394 self.call_cindex(py, "findsnapshots", &c_args, kw)?; | |
395 assert_py_eq(py, "findsnapshots cache", | |
396 &cache_wrapper.into_object(), | |
397 &c_cache.into_object())?; | |
398 Ok(py.None()) | 342 Ok(py.None()) |
399 } | 343 } |
400 | 344 |
401 /// determine revisions with deltas to reconstruct fulltext | 345 /// determine revisions with deltas to reconstruct fulltext |
402 def deltachain(&self, *args, **kw) -> PyResult<PyObject> { | 346 def deltachain(&self, *args, **_kw) -> PyResult<PyObject> { |
403 let index = self.index(py).borrow(); | 347 let index = self.index(py).borrow(); |
404 let rev = args.get_item(py, 0).extract::<BaseRevision>(py)?.into(); | 348 let rev = args.get_item(py, 0).extract::<BaseRevision>(py)?.into(); |
405 let stop_rev = | 349 let stop_rev = |
406 args.get_item(py, 1).extract::<Option<BaseRevision>>(py)?; | 350 args.get_item(py, 1).extract::<Option<BaseRevision>>(py)?; |
407 let rev = index.check_revision(rev).ok_or_else(|| { | 351 let rev = index.check_revision(rev).ok_or_else(|| { |
420 rev, stop_rev, using_general_delta | 364 rev, stop_rev, using_general_delta |
421 ).map_err(|e| { | 365 ).map_err(|e| { |
422 PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string()) | 366 PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string()) |
423 })?; | 367 })?; |
424 | 368 |
425 let cresult = self.call_cindex(py, "deltachain", args, kw)?; | |
426 let cchain: Vec<BaseRevision> = | |
427 cresult.get_item(py, 0)?.extract::<Vec<BaseRevision>>(py)?; | |
428 let chain: Vec<_> = chain.into_iter().map(|r| r.0).collect(); | 369 let chain: Vec<_> = chain.into_iter().map(|r| r.0).collect(); |
429 assert_eq!(chain, cchain); | |
430 assert_eq!(stopped, cresult.get_item(py, 1)?.extract(py)?); | |
431 | |
432 Ok( | 370 Ok( |
433 PyTuple::new( | 371 PyTuple::new( |
434 py, | 372 py, |
435 &[ | 373 &[ |
436 chain.into_py_object(py).into_object(), | 374 chain.into_py_object(py).into_object(), |
440 ) | 378 ) |
441 | 379 |
442 } | 380 } |
443 | 381 |
444 /// slice planned chunk read to reach a density threshold | 382 /// slice planned chunk read to reach a density threshold |
445 def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> { | 383 def slicechunktodensity(&self, *args, **_kw) -> PyResult<PyObject> { |
446 let rust_res = self.inner_slicechunktodensity( | 384 let rust_res = self.inner_slicechunktodensity( |
447 py, | 385 py, |
448 args.get_item(py, 0), | 386 args.get_item(py, 0), |
449 args.get_item(py, 1).extract(py)?, | 387 args.get_item(py, 1).extract(py)?, |
450 args.get_item(py, 2).extract(py)? | 388 args.get_item(py, 2).extract(py)? |
451 )?; | 389 )?; |
452 | |
453 let c_res = self.call_cindex(py, "slicechunktodensity", args, kw)?; | |
454 assert_py_eq(py, "slicechunktodensity", &rust_res, &c_res)?; | |
455 Ok(rust_res) | 390 Ok(rust_res) |
456 } | 391 } |
457 | 392 |
458 // index_sequence_methods and index_mapping_methods. | 393 // index_sequence_methods and index_mapping_methods. |
459 // | 394 // |
466 self.len(py) | 401 self.len(py) |
467 } | 402 } |
468 | 403 |
469 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> { | 404 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> { |
470 let rust_res = self.inner_getitem(py, key.clone_ref(py))?; | 405 let rust_res = self.inner_getitem(py, key.clone_ref(py))?; |
471 | |
472 // this conversion seems needless, but that's actually because | |
473 // `index_getitem` does not handle conversion from PyLong, | |
474 // which expressions such as [e for e in index] internally use. | |
475 // Note that we don't seem to have a direct way to call | |
476 // PySequence_GetItem (does the job), which would possibly be better | |
477 // for performance | |
478 // gracinet 2023: the above comment can be removed when we use | |
479 // the pure Rust impl only. Note also that `key` can be a binary | |
480 // node id. | |
481 let c_key = match key.extract::<BaseRevision>(py) { | |
482 Ok(rev) => rev.to_py_object(py).into_object(), | |
483 Err(_) => key, | |
484 }; | |
485 let c_res = self.cindex(py).borrow().inner().get_item(py, c_key)?; | |
486 | |
487 assert_py_eq(py, "__getitem__", &rust_res, &c_res)?; | |
488 Ok(rust_res) | 406 Ok(rust_res) |
489 } | 407 } |
490 | 408 |
491 def __contains__(&self, item: PyObject) -> PyResult<bool> { | 409 def __contains__(&self, item: PyObject) -> PyResult<bool> { |
492 // ObjectProtocol does not seem to provide contains(), so | 410 // ObjectProtocol does not seem to provide contains(), so |
493 // this is an equivalent implementation of the index_contains() | 411 // this is an equivalent implementation of the index_contains() |
494 // defined in revlog.c | 412 // defined in revlog.c |
495 let cindex = self.cindex(py).borrow(); | |
496 match item.extract::<i32>(py) { | 413 match item.extract::<i32>(py) { |
497 Ok(rev) => { | 414 Ok(rev) => { |
498 Ok(rev >= -1 && rev < self.len(py)? as BaseRevision) | 415 Ok(rev >= -1 && rev < self.len(py)? as BaseRevision) |
499 } | 416 } |
500 Err(_) => { | 417 Err(_) => { |
501 let item_bytes: PyBytes = item.extract(py)?; | 418 let item_bytes: PyBytes = item.extract(py)?; |
502 let rust_res = self.has_node(py, item_bytes)?; | 419 let rust_res = self.has_node(py, item_bytes)?; |
503 | |
504 let c_res = cindex.inner().call_method( | |
505 py, | |
506 "has_node", | |
507 PyTuple::new(py, &[item.clone_ref(py)]), | |
508 None)? | |
509 .extract(py)?; | |
510 | |
511 assert_eq!(rust_res, c_res); | |
512 Ok(rust_res) | 420 Ok(rust_res) |
513 } | 421 } |
514 } | 422 } |
515 } | 423 } |
516 | 424 |
530 } | 438 } |
531 | 439 |
532 @property | 440 @property |
533 def entry_size(&self) -> PyResult<PyInt> { | 441 def entry_size(&self) -> PyResult<PyInt> { |
534 let rust_res: PyInt = INDEX_ENTRY_SIZE.to_py_object(py); | 442 let rust_res: PyInt = INDEX_ENTRY_SIZE.to_py_object(py); |
535 | |
536 let c_res = self.cindex(py).borrow().inner() | |
537 .getattr(py, "entry_size")?; | |
538 assert_py_eq(py, "entry_size", rust_res.as_object(), &c_res)?; | |
539 | |
540 Ok(rust_res) | 443 Ok(rust_res) |
541 } | 444 } |
542 | 445 |
543 @property | 446 @property |
544 def rust_ext_compat(&self) -> PyResult<PyInt> { | 447 def rust_ext_compat(&self) -> PyResult<PyInt> { |
545 // will be entirely removed when the Rust index yet useful to | 448 // will be entirely removed when the Rust index yet useful to |
546 // implement in Rust to detangle things when removing `self.cindex` | 449 // implement in Rust to detangle things when removing `self.cindex` |
547 let rust_res: PyInt = 1.to_py_object(py); | 450 let rust_res: PyInt = 1.to_py_object(py); |
548 | |
549 let c_res = self.cindex(py).borrow().inner() | |
550 .getattr(py, "rust_ext_compat")?; | |
551 assert_py_eq(py, "rust_ext_compat", rust_res.as_object(), &c_res)?; | |
552 | |
553 Ok(rust_res) | 451 Ok(rust_res) |
554 } | 452 } |
555 | 453 |
556 }); | 454 }); |
557 | 455 |
670 struct PySnapshotsCache<'p> { | 568 struct PySnapshotsCache<'p> { |
671 py: Python<'p>, | 569 py: Python<'p>, |
672 dict: PyDict, | 570 dict: PyDict, |
673 } | 571 } |
674 | 572 |
675 impl<'p> PySnapshotsCache<'p> { | |
676 fn into_object(self) -> PyObject { | |
677 self.dict.into_object() | |
678 } | |
679 } | |
680 | |
681 impl<'p> SnapshotsCache for PySnapshotsCache<'p> { | 573 impl<'p> SnapshotsCache for PySnapshotsCache<'p> { |
682 fn insert_for( | 574 fn insert_for( |
683 &mut self, | 575 &mut self, |
684 rev: BaseRevision, | 576 rev: BaseRevision, |
685 value: BaseRevision, | 577 value: BaseRevision, |
729 ) | 621 ) |
730 } | 622 } |
731 | 623 |
732 fn len(&self, py: Python) -> PyResult<usize> { | 624 fn len(&self, py: Python) -> PyResult<usize> { |
733 let rust_index_len = self.index(py).borrow().len(); | 625 let rust_index_len = self.index(py).borrow().len(); |
734 let cindex_len = self.cindex(py).borrow().inner().len(py)?; | |
735 assert_eq!(rust_index_len, cindex_len); | |
736 Ok(rust_index_len) | 626 Ok(rust_index_len) |
737 } | 627 } |
738 | 628 |
739 /// This is scaffolding at this point, but it could also become | 629 /// This is scaffolding at this point, but it could also become |
740 /// a way to start a persistent nodemap or perform a | 630 /// a way to start a persistent nodemap or perform a |
763 let mut nt = NodeTree::load_bytes(readonly, 0); | 653 let mut nt = NodeTree::load_bytes(readonly, 0); |
764 self.fill_nodemap(py, &mut nt)?; | 654 self.fill_nodemap(py, &mut nt)?; |
765 self.nt(py).borrow_mut().replace(nt); | 655 self.nt(py).borrow_mut().replace(nt); |
766 } | 656 } |
767 Ok(self.nt(py)) | 657 Ok(self.nt(py)) |
768 } | |
769 | |
770 /// forward a method call to the underlying C index | |
771 fn call_cindex( | |
772 &self, | |
773 py: Python, | |
774 name: &str, | |
775 args: &PyTuple, | |
776 kwargs: Option<&PyDict>, | |
777 ) -> PyResult<PyObject> { | |
778 self.cindex(py) | |
779 .borrow() | |
780 .inner() | |
781 .call_method(py, name, args, kwargs) | |
782 } | 658 } |
783 | 659 |
784 pub fn clone_cindex(&self, py: Python) -> cindex::Index { | 660 pub fn clone_cindex(&self, py: Python) -> cindex::Index { |
785 self.cindex(py).borrow().clone_ref(py) | 661 self.cindex(py).borrow().clone_ref(py) |
786 } | 662 } |
1142 NodeMapError::MultipleResults => revlog_error(py), | 1018 NodeMapError::MultipleResults => revlog_error(py), |
1143 NodeMapError::RevisionNotInIndex(r) => nodemap_rev_not_in_index(py, r), | 1019 NodeMapError::RevisionNotInIndex(r) => nodemap_rev_not_in_index(py, r), |
1144 } | 1020 } |
1145 } | 1021 } |
1146 | 1022 |
1147 /// assert two Python objects to be equal from a Python point of view | |
1148 /// | |
1149 /// `method` is a label for the assertion error message, intended to be the | |
1150 /// name of the caller. | |
1151 /// `normalizer` is a function that takes a Python variable name and returns | |
1152 /// an expression that the conparison will actually use. | |
1153 /// Foe example: `|v| format!("sorted({})", v)` | |
1154 fn assert_py_eq_normalized( | |
1155 py: Python, | |
1156 method: &str, | |
1157 rust: &PyObject, | |
1158 c: &PyObject, | |
1159 normalizer: impl FnOnce(&str) -> String + Copy, | |
1160 ) -> PyResult<()> { | |
1161 let locals = PyDict::new(py); | |
1162 locals.set_item(py, "rust".into_py_object(py).into_object(), rust)?; | |
1163 locals.set_item(py, "c".into_py_object(py).into_object(), c)?; | |
1164 // let lhs = format!(normalizer_fmt, "rust"); | |
1165 // let rhs = format!(normalizer_fmt, "c"); | |
1166 let is_eq: PyBool = py | |
1167 .eval( | |
1168 &format!("{} == {}", &normalizer("rust"), &normalizer("c")), | |
1169 None, | |
1170 Some(&locals), | |
1171 )? | |
1172 .extract(py)?; | |
1173 assert!( | |
1174 is_eq.is_true(), | |
1175 "{} results differ. Rust: {:?} C: {:?} (before any normalization)", | |
1176 method, | |
1177 rust, | |
1178 c | |
1179 ); | |
1180 Ok(()) | |
1181 } | |
1182 | |
1183 fn assert_py_eq( | |
1184 py: Python, | |
1185 method: &str, | |
1186 rust: &PyObject, | |
1187 c: &PyObject, | |
1188 ) -> PyResult<()> { | |
1189 assert_py_eq_normalized(py, method, rust, c, |v| v.to_owned()) | |
1190 } | |
1191 | |
1192 /// Create the module, with __package__ given from parent | 1023 /// Create the module, with __package__ given from parent |
1193 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { | 1024 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { |
1194 let dotted_name = &format!("{}.revlog", package); | 1025 let dotted_name = &format!("{}.revlog", package); |
1195 let m = PyModule::new(py, dotted_name)?; | 1026 let m = PyModule::new(py, dotted_name)?; |
1196 m.add(py, "__package__", package)?; | 1027 m.add(py, "__package__", package)?; |