From 1666a38ad80c4c46a513f49ed91ea639bdbd42b2 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Mon, 5 Aug 2024 12:36:17 -0500 Subject: [PATCH] python can await a rust future --- .gitignore | 2 + Cargo.lock | 582 ++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 14 + justfile | 53 ++++ requirements.txt | 1 + src/main.rs | 183 +++++++++++++ src/python/farore.pyi | 18 ++ src/python/main.py | 45 ++++ 8 files changed, 898 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 justfile create mode 100644 requirements.txt create mode 100644 src/main.rs create mode 100644 src/python/farore.pyi create mode 100644 src/python/main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4f8ccbe --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +.venv diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..880c285 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,582 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + +[[package]] +name = "cc" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "farore" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "pyo3", + "tokio", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "object" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "portable-atomic" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pyo3" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831e8e819a138c36e212f3af3fd9eeffed6bf1510a805af35b0edee5ffa59433" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8730e591b14492a8945cdff32f089250b05f5accecf74aeddf9e8272ce1fa8" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e97e919d2df92eb88ca80a037969f44e5e70356559654962cbb3316d00300c6" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb57983022ad41f9e683a599f2fd13c3664d7063a3ac5714cae4b7bee7d3f206" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec480c0c51ddec81019531705acac51bcdbeae563557c982aa8263bb96880372" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tokio" +version = "1.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unindent" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..750fb30 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "farore" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.86" +clap = { version = "4.5.13", features = ["derive"] } +pyo3 = { version = "0.22.0", features = ["auto-initialize"] } +tokio = { version = "1", features = ["full", "rt-multi-thread"] } + +# [dependencies.pyo3-asyncio] +# version = "0.20" +# features = ["tokio-runtime"] diff --git a/justfile b/justfile new file mode 100644 index 0000000..0578a18 --- /dev/null +++ b/justfile @@ -0,0 +1,53 @@ +set shell := ["zsh", "-cu"] + +venv_root := justfile_directory() / ".venv" +requirements := justfile_directory() / "requirements.txt" +venv_bins := venv_root / "bin" +python := venv_bins / "python" +pip := venv_bins / "pip" + +[private] +default: + @ just --list + +# remove all the built stuff and caches and all that +clean: + #!/usr/bin/env bash + set -euo pipefail + + rm -rf {{ venv_root }} + rm -rf .mypy_cache + rm -rf .ruff_cache + rm -rf __pycache__ + +# creates a python virtualenv if one does not exist +venv: + #!/usr/bin/env bash + set -euo pipefail + + if [ ! -d {{ venv_root }} ] ; then + echo Creating Python virtualenv + python3 -m venv {{ venv_root }} + # ensure pip is up to date + {{ pip }} install -U pip + fi + +# installs all our build dependencies +deps: venv + #!/usr/bin/env bash + set -euo pipefail + + {{ pip }} install -r {{ requirements }} + + +# convenience method to forward to the pip in our virtualenv +pip +a="": venv + #!/usr/bin/env bash + set -euo pipefail + + {{ pip }} {{ a }} + + + +show-packages: venv + @ {{ python }} -m pip freeze diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..048f1cd --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +maturin ~= 1.7 diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..19d32f5 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,183 @@ +use anyhow; +use clap::Parser; +use pyo3::{prelude::*, types::PyBool}; +use std::{rc::Rc, sync::Mutex, thread}; +use tokio::sync::{mpsc, oneshot}; + +#[derive(Parser, Debug)] +struct Args {} + +mod scripts { + use pyo3::prelude::*; + + pub struct Embedded<'script> { + source: &'script str, + file_name: &'script str, + module_name: &'script str, + } + + impl Embedded<'_> { + pub fn load<'py>(&self, py: Python<'py>) -> Result, PyErr> { + PyModule::from_code_bound(py, self.source, self.file_name, self.module_name) + } + + pub fn install(&self) -> Result, PyErr> { + Python::with_gil(|py| { + let module = + PyModule::from_code_bound(py, self.source, self.file_name, self.module_name)?; + Ok(module.unbind()) + }) + } + } + + #[allow(non_upper_case_globals)] + pub const main: Embedded = Embedded { + source: include_str!("python/main.py"), + file_name: "main.py", + module_name: "main", + }; +} + +#[derive(Clone)] +#[pyclass] +struct OpaqueHandle { + handle: tokio::runtime::Handle, +} + +/// setup_extension creates a Python module consisting of members that are defined +/// in the farore host process (in Rust). +fn setup_extension(bridge: Bridge) -> Result, PyErr> { + Python::with_gil(|py| { + let bound = py.get_type_bound::(); + let module = PyModule::from_code_bound(py, "", "farore.py", "farore")?; + module.add( + "farore", + Core { + handle: bridge.handle.clone(), + }, + )?; + Ok(module.unbind()) + }) +} + +#[pyclass] +struct Bridge { + handle: tokio::runtime::Handle, + numbers: mpsc::Receiver, +} + +#[pymethods] +impl Bridge { + fn receive(&mut self, fut: Bound) -> Result<(), PyErr> { + println!("Rust: Bridge receive value: {}", fut); + let fut = fut.unbind(); + self.handle.spawn(async move { + println!("Rust: receive task going to sleep"); + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + println!("Rust: receive task finished sleep"); + match Python::with_gil(|py| -> Result<(), PyErr> { + let main_module = py.import_bound("main")?; + println!("Rust: receive task has gil"); + let fut = fut.bind(py); + fut.setattr("_asyncio_future_blocking", true); + fut.getattr("set_result")?.call1((5,))?; + println!("Rust: receive task set the result of a future"); + Ok(()) + }) { + Ok(()) => {} + Err(e) => println!("Rust: saw python error: {}", e), + } + }); + Ok(()) + } +} + +/// Creates an asyncio Future and schedules a task in asyncio to wait on that +/// Future. +fn create_asyncio_future() -> Result<(Py, Py), PyErr> { + Python::with_gil(|py| { + let main = py.import_bound("main")?; + let run = main.getattr("create_later_slot")?; + let later = run.call0()?.unbind(); + let dupe = Py::clone_ref(&later, py); + Ok((later, dupe)) + }) +} + +#[pyclass] +struct Core { + handle: tokio::runtime::Handle, +} + +#[pymethods] +impl Core { + fn next_message(&self) -> Result, PyErr> { + let (here, there) = create_asyncio_future()?; + self.handle.spawn(async move { + println!("Going to sleep from the resolve function I guess"); + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + println!("Finished sleeping in the resolve function"); + let py_result = Python::with_gil(|py| -> Result<(), PyErr> { + println!("We've got the gil in the resolve function"); + let later = here.bind(py); + let set_result = later.getattr("set_result")?; + set_result.call1((17,))?; + + Ok(()) + }); + match py_result { + Ok(()) => {} + Err(e) => { + println!("Failed to complete future: {}", e); + } + } + }); + Ok(there) + } +} + +async fn tokio_main(tx: mpsc::Sender) { + println!("Rust: Inside the tokio runtime"); + tokio::time::sleep(std::time::Duration::from_secs(100)).await; + println!("Rust: Tokio runtime is done?"); +} + +fn start_tokio(tx_bridge: oneshot::Sender) -> Result<(), anyhow::Error> { + thread::spawn(move || { + // Start the runtime + let runtime = tokio::runtime::Runtime::new().unwrap(); + + // Create a bridge between the runtimes + let (tx, rx) = mpsc::channel(16); + let bridge = Bridge { + handle: runtime.handle().clone(), + numbers: rx, + }; + let _ = tx_bridge + .send(bridge) + .inspect_err(|_| println!("Rust: failed to send bridge")); + + runtime.block_on(tokio_main(tx)); + }); + Ok(()) +} + +fn run_python(bridge: Bridge) -> Result, PyErr> { + setup_extension(bridge)?; + + Python::with_gil(|py| { + let main = scripts::main.load(py)?; + let run = main.getattr("run")?; + let returned = run.call0()?; + Ok(returned.unbind()) + }) +} + +fn main() -> Result<(), anyhow::Error> { + let (tx, rx) = oneshot::channel(); + start_tokio(tx)?; + let bridge = rx.blocking_recv()?; + let v = run_python(bridge)?; + println!("Rust: Python interpreter completed with result: {}", v); + Ok(()) +} diff --git a/src/python/farore.pyi b/src/python/farore.pyi new file mode 100644 index 0000000..6e4d2d3 --- /dev/null +++ b/src/python/farore.pyi @@ -0,0 +1,18 @@ +from collections.abc import Awaitable +from typing import Any + +class Bridge: + def receive(self, fut): ... + +bridge: Bridge + +class OpaqueHandle: ... + +tokio_handle: OpaqueHandle + +def next_message(h: OpaqueHandle): ... + +class Core: + async def next_message(self) -> Awaitable[Any]: ... + +farore: Core diff --git a/src/python/main.py b/src/python/main.py new file mode 100644 index 0000000..9697a00 --- /dev/null +++ b/src/python/main.py @@ -0,0 +1,45 @@ +import asyncio +from farore import farore +import logging + +logging.basicConfig(level=logging.DEBUG) + +log = logging.getLogger("import") +log.debug(f"Python: farore module: {farore}") + + +async def wait_on(fut: asyncio.Future): + return await fut + + +class Later: + def __init__(self): + loop = asyncio.get_event_loop() + self.fut = loop.create_future() + self.task = loop.create_task(wait_on(self.fut)) + + def set_result(self, v): + def f(): + self.fut.set_result(v) + + loop = self.fut._loop + loop.call_soon_threadsafe(f) + + def __await__(self): + return self.task.__await__() + + +def create_later_slot() -> Later: + return Later() + + +async def main_task(): + log = logging.getLogger("main_task") + while True: + log.debug("Getting next message ...") + m = await farore.next_message() + log.debug(f"next message: {m}") + + +def run(): + asyncio.run(main_task())