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 |
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 |
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)?; |