Skip to content

Commit fedf367

Browse files
dbrattliclaude
andauthored
chore(python): format fable-library-py Rust sources and gate it in CI (#4829)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 5ab1382 commit fedf367

7 files changed

Lines changed: 191 additions & 68 deletions

File tree

.github/workflows/build.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,25 @@ jobs:
230230
uv run --frozen maturin develop --release
231231
uv run --frozen pytest tests
232232
233+
# Formatting gate for the fable-library-py Rust extension. Deliberately a
234+
# separate job rather than a step in test-fable-library-py, so it runs once
235+
# instead of once per cell of that job's 3x3 platform/Python matrix.
236+
lint-fable-library-py:
237+
timeout-minutes: 10
238+
runs-on: ubuntu-latest
239+
240+
steps:
241+
- uses: actions/checkout@v7
242+
243+
- name: Install Rust
244+
uses: dtolnay/rust-toolchain@stable
245+
with:
246+
components: rustfmt
247+
248+
- name: Check formatting
249+
working-directory: ./src/fable-library-py
250+
run: cargo fmt --check
251+
233252
# Separate build job for Rust (will run in parallel)
234253
build-rust:
235254
timeout-minutes: 75

src/fable-library-py/src/array.rs

Lines changed: 139 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,11 @@ fn ensure_array<'py>(py: Python<'py>, ob: &'py Bound<'py, PyAny>) -> PyResult<Ar
141141
// Check if the object is iterable (Python protocol)
142142
if let Ok(iter) = ob.try_iter() {
143143
// Convert iterable directly to FSharpArray
144-
return Ok(ArrayRef::Owned(FSharpArray::new(py, Some(iter.as_any()), None)?));
144+
return Ok(ArrayRef::Owned(FSharpArray::new(
145+
py,
146+
Some(iter.as_any()),
147+
None,
148+
)?));
145149
}
146150

147151
// Check if the object implements IEnumerable (F# protocol with GetEnumerator)
@@ -152,7 +156,11 @@ fn ensure_array<'py>(py: Python<'py>, ob: &'py Bound<'py, PyAny>) -> PyResult<Ar
152156

153157
// If it's a single item, create a singleton array
154158
let singleton_list = PyList::new(py, [ob])?;
155-
Ok(ArrayRef::Owned(FSharpArray::new(py, Some(&singleton_list), None)?))
159+
Ok(ArrayRef::Owned(FSharpArray::new(
160+
py,
161+
Some(&singleton_list),
162+
None,
163+
)?))
156164
}
157165

158166
fn ensure_equal_length_arrays<'py>(
@@ -496,8 +504,7 @@ impl FSharpArray {
496504
let len = slf.storage.len();
497505
// SAFETY: slf.as_ptr() is valid and from_borrowed_ptr increments refcount
498506
let array: Py<FSharpArray> = unsafe {
499-
Bound::from_borrowed_ptr(py, slf.as_ptr())
500-
.cast_into_unchecked::<FSharpArray>()
507+
Bound::from_borrowed_ptr(py, slf.as_ptr()).cast_into_unchecked::<FSharpArray>()
501508
}
502509
.unbind();
503510
let iter = FSharpArrayIter {
@@ -512,12 +519,15 @@ impl FSharpArray {
512519
/// Implements the .NET IEnumerable.GetEnumerator() interface.
513520
#[allow(non_snake_case)]
514521
#[pyo3(signature = (_unit=None))]
515-
pub fn GetEnumerator(slf: PyRef<'_, Self>, py: Python<'_>, _unit: Option<&Bound<'_, PyAny>>) -> PyResult<Py<PyAny>> {
522+
pub fn GetEnumerator(
523+
slf: PyRef<'_, Self>,
524+
py: Python<'_>,
525+
_unit: Option<&Bound<'_, PyAny>>,
526+
) -> PyResult<Py<PyAny>> {
516527
let len = slf.storage.len();
517528
// SAFETY: slf.as_ptr() is valid and from_borrowed_ptr increments refcount
518529
let array: Py<FSharpArray> = unsafe {
519-
Bound::from_borrowed_ptr(py, slf.as_ptr())
520-
.cast_into_unchecked::<FSharpArray>()
530+
Bound::from_borrowed_ptr(py, slf.as_ptr()).cast_into_unchecked::<FSharpArray>()
521531
}
522532
.unbind();
523533
let enumerator = FSharpArrayEnumerator {
@@ -3571,7 +3581,11 @@ pub fn create(py: Python<'_>, count: usize, value: &Bound<'_, PyAny>) -> PyResul
35713581
}
35723582

35733583
#[pyfunction]
3574-
pub fn zero_create(py: Python<'_>, count: usize, value: &Bound<'_, PyAny>) -> PyResult<FSharpArray> {
3584+
pub fn zero_create(
3585+
py: Python<'_>,
3586+
count: usize,
3587+
value: &Bound<'_, PyAny>,
3588+
) -> PyResult<FSharpArray> {
35753589
// Create an array filled with the zero value for the type
35763590
FSharpArray::create(py, count, value)
35773591
}
@@ -3861,7 +3875,11 @@ pub fn fold2(
38613875
}
38623876

38633877
#[pyfunction]
3864-
pub fn iterate(py: Python<'_>, action: &Bound<'_, PyAny>, array: &Bound<'_, PyAny>) -> PyResult<()> {
3878+
pub fn iterate(
3879+
py: Python<'_>,
3880+
action: &Bound<'_, PyAny>,
3881+
array: &Bound<'_, PyAny>,
3882+
) -> PyResult<()> {
38653883
let array = ensure_array(py, array)?;
38663884
array.iterate(py, action)
38673885
}
@@ -3992,7 +4010,11 @@ pub fn scan_back(
39924010
}
39934011

39944012
#[pyfunction]
3995-
pub fn split_into(py: Python<'_>, chunks: usize, array: &Bound<'_, PyAny>) -> PyResult<FSharpArray> {
4013+
pub fn split_into(
4014+
py: Python<'_>,
4015+
chunks: usize,
4016+
array: &Bound<'_, PyAny>,
4017+
) -> PyResult<FSharpArray> {
39964018
let array = ensure_array(py, array)?;
39974019
array.split_into(py, chunks)
39984020
}
@@ -4029,7 +4051,11 @@ pub fn try_find_index_back(
40294051
}
40304052

40314053
#[pyfunction]
4032-
pub fn windowed(py: Python<'_>, window_size: usize, array: &Bound<'_, PyAny>) -> PyResult<FSharpArray> {
4054+
pub fn windowed(
4055+
py: Python<'_>,
4056+
window_size: usize,
4057+
array: &Bound<'_, PyAny>,
4058+
) -> PyResult<FSharpArray> {
40334059
let array = ensure_array(py, array)?;
40344060
array.windowed(py, window_size)
40354061
}
@@ -4140,7 +4166,11 @@ pub fn exists_offset(
41404166
}
41414167

41424168
#[pyfunction]
4143-
pub fn exists(py: Python<'_>, predicate: &Bound<'_, PyAny>, array: &Bound<'_, PyAny>) -> PyResult<bool> {
4169+
pub fn exists(
4170+
py: Python<'_>,
4171+
predicate: &Bound<'_, PyAny>,
4172+
array: &Bound<'_, PyAny>,
4173+
) -> PyResult<bool> {
41444174
let array = ensure_array(py, array)?;
41454175
array.exists(py, predicate)
41464176
}
@@ -4573,11 +4603,7 @@ pub fn random_sample_with(
45734603
}
45744604

45754605
#[pyfunction]
4576-
pub fn random_sample(
4577-
py: Python<'_>,
4578-
count: isize,
4579-
xs: &Bound<'_, PyAny>,
4580-
) -> PyResult<FSharpArray> {
4606+
pub fn random_sample(py: Python<'_>, count: isize, xs: &Bound<'_, PyAny>) -> PyResult<FSharpArray> {
45814607
let xs = ensure_array(py, xs)?;
45824608
xs.random_sample(py, count)
45834609
}
@@ -5119,107 +5145,179 @@ impl FSharpCons {
51195145
impl Int8Array {
51205146
#[new]
51215147
#[pyo3(signature = (elements=None))]
5122-
fn new(py: Python<'_>, elements: Option<&Bound<'_, PyAny>>) -> PyResult<PyClassInitializer<Self>> {
5123-
Ok(PyClassInitializer::from(FSharpArray::new(py, elements, Some("Int8"))?).add_subclass(Int8Array {}))
5148+
fn new(
5149+
py: Python<'_>,
5150+
elements: Option<&Bound<'_, PyAny>>,
5151+
) -> PyResult<PyClassInitializer<Self>> {
5152+
Ok(
5153+
PyClassInitializer::from(FSharpArray::new(py, elements, Some("Int8"))?)
5154+
.add_subclass(Int8Array {}),
5155+
)
51245156
}
51255157
}
51265158

51275159
#[pymethods]
51285160
impl UInt8Array {
51295161
#[new]
51305162
#[pyo3(signature = (elements=None))]
5131-
fn new(py: Python<'_>, elements: Option<&Bound<'_, PyAny>>) -> PyResult<PyClassInitializer<Self>> {
5132-
Ok(PyClassInitializer::from(FSharpArray::new(py, elements, Some("UInt8"))?).add_subclass(UInt8Array {}))
5163+
fn new(
5164+
py: Python<'_>,
5165+
elements: Option<&Bound<'_, PyAny>>,
5166+
) -> PyResult<PyClassInitializer<Self>> {
5167+
Ok(
5168+
PyClassInitializer::from(FSharpArray::new(py, elements, Some("UInt8"))?)
5169+
.add_subclass(UInt8Array {}),
5170+
)
51335171
}
51345172
}
51355173

51365174
#[pymethods]
51375175
impl Int16Array {
51385176
#[new]
51395177
#[pyo3(signature = (elements=None))]
5140-
fn new(py: Python<'_>, elements: Option<&Bound<'_, PyAny>>) -> PyResult<PyClassInitializer<Self>> {
5141-
Ok(PyClassInitializer::from(FSharpArray::new(py, elements, Some("Int16"))?).add_subclass(Int16Array {}))
5178+
fn new(
5179+
py: Python<'_>,
5180+
elements: Option<&Bound<'_, PyAny>>,
5181+
) -> PyResult<PyClassInitializer<Self>> {
5182+
Ok(
5183+
PyClassInitializer::from(FSharpArray::new(py, elements, Some("Int16"))?)
5184+
.add_subclass(Int16Array {}),
5185+
)
51425186
}
51435187
}
51445188

51455189
#[pymethods]
51465190
impl UInt16Array {
51475191
#[new]
51485192
#[pyo3(signature = (elements=None))]
5149-
fn new(py: Python<'_>, elements: Option<&Bound<'_, PyAny>>) -> PyResult<PyClassInitializer<Self>> {
5150-
Ok(PyClassInitializer::from(FSharpArray::new(py, elements, Some("UInt16"))?).add_subclass(UInt16Array {}))
5193+
fn new(
5194+
py: Python<'_>,
5195+
elements: Option<&Bound<'_, PyAny>>,
5196+
) -> PyResult<PyClassInitializer<Self>> {
5197+
Ok(
5198+
PyClassInitializer::from(FSharpArray::new(py, elements, Some("UInt16"))?)
5199+
.add_subclass(UInt16Array {}),
5200+
)
51515201
}
51525202
}
51535203

51545204
#[pymethods]
51555205
impl Int32Array {
51565206
#[new]
51575207
#[pyo3(signature = (elements=None))]
5158-
fn new(py: Python<'_>, elements: Option<&Bound<'_, PyAny>>) -> PyResult<PyClassInitializer<Self>> {
5159-
Ok(PyClassInitializer::from(FSharpArray::new(py, elements, Some("Int32"))?).add_subclass(Int32Array {}))
5208+
fn new(
5209+
py: Python<'_>,
5210+
elements: Option<&Bound<'_, PyAny>>,
5211+
) -> PyResult<PyClassInitializer<Self>> {
5212+
Ok(
5213+
PyClassInitializer::from(FSharpArray::new(py, elements, Some("Int32"))?)
5214+
.add_subclass(Int32Array {}),
5215+
)
51605216
}
51615217
}
51625218

51635219
#[pymethods]
51645220
impl UInt32Array {
51655221
#[new]
51665222
#[pyo3(signature = (elements=None))]
5167-
fn new(py: Python<'_>, elements: Option<&Bound<'_, PyAny>>) -> PyResult<PyClassInitializer<Self>> {
5168-
Ok(PyClassInitializer::from(FSharpArray::new(py, elements, Some("UInt32"))?).add_subclass(UInt32Array {}))
5223+
fn new(
5224+
py: Python<'_>,
5225+
elements: Option<&Bound<'_, PyAny>>,
5226+
) -> PyResult<PyClassInitializer<Self>> {
5227+
Ok(
5228+
PyClassInitializer::from(FSharpArray::new(py, elements, Some("UInt32"))?)
5229+
.add_subclass(UInt32Array {}),
5230+
)
51695231
}
51705232
}
51715233

51725234
#[pymethods]
51735235
impl Int64Array {
51745236
#[new]
51755237
#[pyo3(signature = (elements=None))]
5176-
fn new(py: Python<'_>, elements: Option<&Bound<'_, PyAny>>) -> PyResult<PyClassInitializer<Self>> {
5177-
Ok(PyClassInitializer::from(FSharpArray::new(py, elements, Some("Int64"))?).add_subclass(Int64Array {}))
5238+
fn new(
5239+
py: Python<'_>,
5240+
elements: Option<&Bound<'_, PyAny>>,
5241+
) -> PyResult<PyClassInitializer<Self>> {
5242+
Ok(
5243+
PyClassInitializer::from(FSharpArray::new(py, elements, Some("Int64"))?)
5244+
.add_subclass(Int64Array {}),
5245+
)
51785246
}
51795247
}
51805248

51815249
#[pymethods]
51825250
impl UInt64Array {
51835251
#[new]
51845252
#[pyo3(signature = (elements=None))]
5185-
fn new(py: Python<'_>, elements: Option<&Bound<'_, PyAny>>) -> PyResult<PyClassInitializer<Self>> {
5186-
Ok(PyClassInitializer::from(FSharpArray::new(py, elements, Some("UInt64"))?).add_subclass(UInt64Array {}))
5253+
fn new(
5254+
py: Python<'_>,
5255+
elements: Option<&Bound<'_, PyAny>>,
5256+
) -> PyResult<PyClassInitializer<Self>> {
5257+
Ok(
5258+
PyClassInitializer::from(FSharpArray::new(py, elements, Some("UInt64"))?)
5259+
.add_subclass(UInt64Array {}),
5260+
)
51875261
}
51885262
}
51895263

51905264
#[pymethods]
51915265
impl Float32Array {
51925266
#[new]
51935267
#[pyo3(signature = (elements=None))]
5194-
fn new(py: Python<'_>, elements: Option<&Bound<'_, PyAny>>) -> PyResult<PyClassInitializer<Self>> {
5195-
Ok(PyClassInitializer::from(FSharpArray::new(py, elements, Some("Float32"))?).add_subclass(Float32Array {}))
5268+
fn new(
5269+
py: Python<'_>,
5270+
elements: Option<&Bound<'_, PyAny>>,
5271+
) -> PyResult<PyClassInitializer<Self>> {
5272+
Ok(
5273+
PyClassInitializer::from(FSharpArray::new(py, elements, Some("Float32"))?)
5274+
.add_subclass(Float32Array {}),
5275+
)
51965276
}
51975277
}
51985278

51995279
#[pymethods]
52005280
impl Float64Array {
52015281
#[new]
52025282
#[pyo3(signature = (elements=None))]
5203-
fn new(py: Python<'_>, elements: Option<&Bound<'_, PyAny>>) -> PyResult<PyClassInitializer<Self>> {
5204-
Ok(PyClassInitializer::from(FSharpArray::new(py, elements, Some("Float64"))?).add_subclass(Float64Array {}))
5283+
fn new(
5284+
py: Python<'_>,
5285+
elements: Option<&Bound<'_, PyAny>>,
5286+
) -> PyResult<PyClassInitializer<Self>> {
5287+
Ok(
5288+
PyClassInitializer::from(FSharpArray::new(py, elements, Some("Float64"))?)
5289+
.add_subclass(Float64Array {}),
5290+
)
52055291
}
52065292
}
52075293

52085294
#[pymethods]
52095295
impl BoolArray {
52105296
#[new]
52115297
#[pyo3(signature = (elements=None))]
5212-
fn new(py: Python<'_>, elements: Option<&Bound<'_, PyAny>>) -> PyResult<PyClassInitializer<Self>> {
5213-
Ok(PyClassInitializer::from(FSharpArray::new(py, elements, Some("Bool"))?).add_subclass(BoolArray {}))
5298+
fn new(
5299+
py: Python<'_>,
5300+
elements: Option<&Bound<'_, PyAny>>,
5301+
) -> PyResult<PyClassInitializer<Self>> {
5302+
Ok(
5303+
PyClassInitializer::from(FSharpArray::new(py, elements, Some("Bool"))?)
5304+
.add_subclass(BoolArray {}),
5305+
)
52145306
}
52155307
}
52165308

52175309
#[pymethods]
52185310
impl GenericArray {
52195311
#[new]
52205312
#[pyo3(signature = (elements=None))]
5221-
fn new(py: Python<'_>, elements: Option<&Bound<'_, PyAny>>) -> PyResult<PyClassInitializer<Self>> {
5222-
Ok(PyClassInitializer::from(FSharpArray::new(py, elements, Some("generic"))?).add_subclass(GenericArray {}))
5313+
fn new(
5314+
py: Python<'_>,
5315+
elements: Option<&Bound<'_, PyAny>>,
5316+
) -> PyResult<PyClassInitializer<Self>> {
5317+
Ok(
5318+
PyClassInitializer::from(FSharpArray::new(py, elements, Some("generic"))?)
5319+
.add_subclass(GenericArray {}),
5320+
)
52235321
}
52245322
}
52255323

src/fable-library-py/src/floats.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ macro_rules! float_variant {
1818
impl Deref for $name {
1919
type Target = $type;
2020

21-
2221
#[inline]
2322
fn deref(&self) -> &Self::Target {
2423
&self.0
@@ -145,7 +144,11 @@ macro_rules! float_variant {
145144
Ok(Self(other_val % self.0))
146145
}
147146

148-
pub fn __pow__(&self, other: &Bound<'_, PyAny>, modulo: Option<$type>) -> PyResult<Self> {
147+
pub fn __pow__(
148+
&self,
149+
other: &Bound<'_, PyAny>,
150+
modulo: Option<$type>,
151+
) -> PyResult<Self> {
149152
if modulo.is_some() {
150153
return Err(PyErr::new::<exceptions::PyTypeError, _>(
151154
"pow() with modulo not supported for floats",
@@ -560,7 +563,10 @@ macro_rules! float_variant {
560563
let py = value.py();
561564
// If the value has __float__, extract it as a Python float
562565
if value.hasattr("__float__")? {
563-
Ok(value.call_method0("__float__")?.into_pyobject(py).map(|o| o.unbind())?)
566+
Ok(value
567+
.call_method0("__float__")?
568+
.into_pyobject(py)
569+
.map(|o| o.unbind())?)
564570
} else {
565571
Ok(value.clone().unbind())
566572
}

0 commit comments

Comments
 (0)