Handle empty NamedTuple and TypedDict conversions (#3251)

This commit is contained in:
Charlie Marsh
2023-02-27 11:18:34 -05:00
committed by GitHub
parent e8ba9c9e21
commit 16e2dae0c2
6 changed files with 106 additions and 8 deletions

View File

@@ -35,3 +35,9 @@ MyType = TypedDict("MyType", {"in": int, "x-y": int})
# unpacking (OK)
c = {"c": float}
MyType = TypedDict("MyType", {"a": int, "b": str, **c})
# Empty dict literal
MyType = TypedDict("MyType", {})
# Empty dict call
MyType = TypedDict("MyType", dict())

View File

@@ -23,3 +23,9 @@ MyType = NamedTuple(
# invalid identifiers (OK)
MyType = NamedTuple("MyType", [("x-y", int), ("b", tuple[str, ...])])
# no fields
MyType = typing.NamedTuple("MyType")
# empty fields
MyType = typing.NamedTuple("MyType", [])

View File

@@ -103,11 +103,14 @@ fn match_defaults(keywords: &[Keyword]) -> Result<&[Expr]> {
/// Create a list of property assignments from the `NamedTuple` arguments.
fn create_properties_from_args(args: &[Expr], defaults: &[Expr]) -> Result<Vec<Stmt>> {
let Some(fields) = args.get(1) else {
return Ok(vec![]);
return Ok(vec![create_stmt(StmtKind::Pass)]);
};
let ExprKind::List { elts, .. } = &fields.node else {
bail!("Expected argument to be `ExprKind::List`");
};
if elts.is_empty() {
return Ok(vec![create_stmt(StmtKind::Pass)]);
}
let padded_defaults = if elts.len() >= defaults.len() {
std::iter::repeat(None)
.take(elts.len() - defaults.len())

View File

@@ -78,11 +78,6 @@ fn create_property_assignment_stmt(property: &str, annotation: &ExprKind) -> Stm
})
}
/// Generate a `StmtKind::Pass` statement.
fn create_pass_stmt() -> Stmt {
create_stmt(StmtKind::Pass)
}
/// Generate a `StmtKind:ClassDef` statement based on the provided body,
/// keywords and base class.
fn create_class_def_stmt(
@@ -105,6 +100,10 @@ fn create_class_def_stmt(
}
fn properties_from_dict_literal(keys: &[Option<Expr>], values: &[Expr]) -> Result<Vec<Stmt>> {
if keys.is_empty() {
return Ok(vec![create_stmt(StmtKind::Pass)]);
}
keys.iter()
.zip(values.iter())
.map(|(key, value)| match key {
@@ -134,11 +133,19 @@ fn properties_from_dict_call(func: &Expr, keywords: &[Keyword]) -> Result<Vec<St
if id != "dict" {
bail!("Expected `id` to be `\"dict\"`")
}
if keywords.is_empty() {
return Ok(vec![create_stmt(StmtKind::Pass)]);
}
properties_from_keywords(keywords)
}
// Deprecated in Python 3.11, removed in Python 3.13.
fn properties_from_keywords(keywords: &[Keyword]) -> Result<Vec<Stmt>> {
if keywords.is_empty() {
return Ok(vec![create_stmt(StmtKind::Pass)]);
}
keywords
.iter()
.map(|keyword| {
@@ -185,12 +192,12 @@ fn match_properties_and_total<'a>(
ExprKind::Call { func, keywords, .. } => {
Ok((properties_from_dict_call(func, keywords)?, total))
}
_ => Ok((vec![create_pass_stmt()], total)),
_ => bail!("Expected `arg` to be `ExprKind::Dict` or `ExprKind::Call`"),
}
} else if !keywords.is_empty() {
Ok((properties_from_keywords(keywords)?, None))
} else {
Ok((vec![create_pass_stmt()], None))
Ok((vec![create_stmt(StmtKind::Pass)], None))
}
}

View File

@@ -192,4 +192,42 @@ expression: diagnostics
row: 30
column: 49
parent: ~
- kind:
ConvertTypedDictFunctionalToClass:
name: MyType
fixable: true
location:
row: 40
column: 0
end_location:
row: 40
column: 32
fix:
content: "class MyType(TypedDict):\n pass"
location:
row: 40
column: 0
end_location:
row: 40
column: 32
parent: ~
- kind:
ConvertTypedDictFunctionalToClass:
name: MyType
fixable: true
location:
row: 43
column: 0
end_location:
row: 43
column: 36
fix:
content: "class MyType(TypedDict):\n pass"
location:
row: 43
column: 0
end_location:
row: 43
column: 36
parent: ~

View File

@@ -59,4 +59,42 @@ expression: diagnostics
row: 15
column: 62
parent: ~
- kind:
ConvertNamedTupleFunctionalToClass:
name: MyType
fixable: true
location:
row: 28
column: 0
end_location:
row: 28
column: 36
fix:
content: "class MyType(typing.NamedTuple):\n pass"
location:
row: 28
column: 0
end_location:
row: 28
column: 36
parent: ~
- kind:
ConvertNamedTupleFunctionalToClass:
name: MyType
fixable: true
location:
row: 31
column: 0
end_location:
row: 31
column: 40
fix:
content: "class MyType(typing.NamedTuple):\n pass"
location:
row: 31
column: 0
end_location:
row: 31
column: 40
parent: ~