Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow adapters to process types that deref to their vertex type. #481

Merged
merged 25 commits into from
Oct 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a909468
Add `AsVertex` and `Cast` traits.
obi1kenobi Oct 5, 2023
0faa71b
Add the new trait into the Adapter trait signature.
obi1kenobi Oct 5, 2023
fa63d96
Fix most of the built-in Adapter implementations.
obi1kenobi Oct 5, 2023
dfc8023
Port most of the internals and get trace record/replay working.
obi1kenobi Oct 6, 2023
e23264b
Fix hints tests and cargo fmt.
obi1kenobi Oct 7, 2023
1738bc1
Delint.
obi1kenobi Oct 7, 2023
ad3088c
Clippy fixes.
obi1kenobi Oct 7, 2023
a370e17
All `trustfall_core` tests pass.
obi1kenobi Oct 7, 2023
e7cff29
Update examples and integration test for new API.
obi1kenobi Oct 7, 2023
4890f38
Merge branch 'main' into separate_storage_and_use_types
obi1kenobi Oct 9, 2023
579f63c
Add `AsVertex` to `trustfall` crate. Fix hytradboi demo.
obi1kenobi Oct 12, 2023
982ec2c
Smuggle generic `DataContext` into JS shim as `*mut ()`.
obi1kenobi Oct 12, 2023
f8b2751
Clean up `unsafe` implementation.
obi1kenobi Oct 12, 2023
5a46cb6
Use `*mut ()` opaque type to hide generics in Python bindings.
obi1kenobi Oct 12, 2023
37c5f5a
Adapt stubgen for the new Adapter API + make it test w/ current `trus…
obi1kenobi Oct 12, 2023
c3bcd94
Add new APIs to `DataContext` to enable better composition.
obi1kenobi Oct 17, 2023
73a6637
Merge branch 'main' into separate_storage_and_use_types
obi1kenobi Oct 21, 2023
b0f8548
Add comments to new pub APIs.
obi1kenobi Oct 21, 2023
db84563
Update docs on the Adapter trait and give better examples.
obi1kenobi Oct 21, 2023
431dd6d
Fix recursive expansion of the type.
obi1kenobi Oct 21, 2023
8289f59
Formatting tweak.
obi1kenobi Oct 21, 2023
1017f5c
Stylistic changes.
obi1kenobi Oct 21, 2023
7387a1c
Move comment into a doc comment position.
obi1kenobi Oct 21, 2023
ba706d4
Update fuzzers for new API.
obi1kenobi Oct 21, 2023
3d2880f
Fix recursive type expansion in `map()/flat_map()` without `dyn`.
obi1kenobi Oct 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions demo-hytradboi/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use octorust::types::{ContentFile, FullRepository};
use tokio::runtime::Runtime;
use trustfall::{
provider::{
field_property, resolve_property_with, Adapter, ContextIterator, ContextOutcomeIterator,
EdgeParameters, ResolveEdgeInfo, ResolveInfo, VertexIterator,
field_property, resolve_property_with, Adapter, AsVertex, ContextIterator,
ContextOutcomeIterator, EdgeParameters, ResolveEdgeInfo, ResolveInfo, VertexIterator,
},
FieldValue,
};
Expand Down Expand Up @@ -197,13 +197,13 @@ impl<'a> Adapter<'a> for DemoAdapter {
}
}

fn resolve_property(
fn resolve_property<V: AsVertex<Self::Vertex> + 'a>(
&self,
contexts: ContextIterator<'a, Self::Vertex>,
contexts: ContextIterator<'a, V>,
type_name: &Arc<str>,
property_name: &Arc<str>,
_resolve_info: &ResolveInfo,
) -> ContextOutcomeIterator<'a, Self::Vertex, FieldValue> {
) -> ContextOutcomeIterator<'a, V, FieldValue> {
match (type_name.as_ref(), property_name.as_ref()) {
(_, "__typename") => Box::new(contexts.map(|ctx| {
let value = match ctx.active_vertex() {
Expand Down Expand Up @@ -368,14 +368,14 @@ impl<'a> Adapter<'a> for DemoAdapter {
}
}

fn resolve_neighbors(
fn resolve_neighbors<V: AsVertex<Self::Vertex> + 'a>(
&self,
contexts: ContextIterator<'a, Self::Vertex>,
contexts: ContextIterator<'a, V>,
type_name: &Arc<str>,
edge_name: &Arc<str>,
_parameters: &EdgeParameters,
_resolve_info: &ResolveEdgeInfo,
) -> ContextOutcomeIterator<'a, Self::Vertex, VertexIterator<'a, Self::Vertex>> {
) -> ContextOutcomeIterator<'a, V, VertexIterator<'a, Self::Vertex>> {
match (type_name.as_ref(), edge_name.as_ref()) {
("HackerNewsStory", "byUser") => Box::new(contexts.map(|ctx| {
let vertex = ctx.active_vertex();
Expand Down Expand Up @@ -687,13 +687,13 @@ impl<'a> Adapter<'a> for DemoAdapter {
}
}

fn resolve_coercion(
fn resolve_coercion<V: AsVertex<Self::Vertex> + 'a>(
&self,
contexts: ContextIterator<'a, Self::Vertex>,
contexts: ContextIterator<'a, V>,
type_name: &Arc<str>,
coerce_to_type: &Arc<str>,
_resolve_info: &ResolveInfo,
) -> ContextOutcomeIterator<'a, Self::Vertex, bool> {
) -> ContextOutcomeIterator<'a, V, bool> {
let type_name = type_name.clone();
let coerce_to_type = coerce_to_type.clone();
let iterator = contexts.map(move |ctx| {
Expand Down
116 changes: 83 additions & 33 deletions pytrustfall/src/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use pyo3::{exceptions::PyStopIteration, prelude::*, wrap_pyfunction};
use trustfall_core::{
frontend::{error::FrontendError, parse},
interpreter::{
execution::interpret_ir, Adapter, ContextIterator as BaseContextIterator,
execution::interpret_ir, Adapter, AsVertex, ContextIterator as BaseContextIterator,
ContextOutcomeIterator, DataContext, ResolveEdgeInfo, ResolveInfo, VertexIterator,
},
ir::{EdgeParameters, FieldValue},
Expand Down Expand Up @@ -197,24 +197,49 @@ fn make_iterator(py: Python, value: Py<PyAny>) -> PyResult<Py<PyAny>> {
value.call_method(py, "__iter__", (), None)
}

#[pyclass]
#[pyclass(unsendable)]
#[derive(Debug, Clone)]
pub struct Context(DataContext<Arc<Py<PyAny>>>);
pub(crate) struct Opaque {
data: *mut (),
pub(crate) vertex: Option<Arc<Py<PyAny>>>,
}

impl Opaque {
fn new<V: AsVertex<Arc<Py<PyAny>>> + 'static>(ctx: DataContext<V>) -> Self {
let vertex = ctx.active_vertex::<Arc<Py<PyAny>>>().cloned();
let boxed = Box::new(ctx);
let data = Box::into_raw(boxed) as *mut ();

Self { data, vertex }
}

/// Converts an `Opaque` into the `DataContext<V>` it points to.
///
/// # Safety
///
/// When an `Opaque` is constructed, it does not store the value of the `V` generic parameter
/// it was constructed with. The caller of this function must ensure that the `V` parameter here
/// is the same type as the one used in the `Opaque::new()` call that constructed `self` here.
unsafe fn into_inner<V: AsVertex<Arc<Py<PyAny>>> + 'static>(self) -> DataContext<V> {
let boxed_ctx = unsafe { Box::from_raw(self.data as *mut DataContext<V>) };
*boxed_ctx
}
}

#[pymethods]
impl Context {
impl Opaque {
#[getter]
fn active_vertex(&self) -> PyResult<Option<Py<PyAny>>> {
Ok(self.0.active_vertex().map(|arc| (**arc).clone()))
Ok(self.vertex.as_ref().map(|arc| (**arc).clone()))
}
}

#[pyclass(unsendable)]
pub struct ContextIterator(VertexIterator<'static, Context>);
pub struct ContextIterator(VertexIterator<'static, Opaque>);

impl ContextIterator {
fn new(inner: VertexIterator<'static, DataContext<Arc<Py<PyAny>>>>) -> Self {
Self(Box::new(inner.map(Context)))
fn new<V: AsVertex<Arc<Py<PyAny>>> + 'static>(inner: BaseContextIterator<'static, V>) -> Self {
Self(Box::new(inner.map(Opaque::new)))
}
}

Expand All @@ -224,7 +249,7 @@ impl ContextIterator {
slf
}

fn __next__(mut slf: PyRefMut<Self>) -> Option<Context> {
fn __next__(mut slf: PyRefMut<Self>) -> Option<Opaque> {
slf.0.next()
}
}
Expand Down Expand Up @@ -256,13 +281,13 @@ impl Adapter<'static> for AdapterShim {
})
}

fn resolve_property(
fn resolve_property<V: AsVertex<Self::Vertex> + 'static>(
&self,
contexts: BaseContextIterator<'static, Self::Vertex>,
contexts: BaseContextIterator<'static, V>,
type_name: &Arc<str>,
property_name: &Arc<str>,
_resolve_info: &ResolveInfo,
) -> ContextOutcomeIterator<'static, Self::Vertex, FieldValue> {
) -> ContextOutcomeIterator<'static, V, FieldValue> {
let contexts = ContextIterator::new(contexts);
Python::with_gil(|py| {
let py_iterable = self
Expand All @@ -275,19 +300,28 @@ impl Adapter<'static> for AdapterShim {
)
.unwrap();

let iter = make_iterator(py, py_iterable).unwrap();
Box::new(PythonResolvePropertyIterator::new(iter))
let iter = PythonResolvePropertyIterator::new(
make_iterator(py, py_iterable).expect("failed to use py_iterable as an iterator"),
);

Box::new(iter.map(|(opaque, value)| {
// SAFETY: This `Opaque` was constructed just a few lines ago
// in this `resolve_property()` call, so the `V` type must be the same.
let ctx = unsafe { opaque.into_inner() };

(ctx, value)
}))
})
}

fn resolve_neighbors(
fn resolve_neighbors<V: AsVertex<Self::Vertex> + 'static>(
&self,
contexts: BaseContextIterator<'static, Self::Vertex>,
contexts: BaseContextIterator<'static, V>,
type_name: &Arc<str>,
edge_name: &Arc<str>,
parameters: &EdgeParameters,
_resolve_info: &ResolveEdgeInfo,
) -> ContextOutcomeIterator<'static, Self::Vertex, VertexIterator<'static, Self::Vertex>> {
) -> ContextOutcomeIterator<'static, V, VertexIterator<'static, Self::Vertex>> {
let contexts = ContextIterator::new(contexts);
Python::with_gil(|py| {
let parameter_data: BTreeMap<String, Py<PyAny>> =
Expand All @@ -303,18 +337,26 @@ impl Adapter<'static> for AdapterShim {
)
.unwrap();

let iter = make_iterator(py, py_iterable).unwrap();
Box::new(PythonResolveNeighborsIterator::new(iter))
let iter = PythonResolveNeighborsIterator::new(
make_iterator(py, py_iterable).expect("failed to use py_iterable as an iterator"),
);
Box::new(iter.map(|(opaque, neighbors)| {
// SAFETY: This `Opaque` was constructed just a few lines ago
// in this `resolve_neighbors()` call, so the `V` type must be the same.
let ctx = unsafe { opaque.into_inner() };

(ctx, neighbors)
}))
})
}

fn resolve_coercion(
fn resolve_coercion<V: AsVertex<Self::Vertex> + 'static>(
&self,
contexts: BaseContextIterator<'static, Self::Vertex>,
contexts: BaseContextIterator<'static, V>,
type_name: &Arc<str>,
coerce_to_type: &Arc<str>,
_resolve_info: &ResolveInfo,
) -> ContextOutcomeIterator<'static, Self::Vertex, bool> {
) -> ContextOutcomeIterator<'static, V, bool> {
let contexts = ContextIterator::new(contexts);
Python::with_gil(|py| {
let py_iterable = self
Expand All @@ -327,8 +369,16 @@ impl Adapter<'static> for AdapterShim {
)
.unwrap();

let iter = make_iterator(py, py_iterable).unwrap();
Box::new(PythonResolveCoercionIterator::new(iter))
let iter = PythonResolveCoercionIterator::new(
make_iterator(py, py_iterable).expect("failed to use py_iterable as an iterator"),
);
Box::new(iter.map(|(opaque, value)| {
// SAFETY: This `Opaque` was constructed just a few lines ago
// in this `resolve_coercion()` call, so the `V` type must be the same.
let ctx = unsafe { opaque.into_inner() };

(ctx, value)
}))
})
}
}
Expand Down Expand Up @@ -373,14 +423,14 @@ impl PythonResolvePropertyIterator {
}

impl Iterator for PythonResolvePropertyIterator {
type Item = (DataContext<Arc<Py<PyAny>>>, FieldValue);
type Item = (Opaque, FieldValue);

fn next(&mut self) -> Option<Self::Item> {
Python::with_gil(|py| {
match self.underlying.call_method(py, "__next__", (), None) {
Ok(output) => {
// value is a (context, property_value) tuple here
let context: Context = output
let context: Opaque = output
.call_method(py, "__getitem__", (0i64,), None)
.unwrap()
.extract(py)
Expand All @@ -393,7 +443,7 @@ impl Iterator for PythonResolvePropertyIterator {
)
.unwrap();

Some((context.0, value))
Some((context, value))
}
Err(e) => {
if e.is_instance_of::<PyStopIteration>(py) {
Expand All @@ -420,14 +470,14 @@ impl PythonResolveNeighborsIterator {
}

impl Iterator for PythonResolveNeighborsIterator {
type Item = (DataContext<Arc<Py<PyAny>>>, VertexIterator<'static, Arc<Py<PyAny>>>);
type Item = (Opaque, VertexIterator<'static, Arc<Py<PyAny>>>);

fn next(&mut self) -> Option<Self::Item> {
Python::with_gil(|py| {
match self.underlying.call_method(py, "__next__", (), None) {
Ok(output) => {
// value is a (context, neighbor_iterator) tuple here
let context: Context = output
let context: Opaque = output
.call_method(py, "__getitem__", (0i64,), None)
.unwrap()
.extract(py)
Expand All @@ -441,7 +491,7 @@ impl Iterator for PythonResolveNeighborsIterator {

let neighbors: VertexIterator<'static, Arc<Py<PyAny>>> =
Box::new(PythonVertexIterator::new(neighbors_iter));
Some((context.0, neighbors))
Some((context, neighbors))
}
Err(e) => {
if e.is_instance_of::<PyStopIteration>(py) {
Expand All @@ -468,14 +518,14 @@ impl PythonResolveCoercionIterator {
}

impl Iterator for PythonResolveCoercionIterator {
type Item = (DataContext<Arc<Py<PyAny>>>, bool);
type Item = (Opaque, bool);

fn next(&mut self) -> Option<Self::Item> {
Python::with_gil(|py| {
match self.underlying.call_method(py, "__next__", (), None) {
Ok(output) => {
// value is a (context, can_coerce) tuple here
let context: Context = output
let context: Opaque = output
.call_method(py, "__getitem__", (0i64,), None)
.unwrap()
.extract(py)
Expand All @@ -485,7 +535,7 @@ impl Iterator for PythonResolveCoercionIterator {
.unwrap()
.extract::<bool>(py)
.unwrap();
Some((context.0, can_coerce))
Some((context, can_coerce))
}
Err(e) => {
if e.is_instance_of::<PyStopIteration>(py) {
Expand Down
19 changes: 10 additions & 9 deletions trustfall/examples/feeds/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use trustfall::{
},
FieldValue,
};
use trustfall_core::interpreter::AsVertex;

#[derive(Debug)]
pub(crate) struct FeedAdapter<'a> {
Expand Down Expand Up @@ -56,12 +57,12 @@ impl<'a> BasicAdapter<'a> for FeedAdapter<'a> {
}
}

fn resolve_property(
fn resolve_property<V: AsVertex<Self::Vertex> + 'a>(
&self,
contexts: ContextIterator<'a, Self::Vertex>,
contexts: ContextIterator<'a, V>,
type_name: &str,
property_name: &str,
) -> ContextOutcomeIterator<'a, Self::Vertex, FieldValue> {
) -> ContextOutcomeIterator<'a, V, FieldValue> {
match type_name {
"Feed" => match property_name {
"id" => property(contexts, field_property!(as_feed, id)),
Expand Down Expand Up @@ -152,13 +153,13 @@ impl<'a> BasicAdapter<'a> for FeedAdapter<'a> {
}
}

fn resolve_neighbors(
fn resolve_neighbors<V: AsVertex<Self::Vertex> + 'a>(
&self,
contexts: ContextIterator<'a, Self::Vertex>,
contexts: ContextIterator<'a, V>,
type_name: &str,
edge_name: &str,
_parameters: &EdgeParameters,
) -> ContextOutcomeIterator<'a, Self::Vertex, VertexIterator<'a, Self::Vertex>> {
) -> ContextOutcomeIterator<'a, V, VertexIterator<'a, Self::Vertex>> {
match type_name {
"Feed" => match edge_name {
"title" => neighbors(contexts, iterable!(as_feed, title, Vertex::FeedText)),
Expand Down Expand Up @@ -195,12 +196,12 @@ impl<'a> BasicAdapter<'a> for FeedAdapter<'a> {
}
}

fn resolve_coercion(
fn resolve_coercion<V: AsVertex<Self::Vertex> + 'a>(
&self,
_contexts: ContextIterator<'a, Self::Vertex>,
_contexts: ContextIterator<'a, V>,
type_name: &str,
coerce_to_type: &str,
) -> ContextOutcomeIterator<'a, Self::Vertex, bool> {
) -> ContextOutcomeIterator<'a, V, bool> {
unimplemented!("{type_name} -> {coerce_to_type}")
}
}
Loading