33using System . Runtime . CompilerServices ;
44using System . Text ;
55using Python . Runtime ;
6+ using PythonNETExtensions . AsyncIO ;
7+ using PythonNETExtensions . Core . Handles ;
68using PythonNETExtensions . Helpers ;
9+ using PythonNETExtensions . Modules ;
710
811namespace 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}
0 commit comments