Refactor SpaceRenderer API to separate setup and draw methods
Summary
- Improves the current visualization API for
SpaceRenderer. - Adds
PendingDeprecationWarningsto the old API as an indication that it will be deprecated in the future. - Also changes the variable name from
space_kwargstodraw_space_kwargsandchart_kwargstodraw_chart_kwargsinspace_drawerto keep things consistent with the current API
Motive
Refer to #2874.
Implementation
Separated the draw functions in two parts, setup functions, used to update states and draw functions, used to draw stuff (agents, structure, property layer).
Usage Examples
renderer = SpaceRenderer(model, backend="matplotlib").setup_structure(
lw=2, ls="solid", color="blue", alpha=0.1
).setup_agents(
agent_portrayal, draw_agent_kwargs={}
)
# individual methods to draw
renderer.draw_agents()
renderer.draw_structure()
renderer.draw_propertylayers()
# draw all at once
renderer.render()
Additional Notes
@quaquel I couldn't recreate the matplotlib bug, so it would be very helpful if you can provide some kind of example.
Summary by CodeRabbit
-
Refactor
- Introduced a setup-based configuration for visual renderer (portrayals and draw options stored on the renderer; draw/render calls take no portrayal kwargs).
- Renamed and standardized drawing keyword-argument names for consistency across backends.
- Simplified draw paths to use stored renderer state rather than per-call kwargs.
-
Tests
- Updated tests to the builder-style API and adjusted assertions for argument-less draw/render calls.
-
Examples
- Updated example apps to use setup_* chaining and explicit render/draw steps.
✏️ Tip: You can customize this high-level summary in your review settings.
@coderabbitai full review
✅ Actions performed
Full review triggered.
Walkthrough
The visualization API was refactored to a stateful, chainable setup/draw pattern: SpaceRenderer now stores portrayals and draw kwargs via setup_* methods and executes drawings without per-call kwargs. Space drawer parameter names were standardized (space/ chart kwargs renamed), and Solara integration now calls draw_* without forwarding kwargs.
Changes
| Cohort / File(s) | Summary |
|---|---|
Solara integrationmesa/visualization/solara_viz.py |
Removed forwarding of kwargs to SpaceRenderer.draw_structure(), draw_agents(), and draw_propertylayer(); calls now invoke these methods without parameters and rely on renderer internal state. |
SpaceRenderer API & statemesa/visualization/space_renderer.py |
Added chainable setup methods: setup_structure(), setup_agents(), setup_propertylayer() and made draw_*/render() use stored state. Exposed draw_space_kwargs, draw_agent_kwargs, agent_portrayal, propertylayer_portrayal, and post_process property. Added deprecation warnings/compat handling for legacy kwargs/dict portrayals. |
Space drawer kwargs renamemesa/visualization/space_drawers.py |
Standardized keyword-arg names across all drawers: space_kwargs → draw_space_kwargs, chart_kwargs → draw_chart_kwargs; updated docstrings and internal extraction/propagation for Matplotlib and Altair paths. |
Examples updated to fluent APImesa/examples/.../*.py |
Updated example usages to call setup_agents(...), setup_structure(...), and optionally setup_propertylayer(...) and then draw_*() or .render() without passing portrayals/kwargs at draw time (files include many under mesa/examples/basic/... and mesa/examples/advanced/...). |
Tests updated to fluent APItests/test_solara_viz_updated.py, tests/test_space_renderer.py |
Tests adjusted to configure portrayals via setup_* methods and call render()/draw_*() without passing portrayals; mocks updated accordingly. |
Sequence Diagram(s)
sequenceDiagram
participant Page as Solara Viz
participant Renderer as SpaceRenderer
participant Drawer as Space Drawer
Note over Page,Renderer: New stateful, chainable flow
Page->>Renderer: setup_structure(**draw_space_kwargs)
Renderer-->>Page: self
Page->>Renderer: setup_agents(agent_portrayal, **draw_agent_kwargs)
Renderer-->>Page: self
Page->>Renderer: setup_propertylayer(propertylayer_portrayal)
Renderer-->>Page: self
Page->>Renderer: render()
Note right of Renderer: Uses stored state
Renderer->>Drawer: draw_matplotlib() / draw_altair()
Drawer-->>Renderer: rendered output
Renderer-->>Page: visualization
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
- Attention areas:
- Verify all call sites updated to use setup_* and no longer pass portrayals/kwargs to draw/render.
- Confirm consistent initialization and use of
draw_space_kwargs/draw_agent_kwargsacross matplotlib and altair paths. - Review deprecation-warning logic and dict→callable conversion for propertylayer portrayals.
- Ensure examples and tests fully reflect the new API surface.
Possibly related PRs
- projectmesa/mesa#2819 — touches SpaceRenderer usage and example updates; likely overlaps in API changes for portrayals.
- projectmesa/mesa#2803 — similar refactor of SolaraViz and SpaceRenderer to use stateful setup/draw calls; strong code-level relation.
Suggested labels
enhancement, example
Suggested reviewers
- quaquel
- tpike3
- Corvince
Poem
🐰 With chains of setup, I hop and store,
Draw kwargs tucked behind my door.
No more passing each time you call,
I remember portraits, one and all.
A tiny hop—render once for all. ✨
Pre-merge checks and finishing touches
✅ Passed checks (3 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | ✅ Passed | The title clearly and concisely summarizes the main refactoring change: separating SpaceRenderer API into distinct setup and draw methods. |
| Docstring Coverage | ✅ Passed | Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. |
| Description check | ✅ Passed | Pull request provides clear summary, motive, implementation details, usage examples, and notes. Covers the key aspects of the refactoring. |
✨ Finishing touches
- [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
Performance benchmarks:
| Model | Size | Init time [95% CI] | Run time [95% CI] |
|---|---|---|---|
| BoltzmannWealth | small | 🔵 +0.8% [+0.3%, +1.4%] | 🔵 -0.6% [-0.7%, -0.5%] |
| BoltzmannWealth | large | 🔵 +0.1% [-0.1%, +0.4%] | 🔵 -0.4% [-0.6%, -0.2%] |
| Schelling | small | 🔵 +0.2% [+0.1%, +0.4%] | 🔵 -0.5% [-0.6%, -0.3%] |
| Schelling | large | 🔵 +0.2% [-0.1%, +0.4%] | 🔵 -0.7% [-1.1%, -0.3%] |
| WolfSheep | small | 🔵 +0.0% [-0.2%, +0.2%] | 🔵 +0.8% [+0.6%, +0.9%] |
| WolfSheep | large | 🔵 -0.1% [-0.4%, +0.2%] | 🔵 +0.8% [+0.5%, +1.2%] |
| BoidFlockers | small | 🔵 +2.6% [+2.1%, +3.1%] | 🔵 +1.9% [+1.7%, +2.1%] |
| BoidFlockers | large | 🔵 +2.5% [+2.0%, +3.2%] | 🔵 +2.2% [+1.6%, +3.0%] |
I made this kind of in a hurry. I’ll update the tests and add the documentation over the weekend. In the meantime, can you take a look at just the API to see if it’s working fine (it was for me)?
- Do we want to keep backward compatibility? I am fine with breaking things, but it's good to discuss this
We did just release an API update over on a 3.x version, updating it again in 3.3.x wouldn't be very wise in my opinion.
- All examples still need to be updated. Do you plan to make that part of this PR?
I intend to, what do you suggest?
We did just release an API update over on a 3.x version, updating it again in 3.3.x wouldn't be very wise in my opinion.
Fair enough, but then we should ensure that we don't break it. So, e.g., draw_agent should become something like the following
def draw_agents(self, *args, **kwargs):
if args or kwargs: # implicit booleanness of empty list/kwarg to be checked if correct
warnings.warn("some message", DeprecationWarning, stack_level=2)
self.agent_portrayal = args[0]
self_draw_kwargs = kwargs
# normal code goes here.
I intend to, what do you suggest?
Excellent, fixing it in this pr makes most sense to me.
@coderabbitai full review
✅ Actions performed
Full review triggered.
- You are using a
PendingDerpecationWarning. I am not sure that is the best one to use. See the discussion on stack overflow, I personally favor using aFutureWarning(for all users of a library) these days, or alternatively aDeprecationWarning(for code that uses mesa from within__main__).
the discussion also mentions:
PendingDeprecationWarning means "you're going to have to change something eventually", and FutureWarning means "something in the way you're using this isn't correct, and may lead to failure later."
and the term FutureWarning is not descriptive as to what it's referring to.
As @EwoutH mentioned, there will be a minor release before fully deprecating this API, so using DeprecationWarning is also not an option.
- There are a few other changes here (e.g., the name change of kwargs in space_drawers, which technically were not needed and might cause trouble for users. Curious to know why you made the change. I am inclined to suggest reverting those changes.
I just wanted to make naming consistent in the backend, this is not user facing, so I don't think we should worry about that. Let me know if you feel otherwise.
- Have you tested all examples, or are you only relying on the unit tests for this? I just want to ensure that all examples continue to work correctly after this PR is merged.
I've tested all the examples by running them myself, they will work after this PR is merged if I haven't missed anything.
I will try to write up some guidelines on how we deprecate stuff. Than we don’t have to discuss and reinvent that on every PR.
Ok, fine with me. Let's first agree on deprecation in #2900 and then afterwards merge this.
Could you check if we now follow the policy as described in https://github.com/projectmesa/mesa/pull/2900?
From what I can tell this follows #2900 (step 1). @Sahil-Chhoker and @quaquel is this ready to merge?
just a last check for the deprecations and then yes it's ready to go.
@quaquel your formal review status is still “requested changes”, blocking merging.
@Sahil-Chhoker ready to merge, right?
Thanks Jan!
@Sahil-Chhoker, we're good?
@EwoutH, CI seems to be failing.