protobuf-py-ext 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,373 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "bitvec"
7
+ version = "1.1.1"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837"
10
+ dependencies = [
11
+ "funty",
12
+ "radium",
13
+ "tap",
14
+ "wyz",
15
+ ]
16
+
17
+ [[package]]
18
+ name = "borsh"
19
+ version = "1.7.0"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "2f3f6da4992df95bbcd9af42a6c7dcb994498fc9048230405f3b36ff7cd3f145"
22
+ dependencies = [
23
+ "bytes",
24
+ "cfg_aliases",
25
+ ]
26
+
27
+ [[package]]
28
+ name = "buffa"
29
+ version = "0.7.1"
30
+ source = "registry+https://github.com/rust-lang/crates.io-index"
31
+ checksum = "ec95898e2ef31d6266042f21c398fa5ff8334d14d6b2ac5fb38b55448d94f595"
32
+ dependencies = [
33
+ "bytes",
34
+ "compact_str",
35
+ "ecow",
36
+ "hashbrown",
37
+ "once_cell",
38
+ "serde",
39
+ "serde_json",
40
+ "smol_str",
41
+ "thiserror",
42
+ ]
43
+
44
+ [[package]]
45
+ name = "bytes"
46
+ version = "1.12.0"
47
+ source = "registry+https://github.com/rust-lang/crates.io-index"
48
+ checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593"
49
+
50
+ [[package]]
51
+ name = "castaway"
52
+ version = "0.2.4"
53
+ source = "registry+https://github.com/rust-lang/crates.io-index"
54
+ checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
55
+ dependencies = [
56
+ "rustversion",
57
+ ]
58
+
59
+ [[package]]
60
+ name = "cfg-if"
61
+ version = "1.0.4"
62
+ source = "registry+https://github.com/rust-lang/crates.io-index"
63
+ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
64
+
65
+ [[package]]
66
+ name = "cfg_aliases"
67
+ version = "0.2.1"
68
+ source = "registry+https://github.com/rust-lang/crates.io-index"
69
+ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
70
+
71
+ [[package]]
72
+ name = "compact_str"
73
+ version = "0.9.1"
74
+ source = "registry+https://github.com/rust-lang/crates.io-index"
75
+ checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab"
76
+ dependencies = [
77
+ "castaway",
78
+ "cfg-if",
79
+ "itoa",
80
+ "rustversion",
81
+ "ryu",
82
+ "static_assertions",
83
+ ]
84
+
85
+ [[package]]
86
+ name = "ecow"
87
+ version = "0.2.6"
88
+ source = "registry+https://github.com/rust-lang/crates.io-index"
89
+ checksum = "78e4f79b296fbaab6ce2e22d52cb4c7f010fe0ebe7a32e34fa25885fd797bd02"
90
+
91
+ [[package]]
92
+ name = "foldhash"
93
+ version = "0.1.5"
94
+ source = "registry+https://github.com/rust-lang/crates.io-index"
95
+ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
96
+
97
+ [[package]]
98
+ name = "funty"
99
+ version = "2.0.0"
100
+ source = "registry+https://github.com/rust-lang/crates.io-index"
101
+ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
102
+
103
+ [[package]]
104
+ name = "hashbrown"
105
+ version = "0.15.5"
106
+ source = "registry+https://github.com/rust-lang/crates.io-index"
107
+ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
108
+ dependencies = [
109
+ "foldhash",
110
+ ]
111
+
112
+ [[package]]
113
+ name = "heck"
114
+ version = "0.5.0"
115
+ source = "registry+https://github.com/rust-lang/crates.io-index"
116
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
117
+
118
+ [[package]]
119
+ name = "itoa"
120
+ version = "1.0.18"
121
+ source = "registry+https://github.com/rust-lang/crates.io-index"
122
+ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
123
+
124
+ [[package]]
125
+ name = "libc"
126
+ version = "0.2.186"
127
+ source = "registry+https://github.com/rust-lang/crates.io-index"
128
+ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
129
+
130
+ [[package]]
131
+ name = "memchr"
132
+ version = "2.8.2"
133
+ source = "registry+https://github.com/rust-lang/crates.io-index"
134
+ checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
135
+
136
+ [[package]]
137
+ name = "once_cell"
138
+ version = "1.21.4"
139
+ source = "registry+https://github.com/rust-lang/crates.io-index"
140
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
141
+
142
+ [[package]]
143
+ name = "portable-atomic"
144
+ version = "1.13.1"
145
+ source = "registry+https://github.com/rust-lang/crates.io-index"
146
+ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
147
+
148
+ [[package]]
149
+ name = "proc-macro2"
150
+ version = "1.0.106"
151
+ source = "registry+https://github.com/rust-lang/crates.io-index"
152
+ checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
153
+ dependencies = [
154
+ "unicode-ident",
155
+ ]
156
+
157
+ [[package]]
158
+ name = "protobuf-py-ext"
159
+ version = "0.1.0"
160
+ dependencies = [
161
+ "bitvec",
162
+ "buffa",
163
+ "bytes",
164
+ "pyo3",
165
+ "pyo3-build-config",
166
+ ]
167
+
168
+ [[package]]
169
+ name = "pyo3"
170
+ version = "0.29.0"
171
+ source = "registry+https://github.com/rust-lang/crates.io-index"
172
+ checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c"
173
+ dependencies = [
174
+ "bytes",
175
+ "libc",
176
+ "once_cell",
177
+ "portable-atomic",
178
+ "pyo3-build-config",
179
+ "pyo3-ffi",
180
+ "pyo3-macros",
181
+ ]
182
+
183
+ [[package]]
184
+ name = "pyo3-build-config"
185
+ version = "0.29.0"
186
+ source = "registry+https://github.com/rust-lang/crates.io-index"
187
+ checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078"
188
+ dependencies = [
189
+ "target-lexicon",
190
+ ]
191
+
192
+ [[package]]
193
+ name = "pyo3-ffi"
194
+ version = "0.29.0"
195
+ source = "registry+https://github.com/rust-lang/crates.io-index"
196
+ checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b"
197
+ dependencies = [
198
+ "libc",
199
+ "pyo3-build-config",
200
+ ]
201
+
202
+ [[package]]
203
+ name = "pyo3-macros"
204
+ version = "0.29.0"
205
+ source = "registry+https://github.com/rust-lang/crates.io-index"
206
+ checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771"
207
+ dependencies = [
208
+ "proc-macro2",
209
+ "pyo3-macros-backend",
210
+ "quote",
211
+ "syn",
212
+ ]
213
+
214
+ [[package]]
215
+ name = "pyo3-macros-backend"
216
+ version = "0.29.0"
217
+ source = "registry+https://github.com/rust-lang/crates.io-index"
218
+ checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362"
219
+ dependencies = [
220
+ "heck",
221
+ "proc-macro2",
222
+ "quote",
223
+ "syn",
224
+ ]
225
+
226
+ [[package]]
227
+ name = "quote"
228
+ version = "1.0.45"
229
+ source = "registry+https://github.com/rust-lang/crates.io-index"
230
+ checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
231
+ dependencies = [
232
+ "proc-macro2",
233
+ ]
234
+
235
+ [[package]]
236
+ name = "radium"
237
+ version = "0.7.0"
238
+ source = "registry+https://github.com/rust-lang/crates.io-index"
239
+ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
240
+
241
+ [[package]]
242
+ name = "rustversion"
243
+ version = "1.0.22"
244
+ source = "registry+https://github.com/rust-lang/crates.io-index"
245
+ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
246
+
247
+ [[package]]
248
+ name = "ryu"
249
+ version = "1.0.23"
250
+ source = "registry+https://github.com/rust-lang/crates.io-index"
251
+ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
252
+
253
+ [[package]]
254
+ name = "serde"
255
+ version = "1.0.228"
256
+ source = "registry+https://github.com/rust-lang/crates.io-index"
257
+ checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
258
+ dependencies = [
259
+ "serde_core",
260
+ ]
261
+
262
+ [[package]]
263
+ name = "serde_core"
264
+ version = "1.0.228"
265
+ source = "registry+https://github.com/rust-lang/crates.io-index"
266
+ checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
267
+ dependencies = [
268
+ "serde_derive",
269
+ ]
270
+
271
+ [[package]]
272
+ name = "serde_derive"
273
+ version = "1.0.228"
274
+ source = "registry+https://github.com/rust-lang/crates.io-index"
275
+ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
276
+ dependencies = [
277
+ "proc-macro2",
278
+ "quote",
279
+ "syn",
280
+ ]
281
+
282
+ [[package]]
283
+ name = "serde_json"
284
+ version = "1.0.150"
285
+ source = "registry+https://github.com/rust-lang/crates.io-index"
286
+ checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
287
+ dependencies = [
288
+ "itoa",
289
+ "memchr",
290
+ "serde",
291
+ "serde_core",
292
+ "zmij",
293
+ ]
294
+
295
+ [[package]]
296
+ name = "smol_str"
297
+ version = "0.3.2"
298
+ source = "registry+https://github.com/rust-lang/crates.io-index"
299
+ checksum = "9676b89cd56310a87b93dec47b11af744f34d5fc9f367b829474eec0a891350d"
300
+ dependencies = [
301
+ "borsh",
302
+ "serde",
303
+ ]
304
+
305
+ [[package]]
306
+ name = "static_assertions"
307
+ version = "1.1.0"
308
+ source = "registry+https://github.com/rust-lang/crates.io-index"
309
+ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
310
+
311
+ [[package]]
312
+ name = "syn"
313
+ version = "2.0.118"
314
+ source = "registry+https://github.com/rust-lang/crates.io-index"
315
+ checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
316
+ dependencies = [
317
+ "proc-macro2",
318
+ "quote",
319
+ "unicode-ident",
320
+ ]
321
+
322
+ [[package]]
323
+ name = "tap"
324
+ version = "1.0.1"
325
+ source = "registry+https://github.com/rust-lang/crates.io-index"
326
+ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
327
+
328
+ [[package]]
329
+ name = "target-lexicon"
330
+ version = "0.13.5"
331
+ source = "registry+https://github.com/rust-lang/crates.io-index"
332
+ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
333
+
334
+ [[package]]
335
+ name = "thiserror"
336
+ version = "2.0.18"
337
+ source = "registry+https://github.com/rust-lang/crates.io-index"
338
+ checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
339
+ dependencies = [
340
+ "thiserror-impl",
341
+ ]
342
+
343
+ [[package]]
344
+ name = "thiserror-impl"
345
+ version = "2.0.18"
346
+ source = "registry+https://github.com/rust-lang/crates.io-index"
347
+ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
348
+ dependencies = [
349
+ "proc-macro2",
350
+ "quote",
351
+ "syn",
352
+ ]
353
+
354
+ [[package]]
355
+ name = "unicode-ident"
356
+ version = "1.0.24"
357
+ source = "registry+https://github.com/rust-lang/crates.io-index"
358
+ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
359
+
360
+ [[package]]
361
+ name = "wyz"
362
+ version = "0.5.1"
363
+ source = "registry+https://github.com/rust-lang/crates.io-index"
364
+ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
365
+ dependencies = [
366
+ "tap",
367
+ ]
368
+
369
+ [[package]]
370
+ name = "zmij"
371
+ version = "1.0.21"
372
+ source = "registry+https://github.com/rust-lang/crates.io-index"
373
+ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
@@ -0,0 +1,32 @@
1
+ [package]
2
+ name = "protobuf-py-ext"
3
+ version = "0.1.0"
4
+ edition = "2024"
5
+ readme = "README.md"
6
+
7
+ [lib]
8
+ crate-type = ["cdylib"]
9
+ name = "protobuf_ext"
10
+
11
+ [dependencies]
12
+ bitvec = "1.1.1"
13
+ buffa = "0.7.1"
14
+ bytes = "1.12.0"
15
+ pyo3 = { version = "0.29.0", features = ["bytes"] }
16
+
17
+ [build-dependencies]
18
+ pyo3-build-config = { version = "0.29.0", features = ["resolve-config"] }
19
+
20
+ [lints.clippy]
21
+ pedantic = { level = "deny", priority = -1 }
22
+
23
+ fn_params_excessive_bools = "allow"
24
+ struct_excessive_bools = "allow"
25
+ too_many_arguments = "allow"
26
+ unused_trait_names = "deny"
27
+
28
+ [profile.release]
29
+ debug = 0
30
+ strip = true
31
+ lto = true
32
+ codegen-units = 1
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: protobuf-py-ext
3
+ Version: 0.0.2
4
+ Classifier: Programming Language :: Python :: Implementation :: CPython
5
+ Classifier: Programming Language :: Rust
6
+ Summary: Native extension for protobuf-py
7
+ Requires-Python: >=3.10
8
+ Project-URL: Documentation, https://protobufpython.com/
9
+ Project-URL: Homepage, https://github.com/bufbuild/protobuf-py
10
+ Project-URL: Issues, https://github.com/bufbuild/protobuf-py/issues
11
+ Project-URL: Source, https://github.com/bufbuild/protobuf-py
@@ -0,0 +1,4 @@
1
+ # protobuf-py-ext
2
+
3
+ The native Python extension for accelerating heavyweight operations on Protobuf messages, written in Rust.
4
+ See [protobuf-py](https://github.com/bufbuild/protobuf-py) for more details.
@@ -0,0 +1,3 @@
1
+ fn main() {
2
+ pyo3_build_config::use_pyo3_cfgs();
3
+ }
@@ -0,0 +1,29 @@
1
+ # Copyright (c) 2025-2026 Buf Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Native extension for protobuf marshaling."""
16
+
17
+ from __future__ import annotations
18
+
19
+ __all__ = ["NativeMessage", "Oneof", "generic_setattr", "initialize_message_type"]
20
+
21
+ from . import _protobuf_ext
22
+ from ._protobuf_ext import (
23
+ NativeMessage,
24
+ Oneof,
25
+ generic_setattr,
26
+ initialize_message_type,
27
+ )
28
+
29
+ __doc__ = _protobuf_ext.__doc__
@@ -0,0 +1,35 @@
1
+ # Copyright (c) 2025-2026 Buf Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from typing import Generic, TypeVar, final
16
+
17
+ from protobuf import Message
18
+
19
+ class NativeMessage:
20
+ def to_binary(self, *, write_unknown_fields: bool = True) -> bytes: ...
21
+ def _merge_from_binary(
22
+ self, data: bytes, ignore_unknown_fields: bool = False
23
+ ) -> None: ...
24
+
25
+ _C = TypeVar("_C", bound=str) # Case name
26
+ _V = TypeVar("_V") # Value type
27
+
28
+ @final
29
+ class Oneof(Generic[_C, _V]):
30
+ def __init__(self, field: _C, value: _V) -> None: ...
31
+ field: _C
32
+ value: _V
33
+
34
+ def initialize_message_type(message_type: type[Message]) -> None: ...
35
+ def generic_setattr(obj: object, name: str, value: object) -> None: ...
@@ -0,0 +1,26 @@
1
+ [project]
2
+ name = "protobuf-py-ext"
3
+ version = "0.0.2"
4
+ description = "Native extension for protobuf-py"
5
+ requires-python = ">=3.10"
6
+ classifiers = [
7
+ "Programming Language :: Python :: Implementation :: CPython",
8
+ "Programming Language :: Rust",
9
+ ]
10
+
11
+ [project.urls]
12
+ Documentation = "https://protobufpython.com/"
13
+ Homepage = "https://github.com/bufbuild/protobuf-py"
14
+ Issues = "https://github.com/bufbuild/protobuf-py/issues"
15
+ Source = "https://github.com/bufbuild/protobuf-py"
16
+
17
+ [dependency-groups]
18
+ dev = ["maturin==1.14.1"]
19
+
20
+ [build-system]
21
+ requires = ["maturin>=1.12,<2.0"]
22
+ build-backend = "maturin"
23
+
24
+ [tool.maturin]
25
+ features = ["pyo3/extension-module"]
26
+ module-name = "protobuf_ext._protobuf_ext"
@@ -0,0 +1,164 @@
1
+ use pyo3::{
2
+ Bound, Py, PyAny, PyErr, PyResult, Python,
3
+ exceptions::PyAttributeError,
4
+ pyfunction,
5
+ types::{PyAnyMethods as _, PyString, PyType},
6
+ };
7
+
8
+ /// Access to a Python attribute, to optimize access to slots.
9
+ ///
10
+ /// Message types use slots to store field values which lays them out directly
11
+ /// in the storage of a `PyObject` at offsets fixed at class creation time.
12
+ /// This means, it's also wasteful for us to always resolve the offset via
13
+ /// `getattr` despite precomputing marshaler descriptors. This type extracts the
14
+ /// offset when it can, and reads attributes directly if so.
15
+ ///
16
+ /// It uses standard getattr for attributes that aren't in slots (shouldn't happen
17
+ /// in practice except for uncommon cases like a user-defined Message subclass that
18
+ /// doesn't go through `Registry`), and on abi3, free-threaded, and `PyPy` wheels.
19
+ /// Notably, this means it is not used for unreleased Python versions that we may not
20
+ /// be running tests for yet. This ensures that our direct use of cpython's struct
21
+ /// internals should be verified by tests, or not done at all.
22
+ pub(crate) enum AttributeAccess {
23
+ Offset(usize),
24
+ Name(Py<PyString>),
25
+ }
26
+
27
+ impl AttributeAccess {
28
+ /// Resolves `attr_name` on `py_type`. Uses a cached slot offset when
29
+ /// possible (non-ABI3 + Python 3.11+), otherwise stores the name to retrieve with
30
+ /// `getattr`.
31
+ pub(crate) fn new(
32
+ _py: Python<'_>,
33
+ py_type: &Bound<'_, PyType>,
34
+ attr_name: &Bound<'_, PyString>,
35
+ ) -> Self {
36
+ if let Ok(Some(offset)) = Self::try_compute_slot_offset(py_type, attr_name) {
37
+ Self::Offset(offset)
38
+ } else {
39
+ Self::Name(attr_name.clone().unbind())
40
+ }
41
+ }
42
+
43
+ /// Returns the attribute value from `obj`.
44
+ pub(crate) fn get<'py>(
45
+ &self,
46
+ py: Python<'py>,
47
+ obj: &Bound<'py, PyAny>,
48
+ ) -> PyResult<Bound<'py, PyAny>> {
49
+ match self {
50
+ Self::Offset(offset) => {
51
+ #[allow(
52
+ clippy::cast_ptr_alignment,
53
+ reason = "slot offsets point to PyObject* fields which CPython aligns correctly"
54
+ )]
55
+ let raw = unsafe {
56
+ *((obj.as_ptr() as *const u8)
57
+ .add(*offset)
58
+ .cast::<*mut pyo3::ffi::PyObject>())
59
+ };
60
+ if raw.is_null() {
61
+ // Can't happen in practiec.
62
+ return Err(PyAttributeError::new_err("uninitialized slot"));
63
+ }
64
+ Ok(unsafe { Bound::from_borrowed_ptr(py, raw) })
65
+ }
66
+ Self::Name(name) => obj.getattr(name),
67
+ }
68
+ }
69
+
70
+ /// Sets the attribute on `obj` to `value`.
71
+ pub(crate) fn set(&self, obj: &Bound<'_, PyAny>, value: &Bound<'_, PyAny>) -> PyResult<()> {
72
+ match self {
73
+ Self::Offset(offset) => {
74
+ #[allow(
75
+ clippy::cast_ptr_alignment,
76
+ reason = "slot offsets point to PyObject* fields which CPython aligns correctly"
77
+ )]
78
+ unsafe {
79
+ let slot_ptr = obj
80
+ .as_ptr()
81
+ .cast::<u8>()
82
+ .add(*offset)
83
+ .cast::<*mut pyo3::ffi::PyObject>();
84
+ let new_raw = value.as_ptr();
85
+ pyo3::ffi::Py_INCREF(new_raw);
86
+ let old_raw = *slot_ptr;
87
+ *slot_ptr = new_raw;
88
+ pyo3::ffi::Py_XDECREF(old_raw);
89
+ }
90
+ Ok(())
91
+ }
92
+ Self::Name(name) => generic_setattr(obj, name.bind(obj.py()), value),
93
+ }
94
+ }
95
+
96
+ pub(crate) fn clone_ref(&self, py: Python<'_>) -> Self {
97
+ match self {
98
+ Self::Offset(offset) => Self::Offset(*offset),
99
+ Self::Name(name) => Self::Name(name.clone_ref(py)),
100
+ }
101
+ }
102
+
103
+ /// Attempts to resolve a `__slots__` member offset for `attr_name` on
104
+ /// `py_type`.
105
+ #[cfg(all(not(Py_LIMITED_API), Py_3_11, not(Py_GIL_DISABLED)))]
106
+ fn try_compute_slot_offset(
107
+ py_type: &Bound<'_, PyType>,
108
+ attr_name: &Bound<'_, PyString>,
109
+ ) -> PyResult<Option<usize>> {
110
+ use pyo3::{
111
+ ffi::{PyMemberDef, PyMemberDescrObject},
112
+ types::{PyStringMethods as _, PyTypeMethods as _},
113
+ };
114
+
115
+ let Some(descriptor) = py_type.getattr_opt(attr_name)? else {
116
+ return Ok(None);
117
+ };
118
+ if descriptor.get_type().name()?.to_str()? != "member_descriptor" {
119
+ return Ok(None);
120
+ }
121
+ let offset = unsafe {
122
+ let ptr = descriptor.as_ptr() as *const PyMemberDescrObject;
123
+ let member: *mut PyMemberDef = (*ptr).d_member;
124
+ if member.is_null() {
125
+ return Ok(None);
126
+ }
127
+ let Ok(offset) = usize::try_from((*member).offset) else {
128
+ return Ok(None);
129
+ };
130
+ offset
131
+ };
132
+ Ok(Some(offset))
133
+ }
134
+
135
+ #[cfg(not(all(not(Py_LIMITED_API), Py_3_11, not(Py_GIL_DISABLED))))]
136
+ #[allow(
137
+ clippy::unnecessary_wraps,
138
+ reason = "signature must match the Py_3_11 variant"
139
+ )]
140
+ fn try_compute_slot_offset(
141
+ _py_type: &Bound<'_, PyType>,
142
+ _attr_name: &Bound<'_, PyString>,
143
+ ) -> PyResult<Option<usize>> {
144
+ Ok(None)
145
+ }
146
+ }
147
+
148
+ /// Wrapper around raw ffi for `PyObject_GenericSetAttr`. It is not exposed natively by `PyO3` for being a relatively
149
+ /// niche API. So is this file - we end up needing it after implementing __setattr__ in native since the standard
150
+ /// setattr would call into it leading to infinite recursion.
151
+ #[pyfunction]
152
+ pub(crate) fn generic_setattr(
153
+ obj: &Bound<'_, PyAny>,
154
+ name: &Bound<'_, PyString>,
155
+ value: &Bound<'_, PyAny>,
156
+ ) -> PyResult<()> {
157
+ let ret =
158
+ unsafe { pyo3::ffi::PyObject_GenericSetAttr(obj.as_ptr(), name.as_ptr(), value.as_ptr()) };
159
+ if ret != 0 {
160
+ Err(PyErr::fetch(obj.py()))
161
+ } else {
162
+ Ok(())
163
+ }
164
+ }