diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index cc20f46445..d7a347baf0 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Python * Added `Async.StartChild` (by @dbrattli) +* Fix #3482: Revert removal of `Py.python` and `Py.expr_python` (by @dbrattli) ## 4.2.2 - 2023-10-14 diff --git a/src/Fable.Core/CHANGELOG.md b/src/Fable.Core/CHANGELOG.md index 7b7568f8d4..dafc51e774 100644 --- a/src/Fable.Core/CHANGELOG.md +++ b/src/Fable.Core/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +#### Python + +* Fix #3482: Revert removal of `Py.python` and `Py.expr_python` (by @dbrattli) + ## 4.1.0 * Fix #3482: Remove `Py.python` and `Py.expr_python` diff --git a/src/Fable.Core/Fable.Core.Py.fs b/src/Fable.Core/Fable.Core.Py.fs index aabe6ea038..3285f22e0a 100644 --- a/src/Fable.Core/Fable.Core.Py.fs +++ b/src/Fable.Core/Fable.Core.Py.fs @@ -43,3 +43,7 @@ module Py = /// https://code.visualstudio.com/docs/python/jupyter-support-py [] let NEW_CELL: unit = nativeOnly + + /// Embeds literal Python code into F#. Code will be printed as statements, + /// if you want to return a value use Python `return` keyword within a function. + let python (template: string): 'T = nativeOnly diff --git a/src/Fable.Transforms/Python/Replacements.fs b/src/Fable.Transforms/Python/Replacements.fs index 9ee23b2a96..56a1c41af6 100644 --- a/src/Fable.Transforms/Python/Replacements.fs +++ b/src/Fable.Transforms/Python/Replacements.fs @@ -867,6 +867,12 @@ let fableCoreLib (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Exp | "typedArrays" -> makeBoolConst com.Options.TypedArrays |> Some | "extension" -> makeStrConst com.Options.FileExtension |> Some | _ -> None + | "Fable.Core.Py", ("python" | "expr_python" as meth) -> + let isStatement = meth <> "expr_python" + match args with + | RequireStringConstOrTemplate com ctx r template::_ -> + emitTemplate r t [] isStatement template |> Some + | _ -> None | "Fable.Core.PyInterop", _ -> match i.CompiledName, args with | Naming.StartsWith "import" suffix, _ -> diff --git a/src/fable-library-py/fable_library/async_.py b/src/fable-library-py/fable_library/async_.py index 3b14735d14..dd91ee38c6 100644 --- a/src/fable-library-py/fable_library/async_.py +++ b/src/fable-library-py/fable_library/async_.py @@ -34,6 +34,7 @@ _T = TypeVar("_T") +_U = TypeVar("_U") def cancellation_token() -> Async[CancellationToken]: @@ -110,6 +111,15 @@ def delayed() -> Async[list[_T]]: return delay(delayed) +def parallel2(a: Async[_T], b: Async[_U]) -> Async[list[_T | _U]]: + def delayed() -> Async[list[_T, _T]]: + tasks: Iterable[Future[_T]] = map(start_as_task, [a, b]) # type: ignore + all: Future[list[_T]] = asyncio.gather(*tasks) + return await_task(all) + + return delay(delayed) + + def sequential(computations: Iterable[Async[_T]]) -> Async[list[_T | None]]: def delayed() -> Async[list[_T | None]]: results: list[_T] = [] @@ -266,28 +276,33 @@ def cancel(_: OperationCanceledError) -> None: return tcs.get_task() +def throw_after(milliseconds_due_time: int) -> Async[None]: + def cont(ctx: IAsyncContext[None]) -> None: + def cancel() -> None: + ctx.on_cancel(OperationCanceledError()) + + token_id = ctx.cancel_token.add_listener(cancel) + + def timeout() -> None: + ctx.cancel_token.remove_listener(token_id) + ctx.on_error(TimeoutError()) + + ctx.trampoline.run_later(timeout, milliseconds_due_time / 1000.0) + + return protected_cont(cont) + + def start_child(computation: Async[_T], ms: int | None = None) -> Async[Async[_T]]: if ms: computation_with_timeout = protected_bind( - parallel(computation, throw_after(ms)), lambda xs: protected_return(xs[0]) + parallel2(computation, throw_after(ms)), lambda xs: protected_return(xs[0]) ) return start_child(computation_with_timeout) task = start_as_task(computation) def cont(ctx: IAsyncContext[Async[_T]]) -> None: - def on_success(_: Async[_T]) -> None: - ctx.on_success(await_task(task)) - - on_error = ctx.on_error - on_cancel = ctx.on_cancel - trampoline = ctx.trampoline - cancel_token = ctx.cancel_token - - ctx_ = IAsyncContext.create( - on_success, on_error, on_cancel, trampoline, cancel_token - ) - computation(ctx_) + protected_return(await_task(task))(ctx) return protected_cont(cont)