Repository navigation
Conversation
812a05c to
4e43bd7
Compare
4e43bd7 to
48d0639
Compare
Make the session's cycle-start queue public, so third-party code can change reactive state the way input updates and `invalidate_later()` do: once all of the session's effects have finished and its outputs have been sent, so effects still running keep seeing stable values. `invalidate_later()` now uses it instead of the private `_cycle_start_action()`. `fn` runs with its session (or module session) as the current session, once per call, in order, each starting a cycle of its own. It is dropped if the session ends first, and an error it raises closes the session. Async functions are rejected with a TypeError, since actions must not yield partway through.
f8687c0 to
c6cf363
Compare
|
After feedback w/ team IRL, we should adopt the Drive by ideas:
I like |
I like this too, but technically, isn't it running on the cycle end rather than the cycle start? I got there by wondering if But IIUC, in this case idle means when the flush cycle ends, i.e. when the cycle is complete. I think the key naming principle of So maybe OTOH, I could have also been confused by the original |
How it differs from |
| on_flushed(fn, once=True) | run_once_when_idle(fn) | |
|---|---|---|
| Runs | after the next outputs message, together with every other on_flushed callback | after all on_flushed callbacks, and only if the session is still idle |
| Several registered | all run in the same pass | one per cycle, each after the previous one’s effects finish and its outputs are sent |
| Session already idle with nothing to send | waits for the next flush that sends something | runs on the next loop pass |
| Shares order with client input updates and timers | no | yes, the same queue |
| What its changes belong to | the cycle that’s just ending | a new cycle of its own |
|
From Agent:
|
|
Thinking about this more from first principles, I've talked myself out of the What the harness actually does around
Against the specific
That leaves verb-first names. Two candidates: 1. Strong prior art in asyncio's scheduling family: 2. Keeps the R Note that both drop "once": one call = one run, so the word is redundant ( After all of this, I lean toward |
Stacked on #2515 (which is stacked on #2508). Fixes #2508's TODO: a public API in place of
invalidate_later's privatesession._cycle_start_action().Why
A session holds input changes and
invalidate_later()timers until all of its effects have finished, so an effect paused at anawaitkeeps seeing the same values. Third-party code couldn't do the same thing without calling a private method. On #2182, Joe asked that someone be able to write their owninvalidate_laterwithout touching private members.Change
session.run_once_when_idle(fn). It runsfnonce, at the start of the session's next cycle: once all of the session's effects have finished and its outputs have been sent. If the session is already idle, it runs on the next pass of the event loop, never synchronously.fnruns with its session as the current session; from a module, that's the module's session.fnmust be synchronous, so the cycle it starts can't be interrupted partway. An async function raisesTypeError. Its return value is ignored, solambda: value.set(x)type-checks.invalidate_later()(and sopoll()andfile_reader()) and client input updates now use it.on_flush().Naming: R calls this
cycleStartAction(). It isn't in R's documented session API, and its only users are Shiny itself andMockShinySession. We chose a verb-first name that says the function runs once, and when.Docs
docs/_quartodoc-core.yml.Verification
tests/pytest/test_concurrency.py:pytest tests/pytest: 1315 passed. pyright, pyrefly, flake8, black, the skill validator and the packaging tests are clean.