diff --git a/libcst/_parser/tests/test_parse_errors.py b/libcst/_parser/tests/test_parse_errors.py index 331dd81c5..f36d08e7e 100644 --- a/libcst/_parser/tests/test_parse_errors.py +++ b/libcst/_parser/tests/test_parse_errors.py @@ -6,8 +6,10 @@ from textwrap import dedent from typing import Callable +from unittest.mock import patch import libcst as cst +from libcst._nodes.base import CSTValidationError from libcst._parser.entrypoints import is_native from libcst.testing.utils import data_provider, UnitTest @@ -172,3 +174,10 @@ def test_parser_syntax_error_str( parse_fn() if not is_native(): self.assertEqual(str(cm.exception), expected) + + def test_native_fallible_into_py(self) -> None: + with patch("libcst._nodes.expression.Name._validate") as await_validate: + await_validate.side_effect = CSTValidationError("validate is broken") + with self.assertRaises(Exception) as e: + cst.parse_module("foo") + self.assertIsInstance(e.exception, (SyntaxError, cst.ParserSyntaxError)) diff --git a/native/libcst/src/nodes/expression.rs b/native/libcst/src/nodes/expression.rs index 570c55cab..9345b6c7c 100644 --- a/native/libcst/src/nodes/expression.rs +++ b/native/libcst/src/nodes/expression.rs @@ -19,13 +19,13 @@ use crate::{ }, }; #[cfg(feature = "py")] -use libcst_derive::IntoPy; +use libcst_derive::TryIntoPy; use libcst_derive::{Codegen, Inflate, ParenthesizedNode}; type TokenRef<'a> = Rc>; #[derive(Debug, Eq, PartialEq, Default, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Parameters<'a> { pub params: Vec>, pub star_arg: Option>, @@ -59,7 +59,7 @@ impl<'a> Inflate<'a> for Parameters<'a> { } #[derive(Debug, PartialEq, Eq, Clone, Inflate)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum StarArg<'a> { Star(Box>), Param(Box>), @@ -120,7 +120,7 @@ impl<'a> Codegen<'a> for Parameters<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct ParamSlash<'a> { pub comma: Option>, } @@ -144,7 +144,7 @@ impl<'a> Inflate<'a> for ParamSlash<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct ParamStar<'a> { pub comma: Comma<'a>, } @@ -164,7 +164,7 @@ impl<'a> Inflate<'a> for ParamStar<'a> { } #[derive(Debug, Eq, PartialEq, Default, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Name<'a> { pub value: &'a str, pub lpar: Vec>, @@ -188,7 +188,7 @@ impl<'a> Codegen<'a> for Name<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Param<'a> { pub name: Name<'a>, pub annotation: Option>, @@ -281,7 +281,7 @@ impl<'a> Param<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Arg<'a> { pub value: Expression<'a>, pub keyword: Option>, @@ -345,7 +345,7 @@ impl<'a> WithComma<'a> for Arg<'a> { } #[derive(Debug, Eq, PartialEq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct LeftParen<'a> { /// Any space that appears directly after this left parenthesis. pub whitespace_after: ParenthesizableWhitespace<'a>, @@ -371,7 +371,7 @@ impl<'a> Inflate<'a> for LeftParen<'a> { } #[derive(Debug, Eq, PartialEq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct RightParen<'a> { /// Any space that appears directly before this right parenthesis. pub whitespace_before: ParenthesizableWhitespace<'a>, @@ -397,7 +397,7 @@ impl<'a> Inflate<'a> for RightParen<'a> { } #[derive(Debug, Eq, PartialEq, Clone, ParenthesizedNode, Codegen, Inflate)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum Expression<'a> { Name(Box>), Ellipsis(Box>), @@ -431,7 +431,7 @@ pub enum Expression<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Ellipsis<'a> { pub lpar: Vec>, pub rpar: Vec>, @@ -453,7 +453,7 @@ impl<'a> Inflate<'a> for Ellipsis<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Integer<'a> { /// A string representation of the integer, such as ``"100000"`` or /// ``"100_000"``. @@ -479,7 +479,7 @@ impl<'a> Inflate<'a> for Integer<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Float<'a> { /// A string representation of the floating point number, such as ```"0.05"``, /// ``".050"``, or ``"5e-2"``. @@ -505,7 +505,7 @@ impl<'a> Inflate<'a> for Float<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Imaginary<'a> { /// A string representation of the complex number, such as ``"2j"`` pub value: &'a str, @@ -530,7 +530,7 @@ impl<'a> Inflate<'a> for Imaginary<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Comparison<'a> { pub left: Box>, pub comparisons: Vec>, @@ -559,7 +559,7 @@ impl<'a> Inflate<'a> for Comparison<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct UnaryOperation<'a> { pub operator: UnaryOp<'a>, pub expression: Box>, @@ -587,7 +587,7 @@ impl<'a> Inflate<'a> for UnaryOperation<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct BinaryOperation<'a> { pub left: Box>, pub operator: BinaryOp<'a>, @@ -618,7 +618,7 @@ impl<'a> Inflate<'a> for BinaryOperation<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct BooleanOperation<'a> { pub left: Box>, pub operator: BooleanOp<'a>, @@ -649,7 +649,7 @@ impl<'a> Inflate<'a> for BooleanOperation<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Call<'a> { pub func: Box>, pub args: Vec>, @@ -707,7 +707,7 @@ impl<'a> Codegen<'a> for Call<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Attribute<'a> { pub value: Box>, pub attr: Name<'a>, @@ -738,7 +738,7 @@ impl<'a> Codegen<'a> for Attribute<'a> { } #[derive(Debug, PartialEq, Eq, Clone, Codegen, Inflate)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum NameOrAttribute<'a> { N(Box>), A(Box>), @@ -754,7 +754,7 @@ impl<'a> std::convert::From> for Expression<'a> { } #[derive(Debug, Eq, PartialEq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct ComparisonTarget<'a> { pub operator: CompOp<'a>, pub comparator: Expression<'a>, @@ -776,7 +776,7 @@ impl<'a> Inflate<'a> for ComparisonTarget<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct StarredElement<'a> { pub value: Box>, pub comma: Option>, @@ -895,7 +895,7 @@ impl<'a> std::convert::From> for Element<'a> { } #[derive(Debug, PartialEq, Eq, Clone, Default, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Tuple<'a> { pub elements: Vec>, pub lpar: Vec>, @@ -936,7 +936,7 @@ impl<'a> Codegen<'a> for Tuple<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct GeneratorExp<'a> { pub elt: Box>, pub for_in: Box>, @@ -964,7 +964,7 @@ impl<'a> Inflate<'a> for GeneratorExp<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct ListComp<'a> { pub elt: Box>, pub for_in: Box>, @@ -998,7 +998,7 @@ impl<'a> Inflate<'a> for ListComp<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct LeftSquareBracket<'a> { pub whitespace_after: ParenthesizableWhitespace<'a>, pub(crate) tok: TokenRef<'a>, @@ -1022,7 +1022,7 @@ impl<'a> Inflate<'a> for LeftSquareBracket<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct RightSquareBracket<'a> { pub whitespace_before: ParenthesizableWhitespace<'a>, pub(crate) tok: TokenRef<'a>, @@ -1046,7 +1046,7 @@ impl<'a> Inflate<'a> for RightSquareBracket<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct SetComp<'a> { pub elt: Box>, pub for_in: Box>, @@ -1080,7 +1080,7 @@ impl<'a> Codegen<'a> for SetComp<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct DictComp<'a> { pub key: Box>, pub value: Box>, @@ -1132,7 +1132,7 @@ impl<'a> Codegen<'a> for DictComp<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct LeftCurlyBrace<'a> { pub whitespace_after: ParenthesizableWhitespace<'a>, pub(crate) tok: TokenRef<'a>, @@ -1156,7 +1156,7 @@ impl<'a> Codegen<'a> for LeftCurlyBrace<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct RightCurlyBrace<'a> { pub whitespace_before: ParenthesizableWhitespace<'a>, pub(crate) tok: TokenRef<'a>, @@ -1180,7 +1180,7 @@ impl<'a> Codegen<'a> for RightCurlyBrace<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct CompFor<'a> { pub target: AssignTargetExpression<'a>, pub iter: Expression<'a>, @@ -1256,7 +1256,7 @@ impl<'a> Inflate<'a> for CompFor<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Asynchronous<'a> { pub whitespace_after: ParenthesizableWhitespace<'a>, } @@ -1269,7 +1269,7 @@ impl<'a> Codegen<'a> for Asynchronous<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct CompIf<'a> { pub test: Expression<'a>, pub whitespace_before: ParenthesizableWhitespace<'a>, @@ -1303,7 +1303,7 @@ impl<'a> Inflate<'a> for CompIf<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct List<'a> { pub elements: Vec>, pub lbracket: LeftSquareBracket<'a>, @@ -1346,7 +1346,7 @@ impl<'a> Codegen<'a> for List<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Set<'a> { pub elements: Vec>, pub lbrace: LeftCurlyBrace<'a>, @@ -1388,7 +1388,7 @@ impl<'a> Codegen<'a> for Set<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Dict<'a> { pub elements: Vec>, pub lbrace: LeftCurlyBrace<'a>, @@ -1540,7 +1540,7 @@ impl<'a> WithComma<'a> for DictElement<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct StarredDictElement<'a> { pub value: Expression<'a>, pub comma: Option>, @@ -1577,14 +1577,14 @@ impl<'a> Codegen<'a> for StarredDictElement<'a> { } #[derive(Debug, PartialEq, Eq, Clone, Codegen, Inflate)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum BaseSlice<'a> { Index(Box>), Slice(Box>), } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Index<'a> { pub value: Expression<'a>, } @@ -1603,7 +1603,7 @@ impl<'a> Codegen<'a> for Index<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Slice<'a> { #[cfg_attr(feature = "py", no_py_default)] pub lower: Option>, @@ -1646,7 +1646,7 @@ impl<'a> Codegen<'a> for Slice<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct SubscriptElement<'a> { pub slice: BaseSlice<'a>, pub comma: Option>, @@ -1670,7 +1670,7 @@ impl<'a> Codegen<'a> for SubscriptElement<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Subscript<'a> { pub value: Box>, pub slice: Vec>, @@ -1718,7 +1718,7 @@ impl<'a> Codegen<'a> for Subscript<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct IfExp<'a> { pub test: Box>, pub body: Box>, @@ -1778,7 +1778,7 @@ impl<'a> Codegen<'a> for IfExp<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Lambda<'a> { pub params: Box>, pub body: Box>, @@ -1826,7 +1826,7 @@ impl<'a> Codegen<'a> for Lambda<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct From<'a> { pub item: Expression<'a>, pub whitespace_before_from: Option>, @@ -1864,7 +1864,7 @@ impl<'a> Inflate<'a> for From<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum YieldValue<'a> { Expression(Box>), From(Box>), @@ -1893,7 +1893,7 @@ impl<'a> YieldValue<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Yield<'a> { pub value: Option>>, pub lpar: Vec>, @@ -1936,7 +1936,7 @@ impl<'a> Codegen<'a> for Yield<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Await<'a> { pub expression: Box>, pub lpar: Vec>, @@ -1970,7 +1970,7 @@ impl<'a> Codegen<'a> for Await<'a> { } #[derive(Debug, PartialEq, Eq, Clone, Codegen, Inflate)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum String<'a> { Simple(SimpleString<'a>), Concatenated(ConcatenatedString<'a>), @@ -1988,7 +1988,7 @@ impl<'a> std::convert::From> for Expression<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct ConcatenatedString<'a> { pub left: Box>, pub right: Box>, @@ -2026,7 +2026,7 @@ impl<'a> Codegen<'a> for ConcatenatedString<'a> { } #[derive(Debug, PartialEq, Eq, Clone, Default, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct SimpleString<'a> { /// The texual representation of the string, including quotes, prefix /// characters, and any escape characters present in the original source code, @@ -2051,7 +2051,7 @@ impl<'a> Codegen<'a> for SimpleString<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct FormattedStringText<'a> { pub value: &'a str, } @@ -2069,7 +2069,7 @@ impl<'a> Codegen<'a> for FormattedStringText<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct FormattedStringExpression<'a> { pub expression: Expression<'a>, pub conversion: Option<&'a str>, @@ -2127,14 +2127,14 @@ impl<'a> Codegen<'a> for FormattedStringExpression<'a> { } #[derive(Debug, PartialEq, Eq, Clone, Codegen, Inflate)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum FormattedStringContent<'a> { Text(FormattedStringText<'a>), Expression(Box>), } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct FormattedString<'a> { pub parts: Vec>, pub start: &'a str, @@ -2165,7 +2165,7 @@ impl<'a> Codegen<'a> for FormattedString<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct NamedExpr<'a> { pub target: Box>, pub value: Box>, @@ -2211,43 +2211,45 @@ impl<'a> Inflate<'a> for NamedExpr<'a> { #[cfg(feature = "py")] mod py { - use pyo3::{types::PyModule, IntoPy}; + use pyo3::types::PyModule; use super::*; - use crate::OrElse; + use crate::nodes::traits::py::TryIntoPy; // TODO: this could be a derive helper attribute to override the python class name - impl<'a> IntoPy for Element<'a> { - fn into_py(self, py: pyo3::Python) -> pyo3::PyObject { + impl<'a> TryIntoPy for Element<'a> { + fn try_into_py(self, py: pyo3::Python) -> pyo3::PyResult { match self { - Self::Starred(s) => s.into_py(py), + Self::Starred(s) => s.try_into_py(py), Self::Simple { value, comma } => { - let libcst = PyModule::import(py, "libcst").expect("libcst cannot be imported"); + let libcst = PyModule::import(py, "libcst")?; let kwargs = [ - Some(("value", value.into_py(py))), - comma.map(|x| ("comma", x.into_py(py))), + Some(("value", value.try_into_py(py)?)), + comma + .map(|x| x.try_into_py(py)) + .transpose()? + .map(|x| ("comma", x)), ] .iter() .filter(|x| x.is_some()) .map(|x| x.as_ref().unwrap()) .collect::>() .into_py_dict(py); - libcst + Ok(libcst .getattr("Element") .expect("no Element found in libcst") - .call((), Some(kwargs)) - .expect("conversion failed") - .into() + .call((), Some(kwargs))? + .into()) } } } } // TODO: this could be a derive helper attribute to override the python class name - impl<'a> IntoPy for DictElement<'a> { - fn into_py(self, py: pyo3::Python) -> pyo3::PyObject { + impl<'a> TryIntoPy for DictElement<'a> { + fn try_into_py(self, py: pyo3::Python) -> pyo3::PyResult { match self { - Self::Starred(s) => s.into_py(py), + Self::Starred(s) => s.try_into_py(py), Self::Simple { key, value, @@ -2256,48 +2258,35 @@ mod py { whitespace_before_colon, .. } => { - let libcst = PyModule::import(py, "libcst").expect("libcst cannot be imported"); + let libcst = PyModule::import(py, "libcst")?; let kwargs = [ - Some(("key", key.into_py(py))), - Some(("value", value.into_py(py))), + Some(("key", key.try_into_py(py)?)), + Some(("value", value.try_into_py(py)?)), Some(( "whitespace_before_colon", - whitespace_before_colon.into_py(py), + whitespace_before_colon.try_into_py(py)?, )), - Some(("whitespace_after_colon", whitespace_after_colon.into_py(py))), - comma.map(|x| ("comma", x.into_py(py))), + Some(( + "whitespace_after_colon", + whitespace_after_colon.try_into_py(py)?, + )), + comma + .map(|x| x.try_into_py(py)) + .transpose()? + .map(|x| ("comma", x)), ] .iter() .filter(|x| x.is_some()) .map(|x| x.as_ref().unwrap()) .collect::>() .into_py_dict(py); - libcst + Ok(libcst .getattr("DictElement") .expect("no Element found in libcst") - .call((), Some(kwargs)) - .expect("conversion failed") - .into() + .call((), Some(kwargs))? + .into()) } } } } - - impl<'a> pyo3::conversion::IntoPy for Box> { - fn into_py(self, py: pyo3::Python) -> pyo3::PyObject { - (*self).into_py(py) - } - } - - impl<'a> pyo3::conversion::IntoPy for Box> { - fn into_py(self, py: pyo3::Python) -> pyo3::PyObject { - (*self).into_py(py) - } - } - - impl<'a> pyo3::conversion::IntoPy for Box> { - fn into_py(self, py: pyo3::Python) -> pyo3::PyObject { - (*self).into_py(py) - } - } } diff --git a/native/libcst/src/nodes/mod.rs b/native/libcst/src/nodes/mod.rs index 69463d998..58fa42d12 100644 --- a/native/libcst/src/nodes/mod.rs +++ b/native/libcst/src/nodes/mod.rs @@ -45,7 +45,7 @@ pub use module::Module; mod codegen; pub use codegen::{Codegen, CodegenState}; -mod traits; +pub(crate) mod traits; pub use traits::{Inflate, ParenthesizedNode, WithComma, WithLeadingLines}; pub(crate) mod inflate_helpers; diff --git a/native/libcst/src/nodes/module.rs b/native/libcst/src/nodes/module.rs index 661c7bb74..7bc42385d 100644 --- a/native/libcst/src/nodes/module.rs +++ b/native/libcst/src/nodes/module.rs @@ -17,14 +17,14 @@ use crate::{ tokenizer::whitespace_parser::Config, }; #[cfg(feature = "py")] -use libcst_derive::IntoPy; +use libcst_derive::TryIntoPy; use super::traits::{Inflate, Result, WithLeadingLines}; type TokenRef<'a> = Rc>; #[derive(Debug, Eq, PartialEq)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Module<'a> { pub body: Vec>, pub header: Vec>, diff --git a/native/libcst/src/nodes/op.rs b/native/libcst/src/nodes/op.rs index 800f8ebd5..d857e9a97 100644 --- a/native/libcst/src/nodes/op.rs +++ b/native/libcst/src/nodes/op.rs @@ -14,12 +14,12 @@ use crate::{ }, }; #[cfg(feature = "py")] -use libcst_derive::IntoPy; +use libcst_derive::TryIntoPy; type TokenRef<'a> = Rc>; #[derive(Debug, Eq, PartialEq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Semicolon<'a> { /// Any space that appears directly before this semicolon. pub whitespace_before: ParenthesizableWhitespace<'a>, @@ -51,7 +51,7 @@ impl<'a> Inflate<'a> for Semicolon<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Comma<'a> { /// Any space that appears directly before this comma. pub whitespace_before: ParenthesizableWhitespace<'a>, @@ -95,7 +95,7 @@ impl<'a> Comma<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct AssignEqual<'a> { /// Any space that appears directly before this equal sign. pub whitespace_before: ParenthesizableWhitespace<'a>, @@ -129,7 +129,7 @@ impl<'a> Inflate<'a> for AssignEqual<'a> { } #[derive(Debug, Eq, PartialEq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Dot<'a> { /// Any space that appears directly before this dot. pub whitespace_before: ParenthesizableWhitespace<'a>, @@ -175,7 +175,7 @@ impl<'a> Dot<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct ImportStar {} impl<'a> Codegen<'a> for ImportStar { @@ -191,7 +191,7 @@ impl<'a> Inflate<'a> for ImportStar { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum UnaryOp<'a> { Plus { whitespace_after: ParenthesizableWhitespace<'a>, @@ -284,7 +284,7 @@ impl<'a> Inflate<'a> for UnaryOp<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum BooleanOp<'a> { And { whitespace_before: ParenthesizableWhitespace<'a>, @@ -358,7 +358,7 @@ impl<'a> Inflate<'a> for BooleanOp<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum BinaryOp<'a> { Add { whitespace_before: ParenthesizableWhitespace<'a>, @@ -718,7 +718,7 @@ impl<'a> Inflate<'a> for BinaryOp<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum CompOp<'a> { LessThan { whitespace_before: ParenthesizableWhitespace<'a>, @@ -1040,7 +1040,7 @@ impl<'a> Inflate<'a> for CompOp<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Colon<'a> { pub whitespace_before: ParenthesizableWhitespace<'a>, pub whitespace_after: ParenthesizableWhitespace<'a>, @@ -1072,7 +1072,7 @@ impl<'a> Codegen<'a> for Colon<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum AugOp<'a> { AddAssign { whitespace_before: ParenthesizableWhitespace<'a>, @@ -1432,7 +1432,7 @@ impl<'a> Codegen<'a> for AugOp<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct BitOr<'a> { pub whitespace_before: ParenthesizableWhitespace<'a>, pub whitespace_after: ParenthesizableWhitespace<'a>, diff --git a/native/libcst/src/nodes/statement.rs b/native/libcst/src/nodes/statement.rs index d68ec0557..65c702f31 100644 --- a/native/libcst/src/nodes/statement.rs +++ b/native/libcst/src/nodes/statement.rs @@ -26,14 +26,14 @@ use crate::{ LeftCurlyBrace, LeftSquareBracket, RightCurlyBrace, RightSquareBracket, }; #[cfg(feature = "py")] -use libcst_derive::IntoPy; +use libcst_derive::TryIntoPy; use libcst_derive::{Codegen, Inflate, ParenthesizedNode}; type TokenRef<'a> = Rc>; #[allow(clippy::large_enum_variant)] #[derive(Debug, Eq, PartialEq, Clone, Inflate, Codegen)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum Statement<'a> { Simple(SimpleStatementLine<'a>), Compound(CompoundStatement<'a>), @@ -49,7 +49,7 @@ impl<'a> WithLeadingLines<'a> for Statement<'a> { } #[derive(Debug, PartialEq, Eq, Clone, Inflate, Codegen)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] #[allow(clippy::large_enum_variant)] pub enum CompoundStatement<'a> { FunctionDef(FunctionDef<'a>), @@ -80,14 +80,14 @@ impl<'a> WithLeadingLines<'a> for CompoundStatement<'a> { } #[derive(Debug, PartialEq, Eq, Clone, Inflate, Codegen)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum Suite<'a> { IndentedBlock(IndentedBlock<'a>), SimpleStatementSuite(SimpleStatementSuite<'a>), } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct IndentedBlock<'a> { /// Sequence of statements belonging to this indented block. pub body: Vec>, @@ -178,7 +178,7 @@ impl<'a> Inflate<'a> for IndentedBlock<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct SimpleStatementSuite<'a> { /// Sequence of small statements. All but the last statement are required to have /// a semicolon. @@ -233,7 +233,7 @@ impl<'a> Codegen<'a> for SimpleStatementSuite<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct SimpleStatementLine<'a> { /// Sequence of small statements. All but the last statement are required to have /// a semicolon. @@ -276,7 +276,7 @@ impl<'a> Inflate<'a> for SimpleStatementLine<'a> { #[allow(dead_code, clippy::large_enum_variant)] #[derive(Debug, Eq, PartialEq, Clone, Codegen, Inflate)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum SmallStatement<'a> { Pass(Pass<'a>), Break(Break<'a>), @@ -318,7 +318,7 @@ impl<'a> SmallStatement<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Pass<'a> { pub semicolon: Option>, } @@ -341,7 +341,7 @@ impl<'a> Inflate<'a> for Pass<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Break<'a> { pub semicolon: Option>, } @@ -364,7 +364,7 @@ impl<'a> Inflate<'a> for Break<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Continue<'a> { pub semicolon: Option>, } @@ -387,7 +387,7 @@ impl<'a> Inflate<'a> for Continue<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Expr<'a> { pub value: Expression<'a>, pub semicolon: Option>, @@ -412,7 +412,7 @@ impl<'a> Inflate<'a> for Expr<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Assign<'a> { pub targets: Vec>, pub value: Expression<'a>, @@ -447,7 +447,7 @@ impl<'a> Assign<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct AssignTarget<'a> { pub target: AssignTargetExpression<'a>, pub whitespace_before_equal: SimpleWhitespace<'a>, @@ -480,7 +480,7 @@ impl<'a> Inflate<'a> for AssignTarget<'a> { #[allow(clippy::large_enum_variant)] #[derive(Debug, PartialEq, Eq, Clone, Codegen, ParenthesizedNode, Inflate)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum AssignTargetExpression<'a> { Name(Box>), Attribute(Box>), @@ -491,7 +491,7 @@ pub enum AssignTargetExpression<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Import<'a> { pub names: Vec>, pub semicolon: Option>, @@ -535,7 +535,7 @@ impl<'a> Import<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct ImportFrom<'a> { #[cfg_attr(feature = "py", no_py_default)] pub module: Option>, @@ -651,7 +651,7 @@ impl<'a> ImportFrom<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct ImportAlias<'a> { pub name: NameOrAttribute<'a>, pub asname: Option>, @@ -687,7 +687,7 @@ impl<'a> Codegen<'a> for ImportAlias<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct AsName<'a> { pub name: AssignTargetExpression<'a>, pub whitespace_before_as: ParenthesizableWhitespace<'a>, @@ -721,7 +721,7 @@ impl<'a> Inflate<'a> for AsName<'a> { } #[derive(Debug, PartialEq, Eq, Clone, Inflate)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum ImportNames<'a> { Star(ImportStar), Aliases(Vec>), @@ -744,7 +744,7 @@ impl<'a> Codegen<'a> for ImportNames<'a> { } #[derive(Debug, Eq, PartialEq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct FunctionDef<'a> { pub name: Name<'a>, pub params: Parameters<'a>, @@ -870,7 +870,7 @@ impl<'a> Inflate<'a> for FunctionDef<'a> { } #[derive(Debug, Eq, PartialEq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Decorator<'a> { pub decorator: Expression<'a>, pub leading_lines: Vec>, @@ -913,7 +913,7 @@ impl<'a> Inflate<'a> for Decorator<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct If<'a> { /// The expression that, when evaluated, should give us a truthy value pub test: Expression<'a>, @@ -983,14 +983,14 @@ impl<'a> Inflate<'a> for If<'a> { #[allow(clippy::large_enum_variant)] #[derive(Debug, PartialEq, Eq, Clone, Inflate, Codegen)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum OrElse<'a> { Elif(If<'a>), Else(Else<'a>), } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Else<'a> { pub body: Suite<'a>, /// Sequence of empty lines appearing before this compound statement line. @@ -1034,7 +1034,7 @@ impl<'a> Inflate<'a> for Else<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Annotation<'a> { pub annotation: Expression<'a>, pub whitespace_before_indicator: Option>, @@ -1075,7 +1075,7 @@ impl<'a> Inflate<'a> for Annotation<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct AnnAssign<'a> { pub target: AssignTargetExpression<'a>, pub annotation: Annotation<'a>, @@ -1121,7 +1121,7 @@ impl<'a> AnnAssign<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Return<'a> { pub value: Option>, pub whitespace_after_return: Option>, @@ -1173,7 +1173,7 @@ impl<'a> Return<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Assert<'a> { pub test: Expression<'a>, pub msg: Option>, @@ -1225,7 +1225,7 @@ impl<'a> Assert<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Raise<'a> { pub exc: Option>, pub cause: Option>, @@ -1288,7 +1288,7 @@ impl<'a> Raise<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct NameItem<'a> { pub name: Name<'a>, pub comma: Option>, @@ -1314,7 +1314,7 @@ impl<'a> NameItem<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Global<'a> { pub names: Vec>, pub whitespace_after_global: SimpleWhitespace<'a>, @@ -1355,7 +1355,7 @@ impl<'a> Global<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Nonlocal<'a> { pub names: Vec>, pub whitespace_after_nonlocal: SimpleWhitespace<'a>, @@ -1396,7 +1396,7 @@ impl<'a> Nonlocal<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct For<'a> { pub target: AssignTargetExpression<'a>, pub iter: Expression<'a>, @@ -1489,7 +1489,7 @@ impl<'a> Inflate<'a> for For<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct While<'a> { pub test: Expression<'a>, pub body: Suite<'a>, @@ -1543,7 +1543,7 @@ impl<'a> Inflate<'a> for While<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct ClassDef<'a> { pub name: Name<'a>, pub body: Suite<'a>, @@ -1650,7 +1650,7 @@ impl<'a> ClassDef<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Finally<'a> { pub body: Suite<'a>, pub leading_lines: Vec>, @@ -1691,7 +1691,7 @@ impl<'a> Inflate<'a> for Finally<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct ExceptHandler<'a> { pub body: Suite<'a>, pub r#type: Option>, @@ -1752,7 +1752,7 @@ impl<'a> Inflate<'a> for ExceptHandler<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct ExceptStarHandler<'a> { pub body: Suite<'a>, pub r#type: Expression<'a>, @@ -1815,7 +1815,7 @@ impl<'a> Inflate<'a> for ExceptStarHandler<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Try<'a> { pub body: Suite<'a>, pub handlers: Vec>, @@ -1868,7 +1868,7 @@ impl<'a> Inflate<'a> for Try<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct TryStar<'a> { pub body: Suite<'a>, pub handlers: Vec>, @@ -1921,7 +1921,7 @@ impl<'a> Inflate<'a> for TryStar<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct AugAssign<'a> { pub target: AssignTargetExpression<'a>, pub operator: AugOp<'a>, @@ -1958,7 +1958,7 @@ impl<'a> AugAssign<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct WithItem<'a> { pub item: Expression<'a>, pub asname: Option>, @@ -2000,7 +2000,7 @@ impl<'a> WithComma<'a> for WithItem<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct With<'a> { pub items: Vec>, pub body: Suite<'a>, @@ -2114,7 +2114,7 @@ impl<'a> Inflate<'a> for With<'a> { } #[derive(Debug, PartialEq, Eq, Clone, Codegen, ParenthesizedNode, Inflate)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum DelTargetExpression<'a> { Name(Box>), Attribute(Box>), @@ -2144,7 +2144,7 @@ impl<'a> std::convert::From> for Element<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Del<'a> { pub target: DelTargetExpression<'a>, pub whitespace_after_del: SimpleWhitespace<'a>, @@ -2181,7 +2181,7 @@ impl<'a> Del<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Match<'a> { pub subject: Expression<'a>, pub cases: Vec>, @@ -2257,7 +2257,7 @@ impl<'a> Inflate<'a> for Match<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct MatchCase<'a> { pub pattern: MatchPattern<'a>, pub guard: Option>, @@ -2322,7 +2322,7 @@ impl<'a> Inflate<'a> for MatchCase<'a> { #[allow(clippy::large_enum_variant)] #[derive(Debug, PartialEq, Eq, Clone, Codegen, Inflate, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum MatchPattern<'a> { Value(MatchValue<'a>), Singleton(MatchSingleton<'a>), @@ -2334,7 +2334,7 @@ pub enum MatchPattern<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct MatchValue<'a> { pub value: Expression<'a>, } @@ -2373,7 +2373,7 @@ impl<'a> Inflate<'a> for MatchValue<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct MatchSingleton<'a> { pub value: Name<'a>, } @@ -2413,14 +2413,14 @@ impl<'a> Inflate<'a> for MatchSingleton<'a> { #[allow(clippy::large_enum_variant)] #[derive(Debug, PartialEq, Eq, Clone, Codegen, Inflate, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum MatchSequence<'a> { MatchList(MatchList<'a>), MatchTuple(MatchTuple<'a>), } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct MatchList<'a> { pub patterns: Vec>, pub lbracket: Option>, @@ -2466,7 +2466,7 @@ impl<'a> Inflate<'a> for MatchList<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct MatchTuple<'a> { pub patterns: Vec>, pub lpar: Vec>, @@ -2505,7 +2505,7 @@ impl<'a> Inflate<'a> for MatchTuple<'a> { #[allow(clippy::large_enum_variant)] #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum StarrableMatchSequenceElement<'a> { Simple(MatchSequenceElement<'a>), Starred(MatchStar<'a>), @@ -2541,7 +2541,7 @@ impl<'a> WithComma<'a> for StarrableMatchSequenceElement<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct MatchSequenceElement<'a> { pub value: MatchPattern<'a>, pub comma: Option>, @@ -2582,7 +2582,7 @@ impl<'a> WithComma<'a> for MatchSequenceElement<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct MatchStar<'a> { pub name: Option>, pub comma: Option>, @@ -2636,7 +2636,7 @@ impl<'a> WithComma<'a> for MatchStar<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct MatchMapping<'a> { pub elements: Vec>, pub rest: Option>, @@ -2701,7 +2701,7 @@ impl<'a> Inflate<'a> for MatchMapping<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct MatchMappingElement<'a> { pub key: Expression<'a>, pub pattern: MatchPattern<'a>, @@ -2756,7 +2756,7 @@ impl<'a> WithComma<'a> for MatchMappingElement<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct MatchClass<'a> { pub cls: NameOrAttribute<'a>, pub patterns: Vec>, @@ -2833,7 +2833,7 @@ impl<'a> Inflate<'a> for MatchClass<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct MatchKeywordElement<'a> { pub key: Name<'a>, pub pattern: MatchPattern<'a>, @@ -2887,7 +2887,7 @@ impl<'a> WithComma<'a> for MatchKeywordElement<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct MatchAs<'a> { pub pattern: Option>, pub name: Option>, @@ -2939,7 +2939,7 @@ impl<'a> Inflate<'a> for MatchAs<'a> { } #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct MatchOrElement<'a> { pub pattern: MatchPattern<'a>, pub separator: Option>, @@ -2964,7 +2964,7 @@ impl<'a> Inflate<'a> for MatchOrElement<'a> { } #[derive(Debug, PartialEq, Eq, Clone, ParenthesizedNode)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct MatchOr<'a> { pub patterns: Vec>, pub lpar: Vec>, diff --git a/native/libcst/src/nodes/traits.rs b/native/libcst/src/nodes/traits.rs index aabd27ef1..f8859641f 100644 --- a/native/libcst/src/nodes/traits.rs +++ b/native/libcst/src/nodes/traits.rs @@ -84,3 +84,82 @@ impl<'a, T: Inflate<'a>> Inflate<'a> for Vec { self.into_iter().map(|item| item.inflate(config)).collect() } } +#[cfg(feature = "py")] +pub mod py { + use pyo3::{types::PyTuple, AsPyPointer, IntoPy, PyObject, PyResult, Python}; + + // TODO: replace with upstream implementation once + // https://github.com/PyO3/pyo3/issues/1813 is resolved + pub trait TryIntoPy: Sized { + fn try_into_py(self, py: Python) -> PyResult; + } + + // I wish: + // impl> TryIntoPy for T { + // fn try_into_py(self, py: Python) -> PyResult { + // Ok(self.into_py(py)) + // } + // } + + impl TryIntoPy for bool { + fn try_into_py(self, py: Python) -> PyResult { + Ok(self.into_py(py)) + } + } + + impl> TryIntoPy for Box + where + T: TryIntoPy, + { + fn try_into_py(self, py: Python) -> PyResult { + (*self).try_into_py(py) + } + } + + impl TryIntoPy for Option + where + T: TryIntoPy, + { + fn try_into_py(self, py: Python) -> PyResult { + Ok(match self { + None => py.None(), + Some(x) => x.try_into_py(py)?, + }) + } + } + + impl TryIntoPy for Vec + where + T: TryIntoPy, + { + fn try_into_py(self, py: Python) -> PyResult { + let converted = self + .into_iter() + .map(|x| x.try_into_py(py)) + .collect::>>()? + .into_iter(); + Ok(PyTuple::new(py, converted).into()) + } + } + + impl TryIntoPy for PyTuple { + fn try_into_py(self, py: Python) -> PyResult { + Ok(self.into_py(py)) + } + } + + impl<'a> TryIntoPy for &'a str { + fn try_into_py(self, py: Python) -> PyResult { + Ok(self.into_py(py)) + } + } + + impl TryIntoPy for &'_ T + where + T: AsPyPointer, + { + fn try_into_py(self, py: Python) -> PyResult { + Ok(self.into_py(py)) + } + } +} diff --git a/native/libcst/src/nodes/whitespace.rs b/native/libcst/src/nodes/whitespace.rs index 59b314c2f..474ee3842 100644 --- a/native/libcst/src/nodes/whitespace.rs +++ b/native/libcst/src/nodes/whitespace.rs @@ -4,12 +4,12 @@ // LICENSE file in the root directory of this source tree. #[cfg(feature = "py")] -use libcst_derive::IntoPy; +use libcst_derive::TryIntoPy; use super::{Codegen, CodegenState}; #[derive(Debug, Eq, PartialEq, Default, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct SimpleWhitespace<'a>(pub &'a str); impl<'a> Codegen<'a> for SimpleWhitespace<'a> { @@ -19,7 +19,7 @@ impl<'a> Codegen<'a> for SimpleWhitespace<'a> { } #[derive(Debug, Eq, PartialEq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Comment<'a>(pub &'a str); impl<'a> Default for Comment<'a> { @@ -35,7 +35,7 @@ impl<'a> Codegen<'a> for Comment<'a> { } #[derive(Debug, Eq, PartialEq, Default, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct Newline<'a>(pub Option<&'a str>, pub Fakeness); #[derive(Debug, PartialEq, Eq, Clone)] @@ -64,7 +64,7 @@ impl<'a> Codegen<'a> for Newline<'a> { } #[derive(Debug, Eq, PartialEq, Default, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct TrailingWhitespace<'a> { pub whitespace: SimpleWhitespace<'a>, pub comment: Option>, @@ -82,7 +82,7 @@ impl<'a> Codegen<'a> for TrailingWhitespace<'a> { } #[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct EmptyLine<'a> { pub indent: bool, pub whitespace: SimpleWhitespace<'a>, @@ -131,7 +131,7 @@ impl<'a> EmptyLine<'a> { } #[derive(Debug, Eq, PartialEq, Default, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub struct ParenthesizedWhitespace<'a> { pub first_line: TrailingWhitespace<'a>, pub empty_lines: Vec>, @@ -153,7 +153,7 @@ impl<'a> Codegen<'a> for ParenthesizedWhitespace<'a> { } #[derive(Debug, Eq, PartialEq, Clone)] -#[cfg_attr(feature = "py", derive(IntoPy))] +#[cfg_attr(feature = "py", derive(TryIntoPy))] pub enum ParenthesizableWhitespace<'a> { SimpleWhitespace(SimpleWhitespace<'a>), ParenthesizedWhitespace(ParenthesizedWhitespace<'a>), diff --git a/native/libcst/src/py.rs b/native/libcst/src/py.rs index 4cc30fbe5..960fac3ae 100644 --- a/native/libcst/src/py.rs +++ b/native/libcst/src/py.rs @@ -3,6 +3,7 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree +use crate::nodes::traits::py::TryIntoPy; use pyo3::prelude::*; #[pymodule] @@ -11,19 +12,19 @@ pub fn libcst_native(_py: Python, m: &PyModule) -> PyResult<()> { #[pyfn(m)] fn parse_module(source: String, encoding: Option<&str>) -> PyResult { let m = crate::parse_module(source.as_str(), encoding)?; - Python::with_gil(|py| Ok(m.into_py(py))) + Python::with_gil(|py| m.try_into_py(py)) } #[pyfn(m)] fn parse_expression(source: String) -> PyResult { let expr = crate::parse_expression(source.as_str())?; - Python::with_gil(|py| Ok(expr.into_py(py))) + Python::with_gil(|py| expr.try_into_py(py)) } #[pyfn(m)] fn parse_statement(source: String) -> PyResult { let stm = crate::parse_statement(source.as_str())?; - Python::with_gil(|py| Ok(stm.into_py(py))) + Python::with_gil(|py| stm.try_into_py(py)) } Ok(()) diff --git a/native/libcst_derive/src/into_py.rs b/native/libcst_derive/src/into_py.rs index d37a92d33..e1d85132a 100644 --- a/native/libcst_derive/src/into_py.rs +++ b/native/libcst_derive/src/into_py.rs @@ -38,14 +38,13 @@ fn impl_into_py_enum(ast: &DeriveInput, e: &DataEnum) -> TokenStream { let kwargs_toks = fields_to_kwargs(&var.fields, true); toks.push(quote! { Self::#varname { #(#fieldnames,)* .. } => { - let libcst = pyo3::types::PyModule::import(py, "libcst").expect("libcst couldn't be imported"); + let libcst = pyo3::types::PyModule::import(py, "libcst")?; let kwargs = #kwargs_toks ; - libcst + Ok(libcst .getattr(stringify!(#varname)) .expect(stringify!(no #varname found in libcst)) - .call((), Some(kwargs)) - .expect(stringify!(conversion failed for #varname)) - .into() + .call((), Some(kwargs))? + .into()) } }) } @@ -58,7 +57,7 @@ fn impl_into_py_enum(ast: &DeriveInput, e: &DataEnum) -> TokenStream { } Fields::Unnamed(_) => { toks.push(quote! { - Self::#varname(x, ..) => x.into_py(py), + Self::#varname(x, ..) => x.try_into_py(py), }); } } @@ -68,8 +67,8 @@ fn impl_into_py_enum(ast: &DeriveInput, e: &DataEnum) -> TokenStream { let gen = quote! { use pyo3::types::IntoPyDict as _; #[automatically_derived] - impl#generics pyo3::conversion::IntoPy for #ident #generics { - fn into_py(self, py: pyo3::Python) -> pyo3::PyObject { + impl#generics crate::nodes::traits::py::TryIntoPy for #ident #generics { + fn try_into_py(self, py: pyo3::Python) -> pyo3::PyResult { match self { #(#toks)* } @@ -86,16 +85,15 @@ fn impl_into_py_struct(ast: &DeriveInput, e: &DataStruct) -> TokenStream { let gen = quote! { use pyo3::types::IntoPyDict as _; #[automatically_derived] - impl#generics pyo3::conversion::IntoPy for #ident #generics { - fn into_py(self, py: pyo3::Python) -> pyo3::PyObject { - let libcst = pyo3::types::PyModule::import(py, "libcst").expect("libcst couldn't be imported"); + impl#generics crate::nodes::traits::py::TryIntoPy for #ident #generics { + fn try_into_py(self, py: pyo3::Python) -> pyo3::PyResult { + let libcst = pyo3::types::PyModule::import(py, "libcst")?; let kwargs = #kwargs_toks ; - libcst + Ok(libcst .getattr(stringify!(#ident)) .expect(stringify!(no #ident found in libcst)) - .call((), Some(kwargs)) - .expect(stringify!(conversion failed for #ident)) - .into() + .call((), Some(kwargs))? + .into()) } } }; @@ -108,8 +106,6 @@ fn fields_to_kwargs(fields: &Fields, is_enum: bool) -> quote::__private::TokenSt let mut rust_varnames = vec![]; let mut optional_py_varnames = vec![]; let mut optional_rust_varnames = vec![]; - let mut vec_py_varnames = vec![]; - let mut vec_rust_varnames = vec![]; match &fields { Fields::Named(FieldsNamed { named, .. }) => { for field in named.iter() { @@ -140,23 +136,12 @@ fn fields_to_kwargs(fields: &Fields, is_enum: bool) -> quote::__private::TokenSt } } } - if let Type::Path(TypePath { path, .. }) = &field.ty { - if let Some(first) = path.segments.first() { - if first.ident == "Vec" { - vec_py_varnames.push(pyname); - vec_rust_varnames.push(rustname); - continue; - } - } - } py_varnames.push(pyname); rust_varnames.push(rustname); } } } - empty_kwargs = py_varnames.is_empty() - && optional_py_varnames.is_empty() - && vec_py_varnames.is_empty(); + empty_kwargs = py_varnames.is_empty() && optional_py_varnames.is_empty() } Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { if unnamed.first().is_some() { @@ -171,27 +156,16 @@ fn fields_to_kwargs(fields: &Fields, is_enum: bool) -> quote::__private::TokenSt } }; let kwargs_pairs = quote! { - #(Some((stringify!(#py_varnames), #rust_varnames.into_py(py))),)* + #(Some((stringify!(#py_varnames), #rust_varnames.try_into_py(py)?)),)* }; let optional_pairs = quote! { - #(#optional_rust_varnames.map(|x| (stringify!(#optional_py_varnames), x.into_py(py))),)* - }; - let vec_pairs = quote! { - #(Some(( - stringify!(#vec_py_varnames), - pyo3::IntoPy::::into_py( - pyo3::types::PyTuple::new( - py, - #vec_rust_varnames.into_iter().map(|x| x.into_py(py)), - ), - py, - ))),)* + #(#optional_rust_varnames.map(|x| x.try_into_py(py)).transpose()?.map(|x| (stringify!(#optional_py_varnames), x)),)* }; if empty_kwargs { quote! { pyo3::types::PyDict::new(py) } } else { quote! { - [ #kwargs_pairs #optional_pairs #vec_pairs ] + [ #kwargs_pairs #optional_pairs ] .iter() .filter(|x| x.is_some()) .map(|x| x.as_ref().unwrap()) diff --git a/native/libcst_derive/src/lib.rs b/native/libcst_derive/src/lib.rs index 12ce4ff1d..3686eaa2a 100644 --- a/native/libcst_derive/src/lib.rs +++ b/native/libcst_derive/src/lib.rs @@ -30,7 +30,7 @@ pub fn parenthesized_node_codegen(input: TokenStream) -> TokenStream { impl_codegen(&syn::parse(input).unwrap()) } -#[proc_macro_derive(IntoPy, attributes(skip_py, no_py_default))] +#[proc_macro_derive(TryIntoPy, attributes(skip_py, no_py_default))] pub fn into_py(input: TokenStream) -> TokenStream { impl_into_py(&syn::parse(input).unwrap()) }