rust-py-cache 0.1.1__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,77 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ name: cargo test + pytest
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [ubuntu-latest, macos-latest]
16
+ python-version: ["3.10", "3.12", "3.13"]
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python ${{ matrix.python-version }}
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+
25
+ - name: Set up Rust
26
+ uses: dtolnay/rust-toolchain@stable
27
+
28
+ - name: Cache cargo
29
+ uses: Swatinem/rust-cache@v2
30
+
31
+ # Testes do core Rust (puro, sem Python).
32
+ - name: cargo test
33
+ run: cargo test --verbose
34
+
35
+ # maturin develop exige um virtualenv ativo. O setup-python não cria um,
36
+ # então criamos o .venv e o exportamos para os próximos steps via
37
+ # GITHUB_ENV/GITHUB_PATH (cada step roda num shell novo).
38
+ - name: Create virtualenv
39
+ run: |
40
+ python -m venv .venv
41
+ echo "VIRTUAL_ENV=$PWD/.venv" >> "$GITHUB_ENV"
42
+ echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
43
+
44
+ - name: Install Python deps
45
+ run: |
46
+ python -m pip install --upgrade pip
47
+ pip install maturin pytest
48
+
49
+ # Compila a extensão e instala no .venv.
50
+ - name: maturin develop
51
+ run: maturin develop
52
+
53
+ - name: pytest
54
+ run: pytest -q
55
+
56
+ build-wheels:
57
+ name: build wheels (abi3)
58
+ runs-on: ${{ matrix.os }}
59
+ needs: test
60
+ strategy:
61
+ matrix:
62
+ os: [ubuntu-latest, macos-latest, windows-latest]
63
+ steps:
64
+ - uses: actions/checkout@v4
65
+ - uses: actions/setup-python@v5
66
+ with:
67
+ python-version: "3.12"
68
+ - name: Build wheels
69
+ uses: PyO3/maturin-action@v1
70
+ with:
71
+ command: build
72
+ args: --release --out dist
73
+ - name: Upload artifacts
74
+ uses: actions/upload-artifact@v4
75
+ with:
76
+ name: wheels-${{ matrix.os }}
77
+ path: dist
@@ -0,0 +1,57 @@
1
+ name: Release
2
+
3
+ # Dispara ao publicar uma tag de versão (ex.: v0.1.0). Constrói as wheels para
4
+ # todas as plataformas + sdist e publica no PyPI via Trusted Publishing (OIDC),
5
+ # sem precisar guardar um token no repositório.
6
+ on:
7
+ push:
8
+ tags: ["v*"]
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ build:
15
+ name: build (${{ matrix.os }})
16
+ runs-on: ${{ matrix.os }}
17
+ strategy:
18
+ matrix:
19
+ os: [ubuntu-latest, macos-latest, windows-latest]
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: actions/setup-python@v5
23
+ with:
24
+ python-version: "3.12"
25
+ - name: Build wheels
26
+ uses: PyO3/maturin-action@v1
27
+ with:
28
+ command: build
29
+ args: --release --out dist
30
+ - name: Build sdist
31
+ if: matrix.os == 'ubuntu-latest'
32
+ uses: PyO3/maturin-action@v1
33
+ with:
34
+ command: sdist
35
+ args: --out dist
36
+ - uses: actions/upload-artifact@v4
37
+ with:
38
+ name: dist-${{ matrix.os }}
39
+ path: dist
40
+
41
+ publish:
42
+ name: publish to PyPI
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ environment: pypi
46
+ permissions:
47
+ id-token: write # necessário para o Trusted Publishing (OIDC)
48
+ steps:
49
+ - uses: actions/download-artifact@v4
50
+ with:
51
+ pattern: dist-*
52
+ merge-multiple: true
53
+ path: dist
54
+ - name: Publish
55
+ uses: pypa/gh-action-pypi-publish@release/v1
56
+ with:
57
+ packages-dir: dist
@@ -0,0 +1,7 @@
1
+ /target
2
+ .venv/
3
+ __pycache__/
4
+ *.so
5
+ dist/
6
+ *.egg-info/
7
+ .pytest_cache/
@@ -0,0 +1,283 @@
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.1"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
10
+
11
+ [[package]]
12
+ name = "bitflags"
13
+ version = "2.13.0"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
16
+
17
+ [[package]]
18
+ name = "cfg-if"
19
+ version = "1.0.4"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
22
+
23
+ [[package]]
24
+ name = "crossbeam-utils"
25
+ version = "0.8.21"
26
+ source = "registry+https://github.com/rust-lang/crates.io-index"
27
+ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
28
+
29
+ [[package]]
30
+ name = "dashmap"
31
+ version = "6.2.1"
32
+ source = "registry+https://github.com/rust-lang/crates.io-index"
33
+ checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c"
34
+ dependencies = [
35
+ "cfg-if",
36
+ "crossbeam-utils",
37
+ "hashbrown",
38
+ "lock_api",
39
+ "once_cell",
40
+ "parking_lot_core",
41
+ ]
42
+
43
+ [[package]]
44
+ name = "hashbrown"
45
+ version = "0.14.5"
46
+ source = "registry+https://github.com/rust-lang/crates.io-index"
47
+ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
48
+
49
+ [[package]]
50
+ name = "heck"
51
+ version = "0.5.0"
52
+ source = "registry+https://github.com/rust-lang/crates.io-index"
53
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
54
+
55
+ [[package]]
56
+ name = "indoc"
57
+ version = "2.0.7"
58
+ source = "registry+https://github.com/rust-lang/crates.io-index"
59
+ checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
60
+ dependencies = [
61
+ "rustversion",
62
+ ]
63
+
64
+ [[package]]
65
+ name = "libc"
66
+ version = "0.2.186"
67
+ source = "registry+https://github.com/rust-lang/crates.io-index"
68
+ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
69
+
70
+ [[package]]
71
+ name = "lock_api"
72
+ version = "0.4.14"
73
+ source = "registry+https://github.com/rust-lang/crates.io-index"
74
+ checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
75
+ dependencies = [
76
+ "scopeguard",
77
+ ]
78
+
79
+ [[package]]
80
+ name = "memoffset"
81
+ version = "0.9.1"
82
+ source = "registry+https://github.com/rust-lang/crates.io-index"
83
+ checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
84
+ dependencies = [
85
+ "autocfg",
86
+ ]
87
+
88
+ [[package]]
89
+ name = "once_cell"
90
+ version = "1.21.4"
91
+ source = "registry+https://github.com/rust-lang/crates.io-index"
92
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
93
+
94
+ [[package]]
95
+ name = "parking_lot_core"
96
+ version = "0.9.12"
97
+ source = "registry+https://github.com/rust-lang/crates.io-index"
98
+ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
99
+ dependencies = [
100
+ "cfg-if",
101
+ "libc",
102
+ "redox_syscall",
103
+ "smallvec",
104
+ "windows-link",
105
+ ]
106
+
107
+ [[package]]
108
+ name = "portable-atomic"
109
+ version = "1.13.1"
110
+ source = "registry+https://github.com/rust-lang/crates.io-index"
111
+ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
112
+
113
+ [[package]]
114
+ name = "proc-macro2"
115
+ version = "1.0.106"
116
+ source = "registry+https://github.com/rust-lang/crates.io-index"
117
+ checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
118
+ dependencies = [
119
+ "unicode-ident",
120
+ ]
121
+
122
+ [[package]]
123
+ name = "pyo3"
124
+ version = "0.24.2"
125
+ source = "registry+https://github.com/rust-lang/crates.io-index"
126
+ checksum = "e5203598f366b11a02b13aa20cab591229ff0a89fd121a308a5df751d5fc9219"
127
+ dependencies = [
128
+ "cfg-if",
129
+ "indoc",
130
+ "libc",
131
+ "memoffset",
132
+ "once_cell",
133
+ "portable-atomic",
134
+ "pyo3-build-config",
135
+ "pyo3-ffi",
136
+ "pyo3-macros",
137
+ "unindent",
138
+ ]
139
+
140
+ [[package]]
141
+ name = "pyo3-build-config"
142
+ version = "0.24.2"
143
+ source = "registry+https://github.com/rust-lang/crates.io-index"
144
+ checksum = "99636d423fa2ca130fa5acde3059308006d46f98caac629418e53f7ebb1e9999"
145
+ dependencies = [
146
+ "once_cell",
147
+ "target-lexicon",
148
+ ]
149
+
150
+ [[package]]
151
+ name = "pyo3-ffi"
152
+ version = "0.24.2"
153
+ source = "registry+https://github.com/rust-lang/crates.io-index"
154
+ checksum = "78f9cf92ba9c409279bc3305b5409d90db2d2c22392d443a87df3a1adad59e33"
155
+ dependencies = [
156
+ "libc",
157
+ "pyo3-build-config",
158
+ ]
159
+
160
+ [[package]]
161
+ name = "pyo3-macros"
162
+ version = "0.24.2"
163
+ source = "registry+https://github.com/rust-lang/crates.io-index"
164
+ checksum = "0b999cb1a6ce21f9a6b147dcf1be9ffedf02e0043aec74dc390f3007047cecd9"
165
+ dependencies = [
166
+ "proc-macro2",
167
+ "pyo3-macros-backend",
168
+ "quote",
169
+ "syn",
170
+ ]
171
+
172
+ [[package]]
173
+ name = "pyo3-macros-backend"
174
+ version = "0.24.2"
175
+ source = "registry+https://github.com/rust-lang/crates.io-index"
176
+ checksum = "822ece1c7e1012745607d5cf0bcb2874769f0f7cb34c4cde03b9358eb9ef911a"
177
+ dependencies = [
178
+ "heck",
179
+ "proc-macro2",
180
+ "pyo3-build-config",
181
+ "quote",
182
+ "syn",
183
+ ]
184
+
185
+ [[package]]
186
+ name = "quote"
187
+ version = "1.0.45"
188
+ source = "registry+https://github.com/rust-lang/crates.io-index"
189
+ checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
190
+ dependencies = [
191
+ "proc-macro2",
192
+ ]
193
+
194
+ [[package]]
195
+ name = "redox_syscall"
196
+ version = "0.5.18"
197
+ source = "registry+https://github.com/rust-lang/crates.io-index"
198
+ checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
199
+ dependencies = [
200
+ "bitflags",
201
+ ]
202
+
203
+ [[package]]
204
+ name = "rust_py_cache"
205
+ version = "0.1.1"
206
+ dependencies = [
207
+ "dashmap",
208
+ "pyo3",
209
+ "thiserror",
210
+ ]
211
+
212
+ [[package]]
213
+ name = "rustversion"
214
+ version = "1.0.22"
215
+ source = "registry+https://github.com/rust-lang/crates.io-index"
216
+ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
217
+
218
+ [[package]]
219
+ name = "scopeguard"
220
+ version = "1.2.0"
221
+ source = "registry+https://github.com/rust-lang/crates.io-index"
222
+ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
223
+
224
+ [[package]]
225
+ name = "smallvec"
226
+ version = "1.15.2"
227
+ source = "registry+https://github.com/rust-lang/crates.io-index"
228
+ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
229
+
230
+ [[package]]
231
+ name = "syn"
232
+ version = "2.0.118"
233
+ source = "registry+https://github.com/rust-lang/crates.io-index"
234
+ checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
235
+ dependencies = [
236
+ "proc-macro2",
237
+ "quote",
238
+ "unicode-ident",
239
+ ]
240
+
241
+ [[package]]
242
+ name = "target-lexicon"
243
+ version = "0.13.5"
244
+ source = "registry+https://github.com/rust-lang/crates.io-index"
245
+ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
246
+
247
+ [[package]]
248
+ name = "thiserror"
249
+ version = "2.0.18"
250
+ source = "registry+https://github.com/rust-lang/crates.io-index"
251
+ checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
252
+ dependencies = [
253
+ "thiserror-impl",
254
+ ]
255
+
256
+ [[package]]
257
+ name = "thiserror-impl"
258
+ version = "2.0.18"
259
+ source = "registry+https://github.com/rust-lang/crates.io-index"
260
+ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
261
+ dependencies = [
262
+ "proc-macro2",
263
+ "quote",
264
+ "syn",
265
+ ]
266
+
267
+ [[package]]
268
+ name = "unicode-ident"
269
+ version = "1.0.24"
270
+ source = "registry+https://github.com/rust-lang/crates.io-index"
271
+ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
272
+
273
+ [[package]]
274
+ name = "unindent"
275
+ version = "0.2.4"
276
+ source = "registry+https://github.com/rust-lang/crates.io-index"
277
+ checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
278
+
279
+ [[package]]
280
+ name = "windows-link"
281
+ version = "0.2.1"
282
+ source = "registry+https://github.com/rust-lang/crates.io-index"
283
+ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
@@ -0,0 +1,32 @@
1
+ [package]
2
+ name = "rust_py_cache"
3
+ version = "0.1.1"
4
+ edition = "2021"
5
+ readme = "README.md"
6
+
7
+ # Biblioteca de extensão para Python:
8
+ # - crate-type = ["cdylib"] -> gera um .so/.dylib carregável pelo Python
9
+ # - name = "_rust_py_cache" -> nome do módulo nativo (importado pelo __init__.py)
10
+ [lib]
11
+ name = "_rust_py_cache"
12
+ crate-type = ["cdylib"]
13
+
14
+ [dependencies]
15
+ # abi3-py310: compila contra a Stable ABI do CPython 3.10+, gerando uma única
16
+ # wheel compatível com 3.10, 3.11, 3.12, 3.13...
17
+ # extension-module: não linka o binário do Python (o interpretador resolve em runtime).
18
+ pyo3 = { version = "0.24", features = ["abi3-py310", "extension-module"] }
19
+
20
+ # DashMap: HashMap concorrente (lock por shard). Permite get/set de várias threads
21
+ # Python sem um Mutex global no caminho crítico. Usado pelo RustCache (Etapa 4).
22
+ dashmap = "6"
23
+
24
+ # thiserror: deriva o trait std::error::Error para nossos erros (errors.rs),
25
+ # que depois convertemos em exceções Python.
26
+ thiserror = "2"
27
+
28
+ # --- Reservado para a v0.2 (NÃO incluso ainda para evitar dependência morta) ---
29
+ # serde = { version = "1", features = ["derive"] } # serializer JSON
30
+ # serde_json = "1" # serializer JSON
31
+ # No MVP o TTL usa std::time::SystemTime (epoch em ms), então o crate `time`
32
+ # também não é necessário ainda.
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.4
2
+ Name: rust-py-cache
3
+ Version: 0.1.1
4
+ Classifier: Programming Language :: Rust
5
+ Classifier: Programming Language :: Python :: 3
6
+ Classifier: Programming Language :: Python :: Implementation :: CPython
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
9
+ Classifier: Topic :: Database
10
+ Classifier: Topic :: System :: Distributed Computing
11
+ Summary: An ultra-fast local cache for Python, powered by Rust.
12
+ Keywords: cache,rust,pyo3,ttl,performance
13
+ Author-email: Roberto Lima <robertolima.izphera@gmail.com>
14
+ License: MIT
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
17
+ Project-URL: Homepage, https://github.com/robertolima/rust-py-cache
18
+
19
+ # rust-py-cache
20
+
21
+ > **An ultra-fast local cache for Python, powered by Rust.**
22
+
23
+ A local, in-memory, thread-safe cache with TTL, lazy expiration, and metrics. The
24
+ core is written in Rust (PyO3 + maturin) on top of a concurrent `DashMap`; the
25
+ Python API is minimal. Think of it as a "mini Redis" living **inside** your Python
26
+ process.
27
+
28
+ [![PyPI](https://img.shields.io/pypi/v/rust-py-cache)](https://pypi.org/project/rust-py-cache/)
29
+ [![Python](https://img.shields.io/pypi/pyversions/rust-py-cache)](https://pypi.org/project/rust-py-cache/)
30
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow)](./LICENSE)
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ pip install rust-py-cache
36
+ ```
37
+
38
+ To work on it locally (requires Rust + maturin):
39
+
40
+ ```bash
41
+ python -m venv .venv && source .venv/bin/activate
42
+ pip install maturin pytest
43
+ maturin develop # compiles the Rust core and installs into the venv
44
+ pytest # runs the tests
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ ```python
50
+ from rust_py_cache import Cache
51
+
52
+ cache = Cache()
53
+
54
+ cache.set("user:1", {"name": "Roberto"}, ttl=60) # ttl in seconds
55
+ user = cache.get("user:1") # {"name": "Roberto"}
56
+ cache.get("missing", default=0) # 0
57
+
58
+ cache.exists("user:1") # True (honors TTL)
59
+ cache.delete("user:1") # True if removed, False if absent
60
+ cache.len() # approximate size
61
+ cache.keys() # list of keys
62
+ cache.cleanup_expired() # remove expired entries; returns the count
63
+ cache.clear() # remove everything (keeps counters)
64
+ cache.stats() # {'hits', 'misses', 'sets', 'deletes', 'expired', 'size'}
65
+ ```
66
+
67
+ ### Memoization decorator
68
+
69
+ ```python
70
+ @cache.cached(ttl=60)
71
+ def add(a, b):
72
+ return a + b
73
+
74
+ add(2, 3) # runs and caches
75
+ add(2, 3) # served from cache
76
+
77
+ # custom key (fixed string or callable):
78
+ @cache.cached(ttl=300, key=lambda user_id: f"user:{user_id}")
79
+ def load_user(user_id):
80
+ ...
81
+ ```
82
+
83
+ See full examples under [`examples/`](./examples) (FastAPI and Django).
84
+
85
+ ## API
86
+
87
+ | Method | Description |
88
+ |---|---|
89
+ | `set(key, value, ttl=None)` | Store a value. `ttl` in seconds (`int`/`float`); `None` = no expiration; `ttl <= 0` → `ValueError`. Overwrites. |
90
+ | `get(key, default=None)` | The value, or `default` if missing/expired (expired entries are removed). |
91
+ | `delete(key)` | `True` if removed, `False` if it didn't exist. |
92
+ | `exists(key)` | `True`/`False`, honoring TTL. |
93
+ | `keys()` | List of keys (may include expired-but-not-yet-collected ones). |
94
+ | `len()` / `len(cache)` | Approximate size. |
95
+ | `clear()` | Remove everything (does not reset counters). |
96
+ | `cleanup_expired()` | Remove expired entries; returns how many. |
97
+ | `stats()` | `dict` with `hits, misses, sets, deletes, expired, size`. |
98
+ | `@cache.cached(ttl=None, key=None)` | Memoization decorator. |
99
+
100
+ ## How it works
101
+
102
+ - **Serialization:** in the MVP, values are serialized with `pickle` (on the Python
103
+ side, via PyO3) and stored as opaque bytes (`Vec<u8>`) in the Rust core.
104
+ - **Concurrency:** `DashMap` (a HashMap with per-shard locks) plus `AtomicU64`
105
+ counters, with no global lock on the hot path. Thread-safe, no busy loop.
106
+ - **TTL:** expiration is **lazy** — an expired key is removed when accessed
107
+ (`get`/`exists`) or via `cleanup_expired()`. There is no background thread in the MVP.
108
+
109
+ ## Limitations
110
+
111
+ - The cache is **process-local**: multiple workers = multiple independent caches.
112
+ - It does **not** replace Redis for distributed caching.
113
+ - Data is **lost** when the process restarts.
114
+ - `pickle` must **not** be used to deserialize untrusted data.
115
+ - Lazy TTL: expired items may linger until accessed or until `cleanup_expired()`.
116
+
117
+ ## Development
118
+
119
+ ```bash
120
+ cargo test # Rust core tests
121
+ maturin develop # rebuild and install
122
+ pytest # Python tests
123
+ ```
124
+
125
+ > If `maturin develop` complains about both `VIRTUAL_ENV` and `CONDA_PREFIX` being
126
+ > set, run `conda deactivate` first, or use `env -u CONDA_PREFIX maturin develop`.
127
+
128
+ ## Roadmap
129
+
130
+ Stages and next steps (LRU/LFU eviction, background expiration, configurable
131
+ serializer, namespaces, etc.) are in [ROADMAP.md](./ROADMAP.md).
132
+
133
+ ## License
134
+
135
+ MIT
136
+
@@ -0,0 +1,71 @@
1
+ # Publicação — TestPyPI e PyPI (Etapas 22 e 23)
2
+
3
+ > Estas etapas exigem **suas credenciais** e publicam artefatos externos. Por isso
4
+ > ficam documentadas aqui e **não** são executadas automaticamente. Rode você,
5
+ > conscientemente, quando a v0.1 estiver pronta.
6
+
7
+ ## Pré-requisitos
8
+
9
+ ```bash
10
+ pip install maturin twine
11
+ ```
12
+
13
+ Tenha contas em https://test.pypi.org e https://pypi.org e gere um **API token**
14
+ em cada uma (Account settings → API tokens).
15
+
16
+ ## 1. Build dos artefatos
17
+
18
+ ```bash
19
+ # wheel(s) abi3 + sdist na pasta dist/
20
+ maturin build --release --out dist
21
+ maturin sdist --out dist
22
+ ```
23
+
24
+ ## 2. Publicar no TestPyPI (Etapa 22 — ensaio)
25
+
26
+ ```bash
27
+ twine upload --repository testpypi dist/*
28
+ # usuário: __token__ | senha: o token do TestPyPI (começa com "pypi-")
29
+ ```
30
+
31
+ Teste a instalação a partir do TestPyPI num venv limpo:
32
+
33
+ ```bash
34
+ pip install --index-url https://test.pypi.org/simple/ \
35
+ --extra-index-url https://pypi.org/simple/ \
36
+ rust-py-cache
37
+ python -c "from rust_py_cache import Cache; c=Cache(); c.set('k',1); print(c.get('k'))"
38
+ ```
39
+
40
+ ## 3. Publicar no PyPI (Etapa 23 — produção)
41
+
42
+ ```bash
43
+ twine upload dist/*
44
+ # usuário: __token__ | senha: o token do PyPI
45
+ ```
46
+
47
+ ## Alternativa recomendada: release automático por tag
48
+
49
+ O workflow [`.github/workflows/release.yml`](.github/workflows/release.yml) já faz
50
+ build multi-plataforma e publica no PyPI via **Trusted Publishing (OIDC)** — sem
51
+ token no repositório. Para usar:
52
+
53
+ 1. Em https://pypi.org → projeto → *Publishing*, registre o publisher confiável
54
+ (owner/repo, workflow `release.yml`, environment `pypi`).
55
+ 2. Bump da versão em `Cargo.toml` **e** `pyproject.toml` (manter iguais).
56
+ 3. Crie e publique a tag:
57
+
58
+ ```bash
59
+ git tag v0.1.0
60
+ git push origin v0.1.0
61
+ ```
62
+
63
+ O workflow constrói as wheels (Linux/macOS/Windows) + sdist e publica.
64
+
65
+ ## Checklist de versão
66
+
67
+ - [ ] `version` igual em `Cargo.toml` e `pyproject.toml`
68
+ - [ ] `__version__` em `python/rust_py_cache/__init__.py`
69
+ - [ ] `cargo test` e `pytest` verdes
70
+ - [ ] README atualizado
71
+ - [ ] Tag `vX.Y.Z` criada