drf-accelerator 0.1.0a1__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.
- drf_accelerator-0.1.0a1/PKG-INFO +93 -0
- drf_accelerator-0.1.0a1/README.md +71 -0
- drf_accelerator-0.1.0a1/drf_accelerator/Cargo.lock +243 -0
- drf_accelerator-0.1.0a1/drf_accelerator/Cargo.toml +16 -0
- drf_accelerator-0.1.0a1/drf_accelerator/drf_accelerator/__init__.py +3 -0
- drf_accelerator-0.1.0a1/drf_accelerator/drf_accelerator/mixins.py +52 -0
- drf_accelerator-0.1.0a1/drf_accelerator/src/lib.rs +54 -0
- drf_accelerator-0.1.0a1/drf_accelerator/tests/test_serializer.py +57 -0
- drf_accelerator-0.1.0a1/pyproject.toml +49 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: drf-accelerator
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Classifier: Framework :: Django
|
|
5
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
6
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
11
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
12
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
13
|
+
Classifier: Programming Language :: Rust
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Requires-Dist: djangorestframework>=3.12
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Summary: A high-performance Rust-backed accelerator for Django Rest Framework.
|
|
18
|
+
License: MIT
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
21
|
+
|
|
22
|
+
# DRF Accelerator
|
|
23
|
+
|
|
24
|
+
> [!CAUTION]
|
|
25
|
+
> **Experimental / Under Development**
|
|
26
|
+
> This project is currently an experiment to improve DRF performance using Rust. It is NOT production-ready and has strict scope limitations.
|
|
27
|
+
|
|
28
|
+
A high-performance Rust-backed accelerator for Django Rest Framework.
|
|
29
|
+
|
|
30
|
+
## Performance Benchmark
|
|
31
|
+
| Method | Time (10k items) | Speedup |
|
|
32
|
+
| :--- | :--- | :--- |
|
|
33
|
+
| **Standard DRF** | 0.3288s | 1x |
|
|
34
|
+
| **drf-accelerator** | **0.0389s** | **~8.45x** |
|
|
35
|
+
|
|
36
|
+
*Benchmark run on 10,000 Product models with 5 primitive fields in the `examples` project.*
|
|
37
|
+
|
|
38
|
+
## Installation & Setup
|
|
39
|
+
|
|
40
|
+
### For Users (Stable)
|
|
41
|
+
Currently, the package is in early development. To install it from source:
|
|
42
|
+
|
|
43
|
+
1. **Prerequisites**: Ensure you have [Rust](https://www.rust-lang.org/tools/install) installed.
|
|
44
|
+
2. **Clone the repository**:
|
|
45
|
+
```bash
|
|
46
|
+
git clone https://github.com/p-r-a-v-i-n/drf-accelerator.git
|
|
47
|
+
cd drf-accelerator
|
|
48
|
+
```
|
|
49
|
+
3. **Build and Install**:
|
|
50
|
+
```bash
|
|
51
|
+
pip install -e .
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### For Developers (Try it out)
|
|
55
|
+
If you want to run the benchmarks yourself:
|
|
56
|
+
|
|
57
|
+
1. **Build the extension**:
|
|
58
|
+
```bash
|
|
59
|
+
cd drf_accelerator
|
|
60
|
+
maturin develop --release
|
|
61
|
+
cd ..
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
2. **Setup and Run Benchmark**:
|
|
65
|
+
```bash
|
|
66
|
+
cd examples
|
|
67
|
+
python manage.py migrate
|
|
68
|
+
python bench.py
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
Simply inherit from `FastSerializationMixin` in your `ModelSerializer`:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from drf_accelerator import FastSerializationMixin
|
|
76
|
+
from rest_framework import serializers
|
|
77
|
+
|
|
78
|
+
class MySerializer(FastSerializationMixin, serializers.ModelSerializer):
|
|
79
|
+
class Meta:
|
|
80
|
+
model = MyModel
|
|
81
|
+
fields = ["id", "title", "author", "is_published"]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Limitations (Strict)
|
|
85
|
+
To maintain high performance and safety, the following are **not supported**:
|
|
86
|
+
- **Dotted Sources**: `source="user.profile.age"` will error.
|
|
87
|
+
- **Nested Serializers**: Cannot be used inside an accelerated serializer.
|
|
88
|
+
- **Method Fields**: `SerializerMethodField` is not supported.
|
|
89
|
+
- **Non-Primitives**: Only `int`, `str`, `float`, `bool`, and `None` are supported. Non-primitive types (like `Decimal` or `Date`) will currently trigger a `TypeError`.
|
|
90
|
+
|
|
91
|
+
## How it works
|
|
92
|
+
The Mixin swaps the standard DRF `ListSerializer` for a `FastListSerializer` that offloads the object-to-dict conversion loop to a Rust extension using PyO3. This significantly reduces Python interpreter overhead for large list responses.
|
|
93
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# DRF Accelerator
|
|
2
|
+
|
|
3
|
+
> [!CAUTION]
|
|
4
|
+
> **Experimental / Under Development**
|
|
5
|
+
> This project is currently an experiment to improve DRF performance using Rust. It is NOT production-ready and has strict scope limitations.
|
|
6
|
+
|
|
7
|
+
A high-performance Rust-backed accelerator for Django Rest Framework.
|
|
8
|
+
|
|
9
|
+
## Performance Benchmark
|
|
10
|
+
| Method | Time (10k items) | Speedup |
|
|
11
|
+
| :--- | :--- | :--- |
|
|
12
|
+
| **Standard DRF** | 0.3288s | 1x |
|
|
13
|
+
| **drf-accelerator** | **0.0389s** | **~8.45x** |
|
|
14
|
+
|
|
15
|
+
*Benchmark run on 10,000 Product models with 5 primitive fields in the `examples` project.*
|
|
16
|
+
|
|
17
|
+
## Installation & Setup
|
|
18
|
+
|
|
19
|
+
### For Users (Stable)
|
|
20
|
+
Currently, the package is in early development. To install it from source:
|
|
21
|
+
|
|
22
|
+
1. **Prerequisites**: Ensure you have [Rust](https://www.rust-lang.org/tools/install) installed.
|
|
23
|
+
2. **Clone the repository**:
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/p-r-a-v-i-n/drf-accelerator.git
|
|
26
|
+
cd drf-accelerator
|
|
27
|
+
```
|
|
28
|
+
3. **Build and Install**:
|
|
29
|
+
```bash
|
|
30
|
+
pip install -e .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### For Developers (Try it out)
|
|
34
|
+
If you want to run the benchmarks yourself:
|
|
35
|
+
|
|
36
|
+
1. **Build the extension**:
|
|
37
|
+
```bash
|
|
38
|
+
cd drf_accelerator
|
|
39
|
+
maturin develop --release
|
|
40
|
+
cd ..
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
2. **Setup and Run Benchmark**:
|
|
44
|
+
```bash
|
|
45
|
+
cd examples
|
|
46
|
+
python manage.py migrate
|
|
47
|
+
python bench.py
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
Simply inherit from `FastSerializationMixin` in your `ModelSerializer`:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from drf_accelerator import FastSerializationMixin
|
|
55
|
+
from rest_framework import serializers
|
|
56
|
+
|
|
57
|
+
class MySerializer(FastSerializationMixin, serializers.ModelSerializer):
|
|
58
|
+
class Meta:
|
|
59
|
+
model = MyModel
|
|
60
|
+
fields = ["id", "title", "author", "is_published"]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Limitations (Strict)
|
|
64
|
+
To maintain high performance and safety, the following are **not supported**:
|
|
65
|
+
- **Dotted Sources**: `source="user.profile.age"` will error.
|
|
66
|
+
- **Nested Serializers**: Cannot be used inside an accelerated serializer.
|
|
67
|
+
- **Method Fields**: `SerializerMethodField` is not supported.
|
|
68
|
+
- **Non-Primitives**: Only `int`, `str`, `float`, `bool`, and `None` are supported. Non-primitive types (like `Decimal` or `Date`) will currently trigger a `TypeError`.
|
|
69
|
+
|
|
70
|
+
## How it works
|
|
71
|
+
The Mixin swaps the standard DRF `ListSerializer` for a `FastListSerializer` that offloads the object-to-dict conversion loop to a Rust extension using PyO3. This significantly reduces Python interpreter overhead for large list responses.
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "autocfg"
|
|
7
|
+
version = "1.5.0"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "cfg-if"
|
|
13
|
+
version = "1.0.4"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
16
|
+
|
|
17
|
+
[[package]]
|
|
18
|
+
name = "drf_accelerator"
|
|
19
|
+
version = "0.1.0-alpha1"
|
|
20
|
+
dependencies = [
|
|
21
|
+
"pyo3",
|
|
22
|
+
"serde",
|
|
23
|
+
"serde_json",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[[package]]
|
|
27
|
+
name = "heck"
|
|
28
|
+
version = "0.5.0"
|
|
29
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
30
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
31
|
+
|
|
32
|
+
[[package]]
|
|
33
|
+
name = "indoc"
|
|
34
|
+
version = "2.0.7"
|
|
35
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
36
|
+
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
|
37
|
+
dependencies = [
|
|
38
|
+
"rustversion",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[[package]]
|
|
42
|
+
name = "itoa"
|
|
43
|
+
version = "1.0.17"
|
|
44
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
45
|
+
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
|
46
|
+
|
|
47
|
+
[[package]]
|
|
48
|
+
name = "libc"
|
|
49
|
+
version = "0.2.180"
|
|
50
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
51
|
+
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
|
52
|
+
|
|
53
|
+
[[package]]
|
|
54
|
+
name = "memchr"
|
|
55
|
+
version = "2.7.6"
|
|
56
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
57
|
+
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
|
58
|
+
|
|
59
|
+
[[package]]
|
|
60
|
+
name = "memoffset"
|
|
61
|
+
version = "0.9.1"
|
|
62
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
63
|
+
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
|
64
|
+
dependencies = [
|
|
65
|
+
"autocfg",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
[[package]]
|
|
69
|
+
name = "once_cell"
|
|
70
|
+
version = "1.21.3"
|
|
71
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
72
|
+
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
|
73
|
+
|
|
74
|
+
[[package]]
|
|
75
|
+
name = "portable-atomic"
|
|
76
|
+
version = "1.13.0"
|
|
77
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
78
|
+
checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950"
|
|
79
|
+
|
|
80
|
+
[[package]]
|
|
81
|
+
name = "proc-macro2"
|
|
82
|
+
version = "1.0.105"
|
|
83
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
84
|
+
checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7"
|
|
85
|
+
dependencies = [
|
|
86
|
+
"unicode-ident",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
[[package]]
|
|
90
|
+
name = "pyo3"
|
|
91
|
+
version = "0.23.5"
|
|
92
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
93
|
+
checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872"
|
|
94
|
+
dependencies = [
|
|
95
|
+
"cfg-if",
|
|
96
|
+
"indoc",
|
|
97
|
+
"libc",
|
|
98
|
+
"memoffset",
|
|
99
|
+
"once_cell",
|
|
100
|
+
"portable-atomic",
|
|
101
|
+
"pyo3-build-config",
|
|
102
|
+
"pyo3-ffi",
|
|
103
|
+
"pyo3-macros",
|
|
104
|
+
"unindent",
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
[[package]]
|
|
108
|
+
name = "pyo3-build-config"
|
|
109
|
+
version = "0.23.5"
|
|
110
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
111
|
+
checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb"
|
|
112
|
+
dependencies = [
|
|
113
|
+
"once_cell",
|
|
114
|
+
"target-lexicon",
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
[[package]]
|
|
118
|
+
name = "pyo3-ffi"
|
|
119
|
+
version = "0.23.5"
|
|
120
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
121
|
+
checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d"
|
|
122
|
+
dependencies = [
|
|
123
|
+
"libc",
|
|
124
|
+
"pyo3-build-config",
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
[[package]]
|
|
128
|
+
name = "pyo3-macros"
|
|
129
|
+
version = "0.23.5"
|
|
130
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
131
|
+
checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da"
|
|
132
|
+
dependencies = [
|
|
133
|
+
"proc-macro2",
|
|
134
|
+
"pyo3-macros-backend",
|
|
135
|
+
"quote",
|
|
136
|
+
"syn",
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
[[package]]
|
|
140
|
+
name = "pyo3-macros-backend"
|
|
141
|
+
version = "0.23.5"
|
|
142
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
143
|
+
checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028"
|
|
144
|
+
dependencies = [
|
|
145
|
+
"heck",
|
|
146
|
+
"proc-macro2",
|
|
147
|
+
"pyo3-build-config",
|
|
148
|
+
"quote",
|
|
149
|
+
"syn",
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
[[package]]
|
|
153
|
+
name = "quote"
|
|
154
|
+
version = "1.0.43"
|
|
155
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
156
|
+
checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
|
|
157
|
+
dependencies = [
|
|
158
|
+
"proc-macro2",
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
[[package]]
|
|
162
|
+
name = "rustversion"
|
|
163
|
+
version = "1.0.22"
|
|
164
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
165
|
+
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
|
166
|
+
|
|
167
|
+
[[package]]
|
|
168
|
+
name = "serde"
|
|
169
|
+
version = "1.0.228"
|
|
170
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
171
|
+
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
|
172
|
+
dependencies = [
|
|
173
|
+
"serde_core",
|
|
174
|
+
"serde_derive",
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
[[package]]
|
|
178
|
+
name = "serde_core"
|
|
179
|
+
version = "1.0.228"
|
|
180
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
181
|
+
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
|
182
|
+
dependencies = [
|
|
183
|
+
"serde_derive",
|
|
184
|
+
]
|
|
185
|
+
|
|
186
|
+
[[package]]
|
|
187
|
+
name = "serde_derive"
|
|
188
|
+
version = "1.0.228"
|
|
189
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
190
|
+
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|
191
|
+
dependencies = [
|
|
192
|
+
"proc-macro2",
|
|
193
|
+
"quote",
|
|
194
|
+
"syn",
|
|
195
|
+
]
|
|
196
|
+
|
|
197
|
+
[[package]]
|
|
198
|
+
name = "serde_json"
|
|
199
|
+
version = "1.0.149"
|
|
200
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
201
|
+
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
|
202
|
+
dependencies = [
|
|
203
|
+
"itoa",
|
|
204
|
+
"memchr",
|
|
205
|
+
"serde",
|
|
206
|
+
"serde_core",
|
|
207
|
+
"zmij",
|
|
208
|
+
]
|
|
209
|
+
|
|
210
|
+
[[package]]
|
|
211
|
+
name = "syn"
|
|
212
|
+
version = "2.0.114"
|
|
213
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
214
|
+
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
|
|
215
|
+
dependencies = [
|
|
216
|
+
"proc-macro2",
|
|
217
|
+
"quote",
|
|
218
|
+
"unicode-ident",
|
|
219
|
+
]
|
|
220
|
+
|
|
221
|
+
[[package]]
|
|
222
|
+
name = "target-lexicon"
|
|
223
|
+
version = "0.12.16"
|
|
224
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
225
|
+
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|
226
|
+
|
|
227
|
+
[[package]]
|
|
228
|
+
name = "unicode-ident"
|
|
229
|
+
version = "1.0.22"
|
|
230
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
231
|
+
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
|
232
|
+
|
|
233
|
+
[[package]]
|
|
234
|
+
name = "unindent"
|
|
235
|
+
version = "0.2.4"
|
|
236
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
237
|
+
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
|
238
|
+
|
|
239
|
+
[[package]]
|
|
240
|
+
name = "zmij"
|
|
241
|
+
version = "1.0.15"
|
|
242
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
243
|
+
checksum = "94f63c051f4fe3c1509da62131a678643c5b6fbdc9273b2b79d4378ebda003d2"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "drf_accelerator"
|
|
3
|
+
version = "0.1.0-alpha1"
|
|
4
|
+
description = "A high-performance Rust-backed accelerator for Django Rest Framework."
|
|
5
|
+
license = "MIT"
|
|
6
|
+
repository = "https://github.com/p-r-a-v-i-n/drf-accelerator"
|
|
7
|
+
edition = "2021"
|
|
8
|
+
|
|
9
|
+
[lib]
|
|
10
|
+
name = "drf_accelerator"
|
|
11
|
+
crate-type = ["cdylib"]
|
|
12
|
+
|
|
13
|
+
[dependencies]
|
|
14
|
+
pyo3 = { version = "0.23.3", features = ["extension-module"] }
|
|
15
|
+
serde = { version = "1.0", features = ["derive"] }
|
|
16
|
+
serde_json = "1.0"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from rest_framework.serializers import ListSerializer
|
|
4
|
+
|
|
5
|
+
from .drf_accelerator import FastSerializer
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FastListSerializer(ListSerializer):
|
|
11
|
+
def __init__(self, *args, **kwargs):
|
|
12
|
+
super().__init__(*args, **kwargs)
|
|
13
|
+
self._fast_field_config = self._build_field_config()
|
|
14
|
+
|
|
15
|
+
def _build_field_config(self):
|
|
16
|
+
child = self.child
|
|
17
|
+
config = []
|
|
18
|
+
for field_name, field in child.fields.items():
|
|
19
|
+
source = field.source or field_name
|
|
20
|
+
|
|
21
|
+
if "." in source:
|
|
22
|
+
raise NotImplementedError(
|
|
23
|
+
f"FastSerializer does not support dotted sources: '{source}'. "
|
|
24
|
+
f"Field: '{field_name}'"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
from rest_framework.serializers import BaseSerializer, SerializerMethodField
|
|
28
|
+
|
|
29
|
+
if isinstance(field, BaseSerializer):
|
|
30
|
+
raise NotImplementedError(
|
|
31
|
+
f"FastSerializer does not support nested serializers: "
|
|
32
|
+
f"'{field_name}'"
|
|
33
|
+
)
|
|
34
|
+
if isinstance(field, SerializerMethodField):
|
|
35
|
+
raise NotImplementedError(
|
|
36
|
+
f"FastSerializer does not support SerializerMethodField: "
|
|
37
|
+
f"'{field_name}'"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
config.append((field_name, source))
|
|
41
|
+
return config
|
|
42
|
+
|
|
43
|
+
def to_representation(self, data):
|
|
44
|
+
serializer = FastSerializer(self._fast_field_config)
|
|
45
|
+
return serializer.serialize(data)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class FastSerializationMixin:
|
|
49
|
+
@classmethod
|
|
50
|
+
def many_init(cls, *args, **kwargs):
|
|
51
|
+
kwargs["child"] = cls(*args, **kwargs)
|
|
52
|
+
return FastListSerializer(*args, **kwargs)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
use pyo3::prelude::*;
|
|
2
|
+
use pyo3::types::{PyDict, PyList};
|
|
3
|
+
|
|
4
|
+
#[pyclass]
|
|
5
|
+
struct FastSerializer {
|
|
6
|
+
// List of (output_name, source_attr)
|
|
7
|
+
fields: Vec<(String, String)>,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
#[pymethods]
|
|
11
|
+
impl FastSerializer {
|
|
12
|
+
#[new]
|
|
13
|
+
fn new(fields: Vec<(String, String)>) -> Self {
|
|
14
|
+
FastSerializer { fields }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fn serialize(&self, py: Python<'_>, instances: &Bound<'_, PyAny>) -> PyResult<Py<PyList>> {
|
|
18
|
+
let results = PyList::empty(py);
|
|
19
|
+
|
|
20
|
+
for instance in instances.try_iter()? {
|
|
21
|
+
let instance = instance?;
|
|
22
|
+
let dict = PyDict::new(py);
|
|
23
|
+
|
|
24
|
+
for (output_name, source_attr) in &self.fields {
|
|
25
|
+
let val_obj = instance.getattr(source_attr.as_str())?;
|
|
26
|
+
|
|
27
|
+
if val_obj.is_none()
|
|
28
|
+
|| val_obj.is_instance_of::<pyo3::types::PyString>()
|
|
29
|
+
|| val_obj.is_instance_of::<pyo3::types::PyInt>()
|
|
30
|
+
|| val_obj.is_instance_of::<pyo3::types::PyFloat>()
|
|
31
|
+
|| val_obj.is_instance_of::<pyo3::types::PyBool>()
|
|
32
|
+
{
|
|
33
|
+
dict.set_item(output_name, val_obj)?;
|
|
34
|
+
} else {
|
|
35
|
+
return Err(pyo3::exceptions::PyTypeError::new_err(
|
|
36
|
+
format!(
|
|
37
|
+
"FastSerializer: Field '{}' (source: '{}') returned unsupported type: {}. Only primitives (int, float, bool, str, None) are supported.",
|
|
38
|
+
output_name, source_attr, val_obj.get_type()
|
|
39
|
+
)
|
|
40
|
+
));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
results.append(dict)?;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
Ok(results.into())
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#[pymodule]
|
|
51
|
+
fn drf_accelerator(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
52
|
+
m.add_class::<FastSerializer>()?;
|
|
53
|
+
Ok(())
|
|
54
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from drf_accelerator.drf_accelerator import FastSerializer
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SimpleObject:
|
|
7
|
+
def __init__(self, **kwargs):
|
|
8
|
+
for k, v in kwargs.items():
|
|
9
|
+
setattr(self, k, v)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_fast_serializer_basic():
|
|
13
|
+
fields = [("id", "id"), ("name", "name")]
|
|
14
|
+
serializer = FastSerializer(fields)
|
|
15
|
+
|
|
16
|
+
obj = SimpleObject(id=1, name="Test")
|
|
17
|
+
data = serializer.serialize([obj])
|
|
18
|
+
|
|
19
|
+
assert len(data) == 1
|
|
20
|
+
assert data[0]["id"] == 1
|
|
21
|
+
assert data[0]["name"] == "Test"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_fast_serializer_primitives():
|
|
25
|
+
fields = [("s", "s"), ("i", "i"), ("f", "f"), ("b", "b"), ("n", "n")]
|
|
26
|
+
serializer = FastSerializer(fields)
|
|
27
|
+
|
|
28
|
+
obj = SimpleObject(s="string", i=42, f=3.14, b=True, n=None)
|
|
29
|
+
data = serializer.serialize([obj])
|
|
30
|
+
|
|
31
|
+
assert data[0]["s"] == "string"
|
|
32
|
+
assert data[0]["i"] == 42
|
|
33
|
+
assert data[0]["f"] == 3.14
|
|
34
|
+
assert data[0]["b"] is True
|
|
35
|
+
assert data[0]["n"] is None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_fast_serializer_unsupported_type():
|
|
39
|
+
from datetime import date
|
|
40
|
+
|
|
41
|
+
fields = [("d", "d")]
|
|
42
|
+
serializer = FastSerializer(fields)
|
|
43
|
+
|
|
44
|
+
obj = SimpleObject(d=date(2023, 1, 1))
|
|
45
|
+
with pytest.raises(TypeError) as excinfo:
|
|
46
|
+
serializer.serialize([obj])
|
|
47
|
+
|
|
48
|
+
assert "unsupported type" in str(excinfo.value)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_fast_serializer_missing_attribute():
|
|
52
|
+
fields = [("m", "missing")]
|
|
53
|
+
serializer = FastSerializer(fields)
|
|
54
|
+
|
|
55
|
+
obj = SimpleObject()
|
|
56
|
+
with pytest.raises(AttributeError):
|
|
57
|
+
serializer.serialize([obj])
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
build-backend = "maturin"
|
|
3
|
+
requires = [ "maturin>=1,<2" ]
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "drf-accelerator"
|
|
7
|
+
version = "0.1.0a1"
|
|
8
|
+
description = "A high-performance Rust-backed accelerator for Django Rest Framework."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Framework :: Django",
|
|
15
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Programming Language :: Python :: 3.14",
|
|
21
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
22
|
+
"Programming Language :: Python :: Implementation :: PyPy",
|
|
23
|
+
"Programming Language :: Rust",
|
|
24
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"djangorestframework>=3.12",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[dependency-groups]
|
|
31
|
+
dev = [
|
|
32
|
+
"maturin>=1,<2",
|
|
33
|
+
"pre-commit",
|
|
34
|
+
"ruff",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[tool.maturin]
|
|
38
|
+
python-source = "drf_accelerator"
|
|
39
|
+
manifest-path = "drf_accelerator/Cargo.toml"
|
|
40
|
+
module-name = "drf_accelerator.drf_accelerator"
|
|
41
|
+
|
|
42
|
+
[tool.ruff]
|
|
43
|
+
target-version = "py312"
|
|
44
|
+
line-length = 88
|
|
45
|
+
exclude = [ "examples" ]
|
|
46
|
+
|
|
47
|
+
lint.select = [ "E", "F", "I", "UP", "W" ]
|
|
48
|
+
lint.ignore = [ ]
|
|
49
|
+
lint.isort.known-first-party = [ "drf_accelerator" ]
|