janelkit-store
Los datos compartidos de una aplicación JanelKit, en un solo sitio: una clase con campos reactivos, acciones que agrupan y anotan cada cambio, escritura controlada si se quiere, una instancia por aplicación (la misma en todas las ventanas y vistas) y persistencia en disco.
pip install janelkit-store
En un minuto
# src/stores/carrito.py
from janelkit import background, computed
from janelkit_store import field, store
@store(strict=True, persist="carrito")
class Carrito:
items: list[dict] = []
nota: str = field("", bind=True) # en un store estricto, solo los marcados admiten bind:
_cache: dict = {} # con _: interno (ni reactivo, ni se anota, ni se guarda)
@computed
def total(self) -> float: # derivado, en caché
return sum(i["precio"] for i in self.items)
def agregar(self, item): # cada método es una acción
self.items.append(item)
async def cargar(self):
self.items = await background(leer) # tras el await sigue siendo «cargar»
<script>
from janelkit_store import use
from stores.carrito import Carrito
carrito = use(Carrito) # la misma instancia en todas las vistas y ventanas: sin provide ni props
</script>
<text>{len(carrito.items)} productos · {carrito.total:.2f} €</text>
<textfield bind:value={carrito.nota} />
<button on:click={carrito.agregar(producto)}>Añadir</button>
Cuándo usar un store
| Lo que guardas | Dónde |
|---|---|
| Lo de un solo componente (un borrador, si un panel está abierto) | state en su <script> |
| Lo que comparten varias vistas o ventanas (el usuario, los documentos, el carrito) | Un store |
| Un objeto con estado propio del que hay varios (una cámara, una conexión) | Una clase con state como atributos, o un store con key= |
| Una preferencia suelta que se recuerda | persisted |
Campos, derivados y acciones
- Campos: cada anotación de la clase es un campo reactivo, con su valor por defecto (copiado para cada instancia). Se leen y escriben como atributos normales, sin
.value, en Python y en la plantilla. Lo anidado (una tarjeta dentro de una lista) es del campo que lo contiene. - Derivados:
@computedsobre un método da un valor en caché que se lee sin.valuey no se asigna. - Acciones: cada método es una acción, salvo
setup,teardowny los__dunder__. Agrupa sus cambios en unbatch(lo que depende de ellos se recalcula una vez) y se anota como unaMutation. Una acción dentro de otra del mismo store se suma a ella. En un métodoasync, lo que cambia tras unawaitsigue siendo de esa acción. Lo que crea una acción (uneffect, untimeout, una tarea) es del store y vive lo que él. setup(self)(osetup(self, key)) se ejecuta al crearse la instancia, yteardown(self)al liberarse. No son acciones: lo que cambian no se anota.- Un store no define
__init__: los valores iniciales son los de la clase, ysetupprepara lo demás.
Escritura controlada: strict=True
En un store estricto, fuera de sus métodos solo se lee: carrito.items.append(x) desde una vista (también carrito.items[0]["precio"] = 9) da un StoreError que dice qué hacer, y la escritura no se hace. bind: solo vale con los campos field(..., bind=True); un bind: a otro campo se registra como error.
Sin strict (por defecto) se puede escribir desde cualquier parte; lo que no pasa por una acción se anota como «sin acción».
Una instancia por aplicación: use()
use(Carrito)da la instancia de la aplicación en marcha: la misma en todas las vistas y ventanas. Se crea la primera vez que se pide y se libera al cerrar la aplicación.use(Documento, key=ruta)da una por clave. Si se pide en el script de un componente, se libera (con suteardown) cuando se desmonta el último componente que la pidió.- En las pruebas con
janelkit.inspect.mount, cada montaje empieza con instancias nuevas: una prueba no ve los datos de la anterior. Carrito()crea una instancia suelta, sin aplicación: así se prueba un store sin Qt.Carrito(items=[...])le da valores iniciales.- Un store puede usar otro:
use(Sesion)dentro de un método o desetup.
Seguir los cambios
from janelkit_store import subscribe, snapshot, reset
cancelar = subscribe(carrito, lambda m: print(m)) # Carrito.agregar: items
snapshot(carrito) # {"items": [...], "nota": ""}: copia de los campos públicos
reset(carrito) # vuelve a los valores por defecto (una acción «reset»)
- Cada acción que cambia algo produce una
Mutation:.store(nombre de la clase),.key,.action(el método,"bind:campo"oNone) y.changes, unaChangepor campo con.field,.kind("set"si se asignó,"mutate"si se modificó en el sitio),.oldy.new. Las copias de.oldy.newsolo se hacen si alguien observa (subscribeoJANELKIT_STORE_LOG); si no, sonNone. - Con
JANELKIT_STORE_LOG=1, cadaMutationse registra en el loggerjanelkit_store.
Persistencia
@store(persist="carrito")guarda los campos públicos en<carpeta de datos>/carrito.jsony los lee al crearse la instancia.field(..., persist=False)deja un campo fuera.persisted(clave, valor_inicial)es unstatesuelto que se recuerda:tema = persisted("tema", "claro"). Se usa igual que unstate(.value,bind:); la misma clave da el mismo objeto..flush()escribe ya lo pendiente.- Los cambios se escriben agrupados (como mucho cada 300 ms) y al cerrar la aplicación, de forma atómica. Solo valores JSON: textos, números, booleanos,
None, listas y diccionarios con claves de texto. - La carpeta de datos es la de la sesión:
JANELKIT_DATA_DIR, la temporal dejanelkit.inspect.mount, o la carpeta de datos del usuario más el nombre del proyecto (%APPDATA%\gestoren Windows,~/Library/Application Support/gestoren macOS,~/.config/gestoren Linux). Para probar que algo se recuerda entre dos montajes, fijaJANELKIT_DATA_DIR. - Es para datos pequeños y medianos: cada cambio reescribe el archivo entero.
En pruebas
from janelkit.inspect import mount
from stores.carrito import Carrito
def test_anadir():
with mount("src/App.janel") as app:
app.find("button", text="Añadir").click()
assert len(app.store(Carrito).items) == 1
assert [m.action for m in app.mutations()] == ["agregar"]
Al importarse, el paquete añade a janelkit.inspect app.store(Clase, key=None) (la instancia de la aplicación montada) y app.mutations() (sus mutaciones, de la más antigua a la más reciente).
Referencia
| Nombre | Descripción |
|---|---|
store |
Decorador de clase: @store, @store(strict=True, persist="clave"). |
field |
field(defecto, bind=False, persist=True): un campo con opciones. |
use |
use(Clase, key=None): la instancia de la aplicación (ver arriba). Sin aplicación en marcha, StoreError. |
subscribe |
subscribe(instancia, fn) -> cancelar: fn(mutation) tras cada acción que cambie algo. |
snapshot |
snapshot(instancia) -> dict: copia de los campos públicos. |
reset |
reset(instancia): los campos vuelven a su valor por defecto. |
mutations |
mutations(): las mutaciones de la aplicación en marcha. |
Mutation, Change |
Lo que reciben los suscriptores (ver arriba). |
StoreError |
Escribir fuera de una acción en un store estricto, use() sin aplicación, una clase mal declarada. |
persisted, Persisted |
Un state suelto que se recuerda entre sesiones (ver arriba). |
Ejemplos: apps/examples/carrito (dos ventanas con el mismo store, modo estricto y registro de cambios) y apps/examples/preferencias (persisted).
Cómo está hecho
Solo usa la API pública del núcleo: janelkit.ext.session() (una instancia por aplicación y su carpeta de datos), on_write y own (de qué campo es cada escritura, y el modo estricto), Scope (lo que crea un store es suyo), @computed en métodos y bind: a través de descriptores (__jk_bind__).
Compatibilidad
| janelkit-store | janelkit | Python |
|---|---|---|
| 0.2.x.x | 0.2.x.x | ≥ 3.10 |
Comparte la serie con el núcleo: janelkit-store 0.2.x.x va con janelkit 0.2.x.x. Los dos últimos números son suyos (política de versiones).
Cambios
0.2.0.0 (2026-11-05)
- Primera versión, en la serie 0.2:
store,field,use,subscribe,snapshot,reset,mutations.persistedvenía del núcleo: cambiafrom janelkit import persistedporfrom janelkit_store import persisted(janel migratelo hace).
Licencia
MIT.
Metadata
Release files for janelkit-store 0.2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| janelkit_store-0.2.0.0.tar.gz | 22.4 kB | With provenance | Details for janelkit_store-0.2.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| janelkit_store-0.2.0.0-py3-none-any.whl | Python 3 | none | any | Details for janelkit_store-0.2.0.0-py3-none-any.whl |
Total release size: 38.8 kB
Release files / janelkit_store-0.2.0.0.tar.gz
| Download URL | janelkit_store-0.2.0.0.tar.gz |
|---|---|
| Size | 22.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
efb7fce7f8b406dd792a91e71e9da3249c5f5e83b08f75b6c4114325ae70e94c
|
|
BLAKE2b-256 checksum How to use checksums |
2afb09ba903acde13d6b3b4627759634eb776e461903a40e3014ffd3ec86f5c0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.
Transparency logRelease files / janelkit_store-0.2.0.0-py3-none-any.whl
| Download URL | janelkit_store-0.2.0.0-py3-none-any.whl |
|---|---|
| Size | 16.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
12d5e3a26f3ae5e9d625377b12de0504890cfccbc95114de22a145b45b619782
|
|
BLAKE2b-256 checksum How to use checksums |
bf6dd2c2d862eaf8b3932e847075b72b19aa3c9ee5f92a3b953a2c9860cf12cb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.
Transparency logSecurity for janelkit-store 0.2.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for janelkit_store-0.2.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.