marimo-team / marimo

A reactive notebook for Python — run reproducible experiments, execute as a script, deploy as an app, and version with git.
https://marimo.io
Apache License 2.0
6.8k stars 240 forks source link

State within class doesn't refresh all relevant cells #2549

Closed andrewhill157 closed 1 week ago

andrewhill157 commented 1 week ago

Describe the bug

I'm not sure if this is just the intended behavior, but I was trying to organize some state into a class I could pass rather than having individual getters and setters and noticed that using the getter / setter functions from mo.state seems to result in correct cell reruns but if they are contained within a class they don't seem to trigger the same cell refresh

Environment

{
  "marimo": "0.9.4",
  "OS": "Darwin",
  "OS Version": "23.5.0",
  "Processor": "arm",
  "Python Version": "3.11.10",
  "Binaries": {
    "Browser": "--",
    "Node": "v21.7.1"
  },
  "Dependencies": {
    "click": "8.1.7",
    "importlib-resources": "missing",
    "jedi": "0.19.1",
    "markdown": "3.7",
    "pygments": "2.18.0",
    "pymdown-extensions": "10.11.2",
    "ruff": "0.6.9",
    "starlette": "0.38.6",
    "tomlkit": "0.13.2",
    "typing-extensions": "4.12.2",
    "uvicorn": "0.31.0",
    "websockets": "12.0"
  },
  "Optional Dependencies": {
    "altair": "5.4.1",
    "polars": "1.9.0",
    "pyarrow": "17.0.0"
  }
}

Code to reproduce

import marimo

__generated_with = "0.9.4"
app = marimo.App(width="medium")

@app.cell
def __():
    import marimo as mo

    class ContainsState:
        def __init__(self):
            self.getter, self.setter = mo.state(set(), allow_self_loops=True)

    container = ContainsState()
    return ContainsState, container, mo

@app.cell
def __(mo):
    getter, setter = mo.state(set(), allow_self_loops=True)
    return getter, setter

@app.cell
def __(container):
    container.getter()
    return

@app.cell
def __(container):
    container.setter({"A", "D"})
    return

@app.cell
def __(getter):
    getter()
    return

@app.cell
def __(setter):
    setter({"A", "D"})
    return

if __name__ == "__main__":
    app.run()
akshayka commented 1 week ago

This is by design, marimo doesn't introspect objects. The relevant section from the docs is:

When you call the setter function in one cell, all other cells that reference the getter function via a global variable are automatically run (similar to UI elements).

Appreciate it might be easy to miss that line though (from https://docs.marimo.io/guides/state.html#reactive-state)

andrewhill157 commented 1 week ago

ah thank you and sorry for missing / misinterpreting!