Skip to content

Commit

Permalink
feat: support pyclass on tuple enums (#4072)
Browse files Browse the repository at this point in the history
* feat: support pyclass on tuple enums

* cargo fmt

* changelog

* ruff format

* rebase with adaptation for FnArg refactor

* fix class.md from pr comments

* add enum tuple variant getitem implementation

* fmt

* progress toward getitem and len impl on derive pyclass for complex enum tuple

* working getitem and len slots for complex tuple enum pyclass derivation

* refactor code generation

* address PR concerns
- take py from function argument on get_item
- make more general slot def implementation
- remove unnecessary function arguments
- add testcases for uncovered cases including future feature match_args

* add tracking issue

* fmt

* ruff

* remove me

* support match_args for tuple enum

* integrate FnArg now takes Cow

* fix empty and single element tuples

* use impl_py_slot_def for cimplex tuple enum slots

* reverse erroneous doc change

* Address latest comments

* formatting suggestion

* fix :
- clippy beta
- better compile error (+related doc and test)

---------

Co-authored-by: Chris Arderne <chris@translucent.app>
  • Loading branch information
newcomertv and carderne committed May 17, 2024
1 parent 8de1787 commit 88f2f6f
Show file tree
Hide file tree
Showing 11 changed files with 581 additions and 45 deletions.
21 changes: 12 additions & 9 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,18 @@ enum HttpResponse {
// ...
}

// PyO3 also supports enums with non-unit variants
// PyO3 also supports enums with Struct and Tuple variants
// These complex enums have sligtly different behavior from the simple enums above
// They are meant to work with instance checks and match statement patterns
// The variants can be mixed and matched
// Struct variants have named fields while tuple enums generate generic names for fields in order _0, _1, _2, ...
// Apart from this both types are functionally identical
#[pyclass]
enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
RegularPolygon { side_count: u32, radius: f64 },
Nothing {},
RegularPolygon(u32, f64),
Nothing(),
}
```

Expand Down Expand Up @@ -1180,7 +1183,7 @@ enum BadSubclass {

An enum is complex if it has any non-unit (struct or tuple) variants.

Currently PyO3 supports only struct variants in a complex enum. Support for unit and tuple variants is planned.
PyO3 supports only struct and tuple variants in a complex enum. Unit variants aren't supported at present (the recommendation is to use an empty tuple enum instead).

PyO3 adds a class attribute for each variant, which may be used to construct values and in match patterns. PyO3 also provides getter methods for all fields of each variant.

Expand All @@ -1190,14 +1193,14 @@ PyO3 adds a class attribute for each variant, which may be used to construct val
enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
RegularPolygon { side_count: u32, radius: f64 },
RegularPolygon(u32, f64),
Nothing { },
}

# #[cfg(Py_3_10)]
Python::with_gil(|py| {
let circle = Shape::Circle { radius: 10.0 }.into_py(py);
let square = Shape::RegularPolygon { side_count: 4, radius: 10.0 }.into_py(py);
let square = Shape::RegularPolygon(4, 10.0).into_py(py);
let cls = py.get_type_bound::<Shape>();
pyo3::py_run!(py, circle square cls, r#"
assert isinstance(circle, cls)
Expand All @@ -1206,16 +1209,16 @@ Python::with_gil(|py| {
assert isinstance(square, cls)
assert isinstance(square, cls.RegularPolygon)
assert square.side_count == 4
assert square.radius == 10.0
assert square[0] == 4 # Gets _0 field
assert square[1] == 10.0 # Gets _1 field
def count_vertices(cls, shape):
match shape:
case cls.Circle():
return 0
case cls.Rectangle():
return 4
case cls.RegularPolygon(side_count=n):
case cls.RegularPolygon(n):
return n
case cls.Nothing():
return 0
Expand Down
1 change: 1 addition & 0 deletions newsfragments/4072.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support `#[pyclass]` on enums that have tuple variants.
Loading

0 comments on commit 88f2f6f

Please sign in to comment.