##// END OF EJS Templates
rust-index: stop using C index...
Georges Racinet -
r52139:41e19e8a default
parent child Browse files
Show More
@@ -14,9 +14,9 b' use crate::{'
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,
@@ -123,14 +123,10 b' py_class!(pub class MixedIndex |py| {'
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 }
@@ -202,24 +198,22 b' py_class!(pub class MixedIndex |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)
@@ -237,80 +231,60 b' py_class!(pub class MixedIndex |py| {'
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)?),
@@ -318,51 +292,34 b' py_class!(pub class MixedIndex |py| {'
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,
@@ -382,24 +339,11 b' py_class!(pub class MixedIndex |py| {'
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 =
@@ -422,13 +366,7 b' py_class!(pub class MixedIndex |py| {'
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,
@@ -442,16 +380,13 b' py_class!(pub class MixedIndex |py| {'
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
@@ -468,23 +403,6 b' py_class!(pub class MixedIndex |py| {'
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
@@ -492,7 +410,6 b' py_class!(pub class MixedIndex |py| {'
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)
@@ -500,15 +417,6 b' py_class!(pub class MixedIndex |py| {'
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 }
@@ -532,11 +440,6 b' py_class!(pub class MixedIndex |py| {'
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
@@ -545,11 +448,6 b' py_class!(pub class MixedIndex |py| {'
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
@@ -672,12 +570,6 b" struct PySnapshotsCache<'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,
@@ -731,8 +623,6 b' impl MixedIndex {'
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
@@ -767,20 +657,6 b' impl MixedIndex {'
767 Ok(self.nt(py))
657 Ok(self.nt(py))
768 }
658 }
769
659
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 }
783
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 }
@@ -1144,51 +1020,6 b' fn nodemap_error(py: Python, err: NodeMa'
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);
General Comments 0
You need to be logged in to leave comments. Login now