diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 0e04085..db9c697 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -4,7 +4,7 @@
exclude: Cargo.lock
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.5.0
+ rev: v4.6.0
hooks:
- id: check-ast
- id: check-builtin-literals
@@ -36,7 +36,7 @@ repos:
- id: beautysh
- repo: https://github.com/psf/black
- rev: 23.9.1
+ rev: 24.4.2
hooks:
- id: black
@@ -48,7 +48,7 @@ repos:
exclude: Cargo.lock
- repo: https://github.com/pycqa/isort
- rev: 5.12.0
+ rev: 5.13.2
hooks:
- id: isort
args: ["--profile", "black"]
diff --git a/Cargo.lock b/Cargo.lock
index 1ccabd1..b343dce 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "aho-corasick"
-version = "1.1.2"
+version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
@@ -19,47 +19,48 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstream"
-version = "0.6.13"
+version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
+checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
+ "is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
-version = "1.0.6"
+version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
+checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
[[package]]
name = "anstyle-parse"
-version = "0.2.3"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
-version = "1.0.2"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
-version = "3.0.2"
+version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
dependencies = [
"anstyle",
"windows-sys",
@@ -82,15 +83,15 @@ dependencies = [
[[package]]
name = "autocfg"
-version = "1.1.0"
+version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "bitflags"
-version = "1.3.2"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "bstr"
@@ -105,9 +106,9 @@ dependencies = [
[[package]]
name = "bumpalo"
-version = "3.15.3"
+version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "cast"
@@ -150,9 +151,9 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.5.1"
+version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
+checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
dependencies = [
"clap_builder",
"clap_derive",
@@ -160,9 +161,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.1"
+version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
+checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
dependencies = [
"anstream",
"anstyle",
@@ -172,9 +173,9 @@ dependencies = [
[[package]]
name = "clap_derive"
-version = "4.5.0"
+version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
+checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
dependencies = [
"heck",
"proc-macro2",
@@ -190,9 +191,9 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "colorchoice"
-version = "1.0.0"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
[[package]]
name = "criterion"
@@ -275,13 +276,13 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "either"
-version = "1.10.0"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
+checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
[[package]]
name = "examples"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"pyo3",
"pyo3_bindgen",
@@ -298,9 +299,9 @@ dependencies = [
[[package]]
name = "half"
-version = "2.4.0"
+version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e"
+checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
dependencies = [
"cfg-if",
"crunchy",
@@ -308,9 +309,9 @@ dependencies = [
[[package]]
name = "heck"
-version = "0.4.1"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
@@ -320,9 +321,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "indoc"
-version = "2.0.4"
+version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
+checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
[[package]]
name = "is-terminal"
@@ -335,6 +336,12 @@ dependencies = [
"windows-sys",
]
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
+
[[package]]
name = "itertools"
version = "0.10.5"
@@ -355,30 +362,30 @@ dependencies = [
[[package]]
name = "itoa"
-version = "1.0.10"
+version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "js-sys"
-version = "0.3.68"
+version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "libc"
-version = "0.2.153"
+version = "0.2.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
[[package]]
name = "lock_api"
-version = "0.4.11"
+version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
@@ -392,15 +399,15 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "memchr"
-version = "2.7.1"
+version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "memoffset"
-version = "0.9.0"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
@@ -413,9 +420,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "num-traits"
-version = "0.2.18"
+version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
@@ -434,9 +441,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "parking_lot"
-version = "0.12.1"
+version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
dependencies = [
"lock_api",
"parking_lot_core",
@@ -444,15 +451,15 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.9.9"
+version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
- "windows-targets 0.48.5",
+ "windows-targets",
]
[[package]]
@@ -521,9 +528,9 @@ dependencies = [
[[package]]
name = "prettyplease"
-version = "0.2.16"
+version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
+checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
dependencies = [
"proc-macro2",
"syn",
@@ -531,18 +538,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.78"
+version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
+checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "pyo3"
-version = "0.20.3"
+version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53bdbb96d49157e65d45cc287af5f32ffadd5f4761438b527b055fb0d4bb8233"
+checksum = "a5e00b96a521718e08e03b1a622f01c8a8deb50719335de3f60b3b3950f069d8"
dependencies = [
"cfg-if",
"libc",
@@ -555,9 +562,9 @@ dependencies = [
[[package]]
name = "pyo3-build-config"
-version = "0.20.3"
+version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "deaa5745de3f5231ce10517a1f5dd97d53e5a2fd77aa6b5842292085831d48d7"
+checksum = "7883df5835fafdad87c0d888b266c8ec0f4c9ca48a5bed6bbb592e8dedee1b50"
dependencies = [
"once_cell",
"target-lexicon",
@@ -565,9 +572,9 @@ dependencies = [
[[package]]
name = "pyo3-ffi"
-version = "0.20.3"
+version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b42531d03e08d4ef1f6e85a2ed422eb678b8cd62b762e53891c05faf0d4afa"
+checksum = "01be5843dc60b916ab4dad1dca6d20b9b4e6ddc8e15f50c47fe6d85f1fb97403"
dependencies = [
"libc",
"pyo3-build-config",
@@ -575,7 +582,7 @@ dependencies = [
[[package]]
name = "pyo3_bindgen"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"pyo3",
"pyo3_bindgen_engine",
@@ -584,7 +591,7 @@ dependencies = [
[[package]]
name = "pyo3_bindgen_cli"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"assert_cmd",
"clap",
@@ -596,7 +603,7 @@ dependencies = [
[[package]]
name = "pyo3_bindgen_engine"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"criterion",
"indoc",
@@ -614,7 +621,7 @@ dependencies = [
[[package]]
name = "pyo3_bindgen_macros"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"libc",
"pyo3",
@@ -625,18 +632,18 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.35"
+version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
-version = "1.9.0"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
@@ -654,18 +661,18 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.4.1"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
-version = "1.10.3"
+version = "1.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [
"aho-corasick",
"memchr",
@@ -675,9 +682,9 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.4.5"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
"aho-corasick",
"memchr",
@@ -686,9 +693,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.8.2"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "rustc-hash"
@@ -698,9 +705,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "ryu"
-version = "1.0.17"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "same-file"
@@ -719,18 +726,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
-version = "1.0.197"
+version = "1.0.201"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
+checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.197"
+version = "1.0.201"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
+checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865"
dependencies = [
"proc-macro2",
"quote",
@@ -739,9 +746,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.114"
+version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
+checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [
"itoa",
"ryu",
@@ -750,21 +757,21 @@ dependencies = [
[[package]]
name = "smallvec"
-version = "1.13.1"
+version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "strsim"
-version = "0.11.0"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
-version = "2.0.52"
+version = "2.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
+checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9"
dependencies = [
"proc-macro2",
"quote",
@@ -785,18 +792,18 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
[[package]]
name = "thiserror"
-version = "1.0.57"
+version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
+checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.57"
+version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
+checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
dependencies = [
"proc-macro2",
"quote",
@@ -815,18 +822,18 @@ dependencies = [
[[package]]
name = "typed-builder"
-version = "0.18.1"
+version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "444d8748011b93cb168770e8092458cb0f8854f931ff82fdf6ddfbd72a9c933e"
+checksum = "77739c880e00693faef3d65ea3aad725f196da38b22fdc7ea6ded6e1ce4d3add"
dependencies = [
"typed-builder-macro",
]
[[package]]
name = "typed-builder-macro"
-version = "0.18.1"
+version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "563b3b88238ec95680aef36bdece66896eaa7ce3c0f1b4f39d38fb2435261352"
+checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63"
dependencies = [
"proc-macro2",
"quote",
@@ -866,9 +873,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen"
-version = "0.2.91"
+version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -876,9 +883,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.91"
+version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
"bumpalo",
"log",
@@ -891,9 +898,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.91"
+version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -901,9 +908,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.91"
+version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
@@ -914,170 +921,98 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.91"
+version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "web-sys"
-version = "0.3.68"
+version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446"
+checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
dependencies = [
"js-sys",
"wasm-bindgen",
]
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
[[package]]
name = "winapi-util"
-version = "0.1.6"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
dependencies = [
- "winapi",
+ "windows-sys",
]
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
- "windows-targets 0.52.4",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
-dependencies = [
- "windows_aarch64_gnullvm 0.48.5",
- "windows_aarch64_msvc 0.48.5",
- "windows_i686_gnu 0.48.5",
- "windows_i686_msvc 0.48.5",
- "windows_x86_64_gnu 0.48.5",
- "windows_x86_64_gnullvm 0.48.5",
- "windows_x86_64_msvc 0.48.5",
+ "windows-targets",
]
[[package]]
name = "windows-targets"
-version = "0.52.4"
+version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
- "windows_aarch64_gnullvm 0.52.4",
- "windows_aarch64_msvc 0.52.4",
- "windows_i686_gnu 0.52.4",
- "windows_i686_msvc 0.52.4",
- "windows_x86_64_gnu 0.52.4",
- "windows_x86_64_gnullvm 0.52.4",
- "windows_x86_64_msvc 0.52.4",
+ "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.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.52.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.48.5"
+version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.52.4"
+version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
-version = "0.48.5"
+version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
-name = "windows_i686_gnu"
-version = "0.52.4"
+name = "windows_i686_gnullvm"
+version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.52.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.48.5"
+version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.52.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.48.5"
+version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.52.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.48.5"
+version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.52.4"
+version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
diff --git a/Cargo.toml b/Cargo.toml
index 8b2d55f..443a5c4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,12 +28,12 @@ license = "MIT OR Apache-2.0"
readme = "README.md"
repository = "https://github.com/AndrejOrsula/pyo3_bindgen"
rust-version = "1.74"
-version = "0.4.0"
+version = "0.5.0"
[workspace.dependencies]
-pyo3_bindgen = { path = "pyo3_bindgen", version = "0.4.0" }
-pyo3_bindgen_engine = { path = "pyo3_bindgen_engine", version = "0.4.0" }
-pyo3_bindgen_macros = { path = "pyo3_bindgen_macros", version = "0.4.0" }
+pyo3_bindgen = { path = "pyo3_bindgen", version = "0.5.0" }
+pyo3_bindgen_engine = { path = "pyo3_bindgen_engine", version = "0.5.0" }
+pyo3_bindgen_macros = { path = "pyo3_bindgen_macros", version = "0.5.0" }
assert_cmd = { version = "2" }
clap = { version = "4.5", features = ["derive"] }
@@ -44,8 +44,8 @@ libc = { version = "0.2" }
predicates = { version = "3" }
prettyplease = { version = "0.2" }
proc-macro2 = { version = "1" }
-pyo3 = { version = "0.20", default-features = false }
-pyo3-build-config = { version = "0.20", features = ["resolve-config"] }
+pyo3 = { version = "0.21", default-features = false }
+pyo3-build-config = { version = "0.21", features = ["resolve-config"] }
quote = { version = "1" }
rustc-hash = { version = "1" }
syn = { version = "2" }
diff --git a/README.md b/README.md
index 1326929..492e8c8 100644
--- a/README.md
+++ b/README.md
@@ -74,6 +74,9 @@ This project is intended to simplify the integration or transition of existing P
On its own, the generated Rust code does not provide any performance benefits over using the Python code. However, it can be used as a starting point for further optimization if you decide to rewrite performance-critical parts of your codebase in pure Rust.
+> \[!NOTE\]
+> Version `0.5` of `pyo3_bindgen` adapts the new `pyo3::Bound<'py, T>` smart pointer that was introduced in `pyo3` version `0.21`. Use version `0.4` of `pyo3_bindgen` if you require the old "GIL Refs" API.
+
## Overview
The workspace contains these packages:
@@ -83,6 +86,11 @@ The workspace contains these packages:
- **[pyo3_bindgen_engine](pyo3_bindgen_engine):** The underlying engine for generation of bindings
- **[pyo3_bindgen_macros](pyo3_bindgen_macros):** Procedural macros for in-place generation
+Features of `pyo3_bindgen`:
+
+- **`macros` \[experimental\]:** Enables `import_python!` macro from `pyo3_bindgen_macros` crate
+- **`numpy` \[experimental\]:** Enables type mapping between Python [`numpy::ndarray`](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html) and Rust [`numpy::PyArray`](https://docs.rs/numpy/latest/numpy/array/struct.PyArray.html)
+
## Instructions
### Option 1: Build script
@@ -91,14 +99,15 @@ First, add `pyo3_bindgen` as a **build dependency** to your [`Cargo.toml`](https
```toml
[build-dependencies]
-pyo3_bindgen = { version = "0.4" }
+pyo3_bindgen = { version = "0.5" }
[dependencies]
-pyo3 = { version = "0.20", features = ["auto-initialize"] }
+pyo3 = { version = "0.21", features = ["auto-initialize"] }
```
Then, create a [`build.rs`](https://doc.rust-lang.org/cargo/reference/build-scripts.html) script in the root of your crate that generates bindings to the selected Python modules. In this example, the bindings are simultaneously generated for the "os", "posixpath", and "sys" Python modules. At the end of the generation process, the Rust bindings are written to `${OUT_DIR}/bindings.rs`.
+> \[!TIP\]
> With this approach, you can also customize the generation process via [`pyo3_bindgen::Config`](https://docs.rs/pyo3_bindgen/latest/pyo3_bindgen/struct.Config.html) that can be passed to the constructor, e.g. `Codegen::new(Config::builder().include_private(true).build())`.
```rs
@@ -136,15 +145,16 @@ fn main() -> pyo3::PyResult<()> {
### Option 2: Procedural macros (experimental)
-As an alternative to build scripts, you can use procedural macros to generate the bindings in-place. First, add `pyo3_bindgen_macros` as a **regular dependency** to your [`Cargo.toml`](https://doc.rust-lang.org/cargo/reference/manifest.html) manifest.
+As an alternative to build scripts, you can use procedural macros to generate the bindings in-place. First, add `pyo3_bindgen_macros` as a **regular dependency** to your [`Cargo.toml`](https://doc.rust-lang.org/cargo/reference/manifest.html) manifest and enable the `macros` feature.
```toml
[dependencies]
-pyo3_bindgen = { version = "0.4" }
+pyo3_bindgen = { version = "0.5", features = ["macros"] }
```
Subsequently, the `import_python!` macro can be used to generate Rust bindings for the selected Python modules anywhere in your crate. As demonstrated in the example below, Rust bindings are generated for the "math" Python module and can directly be used in the same scope. Similar to the previous approach, the generated bindings must be used within the `pyo3::Python::with_gil` closure to ensure that Python [GIL](https://wiki.python.org/moin/GlobalInterpreterLock) is held.
+> \[!NOTE\]
> As opposed to using build scripts, this approach does not offer the same level of customization via `pyo3_bindgen::Config`. Furthermore, the procedural macro is quite experimental and might not work in all cases.
```rs
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
index 8aeb35a..580a0ac 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -9,7 +9,7 @@ publish = false
[dependencies]
pyo3 = { workspace = true, features = ["auto-initialize"] }
-pyo3_bindgen = { workspace = true }
+pyo3_bindgen = { workspace = true, features = ["macros"] }
[build-dependencies]
pyo3_bindgen = { workspace = true }
diff --git a/examples/random.rs b/examples/random.rs
index 4903e2c..618a868 100644
--- a/examples/random.rs
+++ b/examples/random.rs
@@ -13,6 +13,8 @@
pyo3_bindgen::import_python!("random");
fn main() -> pyo3::PyResult<()> {
+ use ::pyo3::types::PyAnyMethods;
+
pyo3::Python::with_gil(|py| {
let rand_f64: f64 = random::random(py)?.extract()?;
assert!((0.0..=1.0).contains(&rand_f64));
diff --git a/pyo3_bindgen/Cargo.toml b/pyo3_bindgen/Cargo.toml
index 06cf217..70ec189 100644
--- a/pyo3_bindgen/Cargo.toml
+++ b/pyo3_bindgen/Cargo.toml
@@ -17,13 +17,11 @@ pyo3_bindgen_engine = { workspace = true }
pyo3_bindgen_macros = { workspace = true, optional = true }
[features]
-default = ["macros"]
-macros = ["pyo3_bindgen_macros"]
-
-[lib]
-name = "pyo3_bindgen"
-path = "src/lib.rs"
-crate-type = ["rlib"]
+default = []
+# Enables `import_python!` macro from `pyo3_bindgen_macros` crate
+macros = ["dep:pyo3_bindgen_macros"]
+# Enables type mapping between Python `numpy::ndarray` and Rust `numpy::PyArray`
+numpy = ["pyo3_bindgen_engine/numpy"]
[package.metadata.docs.rs]
all-features = true
diff --git a/pyo3_bindgen/src/lib.rs b/pyo3_bindgen/src/lib.rs
index 973d693..b0aa3c9 100644
--- a/pyo3_bindgen/src/lib.rs
+++ b/pyo3_bindgen/src/lib.rs
@@ -16,10 +16,10 @@
//!
//! ```toml
//! [build-dependencies]
-//! pyo3_bindgen = { version = "0.4" }
+//! pyo3_bindgen = { version = "0.5" }
//!
//! [dependencies]
-//! pyo3 = { version = "0.20", features = ["auto-initialize"] }
+//! pyo3 = { version = "0.21", features = ["auto-initialize"] }
//! ```
//!
//! Then, create a [`build.rs`](https://doc.rust-lang.org/cargo/reference/build-scripts.html) script in the root of your crate that generates bindings to the selected Python modules. In this example, the bindings are simultaneously generated for the "os", "posixpath", and "sys" Python modules. At the end of the generation process, the Rust bindings are written to `${OUT_DIR}/bindings.rs`.
@@ -62,18 +62,18 @@
//!
//! ### Option 2: Procedural macros (experimental)
//!
-//! As an alternative to build scripts, you can use procedural macros to generate the bindings in-place. First, add `pyo3_bindgen_macros` as a **regular dependency** to your [`Cargo.toml`](https://doc.rust-lang.org/cargo/reference/manifest.html) manifest.
+//! As an alternative to build scripts, you can use procedural macros to generate the bindings in-place. First, add `pyo3_bindgen_macros` as a **regular dependency** to your [`Cargo.toml`](https://doc.rust-lang.org/cargo/reference/manifest.html) manifest and enable the `macros` feature.
//!
//! ```toml
//! [dependencies]
-//! pyo3_bindgen = { version = "0.4" }
+//! pyo3_bindgen = { version = "0.5", features = ["macros"] }
//! ```
//!
//! Subsequently, the `import_python!` macro can be used to generate Rust bindings for the selected Python modules anywhere in your crate. As demonstrated in the example below, Rust bindings are generated for the "math" Python module and can directly be used in the same scope. Similar to the previous approach, the generated bindings must be used within the `pyo3::Python::with_gil` closure to ensure that Python [GIL](https://wiki.python.org/moin/GlobalInterpreterLock) is held.
//!
//! > As opposed to using build scripts, this approach does not offer the same level of customization via `pyo3_bindgen::Config`. Furthermore, the procedural macro is quite experimental and might not work in all cases.
//!
-//! ```
+//! ```ignore
//! use pyo3_bindgen::import_python;
//! import_python!("math");
//!
diff --git a/pyo3_bindgen_engine/Cargo.toml b/pyo3_bindgen_engine/Cargo.toml
index 90983d3..928f722 100644
--- a/pyo3_bindgen_engine/Cargo.toml
+++ b/pyo3_bindgen_engine/Cargo.toml
@@ -29,13 +29,9 @@ prettyplease = { workspace = true }
[build-dependencies]
pyo3-build-config = { workspace = true }
-[lib]
-name = "pyo3_bindgen_engine"
-path = "src/lib.rs"
-crate-type = ["rlib"]
-
[features]
default = []
+numpy = []
[[bench]]
name = "bindgen"
diff --git a/pyo3_bindgen_engine/src/codegen.rs b/pyo3_bindgen_engine/src/codegen.rs
index a7757a2..32bf8e0 100644
--- a/pyo3_bindgen_engine/src/codegen.rs
+++ b/pyo3_bindgen_engine/src/codegen.rs
@@ -3,7 +3,8 @@ use crate::{
Config, PyBindgenError, Result,
};
use itertools::Itertools;
-use rustc_hash::FxHashSet as HashSet;
+use pyo3::prelude::*;
+use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
/// Engine for automatic generation of Rust FFI bindings to Python modules.
///
@@ -35,7 +36,7 @@ use rustc_hash::FxHashSet as HashSet;
/// # use pyo3_bindgen_engine::Codegen;
/// fn main() -> Result<(), Box> {
/// Codegen::default()
-/// .module_names(&["html.entities", "html.parser"])?
+/// .module_names(["html.entities", "html.parser"])?
/// .generate()?;
/// Ok(())
/// }
@@ -44,6 +45,8 @@ use rustc_hash::FxHashSet as HashSet;
pub struct Codegen {
cfg: Config,
modules: Vec,
+ /// Python source code included by [`Self::module_from_str()`] in the generated Rust bindings.
+ embedded_source_code: HashMap,
}
impl Codegen {
@@ -57,7 +60,7 @@ impl Codegen {
}
/// Add a Python module to the list of modules for which to generate bindings.
- pub fn module(mut self, module: &pyo3::types::PyModule) -> Result {
+ pub fn module(mut self, module: &pyo3::Bound) -> Result {
crate::io_utils::with_suppressed_python_output(
module.py(),
self.cfg.suppress_python_stdout,
@@ -75,30 +78,41 @@ impl Codegen {
#[cfg(not(PyPy))]
pyo3::prepare_freethreaded_python();
pyo3::Python::with_gil(|py| {
- let module = py.import(module_name)?;
- self.module(module)
+ let module = py.import_bound(module_name)?;
+ self.module(&module)
})
}
/// Add a Python module from its source code and name to the list of modules for which to generate bindings.
- pub fn module_from_str(self, source_code: &str, new_module_name: &str) -> Result {
+ ///
+ /// # Note
+ ///
+ /// When including a module in this way, the Python source code must be available also during runtime for
+ /// the underlying Python interpreter.
+ ///
+ /// For convenience, you can call `module_name::pyo3_embed_python_source_code()` that is automatically
+ /// generated in the Rust bindings. This function must be called before attempting to use any functions
+ /// of classes from the module.
+ pub fn module_from_str(mut self, source_code: &str, module_name: &str) -> Result {
+ self.embedded_source_code
+ .insert(module_name.to_owned(), source_code.to_owned());
#[cfg(not(PyPy))]
pyo3::prepare_freethreaded_python();
pyo3::Python::with_gil(|py| {
- let module = pyo3::types::PyModule::from_code(
+ let module = pyo3::types::PyModule::from_code_bound(
py,
source_code,
- &format!("{new_module_name}/__init__.py"),
- new_module_name,
+ &format!("{module_name}/__init__.py"),
+ module_name,
)?;
- self.module(module)
+ self.module(&module)
})
}
/// Add multiple Python modules to the list of modules for which to generate bindings.
pub fn modules<'py>(
mut self,
- modules: impl AsRef<[&'py pyo3::types::PyModule]>,
+ modules: impl AsRef<[pyo3::Bound<'py, pyo3::types::PyModule>]>,
) -> Result {
let modules = modules.as_ref();
self.modules.reserve(modules.len());
@@ -134,6 +148,13 @@ impl Codegen {
// Canonicalize the module tree
self.canonicalize();
+ // Embed the source code of the modules
+ self.modules.iter_mut().for_each(|module| {
+ if let Some(source_code) = self.embedded_source_code.get(&module.name.to_rs()) {
+ module.source_code = Some(source_code.clone());
+ }
+ });
+
// Generate the bindings for all modules
self.modules
.iter()
@@ -178,7 +199,7 @@ impl Codegen {
// Get the last valid module within the path of the import
.map(|import| {
let mut last_module = py
- .import(
+ .import_bound(
import
.root()
.unwrap_or_else(|| unreachable!())
@@ -188,7 +209,7 @@ impl Codegen {
.unwrap();
for path in &import[1..] {
if let Ok(attr) = last_module.getattr(path.as_py()) {
- if let Ok(module) = attr.extract::<&pyo3::types::PyModule>() {
+ if let Ok(module) = attr.downcast_into::() {
last_module = module;
} else {
break;
@@ -204,14 +225,14 @@ impl Codegen {
// Filter attributes based on various configurable conditions
.filter(|module| {
self.cfg.is_attr_allowed(
- &Ident::from_py(module.name().unwrap()),
+ &Ident::from_py(&module.name().unwrap().to_string()),
&Path::from_py(
&module
.getattr(pyo3::intern!(py, "__module__"))
- .map(std::string::ToString::to_string)
+ .map(|a| a.to_string())
.unwrap_or_default(),
),
- py.get_type::(),
+ &py.get_type_bound::(),
)
})
.try_for_each(|module| {
@@ -220,7 +241,7 @@ impl Codegen {
self.cfg.suppress_python_stdout,
self.cfg.suppress_python_stderr,
|| {
- self.modules.push(Module::parse(&self.cfg, module)?);
+ self.modules.push(Module::parse(&self.cfg, &module)?);
Ok(())
},
)
diff --git a/pyo3_bindgen_engine/src/config.rs b/pyo3_bindgen_engine/src/config.rs
index cd24b9c..6d53be8 100644
--- a/pyo3_bindgen_engine/src/config.rs
+++ b/pyo3_bindgen_engine/src/config.rs
@@ -79,7 +79,7 @@ impl Config {
&self,
attr_name: &Ident,
attr_module: &Path,
- _attr_type: &pyo3::types::PyType,
+ _attr_type: &pyo3::Bound,
) -> bool {
if
// Skip always forbidden attribute names
diff --git a/pyo3_bindgen_engine/src/syntax/class.rs b/pyo3_bindgen_engine/src/syntax/class.rs
index 015f3cf..e54f538 100644
--- a/pyo3_bindgen_engine/src/syntax/class.rs
+++ b/pyo3_bindgen_engine/src/syntax/class.rs
@@ -1,8 +1,10 @@
use super::{
- AttributeVariant, Function, FunctionType, Ident, MethodType, Path, Property, PropertyOwner,
+ AttributeVariant, Function, FunctionImplementation, FunctionType, Ident, MethodType, Path,
+ Property, PropertyOwner, TraitMethod,
};
use crate::{Config, Result};
use itertools::Itertools;
+use pyo3::prelude::*;
use rustc_hash::FxHashMap as HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -15,7 +17,11 @@ pub struct Class {
}
impl Class {
- pub fn parse(cfg: &Config, class: &pyo3::types::PyType, name: Path) -> Result {
+ pub fn parse(
+ cfg: &Config,
+ class: &pyo3::Bound,
+ name: Path,
+ ) -> Result {
let py = class.py();
// Initialize lists for all members of the class
@@ -40,7 +46,7 @@ impl Class {
let attr_module = Path::from_py(
&attr
.getattr(pyo3::intern!(py, "__module__"))
- .map(std::string::ToString::to_string)
+ .map(|a|a.to_string())
.unwrap_or_default(),
);
let attr_type = attr.get_type();
@@ -61,7 +67,7 @@ impl Class {
// Iterate over the remaining attributes and parse them
.try_for_each(|(attr, attr_name, attr_module, attr_type)| {
let attr_name_full = name.join(&attr_name.clone().into());
- match AttributeVariant::determine(py, attr, attr_type, &attr_module, &name, false)
+ match AttributeVariant::determine(py, &attr, &attr_type, &attr_module, &name, false)
?
{
AttributeVariant::Import => {
@@ -83,7 +89,7 @@ impl Class {
AttributeVariant::Function | AttributeVariant::Method => {
let method = Function::parse(
cfg,
- attr,
+ &attr,
attr_name_full,
FunctionType::Method {
class_path: name.clone(),
@@ -106,7 +112,7 @@ impl Class {
AttributeVariant::Property => {
let property = Property::parse(
cfg,
- attr,
+ &attr,
attr_name_full,
PropertyOwner::Class,
)
@@ -189,8 +195,11 @@ impl Class {
let object_name = self.name.to_py();
output.extend(quote::quote! {
::pyo3::pyobject_native_type_named!(#struct_ident);
- ::pyo3::pyobject_native_type_info!(#struct_ident, ::pyo3::pyobject_native_static_type_object!(::pyo3::ffi::PyBaseObject_Type), ::std::option::Option::Some(#object_name));
- ::pyo3::pyobject_native_type_extract!(#struct_ident);
+ ::pyo3::pyobject_native_type_info!(
+ #struct_ident,
+ ::pyo3::pyobject_native_static_type_object!(::pyo3::ffi::PyBaseObject_Type),
+ ::std::option::Option::Some(#object_name)
+ );
});
// Get the names of all methods to avoid name clashes
@@ -200,15 +209,26 @@ impl Class {
.map(|method| method.name.name())
.collect::>();
- // Generate the struct implementation block
+ // Generate the struct implementation blocks
let mut struct_impl = proc_macro2::TokenStream::new();
+ let mut method_defs = proc_macro2::TokenStream::new();
+ let mut method_impls = proc_macro2::TokenStream::new();
// Methods
- struct_impl.extend(
- self.methods
- .iter()
- .map(|method| method.generate(cfg, &scoped_function_idents, local_types))
- .collect::>()?,
- );
+ self.methods
+ .iter()
+ .map(|method| method.generate(cfg, &scoped_function_idents, local_types))
+ .try_for_each(|def| {
+ match def? {
+ FunctionImplementation::Function(impl_fn) => {
+ struct_impl.extend(impl_fn);
+ }
+ FunctionImplementation::Method(TraitMethod { trait_fn, impl_fn }) => {
+ method_defs.extend(trait_fn);
+ method_impls.extend(impl_fn);
+ }
+ }
+ Result::Ok(())
+ })?;
// Properties
{
let mut scoped_function_idents_extra = Vec::with_capacity(2);
@@ -235,15 +255,24 @@ impl Class {
scoped_function_idents_extra.push(Ident::from_py("call"));
}
scoped_function_idents.extend(scoped_function_idents_extra.iter());
- struct_impl.extend(
- self.properties
- .iter()
- .map(|property| property.generate(cfg, &scoped_function_idents, local_types))
- .collect::>()?,
- );
+ self.properties
+ .iter()
+ .map(|property| property.generate(cfg, &scoped_function_idents, local_types))
+ .try_for_each(|def| {
+ match def? {
+ FunctionImplementation::Function(impl_fn) => {
+ struct_impl.extend(impl_fn);
+ }
+ FunctionImplementation::Method(TraitMethod { trait_fn, impl_fn }) => {
+ method_defs.extend(trait_fn);
+ method_impls.extend(impl_fn);
+ }
+ }
+ Result::Ok(())
+ })?;
}
- // Finalize the implementation block of the struct
+ // Add the implementation block for the struct
output.extend(quote::quote! {
#[automatically_derived]
impl #struct_ident {
@@ -251,6 +280,25 @@ impl Class {
}
});
+ // Add the trait and implementation block for bounded struct
+ let trait_ident: syn::Ident =
+ Ident::from_py(&format!("{struct_ident}Methods")).try_into()?;
+ let struct_ident_str = struct_ident.to_string();
+ output.extend(quote::quote! {
+ /// These methods are defined for the `Bound<'py, T>` smart pointer, so to use
+ /// method call syntax these methods are separated into a trait, because stable
+ /// Rust does not yet support `arbitrary_self_types`.
+ #[doc(alias = #struct_ident_str)]
+ #[automatically_derived]
+ pub trait #trait_ident {
+ #method_defs
+ }
+ #[automatically_derived]
+ impl #trait_ident for ::pyo3::Bound<'_, #struct_ident> {
+ #method_impls
+ }
+ });
+
Ok(output)
}
}
diff --git a/pyo3_bindgen_engine/src/syntax/common/attribute_variant.rs b/pyo3_bindgen_engine/src/syntax/common/attribute_variant.rs
index ccdd722..80317fb 100644
--- a/pyo3_bindgen_engine/src/syntax/common/attribute_variant.rs
+++ b/pyo3_bindgen_engine/src/syntax/common/attribute_variant.rs
@@ -2,6 +2,7 @@ use crate::{
syntax::{Ident, Path},
Result,
};
+use pyo3::prelude::*;
pub enum AttributeVariant {
Import,
@@ -17,20 +18,20 @@ pub enum AttributeVariant {
impl AttributeVariant {
pub fn determine(
py: pyo3::prelude::Python,
- attr: &pyo3::prelude::PyAny,
- attr_type: &pyo3::types::PyType,
+ attr: &pyo3::Bound,
+ attr_type: &pyo3::Bound,
attr_module: &Path,
owner_name: &Path,
consider_import: bool,
) -> Result {
- let inspect = py.import("inspect")?;
+ let inspect = py.import_bound("inspect")?;
// Get the name and module of the attribute type
- let attr_type_name = Ident::from_py(attr_type.name().unwrap_or_default());
+ let attr_type_name = Ident::from_py(&attr_type.name().unwrap_or_default());
let attr_type_module = Path::from_py(
&attr_type
.getattr(pyo3::intern!(py, "__module__"))
- .map(std::string::ToString::to_string)
+ .map(|a| a.to_string())
.unwrap_or_default(),
);
@@ -46,10 +47,10 @@ impl AttributeVariant {
.unwrap_or(false);
let is_function = inspect
.call_method1(pyo3::intern!(py, "isfunction"), (attr,))?
- .is_true()?;
+ .is_truthy()?;
let is_method = inspect
.call_method1(pyo3::intern!(py, "ismethod"), (attr,))?
- .is_true()?;
+ .is_truthy()?;
let is_closure =
attr_type_module.to_py().as_str() == "functools" && attr_type_name.as_py() == "partial";
let is_type = ["typing", "types"].contains(&attr_type_module.to_py().as_str());
diff --git a/pyo3_bindgen_engine/src/syntax/common/function_definition.rs b/pyo3_bindgen_engine/src/syntax/common/function_definition.rs
new file mode 100644
index 0000000..2301894
--- /dev/null
+++ b/pyo3_bindgen_engine/src/syntax/common/function_definition.rs
@@ -0,0 +1,28 @@
+pub enum FunctionImplementation {
+ Function(proc_macro2::TokenStream),
+ Method(TraitMethod),
+}
+
+impl FunctionImplementation {
+ pub fn empty_function() -> Self {
+ Self::Function(proc_macro2::TokenStream::new())
+ }
+
+ pub fn empty_method() -> Self {
+ Self::Method(TraitMethod::empty())
+ }
+}
+
+pub struct TraitMethod {
+ pub trait_fn: proc_macro2::TokenStream,
+ pub impl_fn: proc_macro2::TokenStream,
+}
+
+impl TraitMethod {
+ pub fn empty() -> Self {
+ Self {
+ trait_fn: proc_macro2::TokenStream::new(),
+ impl_fn: proc_macro2::TokenStream::new(),
+ }
+ }
+}
diff --git a/pyo3_bindgen_engine/src/syntax/common/mod.rs b/pyo3_bindgen_engine/src/syntax/common/mod.rs
index 42a109c..f63f03e 100644
--- a/pyo3_bindgen_engine/src/syntax/common/mod.rs
+++ b/pyo3_bindgen_engine/src/syntax/common/mod.rs
@@ -1,7 +1,9 @@
pub(crate) mod attribute_variant;
+pub(crate) mod function_definition;
pub(crate) mod ident;
pub(crate) mod path;
pub use attribute_variant::AttributeVariant;
+pub use function_definition::{FunctionImplementation, TraitMethod};
pub use ident::Ident;
pub use path::Path;
diff --git a/pyo3_bindgen_engine/src/syntax/common/path.rs b/pyo3_bindgen_engine/src/syntax/common/path.rs
index 957bb4a..789a005 100644
--- a/pyo3_bindgen_engine/src/syntax/common/path.rs
+++ b/pyo3_bindgen_engine/src/syntax/common/path.rs
@@ -199,7 +199,7 @@ impl Path {
let mut package_path = self.root().unwrap_or_else(|| unreachable!());
for i in (1..self.len()).rev() {
let module_name = Self::from(&self[..i]);
- if py.import(module_name.to_py().as_str()).is_ok() {
+ if py.import_bound(module_name.to_py().as_str()).is_ok() {
package_path = module_name;
break;
}
@@ -218,9 +218,12 @@ impl Path {
.collect_vec();
// Generate the import code
- quote::quote! {
- py.import(::pyo3::intern!(py, #package_path))?#(.getattr(::pyo3::intern!(py, #remaining_path))?)*
- }
+ remaining_path.into_iter().fold(
+ quote::quote! { py.import_bound(::pyo3::intern!(py, #package_path))? },
+ |acc, ident| {
+ quote::quote! { ::pyo3::types::PyAnyMethods::getattr(#acc.as_any(), ::pyo3::intern!(py, #ident))? }
+ },
+ )
}
}
diff --git a/pyo3_bindgen_engine/src/syntax/function.rs b/pyo3_bindgen_engine/src/syntax/function.rs
index 73f2ba4..bf046c3 100644
--- a/pyo3_bindgen_engine/src/syntax/function.rs
+++ b/pyo3_bindgen_engine/src/syntax/function.rs
@@ -1,7 +1,8 @@
-use super::{Ident, Path};
+use super::{FunctionImplementation, Ident, Path, TraitMethod};
use crate::{typing::Type, Config, Result};
use itertools::Itertools;
-use pyo3::{types::IntoPyDict, ToPyObject};
+use proc_macro2::TokenStream;
+use pyo3::{prelude::*, types::IntoPyDict, ToPyObject};
use rustc_hash::FxHashMap as HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -33,7 +34,7 @@ pub enum MethodType {
impl Function {
pub fn parse(
_cfg: &Config,
- function: &pyo3::types::PyAny,
+ function: &pyo3::Bound,
name: Path,
mut typ: FunctionType,
) -> Result {
@@ -51,7 +52,7 @@ impl Function {
// Extract the signature of the function
if let Ok(function_signature) = py
- .import(pyo3::intern!(py, "inspect"))?
+ .import_bound(pyo3::intern!(py, "inspect"))?
.call_method1(pyo3::intern!(py, "signature"), (function,))
{
// Extract the parameters of the function
@@ -75,7 +76,7 @@ impl Function {
})),
_ => {
let annotation = param.getattr(pyo3::intern!(py, "annotation"))?;
- if annotation.is(param.getattr(pyo3::intern!(py, "empty"))?) {
+ if annotation.is(¶m.getattr(pyo3::intern!(py, "empty"))?) {
Type::Unknown
} else {
annotation.try_into()?
@@ -85,7 +86,7 @@ impl Function {
let default = {
let default = param.getattr(pyo3::intern!(py, "default"))?;
- if default.is(param.getattr(pyo3::intern!(py, "empty"))?) {
+ if default.is(¶m.getattr(pyo3::intern!(py, "empty"))?) {
None
} else {
Some(default.to_object(py))
@@ -108,7 +109,7 @@ impl Function {
let return_annotation = {
let return_annotation =
function_signature.getattr(pyo3::intern!(py, "return_annotation"))?;
- if return_annotation.is(function_signature.getattr(pyo3::intern!(py, "empty"))?) {
+ if return_annotation.is(&function_signature.getattr(pyo3::intern!(py, "empty"))?) {
Type::Unknown
} else {
return_annotation.try_into()?
@@ -123,7 +124,7 @@ impl Function {
} if *method_typ == MethodType::Unknown => {
// Get the class object from its class path
let class = py
- .import(
+ .import_bound(
class_path
.root()
.unwrap_or_else(|| unreachable!())
@@ -143,15 +144,15 @@ impl Function {
.getattr(pyo3::intern!(py, "__dict__"))?
.get_item(name.name().as_py())
}) {
- let locals = [("obj", static_fn_obj)].into_py_dict(py);
+ let locals = [("obj", static_fn_obj)].into_py_dict_bound(py);
let method_type = if py
- .eval("isinstance(obj, classmethod)", None, Some(locals))?
- .is_true()?
+ .eval_bound("isinstance(obj, classmethod)", None, Some(&locals))?
+ .is_truthy()?
{
MethodType::ClassMethod
} else if py
- .eval("isinstance(obj, staticmethod)", None, Some(locals))?
- .is_true()?
+ .eval_bound("isinstance(obj, staticmethod)", None, Some(&locals))?
+ .is_truthy()?
{
MethodType::StaticMethod
} else {
@@ -321,14 +322,14 @@ impl Function {
cfg: &Config,
scoped_function_idents: &[&Ident],
local_types: &HashMap,
- ) -> Result {
- let mut output = proc_macro2::TokenStream::new();
+ ) -> Result {
+ let mut impl_fn = proc_macro2::TokenStream::new();
// Documentation
if cfg.generate_docs {
if let Some(mut docstring) = self.docstring.clone() {
crate::utils::text::format_docstring(&mut docstring);
- output.extend(quote::quote! {
+ impl_fn.extend(quote::quote! {
#[doc = #docstring]
});
}
@@ -339,7 +340,7 @@ impl Function {
let name = self.name.name();
if let Ok(ident) = name.try_into() {
if crate::config::FORBIDDEN_FUNCTION_NAMES.contains(&name.as_py()) {
- return Ok(proc_macro2::TokenStream::new());
+ return Ok(FunctionImplementation::empty_function());
} else {
ident
}
@@ -360,7 +361,7 @@ impl Function {
"WARN: Function '{}' is an invalid Rust ident for a function name. Renaming failed. Bindings will not be generated.",
self.name
);
- return Ok(proc_macro2::TokenStream::new());
+ return Ok(FunctionImplementation::empty_function());
}
}
};
@@ -386,15 +387,14 @@ impl Function {
.map(|param| Result::Ok(param.annotation.clone().into_rs_borrowed(local_types)))
.collect::>>()?;
let return_type = self.return_annotation.clone().into_rs_owned(local_types);
- output.extend(match &self.typ {
+ let fn_contract = match &self.typ {
FunctionType::Method {
typ: MethodType::InstanceMethod,
..
} => {
quote::quote! {
- pub fn #function_ident<'py>(
+ fn #function_ident<'py>(
&'py self,
- py: ::pyo3::marker::Python<'py>,
#(#param_idents: #param_types),*
) -> ::pyo3::PyResult<#return_type>
}
@@ -418,9 +418,8 @@ impl Function {
}
.try_into()?;
quote::quote! {
- pub fn #call_fn_ident<'py>(
+ fn #call_fn_ident<'py>(
&'py self,
- py: ::pyo3::marker::Python<'py>,
#(#param_idents: #param_types),*
) -> ::pyo3::PyResult<#return_type>
}
@@ -447,7 +446,7 @@ impl Function {
pub fn #new_fn_ident<'py>(
py: ::pyo3::marker::Python<'py>,
#(#param_idents: #param_types),*
- ) -> ::pyo3::PyResult<&'py Self>
+ ) -> ::pyo3::PyResult<::pyo3::Bound<'py, Self>>
}
}
_ => {
@@ -458,7 +457,19 @@ impl Function {
) -> ::pyo3::PyResult<#return_type>
}
}
- });
+ };
+ impl_fn.extend(fn_contract.clone());
+
+ // If the function is a method with `self` as a parameter, extract the Python marker from `self`
+ let maybe_extract_py = match &self.typ {
+ FunctionType::Method {
+ typ: MethodType::InstanceMethod | MethodType::Callable,
+ ..
+ } => quote::quote! {
+ let py = self.py();
+ },
+ _ => TokenStream::new(),
+ };
// Function body (function dispatcher)
let function_dispatcher = match &self.typ {
@@ -477,7 +488,7 @@ impl Function {
..
} => {
quote::quote! {
- self.0
+ self
}
}
FunctionType::Method {
@@ -488,7 +499,7 @@ impl Function {
"WARN: Method '{}' has an unknown type. Bindings will not be generated.",
self.name
);
- return Ok(proc_macro2::TokenStream::new());
+ return Ok(FunctionImplementation::empty_method());
}
};
@@ -519,13 +530,12 @@ impl Function {
}
} else {
let n_args_fixed = positional_args_idents.len();
- // TODO: The reference here might be incorrect (positional_args_idents could cause double reference) - check
quote::quote! {
{
- let mut __internal__args = Vec::with_capacity(#n_args_fixed + #var_positional_args_ident.len());
+ let mut __internal__args = Vec::with_capacity(#n_args_fixed + ::pyo3::types::PyTupleMethods::len(#var_positional_args_ident));
__internal__args.extend([#(::pyo3::ToPyObject::to_object(positional_args_idents, py),)*]);
- __internal__args.extend(#var_positional_args_ident.iter().map(|__internal__arg| ::pyo3::ToPyObject::to_object(__internal__arg, py)));
- ::pyo3::types::PyTuple::new(
+ __internal__args.extend(::pyo3::types::PyTupleMethods::iter(#var_positional_args_ident).map(|__internal__arg| ::pyo3::ToPyObject::to_object(&__internal__arg, py)));
+ ::pyo3::types::PyTuple::new_bound(
py,
__internal__args,
)
@@ -537,9 +547,8 @@ impl Function {
()
}
} else {
- // TODO: The reference here might be incorrect (positional_args_idents could cause double reference) - check
quote::quote! {
- ::pyo3::types::PyTuple::new(
+ ::pyo3::types::PyTuple::new_bound(
py,
[#(::pyo3::ToPyObject::to_object(positional_args_idents, py),)*],
)
@@ -575,7 +584,7 @@ impl Function {
{
let __internal__kwargs = #var_keyword_args_ident;
#(
- __internal__kwargs.set_item(::pyo3::intern!(py, #keyword_args_names), #keyword_args_idents);
+ ::pyo3::types::PyDictMethods::set_item(&__internal__kwargs, ::pyo3::intern!(py, #keyword_args_names), #keyword_args_idents);
)*
__internal__kwargs
}
@@ -583,14 +592,14 @@ impl Function {
}
} else if keyword_args_idents.is_empty() {
quote::quote! {
- ::pyo3::types::PyDict::new(py)
+ ::pyo3::types::PyDict::new_bound(py)
}
} else {
quote::quote! {
{
- let __internal__kwargs = ::pyo3::types::PyDict::new(py);
+ let __internal__kwargs = ::pyo3::types::PyDict::new_bound(py);
#(
- __internal__kwargs.set_item(::pyo3::intern!(py, #keyword_args_names), #keyword_args_idents);
+ ::pyo3::types::PyDictMethods::set_item(&__internal__kwargs, ::pyo3::intern!(py, #keyword_args_names), #keyword_args_idents);
)*
__internal__kwargs
}
@@ -604,45 +613,55 @@ impl Function {
{
if has_keyword_args {
quote::quote! {
- call(#positional_args, Some(#keyword_args))
+ ::pyo3::types::PyAnyMethods::call(#function_dispatcher.as_any(), #positional_args, Some(keyword_args))
}
} else if has_positional_args {
quote::quote! {
- call1(#positional_args)
+ ::pyo3::types::PyAnyMethods::call1(#function_dispatcher.as_any(), #positional_args)
}
} else {
quote::quote! {
- call0()
+ ::pyo3::types::PyAnyMethods::call0(#function_dispatcher.as_any())
}
}
} else {
let method_name = self.name.name().as_py();
if has_keyword_args {
quote::quote! {
- call_method(::pyo3::intern!(py, #method_name), #positional_args, Some(#keyword_args))
+ ::pyo3::types::PyAnyMethods::call_method(#function_dispatcher.as_any(), ::pyo3::intern!(py, #method_name), #positional_args, Some(keyword_args))
}
} else if has_positional_args {
quote::quote! {
- call_method1(::pyo3::intern!(py, #method_name), #positional_args)
+ ::pyo3::types::PyAnyMethods::call_method1(#function_dispatcher.as_any(), ::pyo3::intern!(py, #method_name), #positional_args)
}
} else {
quote::quote! {
- call_method0(::pyo3::intern!(py, #method_name))
+ ::pyo3::types::PyAnyMethods::call_method0(#function_dispatcher.as_any(), ::pyo3::intern!(py, #method_name))
}
}
};
// Function body
- output.extend(quote::quote! {
+ impl_fn.extend(quote::quote! {
{
+ #maybe_extract_py
#param_preprocessing
- ::pyo3::FromPyObject::extract(
- #function_dispatcher.#call?
+ ::pyo3::types::PyAnyMethods::extract(
+ call?
)
}
});
- Ok(output)
+ Ok(match &self.typ {
+ FunctionType::Method {
+ typ: MethodType::InstanceMethod | MethodType::Callable,
+ ..
+ } => FunctionImplementation::Method(TraitMethod {
+ trait_fn: quote::quote! { #fn_contract ; },
+ impl_fn,
+ }),
+ _ => FunctionImplementation::Function(impl_fn),
+ })
}
}
diff --git a/pyo3_bindgen_engine/src/syntax/mod.rs b/pyo3_bindgen_engine/src/syntax/mod.rs
index 2ad8f1e..0b89679 100644
--- a/pyo3_bindgen_engine/src/syntax/mod.rs
+++ b/pyo3_bindgen_engine/src/syntax/mod.rs
@@ -7,7 +7,7 @@ pub(crate) mod property;
pub(crate) mod type_var;
pub use class::Class;
-pub use common::{AttributeVariant, Ident, Path};
+pub use common::{AttributeVariant, FunctionImplementation, Ident, Path, TraitMethod};
pub use function::{Function, FunctionType, MethodType};
pub use import::Import;
pub use module::Module;
diff --git a/pyo3_bindgen_engine/src/syntax/module.rs b/pyo3_bindgen_engine/src/syntax/module.rs
index 629a8e7..2b2a39f 100644
--- a/pyo3_bindgen_engine/src/syntax/module.rs
+++ b/pyo3_bindgen_engine/src/syntax/module.rs
@@ -1,9 +1,10 @@
use super::{
- AttributeVariant, Class, Function, FunctionType, Ident, Import, Path, Property, PropertyOwner,
- TypeVar,
+ AttributeVariant, Class, Function, FunctionImplementation, FunctionType, Ident, Import, Path,
+ Property, PropertyOwner, TypeVar,
};
use crate::{Config, Result};
use itertools::Itertools;
+use pyo3::prelude::*;
use rustc_hash::FxHashSet as HashSet;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -18,11 +19,12 @@ pub struct Module {
pub properties: Vec,
pub docstring: Option,
pub is_package: bool,
+ pub source_code: Option,
}
impl Module {
pub fn empty(py: pyo3::Python, name: Path) -> Result {
- let module = py.import(name.to_py().as_str())?;
+ let module = py.import_bound(name.to_py().as_str())?;
// Extract the docstring of the module
let docstring = {
@@ -45,14 +47,15 @@ impl Module {
properties: Vec::default(),
docstring,
is_package: true,
+ source_code: None,
})
}
- pub fn parse(cfg: &Config, module: &pyo3::types::PyModule) -> Result {
+ pub fn parse(cfg: &Config, module: &pyo3::Bound) -> Result {
let py = module.py();
// Extract the name of the module
- let name = Path::from_py(module.name()?);
+ let name = Path::from_py(&module.name().unwrap().to_string());
// Extract the index of the module as prelude (if enabled)
let prelude = if cfg.generate_preludes {
@@ -97,7 +100,7 @@ impl Module {
let attr_module = Path::from_py(
&attr
.getattr(pyo3::intern!(py, "__module__"))
- .map(std::string::ToString::to_string)
+ .map(|a| a.to_string())
.unwrap_or_default(),
);
let attr_type = attr.get_type();
@@ -117,14 +120,14 @@ impl Module {
// Iterate over the remaining attributes and parse them
.try_for_each(|(attr, attr_name, attr_module, attr_type)| {
let attr_name_full = name.join(&attr_name.clone().into());
- match AttributeVariant::determine(py, attr, attr_type, &attr_module, &name, true)
+ match AttributeVariant::determine(py, &attr, &attr_type, &attr_module, &name, true)
?
{
AttributeVariant::Import => {
let origin = attr_module.join(&Path::from_py(
&attr
.getattr(pyo3::intern!(py, "__name__"))
- .map(std::string::ToString::to_string)
+ .map(|a| a.to_string())
.unwrap_or(attr_name.as_py().to_owned()),
));
@@ -138,11 +141,11 @@ impl Module {
let attr_name = &origin[i];
let attr_module = origin[..i].into();
let attr_type = if i == origin.len() - 1 {
- attr_type
+ attr_type.clone()
} else {
- py.get_type::()
+ py.get_type_bound::()
};
- cfg.is_attr_allowed(attr_name, &attr_module, attr_type)
+ cfg.is_attr_allowed(attr_name, &attr_module, &attr_type)
});
if !is_origin_attr_allowed {
return Ok(());
@@ -178,7 +181,7 @@ impl Module {
}
AttributeVariant::Function => {
let function =
- Function::parse(cfg, attr, attr_name_full, FunctionType::Function)
+ Function::parse(cfg, &attr, attr_name_full, FunctionType::Function)
?;
functions.push(function);
}
@@ -187,14 +190,14 @@ impl Module {
}
AttributeVariant::Closure => {
let function =
- Function::parse(cfg, attr, attr_name_full, FunctionType::Closure)
+ Function::parse(cfg, &attr, attr_name_full, FunctionType::Closure)
?;
functions.push(function);
}
AttributeVariant::Property => {
let property = Property::parse(
cfg,
- attr,
+ &attr,
attr_name_full,
PropertyOwner::Module,
)
@@ -218,10 +221,14 @@ impl Module {
.find(|import| import.target == full_submodule_name)
{
if let Ok(submodule) = py
- .import(full_submodule_name.to_py().as_str())
+ .import_bound(full_submodule_name.to_py().as_str())
.map_err(crate::PyBindgenError::from)
- .and_then(|attr| Ok(attr.downcast::()?))
- .and_then(|module| Self::parse(cfg, module))
+ .map(|attr| {
+ as Clone>::clone(&attr)
+ .downcast_into::()
+ .unwrap()
+ })
+ .and_then(|module| Self::parse(cfg, &module))
{
// It could be any attribute, so all of them need to be checked
if let Some(mut import) = submodule
@@ -269,15 +276,15 @@ impl Module {
}
// Try to import both as a package and as a attribute of the current module
- py.import(full_submodule_name.to_py().as_str())
+ py.import_bound(full_submodule_name.to_py().as_str())
.or_else(|_| {
module
.getattr(submodule_name.as_py())
- .and_then(|attr| Ok(attr.downcast::()?))
+ .and_then(|attr| Ok(attr.downcast_into::()?))
})
.ok()
})
- .map(|submodule| Self::parse(cfg, submodule))
+ .map(|submodule| Self::parse(cfg, &submodule))
.collect::>()?
} else {
Vec::default()
@@ -304,6 +311,7 @@ impl Module {
properties,
docstring,
is_package,
+ source_code: None,
})
}
@@ -369,7 +377,7 @@ impl Module {
{
let mut path = Path::from_py(stripped_path);
// Overwrite the first segment with the target name to support aliasing
- path[0] = import.target.name().to_owned();
+ import.target.name().clone_into(&mut path[0]);
path
} else {
import.target.name().to_owned().into()
@@ -424,7 +432,17 @@ impl Module {
module_content.extend(
self.functions
.iter()
- .map(|function| function.generate(cfg, &scoped_function_idents, &local_types))
+ .map(|function| {
+ function
+ .generate(cfg, &scoped_function_idents, &local_types)
+ .map(|def| {
+ if let FunctionImplementation::Function(impl_fn) = def {
+ impl_fn
+ } else {
+ unreachable!("Methods in modules are not possible")
+ }
+ })
+ })
.collect::>()?,
);
}
@@ -433,7 +451,17 @@ impl Module {
module_content.extend(
self.properties
.iter()
- .map(|property| property.generate(cfg, &scoped_function_idents, &local_types))
+ .map(|property| {
+ property
+ .generate(cfg, &scoped_function_idents, &local_types)
+ .map(|def| {
+ if let FunctionImplementation::Function(impl_fn) = def {
+ impl_fn
+ } else {
+ unreachable!("Methods in modules are not possible")
+ }
+ })
+ })
.collect::>()?,
);
}
@@ -447,10 +475,39 @@ impl Module {
);
}
+ // Embed the source code if the module was parsed directly from source code
+ let embed_source_code_fn = if let Some(source_code) = &self.source_code {
+ let module_name = self.name.to_rs();
+ let file_name = format!("{module_name}/__init__.py");
+ quote::quote! {
+ /// Embed the Python source code of the module into the Python interpreter
+ /// in order to enable the use of the generated Rust bindings.
+ pub fn pyo3_embed_python_source_code<'py>(py: ::pyo3::marker::Python<'py>) -> ::pyo3::PyResult<()> {
+ const SOURCE_CODE: &str = #source_code;
+ pyo3::types::PyAnyMethods::set_item(
+ &pyo3::types::PyAnyMethods::getattr(
+ py.import_bound(pyo3::intern!(py, "sys"))?.as_any(),
+ pyo3::intern!(py, "modules"),
+ )?,
+ #module_name,
+ pyo3::types::PyModule::from_code_bound(
+ py,
+ SOURCE_CODE,
+ #file_name,
+ #module_name,
+ )?,
+ )
+ }
+ }
+ } else {
+ proc_macro2::TokenStream::new()
+ };
+
// Finalize the module with its content
let module_ident: syn::Ident = self.name.name().try_into()?;
output.extend(quote::quote! {
pub mod #module_ident {
+ #embed_source_code_fn
#module_content
}
});
@@ -458,20 +515,22 @@ impl Module {
Ok(output)
}
- fn extract_submodules(cfg: &Config, module: &pyo3::types::PyModule) -> Result> {
+ fn extract_submodules(
+ cfg: &Config,
+ module: &pyo3::Bound,
+ ) -> Result> {
let py = module.py();
- let pkgutil = py.import(pyo3::intern!(py, "pkgutil"))?;
+ let pkgutil = py.import_bound(pyo3::intern!(py, "pkgutil"))?;
// Extract the paths of the module
let module_paths = module
.getattr(pyo3::intern!(py, "__path__"))?
- .extract::<&pyo3::types::PyList>()?
- .iter()
- .map(|x| std::path::PathBuf::from(x.to_string()))
- .collect_vec();
+ .iter()?
+ .map(|x| Ok(std::path::PathBuf::from(x?.to_string())))
+ .collect::>>()?;
// Extract the names of all submodules via `pkgutil.iter_modules`
- let module_name = Path::from_py(module.name()?);
+ let module_name = Path::from_py(&module.name().unwrap().to_string());
pkgutil
.call_method1(pyo3::intern!(py, "iter_modules"), (module_paths,))?
.iter()?
@@ -486,7 +545,7 @@ impl Module {
cfg.is_attr_allowed(
submodule_name,
&module_name,
- py.get_type::(),
+ &py.get_type_bound::(),
)
})
})
@@ -495,7 +554,7 @@ impl Module {
fn extract_prelude(
cfg: &Config,
- module: &pyo3::types::PyModule,
+ module: &pyo3::Bound,
module_name: &Path,
) -> Vec {
// Extract the index (__all__) of the module if it exists
@@ -533,7 +592,7 @@ impl Module {
let attr_module = Path::from_py(
&attr
.getattr(pyo3::intern!(module.py(), "__module__"))
- .map(std::string::ToString::to_string)
+ .map(|a| a.to_string())
.unwrap_or_default(),
);
attr_module.starts_with(&root_module)
@@ -549,7 +608,7 @@ impl Module {
index_attr_names.retain(|attr_name| {
if let Ok(attr) = module.getattr(attr_name.as_py()) {
let attr_type = attr.get_type();
- cfg.is_attr_allowed(attr_name, module_name, attr_type)
+ cfg.is_attr_allowed(attr_name, module_name, &attr_type)
} else {
false
}
diff --git a/pyo3_bindgen_engine/src/syntax/property.rs b/pyo3_bindgen_engine/src/syntax/property.rs
index 419cb12..3f89eff 100644
--- a/pyo3_bindgen_engine/src/syntax/property.rs
+++ b/pyo3_bindgen_engine/src/syntax/property.rs
@@ -1,5 +1,6 @@
-use super::{Ident, Path};
+use super::{FunctionImplementation, Ident, Path, TraitMethod};
use crate::{typing::Type, Config, Result};
+use pyo3::prelude::*;
use rustc_hash::FxHashMap as HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -22,7 +23,7 @@ pub enum PropertyOwner {
impl Property {
pub fn parse(
_cfg: &Config,
- property: &pyo3::types::PyAny,
+ property: &pyo3::Bound,
name: Path,
owner: PropertyOwner,
) -> Result {
@@ -35,27 +36,28 @@ impl Property {
let mut docstring = None;
// Determine the mutability and type of the property
- let (is_mutable, annotation, setter_annotation, mut setter_docstring);
+ let (is_mutable, annotation, setter_annotation);
+ let mut setter_docstring = None;
match owner {
PropertyOwner::Module => {
is_mutable = true;
annotation = Type::try_from(typ)?;
setter_annotation = annotation.clone();
- setter_docstring = docstring.clone();
+ docstring.clone_from(&setter_docstring);
}
PropertyOwner::Class => {
let signature = py
- .import(pyo3::intern!(py, "inspect"))?
+ .import_bound(pyo3::intern!(py, "inspect"))?
.getattr(pyo3::intern!(py, "signature"))?;
if let Ok(getter) = property.getattr(pyo3::intern!(py, "fget")) {
// Extract the annotation from the return of the function (if available)
- if let Ok(function_signature) = signature.call1((getter,)) {
+ if let Ok(function_signature) = signature.call1((&getter,)) {
annotation = {
let return_annotation = function_signature
.getattr(pyo3::intern!(py, "return_annotation"))?;
if return_annotation
- .is(function_signature.getattr(pyo3::intern!(py, "empty"))?)
+ .is(&function_signature.getattr(pyo3::intern!(py, "empty"))?)
{
Type::Unknown
} else {
@@ -87,7 +89,7 @@ impl Property {
is_mutable = true;
// Extract the annotation from the parameter of the function (if available)
- if let Ok(function_signature) = signature.call1((setter,)) {
+ if let Ok(function_signature) = signature.call1((&setter,)) {
setter_annotation = {
let param = function_signature
.getattr(pyo3::intern!(py, "parameters"))?
@@ -96,7 +98,7 @@ impl Property {
.nth(1)
.unwrap()?;
let annotation = param.getattr(pyo3::intern!(py, "annotation"))?;
- if annotation.is(param.getattr(pyo3::intern!(py, "empty"))?) {
+ if annotation.is(¶m.getattr(pyo3::intern!(py, "empty"))?) {
Type::Unknown
} else {
annotation.try_into()?
@@ -118,10 +120,10 @@ impl Property {
if docstring.is_none() {
// Update the getter docstring to match setter docstring if it is still empty
- docstring = setter_docstring.clone();
+ docstring.clone_from(&setter_docstring);
} else if setter_docstring.is_none() {
// Otherwise, update the setter docstring to match the getter docstring if it is still empty
- setter_docstring = docstring.clone();
+ setter_docstring.clone_from(&docstring);
}
}
_ => {
@@ -149,18 +151,46 @@ impl Property {
cfg: &Config,
scoped_function_idents: &[&Ident],
local_types: &HashMap,
- ) -> Result {
- let mut output = proc_macro2::TokenStream::new();
+ ) -> Result {
+ Ok(match self.owner {
+ PropertyOwner::Module => {
+ let mut functions = proc_macro2::TokenStream::new();
- // Getter
- output.extend(self.generate_getter(cfg, scoped_function_idents, local_types)?);
+ // Getter
+ let impl_fn = self
+ .generate_getter(cfg, scoped_function_idents, local_types)?
+ .impl_fn;
+ functions.extend(quote::quote! { pub #impl_fn });
- // Setter (if mutable)
- if self.is_mutable {
- output.extend(self.generate_setter(cfg, scoped_function_idents, local_types)?);
- }
+ // Setter (if mutable)
+ if self.is_mutable {
+ let impl_fn = self
+ .generate_setter(cfg, scoped_function_idents, local_types)?
+ .impl_fn;
+ functions.extend(quote::quote! { pub #impl_fn });
+ }
+
+ FunctionImplementation::Function(functions)
+ }
+ PropertyOwner::Class => {
+ let mut trait_fn = proc_macro2::TokenStream::new();
+ let mut impl_fn = proc_macro2::TokenStream::new();
+
+ // Getter
+ let getter = self.generate_getter(cfg, scoped_function_idents, local_types)?;
+ trait_fn.extend(getter.trait_fn);
+ impl_fn.extend(getter.impl_fn);
- Ok(output)
+ // Setter (if mutable)
+ if self.is_mutable {
+ let setter = self.generate_setter(cfg, scoped_function_idents, local_types)?;
+ trait_fn.extend(setter.trait_fn);
+ impl_fn.extend(setter.impl_fn);
+ }
+
+ FunctionImplementation::Method(TraitMethod { trait_fn, impl_fn })
+ }
+ })
}
pub fn generate_getter(
@@ -168,14 +198,15 @@ impl Property {
cfg: &Config,
scoped_function_idents: &[&Ident],
local_types: &HashMap,
- ) -> Result {
- let mut output = proc_macro2::TokenStream::new();
+ ) -> Result {
+ let mut trait_fn = proc_macro2::TokenStream::new();
+ let mut impl_fn = proc_macro2::TokenStream::new();
// Documentation
if cfg.generate_docs {
if let Some(mut docstring) = self.docstring.clone() {
crate::utils::text::format_docstring(&mut docstring);
- output.extend(quote::quote! {
+ impl_fn.extend(quote::quote! {
#[doc = #docstring]
});
}
@@ -192,7 +223,7 @@ impl Property {
if scoped_function_idents.contains(&&getter_name)
|| crate::config::FORBIDDEN_FUNCTION_NAMES.contains(&getter_name.as_py())
{
- return Ok(proc_macro2::TokenStream::new());
+ return Ok(TraitMethod::empty());
} else {
getter_name.try_into()?
}
@@ -204,7 +235,7 @@ impl Property {
if scoped_function_idents.contains(&&getter_name)
|| crate::config::FORBIDDEN_FUNCTION_NAMES.contains(&getter_name.as_py())
{
- return Ok(proc_macro2::TokenStream::new());
+ return Ok(TraitMethod::empty());
} else {
getter_name.try_into()?
}
@@ -220,12 +251,12 @@ impl Property {
.unwrap_or_else(|| unreachable!())
.import_quote(py)
});
- output.extend(quote::quote! {
- pub fn #function_ident<'py>(
+ impl_fn.extend(quote::quote! {
+ fn #function_ident<'py>(
py: ::pyo3::marker::Python<'py>,
) -> ::pyo3::PyResult<#param_type> {
- ::pyo3::FromPyObject::extract(
- #import.getattr(::pyo3::intern!(py, #param_name))?
+ ::pyo3::types::PyAnyMethods::extract(
+ &::pyo3::types::PyAnyMethods::getattr(#import.as_any(), ::pyo3::intern!(py, #param_name))?
)
}
});
@@ -233,19 +264,24 @@ impl Property {
PropertyOwner::Class => {
let param_name = self.name.name().as_py();
- output.extend(quote::quote! {
- pub fn #function_ident<'py>(
+ trait_fn.extend(quote::quote! {
+ fn #function_ident<'py>(
+ &'py self,
+ ) -> ::pyo3::PyResult<#param_type>;
+ });
+ impl_fn.extend(quote::quote! {
+ fn #function_ident<'py>(
&'py self,
- py: ::pyo3::marker::Python<'py>,
) -> ::pyo3::PyResult<#param_type> {
- self.0.getattr(::pyo3::intern!(py, #param_name))?
- .extract()
+ ::pyo3::types::PyAnyMethods::extract(
+ &::pyo3::types::PyAnyMethods::getattr(self.as_any(), ::pyo3::intern!(self.py(), #param_name))?
+ )
}
});
}
}
- Ok(output)
+ Ok(TraitMethod { trait_fn, impl_fn })
}
pub fn generate_setter(
@@ -253,14 +289,15 @@ impl Property {
cfg: &Config,
scoped_function_idents: &[&Ident],
local_types: &HashMap,
- ) -> Result {
- let mut output = proc_macro2::TokenStream::new();
+ ) -> Result {
+ let mut trait_fn = proc_macro2::TokenStream::new();
+ let mut impl_fn = proc_macro2::TokenStream::new();
// Documentation
if cfg.generate_docs {
if let Some(mut docstring) = self.setter_docstring.clone() {
crate::utils::text::format_docstring(&mut docstring);
- output.extend(quote::quote! {
+ impl_fn.extend(quote::quote! {
#[doc = #docstring]
});
}
@@ -272,7 +309,7 @@ impl Property {
if scoped_function_idents.contains(&&setter_name)
|| crate::config::FORBIDDEN_FUNCTION_NAMES.contains(&setter_name.as_py())
{
- return Ok(proc_macro2::TokenStream::new());
+ return Ok(TraitMethod::empty());
} else {
setter_name.try_into()?
}
@@ -291,30 +328,36 @@ impl Property {
.unwrap_or_else(|| unreachable!())
.import_quote(py)
});
- output.extend(quote::quote! {
- pub fn #function_ident<'py>(
+ impl_fn.extend(quote::quote! {
+ fn #function_ident<'py>(
py: ::pyo3::marker::Python<'py>,
p_value: #param_type,
) -> ::pyo3::PyResult<()> {
#param_preprocessing
- #import.setattr(::pyo3::intern!(py, #param_name), p_value)
+ ::pyo3::types::PyAnyMethods::setattr(#import.as_any(), ::pyo3::intern!(py, #param_name), p_value)
}
});
}
PropertyOwner::Class => {
- output.extend(quote::quote! {
- pub fn #function_ident<'py>(
+ trait_fn.extend(quote::quote! {
+ fn #function_ident<'py>(
+ &'py self,
+ p_value: #param_type,
+ ) -> ::pyo3::PyResult<()>;
+ });
+ impl_fn.extend(quote::quote! {
+ fn #function_ident<'py>(
&'py self,
- py: ::pyo3::marker::Python<'py>,
p_value: #param_type,
) -> ::pyo3::PyResult<()> {
+ let py = self.py();
#param_preprocessing
- self.0.setattr(::pyo3::intern!(py, #param_name), p_value)
+ ::pyo3::types::PyAnyMethods::setattr(self.as_any(), ::pyo3::intern!(py, #param_name), p_value)
}
});
}
}
- Ok(output)
+ Ok(TraitMethod { trait_fn, impl_fn })
}
}
diff --git a/pyo3_bindgen_engine/src/typing/from_py.rs b/pyo3_bindgen_engine/src/typing/from_py.rs
index 5519ffe..50d259b 100644
--- a/pyo3_bindgen_engine/src/typing/from_py.rs
+++ b/pyo3_bindgen_engine/src/typing/from_py.rs
@@ -1,17 +1,19 @@
use super::Type;
use crate::{PyBindgenError, Result};
use itertools::Itertools;
+use pyo3::prelude::*;
use std::str::FromStr;
-impl TryFrom<&pyo3::types::PyAny> for Type {
+impl TryFrom> for Type {
type Error = PyBindgenError;
- fn try_from(value: &pyo3::types::PyAny) -> Result {
+ fn try_from(value: pyo3::Bound) -> Result {
match value {
// None -> Unknown type
none if none.is_none() => Ok(Self::Unknown),
// Handle PyType
t if t.is_instance_of::() => {
- Self::try_from(t.downcast::()?)
+ let x = t.downcast_into::().unwrap();
+ Self::try_from(x)
}
// Handle typing
typing
@@ -26,7 +28,12 @@ impl TryFrom<&pyo3::types::PyAny> for Type {
// Handle everything else as string
_ => {
if value.is_instance_of::() {
- Self::from_str(value.downcast::()?.to_str()?)
+ Self::from_str(
+ value
+ .downcast::()
+ .unwrap()
+ .to_str()?,
+ )
} else {
Self::from_str(&value.to_string())
}
@@ -35,9 +42,9 @@ impl TryFrom<&pyo3::types::PyAny> for Type {
}
}
-impl TryFrom<&pyo3::types::PyType> for Type {
+impl TryFrom> for Type {
type Error = PyBindgenError;
- fn try_from(value: &pyo3::types::PyType) -> Result {
+ fn try_from(value: pyo3::Bound) -> Result {
Ok(match value {
// Primitives
t if t.is_subclass_of::()? => Self::PyBool,
@@ -104,7 +111,7 @@ impl TryFrom<&pyo3::types::PyType> for Type {
}
impl Type {
- fn from_typing(value: &pyo3::types::PyAny) -> Result {
+ fn from_typing(value: pyo3::Bound) -> Result {
let py = value.py();
debug_assert_eq!(
value
@@ -117,9 +124,12 @@ impl Type {
if let Ok(wrapping_type) = value.getattr(pyo3::intern!(py, "__origin__")) {
let wrapping_type = Self::try_from(wrapping_type)?;
Ok(
- if let Ok(inner_types) = value
- .getattr(pyo3::intern!(py, "__args__"))
- .and_then(|inner_types| Ok(inner_types.downcast::()?))
+ if let Ok(inner_types) =
+ value
+ .getattr(pyo3::intern!(py, "__args__"))
+ .and_then(|inner_types| {
+ Ok(inner_types.downcast_into::()?)
+ })
{
let inner_types = inner_types
.iter()
diff --git a/pyo3_bindgen_engine/src/typing/into_rs.rs b/pyo3_bindgen_engine/src/typing/into_rs.rs
index bef02ab..d9f27f1 100644
--- a/pyo3_bindgen_engine/src/typing/into_rs.rs
+++ b/pyo3_bindgen_engine/src/typing/into_rs.rs
@@ -34,13 +34,13 @@ impl Type {
.contains("PyAny") =>
{
quote! {
- let #ident = ::pyo3::types::IntoPyDict::into_py_dict(#ident, py);
+ let #ident = ::pyo3::types::IntoPyDict::into_py_dict_bound(#ident, py);
}
}
Self::PyTuple(inner_types) if inner_types.len() < 2 => {
quote! {
let #ident = ::pyo3::IntoPy::<::pyo3::Py<::pyo3::types::PyTuple>>::into_py(#ident, py);
- let #ident = #ident.as_ref(py);
+ let #ident = #ident.bind(py);
}
}
Self::PyAny
@@ -51,14 +51,14 @@ impl Type {
| Self::PyEllipsis => {
quote! {
let #ident = ::pyo3::IntoPy::<::pyo3::Py<::pyo3::types::PyAny>>::into_py(#ident, py);
- let #ident = #ident.as_ref(py);
+ let #ident = #ident.bind(py);
}
}
#[cfg(not(all(not(Py_LIMITED_API), not(PyPy))))]
Self::PyFunction { .. } => {
quote! {
let #ident = ::pyo3::IntoPy::<::pyo3::Py<::pyo3::types::PyAny>>::into_py(#ident, py);
- let #ident = #ident.as_ref(py);
+ let #ident = #ident.bind(py);
}
}
Self::Other(type_name)
@@ -67,7 +67,7 @@ impl Type {
{
quote! {
let #ident = ::pyo3::IntoPy::<::pyo3::Py<::pyo3::types::PyAny>>::into_py(#ident, py);
- let #ident = #ident.as_ref(py);
+ let #ident = #ident.bind(py);
}
}
Self::Optional(inner_type) => match inner_type.as_ref() {
@@ -84,9 +84,9 @@ impl Type {
{
quote! {
let #ident = if let Some(#ident) = #ident {
- ::pyo3::types::IntoPyDict::into_py_dict(#ident, py)
+ ::pyo3::types::IntoPyDict::into_py_dict_bound(#ident, py)
} else {
- ::pyo3::types::PyDict::new(py)
+ ::pyo3::types::PyDict::new_bound(py)
};
}
}
@@ -99,7 +99,7 @@ impl Type {
fn into_rs(self, local_types: &HashMap) -> OutputType {
match self {
Self::PyAny | Self::Unknown => OutputType::new(
- quote!(&'py ::pyo3::types::PyAny),
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyAny>),
quote!(impl ::pyo3::IntoPy<::pyo3::Py<::pyo3::types::PyAny>>),
),
Self::Other(..) => self.map_type(local_types),
@@ -119,14 +119,14 @@ impl Type {
Self::Union(_inner_types) => {
// TODO: Support Rust enums where possible | alternatively, overload functions for each variant
OutputType::new(
- quote!(&'py ::pyo3::types::PyAny),
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyAny>),
quote!(impl ::pyo3::IntoPy<::pyo3::Py<::pyo3::types::PyAny>>),
)
}
Self::PyNone => {
// TODO: Determine if PyNone is even possible
OutputType::new(
- quote!(&'py ::pyo3::types::PyAny),
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyAny>),
quote!(impl ::pyo3::IntoPy<::pyo3::Py<::pyo3::types::PyAny>>),
)
}
@@ -145,7 +145,7 @@ impl Type {
)
} else {
OutputType::new(
- quote!(&'py ::pyo3::types::PyDict),
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyDict>),
quote!(impl ::pyo3::types::IntoPyDict),
)
}
@@ -158,7 +158,10 @@ impl Type {
quote!(&::std::collections::HashSet<#inner_type>),
)
} else {
- OutputType::new_identical(quote!(&'py ::pyo3::types::PyFrozenSet))
+ OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyFrozenSet>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PyFrozenSet>),
+ )
}
}
Self::PyList(inner_type) => {
@@ -173,13 +176,16 @@ impl Type {
quote!(&::std::collections::HashSet<#inner_type>),
)
} else {
- OutputType::new_identical(quote!(&'py ::pyo3::types::PySet))
+ OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PySet>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PySet>),
+ )
}
}
Self::PyTuple(inner_types) => {
if inner_types.len() < 2 {
OutputType::new(
- quote!(&'py ::pyo3::types::PyTuple),
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyTuple>),
quote!(impl ::pyo3::IntoPy<::pyo3::Py<::pyo3::types::PyTuple>>),
)
} else if inner_types.len() == 2
@@ -200,58 +206,101 @@ impl Type {
Self::IpV6Addr => OutputType::new_identical(quote!(::std::net::IpV6Addr)),
Self::Path => OutputType::new(quote!(::std::path::PathBuf), quote!(&::std::path::Path)),
// TODO: Map `PySlice` to `std::ops::Range` if possible
- Self::PySlice => OutputType::new_identical(quote!(&'py ::pyo3::types::PySlice)),
+ Self::PySlice => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PySlice>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PySlice>),
+ ),
// Additional types - num-complex
// TODO: Support conversion of `PyComplex` to `num_complex::Complex` if enabled via `num-complex` feature
- Self::PyComplex => OutputType::new_identical(quote!(&'py ::pyo3::types::PyComplex)),
+ Self::PyComplex => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyComplex>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PyComplex>),
+ ),
// Additional types - datetime
#[cfg(not(Py_LIMITED_API))]
- Self::PyDate => OutputType::new_identical(quote!(&'py ::pyo3::types::PyDate)),
+ Self::PyDate => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyDate>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PyDate>),
+ ),
#[cfg(not(Py_LIMITED_API))]
- Self::PyDateTime => OutputType::new_identical(quote!(&'py ::pyo3::types::PyDateTime)),
+ Self::PyDateTime => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyDateTime>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PyDateTime>),
+ ),
Self::PyDelta => {
// The trait `ToPyObject` is not implemented for `Duration`, so we can't use it here yet
// OutputType::new_identical(quote!(::std::time::Duration))
OutputType::new(
- quote!(&'py ::pyo3::types::PyAny),
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyAny>),
quote!(impl ::pyo3::IntoPy<::pyo3::Py<::pyo3::types::PyAny>>),
)
}
#[cfg(not(Py_LIMITED_API))]
- Self::PyTime => OutputType::new_identical(quote!(&'py ::pyo3::types::PyTime)),
+ Self::PyTime => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyTime>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PyTime>),
+ ),
#[cfg(not(Py_LIMITED_API))]
- Self::PyTzInfo => OutputType::new_identical(quote!(&'py ::pyo3::types::PyTzInfo)),
+ Self::PyTzInfo => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyTzInfo>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PyTzInfo>),
+ ),
// Python-specific types
- Self::PyCapsule => OutputType::new_identical(quote!(&'py ::pyo3::types::PyCapsule)),
- Self::PyCFunction => OutputType::new_identical(quote!(&'py ::pyo3::types::PyCFunction)),
+ Self::PyCapsule => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyCapsule>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PyCapsule>),
+ ),
+ Self::PyCFunction => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyCFunction>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PyCFunction>),
+ ),
#[cfg(not(Py_LIMITED_API))]
- Self::PyCode => OutputType::new_identical(quote!(&'py ::pyo3::types::PyCode)),
+ Self::PyCode => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyCode>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PyCode>),
+ ),
Self::PyEllipsis => {
// TODO: Determine if PyEllipsis is even possible
OutputType::new(
- quote!(&'py ::pyo3::types::PyAny),
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyAny>),
quote!(impl ::pyo3::IntoPy<::pyo3::Py<::pyo3::types::PyAny>>),
)
}
#[cfg(all(not(Py_LIMITED_API), not(PyPy)))]
- Self::PyFrame => OutputType::new_identical(quote!(&'py ::pyo3::types::PyFrame)),
+ Self::PyFrame => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyFrame>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PyFrame>),
+ ),
#[cfg(all(not(Py_LIMITED_API), not(PyPy)))]
- Self::PyFunction { .. } => {
- OutputType::new_identical(quote!(&'py ::pyo3::types::PyFunction))
- }
+ Self::PyFunction { .. } => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyFunction>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PyFunction>),
+ ),
#[cfg(not(all(not(Py_LIMITED_API), not(PyPy))))]
Self::PyFunction { .. } => OutputType::new(
- quote!(&'py ::pyo3::types::PyAny),
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyAny>),
quote!(impl ::pyo3::IntoPy<::pyo3::Py<::pyo3::types::PyAny>>),
),
- Self::PyModule => OutputType::new_identical(quote!(&'py ::pyo3::types::PyModule)),
+ Self::PyModule => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyModule>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PyModule>),
+ ),
#[cfg(not(PyPy))]
- Self::PySuper => OutputType::new_identical(quote!(&'py ::pyo3::types::PySuper)),
- Self::PyTraceback => OutputType::new_identical(quote!(&'py ::pyo3::types::PyTraceback)),
- Self::PyType => OutputType::new_identical(quote!(&'py ::pyo3::types::PyType)),
+ Self::PySuper => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PySuper>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PySuper>),
+ ),
+ Self::PyTraceback => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyTraceback>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PyTraceback>),
+ ),
+ Self::PyType => OutputType::new(
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyType>),
+ quote!(&::pyo3::Bound<'py, ::pyo3::types::PyType>),
+ ),
}
}
@@ -267,21 +316,55 @@ impl Type {
}
// Try to map the local types
- if let Some(relative_path) = local_types.get(&Path::from_py(&type_name)) {
+ let type_name_without_delimiters =
+ type_name.split_once('[').map(|s| s.0).unwrap_or(&type_name);
+ if let Some(relative_path) = local_types.get(&Path::from_py(type_name_without_delimiters)) {
let relative_path: syn::Path = relative_path.try_into().unwrap();
- return OutputType::new_identical(quote!(&'py #relative_path));
+ return OutputType::new(
+ quote!(::pyo3::Bound<'py, #relative_path>),
+ quote!(&::pyo3::Bound<'py, #relative_path>),
+ );
}
// Unhandled types
OutputType::new(
- quote!(&'py ::pyo3::types::PyAny),
+ quote!(::pyo3::Bound<'py, ::pyo3::types::PyAny>),
quote!(impl ::pyo3::IntoPy<::pyo3::Py<::pyo3::types::PyAny>>),
)
}
- fn try_map_external_type(_type_name: &str) -> Option {
- // TODO: Handle types from other packages with Rust bindings here (e.g. NumPy)
- None
+ fn try_map_external_type(type_name: &str) -> Option {
+ // TODO: Handle types from other packages with Rust bindings here
+ match type_name {
+ #[cfg(feature = "numpy")]
+ numpy_ndarray
+ if numpy_ndarray
+ .split_once('[')
+ .map(|s| s.0)
+ .unwrap_or(numpy_ndarray)
+ .split('.')
+ .last()
+ .unwrap_or(numpy_ndarray)
+ .to_lowercase()
+ == "ndarray" =>
+ {
+ Some(OutputType::new(
+ quote!(
+ ::pyo3::Bound<
+ 'py,
+ ::numpy::PyArray<::pyo3::Py<::pyo3::types::PyAny>, ::numpy::IxDyn>,
+ >
+ ),
+ quote!(
+ &::pyo3::Bound<
+ 'py,
+ ::numpy::PyArray<::pyo3::Py<::pyo3::types::PyAny>, ::numpy::IxDyn>,
+ >
+ ),
+ ))
+ }
+ _ => None,
+ }
}
}
@@ -292,10 +375,10 @@ struct OutputType {
}
impl OutputType {
- fn new(own: proc_macro2::TokenStream, bor: proc_macro2::TokenStream) -> Self {
+ fn new(owned: proc_macro2::TokenStream, borrowed: proc_macro2::TokenStream) -> Self {
Self {
- owned: Rc::new(own),
- borrowed: Rc::new(bor),
+ owned: Rc::new(owned),
+ borrowed: Rc::new(borrowed),
}
}
diff --git a/pyo3_bindgen_engine/src/utils/error.rs b/pyo3_bindgen_engine/src/utils/error.rs
index d9b4d84..de08d2d 100644
--- a/pyo3_bindgen_engine/src/utils/error.rs
+++ b/pyo3_bindgen_engine/src/utils/error.rs
@@ -13,6 +13,8 @@ pub enum PyBindgenError {
ParseError(String),
#[error("Failed to generate Rust code: {0}")]
CodegenError(String),
+ #[error(transparent)]
+ Infallible(#[from] std::convert::Infallible),
}
impl From> for PyBindgenError {
diff --git a/pyo3_bindgen_engine/src/utils/io.rs b/pyo3_bindgen_engine/src/utils/io.rs
index 06f898f..a00b1b4 100644
--- a/pyo3_bindgen_engine/src/utils/io.rs
+++ b/pyo3_bindgen_engine/src/utils/io.rs
@@ -1,4 +1,5 @@
use crate::Result;
+use pyo3::prelude::*;
pub fn with_suppressed_python_output(
py: pyo3::Python,
@@ -11,7 +12,7 @@ pub fn with_suppressed_python_output(
return f();
}
- let sys = py.import(pyo3::intern!(py, "sys"))?;
+ let sys = py.import_bound(pyo3::intern!(py, "sys"))?;
let stdout_ident = pyo3::intern!(py, "stdout");
let stderr_ident = pyo3::intern!(py, "stderr");
@@ -20,12 +21,12 @@ pub fn with_suppressed_python_output(
let original_stderr = sys.getattr(stderr_ident)?;
// Suppress the output
- let supressed_output = py.eval(r"lambda: type('SupressedOutput', (), {'write': lambda self, x: None, 'flush': lambda self: None})", None, None)?;
+ let supressed_output = py.eval_bound(r"lambda: type('SupressedOutput', (), {'write': lambda self, x: None, 'flush': lambda self: None})", None, None)?;
if suppress_stdout {
- sys.setattr(stdout_ident, supressed_output)?;
+ sys.setattr(stdout_ident, &supressed_output)?;
}
if suppress_stderr {
- sys.setattr(stderr_ident, supressed_output)?;
+ sys.setattr(stderr_ident, &supressed_output)?;
}
// Run the function
diff --git a/pyo3_bindgen_engine/tests/bindgen.rs b/pyo3_bindgen_engine/tests/bindgen.rs
index 4eb6c15..8ab3dd3 100644
--- a/pyo3_bindgen_engine/tests/bindgen.rs
+++ b/pyo3_bindgen_engine/tests/bindgen.rs
@@ -37,34 +37,59 @@ test_bindgen! {
bindgen_property
py: r#"
- my_property: float = 0.42
+ my_property: float = 0.42
"#
rs: r#"
- #[allow(
- clippy::all,
- clippy::nursery,
- clippy::pedantic,
- non_camel_case_types,
- non_snake_case,
- non_upper_case_globals,
- unused
- )]
- pub mod mod_bindgen_property {
- pub fn my_property<'py>(py: ::pyo3::marker::Python<'py>) -> ::pyo3::PyResult {
- ::pyo3::FromPyObject::extract(
- py.import(::pyo3::intern!(py, "mod_bindgen_property"))?
- .getattr(::pyo3::intern!(py, "my_property"))?,
- )
- }
- pub fn set_my_property<'py>(
- py: ::pyo3::marker::Python<'py>,
- p_value: f64,
- ) -> ::pyo3::PyResult<()> {
- py.import(::pyo3::intern!(py, "mod_bindgen_property"))?
- .setattr(::pyo3::intern!(py, "my_property"), p_value)
- }
+ #[allow(
+ clippy::all,
+ clippy::nursery,
+ clippy::pedantic,
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unused
+ )]
+ pub mod mod_bindgen_property {
+ /// Embed the Python source code of the module into the Python interpreter
+ /// in order to enable the use of the generated Rust bindings.
+ pub fn pyo3_embed_python_source_code<'py>(
+ py: ::pyo3::marker::Python<'py>,
+ ) -> ::pyo3::PyResult<()> {
+ const SOURCE_CODE: &str = "my_property: float = 0.42\n";
+ pyo3::types::PyAnyMethods::set_item(
+ &pyo3::types::PyAnyMethods::getattr(
+ py.import_bound(pyo3::intern!(py, "sys"))?.as_any(),
+ pyo3::intern!(py, "modules"),
+ )?,
+ "mod_bindgen_property",
+ pyo3::types::PyModule::from_code_bound(
+ py,
+ SOURCE_CODE,
+ "mod_bindgen_property/__init__.py",
+ "mod_bindgen_property",
+ )?,
+ )
}
+ pub fn my_property<'py>(py: ::pyo3::marker::Python<'py>) -> ::pyo3::PyResult {
+ ::pyo3::types::PyAnyMethods::extract(
+ &::pyo3::types::PyAnyMethods::getattr(
+ py.import_bound(::pyo3::intern!(py, "mod_bindgen_property"))?.as_any(),
+ ::pyo3::intern!(py, "my_property"),
+ )?,
+ )
+ }
+ pub fn set_my_property<'py>(
+ py: ::pyo3::marker::Python<'py>,
+ p_value: f64,
+ ) -> ::pyo3::PyResult<()> {
+ ::pyo3::types::PyAnyMethods::setattr(
+ py.import_bound(::pyo3::intern!(py, "mod_bindgen_property"))?.as_any(),
+ ::pyo3::intern!(py, "my_property"),
+ p_value,
+ )
+ }
+ }
"#
}
@@ -72,39 +97,59 @@ test_bindgen! {
bindgen_function
py: r#"
- def my_function(my_arg1: str) -> int:
- """My docstring for `my_function`"""
- ...
+ def my_function(my_arg1: str) -> int:
+ """My docstring for `my_function`"""
+ ...
"#
rs: r#"
- #[allow(
- clippy::all,
- clippy::nursery,
- clippy::pedantic,
- non_camel_case_types,
- non_snake_case,
- non_upper_case_globals,
- unused
- )]
- pub mod mod_bindgen_function {
- /// My docstring for `my_function`
- pub fn my_function<'py>(
- py: ::pyo3::marker::Python<'py>,
- p_my_arg1: &str,
- ) -> ::pyo3::PyResult {
- ::pyo3::FromPyObject::extract(
- py.import(::pyo3::intern!(py, "mod_bindgen_function"))?
- .call_method1(
- ::pyo3::intern!(py, "my_function"),
- ::pyo3::types::PyTuple::new(
- py,
- [::pyo3::ToPyObject::to_object(&p_my_arg1, py)],
- ),
- )?,
- )
- }
+ #[allow(
+ clippy::all,
+ clippy::nursery,
+ clippy::pedantic,
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unused
+ )]
+ pub mod mod_bindgen_function {
+ /// Embed the Python source code of the module into the Python interpreter
+ /// in order to enable the use of the generated Rust bindings.
+ pub fn pyo3_embed_python_source_code<'py>(
+ py: ::pyo3::marker::Python<'py>,
+ ) -> ::pyo3::PyResult<()> {
+ const SOURCE_CODE: &str = "def my_function(my_arg1: str) -> int:\n \"\"\"My docstring for `my_function`\"\"\"\n ...\n";
+ pyo3::types::PyAnyMethods::set_item(
+ &pyo3::types::PyAnyMethods::getattr(
+ py.import_bound(pyo3::intern!(py, "sys"))?.as_any(),
+ pyo3::intern!(py, "modules"),
+ )?,
+ "mod_bindgen_function",
+ pyo3::types::PyModule::from_code_bound(
+ py,
+ SOURCE_CODE,
+ "mod_bindgen_function/__init__.py",
+ "mod_bindgen_function",
+ )?,
+ )
+ }
+ /// My docstring for `my_function`
+ pub fn my_function<'py>(
+ py: ::pyo3::marker::Python<'py>,
+ p_my_arg1: &str,
+ ) -> ::pyo3::PyResult {
+ ::pyo3::types::PyAnyMethods::extract(
+ &::pyo3::types::PyAnyMethods::call_method1(
+ py.import_bound(::pyo3::intern!(py, "mod_bindgen_function"))?.as_any(),
+ ::pyo3::intern!(py, "my_function"),
+ ::pyo3::types::PyTuple::new_bound(
+ py,
+ [::pyo3::ToPyObject::to_object(&p_my_arg1, py)],
+ ),
+ )?,
+ )
}
+ }
"#
}
@@ -112,127 +157,179 @@ test_bindgen! {
bindgen_class
py: r#"
- from typing import Dict, Optional
- class MyClass:
- """My docstring for `MyClass`"""
- def __init__(self, my_arg1: str, my_arg2: Optional[int] = None):
- """My docstring for __init__"""
- ...
- def my_method(self, my_arg1: Dict[str, int], **kwargs):
- """My docstring for `my_method`"""
- ...
- @property
- def my_property(self) -> int:
- ...
- @my_property.setter
- def my_property(self, value: int):
- ...
-
- def my_function_with_class_param(my_arg1: MyClass):
+ from typing import Dict, Optional
+ class MyClass:
+ """My docstring for `MyClass`"""
+ def __init__(self, my_arg1: str, my_arg2: Optional[int] = None):
+ """My docstring for __init__"""
...
-
- def my_function_with_class_return() -> MyClass:
+ def my_method(self, my_arg1: Dict[str, int], **kwargs):
+ """My docstring for `my_method`"""
+ ...
+ @property
+ def my_property(self) -> int:
+ ...
+ @my_property.setter
+ def my_property(self, value: int):
...
+
+ def my_function_with_class_param(my_arg1: MyClass):
+ ...
+
+ def my_function_with_class_return() -> MyClass:
+ ...
"#
rs: r#"
- #[allow(
- clippy::all,
- clippy::nursery,
- clippy::pedantic,
- non_camel_case_types,
- non_snake_case,
- non_upper_case_globals,
- unused
- )]
- pub mod mod_bindgen_class {
- /// My docstring for `MyClass`
- #[repr(transparent)]
- pub struct MyClass(::pyo3::PyAny);
- ::pyo3::pyobject_native_type_named!(MyClass);
- ::pyo3::pyobject_native_type_info!(
- MyClass,
- ::pyo3::pyobject_native_static_type_object!(::pyo3::ffi::PyBaseObject_Type),
- ::std::option::Option::Some("mod_bindgen_class.MyClass")
- );
- ::pyo3::pyobject_native_type_extract!(MyClass);
- #[automatically_derived]
- impl MyClass {
- /// My docstring for __init__
- pub fn new<'py>(
- py: ::pyo3::marker::Python<'py>,
- p_my_arg1: &str,
- p_my_arg2: ::std::option::Option,
- ) -> ::pyo3::PyResult<&'py Self> {
- ::pyo3::FromPyObject::extract(
- py.import(::pyo3::intern!(py, "mod_bindgen_class"))?
- .getattr(::pyo3::intern!(py, "MyClass"))?
- .call1(::pyo3::types::PyTuple::new(
- py,
- [
- ::pyo3::ToPyObject::to_object(&p_my_arg1, py),
- ::pyo3::ToPyObject::to_object(&p_my_arg2, py),
- ],
- ))?,
- )
- }
- /// My docstring for `my_method`
- pub fn my_method<'py>(
- &'py self,
- py: ::pyo3::marker::Python<'py>,
- p_my_arg1: &::std::collections::HashMap<::std::string::String, i64>,
- p_kwargs: ::std::option::Option<&'py ::pyo3::types::PyDict>,
- ) -> ::pyo3::PyResult<&'py ::pyo3::types::PyAny> {
- let p_kwargs = if let Some(p_kwargs) = p_kwargs {
- ::pyo3::types::IntoPyDict::into_py_dict(p_kwargs, py)
- } else {
- ::pyo3::types::PyDict::new(py)
- };
- ::pyo3::FromPyObject::extract(self.0.call_method(
+ #[allow(
+ clippy::all,
+ clippy::nursery,
+ clippy::pedantic,
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unused
+ )]
+ pub mod mod_bindgen_class {
+ /// Embed the Python source code of the module into the Python interpreter
+ /// in order to enable the use of the generated Rust bindings.
+ pub fn pyo3_embed_python_source_code<'py>(
+ py: ::pyo3::marker::Python<'py>,
+ ) -> ::pyo3::PyResult<()> {
+ const SOURCE_CODE: &str = "from typing import Dict, Optional\nclass MyClass:\n \"\"\"My docstring for `MyClass`\"\"\"\n def __init__(self, my_arg1: str, my_arg2: Optional[int] = None):\n \"\"\"My docstring for __init__\"\"\"\n ...\n def my_method(self, my_arg1: Dict[str, int], **kwargs):\n \"\"\"My docstring for `my_method`\"\"\"\n ...\n @property\n def my_property(self) -> int:\n ...\n @my_property.setter\n def my_property(self, value: int):\n ...\n\ndef my_function_with_class_param(my_arg1: MyClass):\n ...\n\ndef my_function_with_class_return() -> MyClass:\n ...\n";
+ pyo3::types::PyAnyMethods::set_item(
+ &pyo3::types::PyAnyMethods::getattr(
+ py.import_bound(pyo3::intern!(py, "sys"))?.as_any(),
+ pyo3::intern!(py, "modules"),
+ )?,
+ "mod_bindgen_class",
+ pyo3::types::PyModule::from_code_bound(
+ py,
+ SOURCE_CODE,
+ "mod_bindgen_class/__init__.py",
+ "mod_bindgen_class",
+ )?,
+ )
+ }
+ /// My docstring for `MyClass`
+ #[repr(transparent)]
+ pub struct MyClass(::pyo3::PyAny);
+ ::pyo3::pyobject_native_type_named!(MyClass);
+ ::pyo3::pyobject_native_type_info!(
+ MyClass,
+ ::pyo3::pyobject_native_static_type_object!(::pyo3::ffi::PyBaseObject_Type),
+ ::std::option::Option::Some("mod_bindgen_class.MyClass")
+ );
+ #[automatically_derived]
+ impl MyClass {
+ /// My docstring for __init__
+ pub fn new<'py>(
+ py: ::pyo3::marker::Python<'py>,
+ p_my_arg1: &str,
+ p_my_arg2: ::std::option::Option,
+ ) -> ::pyo3::PyResult<::pyo3::Bound<'py, Self>> {
+ ::pyo3::types::PyAnyMethods::extract(
+ &::pyo3::types::PyAnyMethods::call1(
+ ::pyo3::types::PyAnyMethods::getattr(
+ py
+ .import_bound(::pyo3::intern!(py, "mod_bindgen_class"))?
+ .as_any(),
+ ::pyo3::intern!(py, "MyClass"),
+ )?
+ .as_any(),
+ ::pyo3::types::PyTuple::new_bound(
+ py,
+ [
+ ::pyo3::ToPyObject::to_object(&p_my_arg1, py),
+ ::pyo3::ToPyObject::to_object(&p_my_arg2, py),
+ ],
+ ),
+ )?,
+ )
+ }
+ }
+ /// These methods are defined for the `Bound<'py, T>` smart pointer, so to use
+ /// method call syntax these methods are separated into a trait, because stable
+ /// Rust does not yet support `arbitrary_self_types`.
+ #[doc(alias = "MyClass")]
+ #[automatically_derived]
+ pub trait MyClassMethods {
+ fn my_method<'py>(
+ &'py self,
+ p_my_arg1: &::std::collections::HashMap<::std::string::String, i64>,
+ p_kwargs: ::std::option::Option<::pyo3::Bound<'py, ::pyo3::types::PyDict>>,
+ ) -> ::pyo3::PyResult<::pyo3::Bound<'py, ::pyo3::types::PyAny>>;
+ fn my_property<'py>(&'py self) -> ::pyo3::PyResult;
+ fn set_my_property<'py>(&'py self, p_value: i64) -> ::pyo3::PyResult<()>;
+ }
+ #[automatically_derived]
+ impl MyClassMethods for ::pyo3::Bound<'_, MyClass> {
+ /// My docstring for `my_method`
+ fn my_method<'py>(
+ &'py self,
+ p_my_arg1: &::std::collections::HashMap<::std::string::String, i64>,
+ p_kwargs: ::std::option::Option<::pyo3::Bound<'py, ::pyo3::types::PyDict>>,
+ ) -> ::pyo3::PyResult<::pyo3::Bound<'py, ::pyo3::types::PyAny>> {
+ let py = self.py();
+ let p_kwargs = if let Some(p_kwargs) = p_kwargs {
+ ::pyo3::types::IntoPyDict::into_py_dict_bound(p_kwargs, py)
+ } else {
+ ::pyo3::types::PyDict::new_bound(py)
+ };
+ ::pyo3::types::PyAnyMethods::extract(
+ &::pyo3::types::PyAnyMethods::call_method(
+ self.as_any(),
::pyo3::intern!(py, "my_method"),
- ::pyo3::types::PyTuple::new(py, [::pyo3::ToPyObject::to_object(&p_my_arg1, py)]),
- Some(p_kwargs),
- )?)
- }
- pub fn my_property<'py>(
- &'py self,
- py: ::pyo3::marker::Python<'py>,
- ) -> ::pyo3::PyResult {
- self.0
- .getattr(::pyo3::intern!(py, "my_property"))?
- .extract()
- }
- pub fn set_my_property<'py>(
- &'py self,
- py: ::pyo3::marker::Python<'py>,
- p_value: i64,
- ) -> ::pyo3::PyResult<()> {
- self.0.setattr(::pyo3::intern!(py, "my_property"), p_value)
- }
+ ::pyo3::types::PyTuple::new_bound(
+ py,
+ [::pyo3::ToPyObject::to_object(&p_my_arg1, py)],
+ ),
+ Some(&p_kwargs),
+ )?,
+ )
}
- pub fn my_function_with_class_param<'py>(
- py: ::pyo3::marker::Python<'py>,
- p_my_arg1: &'py MyClass,
- ) -> ::pyo3::PyResult<&'py ::pyo3::types::PyAny> {
- ::pyo3::FromPyObject::extract(
- py.import(::pyo3::intern!(py, "mod_bindgen_class"))?
- .call_method1(
- ::pyo3::intern!(py, "my_function_with_class_param"),
- ::pyo3::types::PyTuple::new(
- py,
- [::pyo3::ToPyObject::to_object(&p_my_arg1, py)],
- ),
- )?,
+ fn my_property<'py>(&'py self) -> ::pyo3::PyResult {
+ ::pyo3::types::PyAnyMethods::extract(
+ &::pyo3::types::PyAnyMethods::getattr(
+ self.as_any(),
+ ::pyo3::intern!(self.py(), "my_property"),
+ )?,
)
}
- pub fn my_function_with_class_return<'py>(
- py: ::pyo3::marker::Python<'py>,
- ) -> ::pyo3::PyResult<&'py MyClass> {
- ::pyo3::FromPyObject::extract(
- py.import(::pyo3::intern!(py, "mod_bindgen_class"))?
- .call_method0(::pyo3::intern!(py, "my_function_with_class_return"))?,
+ fn set_my_property<'py>(&'py self, p_value: i64) -> ::pyo3::PyResult<()> {
+ let py = self.py();
+ ::pyo3::types::PyAnyMethods::setattr(
+ self.as_any(),
+ ::pyo3::intern!(py, "my_property"),
+ p_value,
)
}
}
+ pub fn my_function_with_class_param<'py>(
+ py: ::pyo3::marker::Python<'py>,
+ p_my_arg1: &::pyo3::Bound<'py, MyClass>,
+ ) -> ::pyo3::PyResult<::pyo3::Bound<'py, ::pyo3::types::PyAny>> {
+ ::pyo3::types::PyAnyMethods::extract(
+ &::pyo3::types::PyAnyMethods::call_method1(
+ py.import_bound(::pyo3::intern!(py, "mod_bindgen_class"))?.as_any(),
+ ::pyo3::intern!(py, "my_function_with_class_param"),
+ ::pyo3::types::PyTuple::new_bound(
+ py,
+ [::pyo3::ToPyObject::to_object(&p_my_arg1, py)],
+ ),
+ )?,
+ )
+ }
+ pub fn my_function_with_class_return<'py>(
+ py: ::pyo3::marker::Python<'py>,
+ ) -> ::pyo3::PyResult<::pyo3::Bound<'py, MyClass>> {
+ ::pyo3::types::PyAnyMethods::extract(
+ &::pyo3::types::PyAnyMethods::call_method0(
+ py.import_bound(::pyo3::intern!(py, "mod_bindgen_class"))?.as_any(),
+ ::pyo3::intern!(py, "my_function_with_class_return"),
+ )?,
+ )
+ }
+ }
"#
}
diff --git a/pyo3_bindgen_macros/src/utils.rs b/pyo3_bindgen_macros/src/utils.rs
index 16da82e..140946d 100644
--- a/pyo3_bindgen_macros/src/utils.rs
+++ b/pyo3_bindgen_macros/src/utils.rs
@@ -1,3 +1,5 @@
+use pyo3::prelude::*;
+
/// Ensure that the symbols of the libpython shared library are loaded globally.
///
/// # Explanation
@@ -19,7 +21,7 @@ pub fn try_load_libpython_symbols() -> pyo3::PyResult<()> {
pyo3::prepare_freethreaded_python();
pyo3::Python::with_gil(|py| {
let fn_get_config_var = py
- .import(pyo3::intern!(py, "sysconfig"))?
+ .import_bound(pyo3::intern!(py, "sysconfig"))?
.getattr(pyo3::intern!(py, "get_config_var"))?;
let libpython_dir = fn_get_config_var.call1(("LIBDIR",))?.to_string();
let libpython_so_name = fn_get_config_var.call1(("INSTSONAME",))?.to_string();