Skip to content

Commit b9ca9aa

Browse files
authored
Merge pull request pvieito#54 from philipturner/ergonomic-args
Use @_disfavoredOverload to enable more ergonomic syntax for PythonFunction
2 parents 44a4070 + dee644f commit b9ca9aa

File tree

2 files changed

+31
-27
lines changed

2 files changed

+31
-27
lines changed

PythonKit/Python.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1606,7 +1606,7 @@ final class PyFunction {
16061606
precondition(callingConvention == calledConvention,
16071607
"Called PyFunction with convention \(calledConvention), but expected \(callingConvention)")
16081608
}
1609-
1609+
16101610
func callAsFunction(_ argumentsTuple: PythonObject) throws -> PythonConvertible {
16111611
checkConvention(.varArgs)
16121612
let callSwiftFunction = unsafeBitCast(self.callSwiftFunction, to: VarArgsFunction.self)
@@ -1624,6 +1624,7 @@ public struct PythonFunction {
16241624
/// Called directly by the Python C API
16251625
private var function: PyFunction
16261626

1627+
@_disfavoredOverload
16271628
public init(_ fn: @escaping (PythonObject) throws -> PythonConvertible) {
16281629
function = PyFunction { argumentsAsTuple in
16291630
return try fn(argumentsAsTuple[0])
@@ -1826,6 +1827,7 @@ struct PyMethodDef {
18261827
public struct PythonInstanceMethod {
18271828
private var function: PythonFunction
18281829

1830+
@_disfavoredOverload
18291831
public init(_ fn: @escaping (PythonObject) throws -> PythonConvertible) {
18301832
function = PythonFunction(fn)
18311833
}

Tests/PythonKitTests/PythonFunctionTests.swift

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ class PythonFunctionTests: XCTestCase {
1313
return
1414
}
1515

16-
let pythonAdd = PythonFunction { (args: [PythonObject]) in
16+
let pythonAdd = PythonFunction { args in
1717
let lhs = args[0]
1818
let rhs = args[1]
1919
return lhs + rhs
2020
}.pythonObject
21-
21+
2222
let pythonSum = pythonAdd(2, 3)
2323
XCTAssertNotNil(Double(pythonSum))
2424
XCTAssertEqual(pythonSum, 5)
@@ -54,35 +54,33 @@ class PythonFunctionTests: XCTestCase {
5454
return
5555
}
5656

57-
let constructor = PythonInstanceMethod { (args: [PythonObject]) in
57+
let constructor = PythonInstanceMethod { args in
5858
let `self` = args[0]
59-
let arg = args[1]
60-
`self`.constructor_arg = arg
59+
`self`.constructor_arg = args[1]
6160
return Python.None
6261
}
63-
62+
6463
// Instead of calling `print`, use this to test what would be output.
6564
var printOutput: String?
66-
65+
66+
// Example of function using an alternative syntax for `args`.
6767
let displayMethod = PythonInstanceMethod { (args: [PythonObject]) in
68-
// let `self` = params[0]
69-
let arg = args[1]
70-
printOutput = String(arg)
68+
// let `self` = args[0]
69+
printOutput = String(args[1])
7170
return Python.None
7271
}
73-
74-
let classMethodOriginal = PythonInstanceMethod { (args: [PythonObject]) in
75-
// let cls = params[0]
76-
let arg = args[1]
77-
printOutput = String(arg)
72+
73+
let classMethodOriginal = PythonInstanceMethod { args in
74+
// let cls = args[0]
75+
printOutput = String(args[1])
7876
return Python.None
7977
}
80-
78+
8179
// Did not explicitly convert `constructor` or `displayMethod` to
8280
// PythonObject. This is intentional, as the `PythonClass` initializer
8381
// should take any `PythonConvertible` and not just `PythonObject`.
8482
let classMethod = Python.classmethod(classMethodOriginal.pythonObject)
85-
83+
8684
let Geeks = PythonClass("Geeks", members: [
8785
// Constructor
8886
"__init__": constructor,
@@ -100,10 +98,10 @@ class PythonFunctionTests: XCTestCase {
10098
XCTAssertEqual(obj.constructor_arg, "constructor argument")
10199
XCTAssertEqual(obj.string_attribute, "Geeks 4 geeks!")
102100
XCTAssertEqual(obj.int_attribute, 1706256)
103-
101+
104102
obj.func_arg("Geeks for Geeks")
105103
XCTAssertEqual(printOutput, "Geeks for Geeks")
106-
104+
107105
Geeks.class_func("Class Dynamically Created!")
108106
XCTAssertEqual(printOutput, "Class Dynamically Created!")
109107
}
@@ -136,16 +134,16 @@ class PythonFunctionTests: XCTestCase {
136134

137135
var helloOutput: String?
138136
var helloWorldOutput: String?
139-
137+
140138
// Declare subclasses of `Python.Exception`
141-
139+
142140
let HelloException = PythonClass(
143141
"HelloException",
144142
superclasses: [Python.Exception],
145143
members: [
146144
"str_prefix": "HelloException-prefix ",
147145

148-
"__init__": PythonInstanceMethod { (args: [PythonObject]) in
146+
"__init__": PythonInstanceMethod { args in
149147
let `self` = args[0]
150148
let message = "hello \(args[1])"
151149
helloOutput = String(message)
@@ -155,6 +153,7 @@ class PythonFunctionTests: XCTestCase {
155153
return Python.None
156154
},
157155

156+
// Example of function using the `self` convention instead of `args`.
158157
"__str__": PythonInstanceMethod { (`self`: PythonObject) in
159158
return `self`.str_prefix + Python.repr(`self`)
160159
}
@@ -167,7 +166,7 @@ class PythonFunctionTests: XCTestCase {
167166
members: [
168167
"str_prefix": "HelloWorldException-prefix ",
169168

170-
"__init__": PythonInstanceMethod { (args: [PythonObject]) in
169+
"__init__": PythonInstanceMethod { args in
171170
let `self` = args[0]
172171
let message = "world \(args[1])"
173172
helloWorldOutput = String(message)
@@ -179,14 +178,15 @@ class PythonFunctionTests: XCTestCase {
179178
return Python.None
180179
},
181180

181+
// Example of function using the `self` convention instead of `args`.
182182
"custom_method": PythonInstanceMethod { (`self`: PythonObject) in
183183
return `self`.int_param
184184
}
185185
]
186186
).pythonObject
187187

188188
// Test that inheritance works as expected
189-
189+
190190
let error1 = HelloException("test 1")
191191
XCTAssertEqual(helloOutput, "hello test 1")
192192
XCTAssertEqual(Python.str(error1), "HelloException-prefix HelloException('hello test 1')")
@@ -201,8 +201,10 @@ class PythonFunctionTests: XCTestCase {
201201
XCTAssertNotEqual(error2.custom_method(), "123")
202202

203203
// Test that subclasses behave like Python exceptions
204-
205-
let testFunction = PythonFunction { (_: [PythonObject]) in
204+
205+
// Example of function with no named parameters, which can be stated
206+
// ergonomically using an underscore. The ignored input is a [PythonObject].
207+
let testFunction = PythonFunction { _ in
206208
throw HelloWorldException("EXAMPLE ERROR MESSAGE", 2)
207209
}.pythonObject
208210

0 commit comments

Comments
 (0)