Skip to content

Commit 3489897

Browse files
committed
Implemented async APIs. Updated sample code to reflect it
1 parent de227b5 commit 3489897

2 files changed

Lines changed: 85 additions & 26 deletions

File tree

PythonNETExtensions/Core/RawPython.cs

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
using System.Runtime.CompilerServices;
44
using System.Text;
55
using Python.Runtime;
6+
using PythonNETExtensions.AsyncIO;
7+
using PythonNETExtensions.Core.Handles;
68
using PythonNETExtensions.Helpers;
9+
using PythonNETExtensions.Modules;
710

811
namespace PythonNETExtensions.Core
912
{
@@ -19,15 +22,26 @@ public enum CompilationOption
1922
public interface IRunOptions
2023
{
2124
public static abstract CompilationOption CompilationOption { get; }
25+
2226
public static abstract bool UseCachedScope { get; }
2327
}
28+
29+
public interface IAsyncRunOptions: IRunOptions
30+
{
31+
static bool IRunOptions.UseCachedScope => false;
32+
}
2433

2534
public struct DefaultRunOptions: IRunOptions
2635
{
2736
public static CompilationOption CompilationOption => CompilationOption.Auto;
2837
public static bool UseCachedScope => false;
2938
}
3039

40+
public struct DefaultAsyncRunOptions : IAsyncRunOptions
41+
{
42+
public static CompilationOption CompilationOption { get; }
43+
}
44+
3145
public struct CachedScopeRunOptions: IRunOptions
3246
{
3347
public static CompilationOption CompilationOption => CompilationOption.Auto;
@@ -141,43 +155,76 @@ public override string ToString()
141155
}
142156
}
143157

158+
private static readonly AsyncIOModule ASYNC_IO_MODULE = PythonExtensions.GetConcretePythonModule<AsyncIOModule>();
159+
144160
private static readonly ConcurrentDictionary<string, PyObject> CODE_TO_COMPILATION_MAP = new();
145161

146162
private const string RET_VAR_NAME = "py_ret";
147-
163+
148164
public static void Run(CodeInterpolator<DefaultRunOptions> code)
149165
{
150-
Run<NoRet, DefaultRunOptions>(code);
166+
RunInternal<NoRet, DefaultRunOptions>(code, isAsync: false);
151167
}
152168

153169
public static void RunWithCachedScope(CodeInterpolator<CachedScopeRunOptions> code)
154170
{
155-
Run<NoRet, CachedScopeRunOptions>(code);
171+
RunInternal<NoRet, CachedScopeRunOptions>(code, isAsync: false);
156172
}
157173

158174
public static RetT Run<RetT>(CodeInterpolator<DefaultRunOptions> code)
159175
{
160-
return Run<RetT, DefaultRunOptions>(code);
176+
return RunInternal<RetT, DefaultRunOptions>(code, isAsync: false);
161177
}
162178

163179
public static RetT RunWithCachedScope<RetT>(CodeInterpolator<CachedScopeRunOptions> code)
164180
{
165-
return Run<RetT, CachedScopeRunOptions>(code);
181+
return RunInternal<RetT, CachedScopeRunOptions>(code, isAsync: false);
166182
}
167-
183+
168184
public static RetT Run<RetT, OptsT>(CodeInterpolator<OptsT> code)
185+
where OptsT : IRunOptions
186+
{
187+
return RunInternal<RetT, OptsT>(code, isAsync: false);
188+
}
189+
190+
public static AsyncIOCoroutineAwaiter RunAsync(CodeInterpolator<DefaultAsyncRunOptions> code, AsyncPythonHandle handle)
191+
{
192+
return ASYNC_IO_MODULE.RunCoroutine(RunInternal<dynamic, DefaultAsyncRunOptions>(code, isAsync: true), handle);
193+
}
194+
195+
public static AsyncIOCoroutineAwaiter RunAsync<OptsT>(CodeInterpolator<OptsT> code, AsyncPythonHandle handle)
196+
where OptsT : IAsyncRunOptions
197+
{
198+
return ASYNC_IO_MODULE.RunCoroutine(RunInternal<dynamic, OptsT>(code, isAsync: true), handle);
199+
}
200+
201+
public static AsyncIOCoroutineAwaiter<RetT> RunAsync<RetT>(CodeInterpolator<DefaultAsyncRunOptions> code, AsyncPythonHandle handle)
202+
{
203+
return ASYNC_IO_MODULE.RunCoroutine<RetT>(RunInternal<dynamic, DefaultAsyncRunOptions>(code, isAsync: true), handle);
204+
}
205+
206+
public static AsyncIOCoroutineAwaiter<RetT> RunAsync<RetT, OptsT>(CodeInterpolator<OptsT> code, AsyncPythonHandle handle)
207+
where OptsT : IAsyncRunOptions
208+
{
209+
return ASYNC_IO_MODULE.RunCoroutine<RetT>(RunInternal<dynamic, OptsT>(code, isAsync: true), handle);
210+
}
211+
212+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
213+
private static RetT RunInternal<RetT, OptsT>(CodeInterpolator<OptsT> code, bool isAsync)
169214
where OptsT: IRunOptions
170215
{
171216
var hasRet = typeof(RetT) != typeof(NoRet);
172217

173218
var codeText = code.ToString();
174219

175220
const string METHOD_WRAPPER_NAME = "py_wrapper_method";
176-
221+
222+
var asyncModifer = isAsync ? "async " : string.Empty;
223+
177224
// TODO: Somehow optimize performance of this
178225
codeText = hasRet ?
179226
$"""
180-
def {METHOD_WRAPPER_NAME}():
227+
{asyncModifer}def {METHOD_WRAPPER_NAME}():
181228
{codeText.IndentCode()}
182229
183230
{RET_VAR_NAME} = {METHOD_WRAPPER_NAME}();
@@ -217,15 +264,30 @@ public static RetT Run<RetT, OptsT>(CodeInterpolator<OptsT> code)
217264
{
218265
scope.Exec(codeText);
219266
}
220-
221-
var ret = scope.Get<RetT>(RET_VAR_NAME);
222267

223-
if (OptsT.UseCachedScope)
268+
var variables = scope.Variables();
269+
270+
if (hasRet)
224271
{
225-
scope.Variables().Clear();
272+
var ret = scope.Get<RetT>(RET_VAR_NAME);
273+
274+
if (OptsT.UseCachedScope)
275+
{
276+
variables.Clear();
277+
}
278+
279+
return ret;
226280
}
227281

228-
return ret;
282+
else
283+
{
284+
if (OptsT.UseCachedScope)
285+
{
286+
variables.Clear();
287+
}
288+
289+
return default;
290+
}
229291
}
230292
}
231293
}

SampleCode/Program.cs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Diagnostics;
23
using System.Threading;
34
using System.Threading.Tasks;
45
using PythonNETExtensions.Config;
@@ -60,22 +61,18 @@ private static async Task Sample()
6061

6162
var asyncIO = PythonExtensions.GetConcretePythonModule<AsyncIOModule>();
6263

63-
const int DELAY_SECONDS = 3;
64+
const int DELAY_SECONDS = 2;
6465

65-
var asyncCoroutine = RawPython.Run<dynamic>
66+
Debug.Assert(DELAY_SECONDS >= 2);
67+
68+
var awaiter = RawPython.RunAsync
6669
(
6770
$"""
68-
async def async_coroutine():
69-
print("{nameof(asyncIO)} is running!");
70-
await {asyncIO.Sleep(DELAY_SECONDS):py};
71-
72-
return async_coroutine();
73-
"""
74-
);
75-
76-
var task = asyncIO.RunCoroutine(asyncCoroutine, handle);
77-
78-
await task;
71+
print("{nameof(asyncIO)} is running!");
72+
await {asyncIO.Sleep(DELAY_SECONDS):py};
73+
""", handle);
74+
75+
await awaiter;
7976

8077
Console.WriteLine($"Hello after {DELAY_SECONDS} seconds");
8178

0 commit comments

Comments
 (0)