-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update DependencyMap so that it actually works (#21)
The existing `Rc::into_inner` trick _never_ works, because the Replica always holds one count of the `Rc` for itself, and returns a second count -- so the reference count is always two. And, Rc::unwrap_or_clone` doesn't work because `DependencyMap` can't be cloned. This is a giant hack, as explained by comments inline. It's also temporary, until GothenburgBitFactory/taskchampion#514 is released.
- Loading branch information
Showing
5 changed files
with
91 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,48 @@ | ||
use pyo3::prelude::*; | ||
use taskchampion::{DependencyMap as TCDependencyMap, Uuid}; | ||
|
||
// See `Replica::dependency_map` for the rationale for using a raw pointer here. | ||
|
||
#[pyclass] | ||
pub struct DependencyMap(pub(crate) TCDependencyMap); | ||
pub struct DependencyMap(*const TCDependencyMap); | ||
|
||
// SAFETY: `Replica::dependency_map` ensures that the TCDependencyMap is never freed (as the Rc is | ||
// leaked) and TaskChampion does not modify it, so no races can occur. | ||
unsafe impl Send for DependencyMap {} | ||
|
||
#[pymethods] | ||
impl DependencyMap { | ||
// TODO: possibly optimize this later, if possible | ||
pub fn __repr__(&self) -> String { | ||
format!("{:?}", self.as_ref()) | ||
} | ||
|
||
pub fn dependencies(&self, dep_of: String) -> Vec<String> { | ||
let uuid = Uuid::parse_str(&dep_of).unwrap(); | ||
self.0.dependencies(uuid).map(|uuid| uuid.into()).collect() | ||
self.as_ref() | ||
.dependencies(uuid) | ||
.map(|uuid| uuid.into()) | ||
.collect() | ||
} | ||
|
||
pub fn dependents(&self, dep_on: String) -> Vec<String> { | ||
let uuid = Uuid::parse_str(&dep_on).unwrap(); | ||
self.as_ref() | ||
.dependents(uuid) | ||
.map(|uuid| uuid.into()) | ||
.collect() | ||
} | ||
} | ||
|
||
impl From<*const TCDependencyMap> for DependencyMap { | ||
fn from(value: *const TCDependencyMap) -> Self { | ||
DependencyMap(value) | ||
} | ||
} | ||
|
||
self.0.dependents(uuid).map(|uuid| uuid.into()).collect() | ||
impl AsRef<TCDependencyMap> for DependencyMap { | ||
fn as_ref(&self) -> &TCDependencyMap { | ||
// SAFETY: `Replica::dependency_map` ensures that the TCDependencyMap is never freed (as | ||
// the Rc is leaked) and TaskChampion does not modify it, so no races can occur. | ||
unsafe { &*self.0 as &TCDependencyMap } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import uuid | ||
import pytest | ||
from taskchampion import Replica, TaskData | ||
|
||
|
||
def test_dependency_map(): | ||
r = Replica.new_in_memory() | ||
u1 = str(uuid.uuid4()) | ||
u2 = str(uuid.uuid4()) | ||
u3 = str(uuid.uuid4()) | ||
ops = [] | ||
|
||
# Set up t3 depending on t2 depending on t1. | ||
t1, op = TaskData.create(u1) | ||
ops.append(op) | ||
op = t1.update("status", "pending") | ||
ops.append(op) | ||
t2, op = TaskData.create(u2) | ||
ops.append(op) | ||
op = t2.update("status", "pending") | ||
ops.append(op) | ||
op = t2.update(f"dep_{u1}", "x") | ||
ops.append(op) | ||
t3, op = TaskData.create(u3) | ||
ops.append(op) | ||
op = t3.update("status", "pending") | ||
ops.append(op) | ||
op = t3.update(f"dep_{u2}", "x") | ||
ops.append(op) | ||
|
||
r.commit_operations(ops) | ||
|
||
dm = r.dependency_map(True) | ||
assert dm.dependencies(u1) == [] | ||
assert dm.dependents(u1) == [u2] | ||
assert dm.dependencies(u2) == [u1] | ||
assert dm.dependents(u2) == [u3] | ||
assert dm.dependencies(u3) == [u2] | ||
|
||
|
||
def test_dependency_map_repr(): | ||
r = Replica.new_in_memory() | ||
dm = r.dependency_map(True) | ||
assert repr(dm) == "DependencyMap { edges: [] }" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters