Skip to content

Commit 5b2ffac

Browse files
authored
[Python] Fix String.Concat spread operator issue in Python transpilation (#4298)
* [Python] Fix String.Concat spread operator issue in Python transpilation
1 parent 821b8ba commit 5b2ffac

File tree

6 files changed

+19
-42
lines changed

6 files changed

+19
-42
lines changed

pyproject.toml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,24 +47,29 @@ reportUnusedVariable = false
4747
reportUnnecessaryIsInstance = true
4848
reportUnnecessaryComparison = true
4949
reportUnnecessaryCast = true
50-
reportPrivateUsage = true
50+
reportPrivateUsage = false # Getters/setters reference private members from outside the class
5151
reportImportCycles = true
5252
reportDuplicateImport = true
5353
reportConstantRedefinition = true
5454
reportOverlappingOverload = true
5555
reportInconsistentConstructor = true
5656
reportImplicitStringConcatenation = true
57+
5758
pythonVersion = "3.12"
5859
typeCheckingMode = "standard"
5960

6061
[tool.ruff]
61-
# Keep in sync with .pre-commit-config.yaml
6262
line-length = 120
63-
lint.ignore = []
64-
lint.select = ["E", "W", "F", "I", "T", "RUF", "TID", "UP"]
6563
target-version = "py312"
64+
respect-gitignore = false
6665
include =["*.py"]
6766

67+
[tool.ruff.lint]
68+
select = ["E", "W", "F", "I", "T", "RUF", "TID", "UP"]
69+
ignore = [
70+
"F841" # Ignore unused variable warnings
71+
]
72+
6873
[tool.ruff.lint.flake8-bugbear]
6974
# These Rust extension module types are immutable (frozen)
7075
extend-immutable-calls = [
@@ -91,4 +96,4 @@ minversion = "8.0"
9196
pythonpath = ["temp/tests/Python", "."]
9297
testpaths = [
9398
"temp/tests/Python",
94-
]
99+
]

src/Fable.Cli/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919

2020
### Fixed
2121

22+
* [Python] Fix String.Concat spread operator issue in Python transpilation (by @dbrattli)
2223
* [Python] Fix regression `[<Erase>]` on class types not preventing them from being emitted to Python (by @dbrattli)
2324
* [Python] Fix regression `%A` format specifier to output booleans as lowercase `true`/`false` (by @dbrattli)
2425
* [Python] Fix various bugs in fable-library numeric types and string operations (by @dbrattli)

src/Fable.Compiler/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919

2020
### Fixed
2121

22+
* [Python] Fix String.Concat spread operator issue in Python transpilation (by @dbrattli)
2223
* [Python] Fix regression `[<Erase>]` on class types not preventing them from being emitted to Python (by @dbrattli)
2324
* [Python] Fix regression `%A` format specifier to output booleans as lowercase `true`/`false` (by @dbrattli)
2425
* [Python] Fix various bugs in fable-library numeric types and string operations (by @dbrattli)

src/Fable.Transforms/Python/Replacements.fs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,11 +1486,9 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt
14861486
| "Concat", None, _ ->
14871487
match i.SignatureArgTypes with
14881488
| [ MaybeNullable(Array _) | MaybeNullable(IEnumerable) ] ->
1489-
Helper.LibCall(com, "string", "join", t, ((makeStrConst "") :: args), ?loc = r)
1490-
|> Some
1491-
| _ ->
1492-
Helper.LibCall(com, "string", "concat", t, args, hasSpread = true, ?loc = r)
1489+
Helper.LibCall(com, "string", "join", t, makeStrConst "" :: args, ?loc = r)
14931490
|> Some
1491+
| _ -> Helper.LibCall(com, "string", "concat", t, args, ?loc = r) |> Some
14941492
| "CompareOrdinal", None, _ -> Helper.LibCall(com, "string", "compare_ordinal", t, args, ?loc = r) |> Some
14951493
| Patterns.SetContains implementedStringFunctions, thisArg, args ->
14961494
Helper.LibCall(

src/fable-library-py/fable_library/core/strings.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def initialize(n: int, f: Callable[[int], str]) -> str: ...
4747
def insert(string: str, start_index: int, value: str) -> str: ...
4848
def is_null_or_empty(string: str | None) -> bool: ...
4949
def is_null_or_white_space(string: str | None) -> bool: ...
50-
def concat(strings: Any) -> str: ...
50+
def concat(*strings: str) -> str: ...
5151
def join(delimiter: str, strings: Any) -> str: ...
5252
def pad_left(string: str, length: int, ch: str | None = None, is_right: bool = False) -> str: ...
5353
def pad_right(string: str, length: int, ch: str | None = None) -> str: ...

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

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,39 +1221,11 @@ mod formatting {
12211221
string.is_none_or(|s| s.trim().is_empty())
12221222
}
12231223

1224-
/// Concatenate strings - supports both F# varargs and iterable patterns
1224+
/// Concatenate strings - varargs of strings
12251225
#[pyfunction]
1226-
#[pyo3(signature = (*args))]
1227-
pub fn concat(args: &Bound<'_, pyo3::types::PyTuple>) -> PyResult<String> {
1228-
match args.len() {
1229-
0 => Ok(String::new()),
1230-
1 => {
1231-
// Single argument - check if it's iterable
1232-
if let Ok(iter) = args.get_item(0)?.try_iter() {
1233-
// Zero-cost abstraction: iterator chain with collect
1234-
Ok(iter
1235-
.map(|item| -> PyResult<String> {
1236-
let item = item?;
1237-
Ok(item.str()?.to_string())
1238-
})
1239-
.collect::<PyResult<Vec<_>>>()?
1240-
.join(""))
1241-
} else {
1242-
// Single non-iterable argument
1243-
Ok(args.get_item(0)?.str()?.to_string())
1244-
}
1245-
}
1246-
_ => {
1247-
// Multiple arguments - zero-cost iterator chain
1248-
Ok((0..args.len())
1249-
.map(|i| -> PyResult<String> {
1250-
let item = args.get_item(i)?;
1251-
Ok(item.str()?.to_string())
1252-
})
1253-
.collect::<PyResult<Vec<_>>>()?
1254-
.join(""))
1255-
}
1256-
}
1226+
#[pyo3(signature = (*strings))]
1227+
pub fn concat(strings: Vec<String>) -> String {
1228+
strings.join("")
12571229
}
12581230

12591231
/// Join strings with delimiter

0 commit comments

Comments
 (0)