Skip to content

Refactor SpaceRenderer API to separate setup and draw methods#2893

Merged
Sahil-Chhoker merged 30 commits intomesa:mainfrom
Sahil-Chhoker:viz-api-update
Dec 16, 2025
Merged

Refactor SpaceRenderer API to separate setup and draw methods#2893
Sahil-Chhoker merged 30 commits intomesa:mainfrom
Sahil-Chhoker:viz-api-update

Conversation

@Sahil-Chhoker
Copy link
Copy Markdown
Collaborator

@Sahil-Chhoker Sahil-Chhoker commented Nov 20, 2025

Summary

  1. Improves the current visualization API for SpaceRenderer.
  2. Adds PendingDeprecationWarnings to the old API as an indication that it will be deprecated in the future.
  3. Also changes the variable name from space_kwargs to draw_space_kwargs and chart_kwargs to draw_chart_kwargs in space_drawer to 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.

@Sahil-Chhoker
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 20, 2025

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 20, 2025

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 integration
mesa/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 & state
mesa/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 rename
mesa/visualization/space_drawers.py
Standardized keyword-arg names across all drawers: space_kwargsdraw_space_kwargs, chart_kwargsdraw_chart_kwargs; updated docstrings and internal extraction/propagation for Matplotlib and Altair paths.
Examples updated to fluent API
mesa/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 API
tests/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
Loading

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_kwargs across 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

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
mesa/visualization/space_renderer.py (1)

350-361: Critical bug: References to non-existent attributes.

Lines 355 and 357 reference self.space_kwargs and self.agent_kwargs, but these attributes don't exist. The correct names are self.draw_space_kwargs and self.draw_agent_kwargs.

Apply this diff to fix the bug:

             if self.space_mesh:
-                structure = self.draw_structure(**self.space_kwargs)
+                structure = self.draw_structure()
             if self.agent_mesh:
-                agents = self.draw_agents(self.agent_portrayal, **self.agent_kwargs)
+                agents = self.draw_agents()
             if self.propertylayer_mesh:
-                prop_base, prop_cbar = self.draw_propertylayer(
-                    self.propertylayer_portrayal
-                )
+                prop_base, prop_cbar = self.draw_propertylayer()
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d3c36c and f88aff5.

📒 Files selected for processing (3)
  • mesa/visualization/solara_viz.py (2 hunks)
  • mesa/visualization/space_drawers.py (21 hunks)
  • mesa/visualization/space_renderer.py (5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Sahil-Chhoker
Repo: projectmesa/mesa PR: 2803
File: mesa/visualization/backends/altair_backend.py:312-553
Timestamp: 2025-06-20T16:44:20.677Z
Learning: In mesa/visualization/backends/altair_backend.py, the author prefers to keep the draw_propertylayer method intact despite its complexity, as they believe breaking it into smaller functions would divert attention from the main logic flow. The method is well-documented and follows a clear sequence of operations for property layer rendering.
📚 Learning: 2025-06-20T16:44:20.677Z
Learnt from: Sahil-Chhoker
Repo: projectmesa/mesa PR: 2803
File: mesa/visualization/backends/altair_backend.py:312-553
Timestamp: 2025-06-20T16:44:20.677Z
Learning: In mesa/visualization/backends/altair_backend.py, the author prefers to keep the draw_propertylayer method intact despite its complexity, as they believe breaking it into smaller functions would divert attention from the main logic flow. The method is well-documented and follows a clear sequence of operations for property layer rendering.

Applied to files:

  • mesa/visualization/space_renderer.py
  • mesa/visualization/solara_viz.py
  • mesa/visualization/space_drawers.py
🪛 GitHub Actions: build
mesa/visualization/space_renderer.py

[error] 1-1: SpaceRenderer API mismatch: draw_structure() does not accept grid_color as a keyword, causing failures in Boltzmann model visualization tests.


[error] 1-1: SpaceRenderer API mismatch: draw_agents() does not accept agent_portrayal as a keyword, causing failures in Conways and EpStein visualization tests.


[error] 1-1: SpaceRenderer API mismatch: render() does not accept agent_portrayal as a keyword, causing failures in Schelling, Boid Flockers, and Sugarscape tests.


[error] 1-1: SpaceRenderer API mismatch: draw_structure() does not accept node_kwargs as a keyword, causing failures in Virus on Network visualization.


[error] 1-1: SpaceRenderer API mismatch: draw_agents() does not accept agent_portrayal as a keyword, causing failures in EpStein Civil Violence and Wolf-Sheep tests.


[error] 1-1: SpaceRenderer API mismatch: draw_propertylayer() is invoked with a positional argument in tests, but the method signature does not expect it, causing failure in no-property-layers test.

🔇 Additional comments (8)
mesa/visualization/space_drawers.py (2)

85-90: LGTM - Parameter naming improved.

The parameter rename from space_kwargs to draw_space_kwargs makes the API more explicit and clear.


126-133: LGTM - Consistent parameter naming.

The parameter rename from chart_kwargs to draw_chart_kwargs is consistent with the matplotlib path changes.

mesa/visualization/space_renderer.py (5)

7-7: LGTM - Future annotations import.

Good practice to enable forward references for type hints.


73-77: Good: Internal state for chainable API.

The addition of internal state variables enables the chainable setup/draw pattern, which improves the API ergonomics.


173-186: LGTM - Chainable setup method.

The setup_structure() method follows the builder pattern by returning self for method chaining.


187-202: LGTM - Chainable setup method for agents.

The setup_agents() method properly stores both the portrayal function and drawing kwargs.


203-217: LGTM - Chainable setup method for property layers.

The setup_propertylayer() method completes the setup API trio.

mesa/visualization/solara_viz.py (1)

273-277: Rewrite the review comment: The draw methods are called without setup_* methods, and without ensuring the renderer has configured portrayal functions. This will cause collect_agent_data to crash when trying to call None as the agent_portrayal function.

Either:

  • Call setup_agents(), setup_structure(), and setup_propertylayer() with appropriate portrayal functions before drawing, or
  • Provide default portrayal functions (as done in make_altair_space), or
  • Document that the renderer must have portrayal pre-configured before being passed to SpaceRendererComponent

Verify the intended API pattern and ensure portrayal is properly initialized.

@github-actions

This comment was marked as off-topic.

@Sahil-Chhoker Sahil-Chhoker changed the title SpaceRenderer API update Refactor SpaceRenderer API to separate setup and draw methods Nov 20, 2025
@Sahil-Chhoker
Copy link
Copy Markdown
Collaborator Author

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)?

Copy link
Copy Markdown
Member

@quaquel quaquel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks basically fine to me.

2 points:

  1. Do we want to keep backward compatibility? I am fine with breaking things, but it's good to discuss this
  2. All examples still need to be updated. Do you plan to make that part of this PR?

@Sahil-Chhoker
Copy link
Copy Markdown
Collaborator Author

  1. 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.

  1. All examples still need to be updated. Do you plan to make that part of this PR?

I intend to, what do you suggest?

@quaquel
Copy link
Copy Markdown
Member

quaquel commented Nov 21, 2025

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.

@Sahil-Chhoker Sahil-Chhoker added example Changes the examples or adds to them. enhancement Release notes label labels Nov 22, 2025
Copy link
Copy Markdown
Member

@EwoutH EwoutH left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the effort so far!

Doesn’t need to be this PR (it can), but before we deprecate this officially we should have a section in the migration guide and the deprecation messages should point to that section.

Copy link
Copy Markdown
Member

@quaquel quaquel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. 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 a FutureWarning (for all users of a library) these days, or alternatively aDeprecationWarning (for code that uses mesa from within __main__).
  2. 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.
  3. 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.

@Sahil-Chhoker
Copy link
Copy Markdown
Collaborator Author

  1. 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 a FutureWarning (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.

  1. 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.

  1. 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.

@EwoutH
Copy link
Copy Markdown
Member

EwoutH commented Nov 26, 2025

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.

@quaquel
Copy link
Copy Markdown
Member

quaquel commented Dec 1, 2025

Ok, fine with me. Let's first agree on deprecation in #2900 and then afterwards merge this.

@EwoutH
Copy link
Copy Markdown
Member

EwoutH commented Dec 2, 2025

Could you check if we now follow the policy as described in #2900?

@tpike3
Copy link
Copy Markdown
Member

tpike3 commented Dec 6, 2025

From what I can tell this follows #2900 (step 1). @Sahil-Chhoker and @quaquel is this ready to merge?

@quaquel
Copy link
Copy Markdown
Member

quaquel commented Dec 7, 2025

just a last check for the deprecations and then yes it's ready to go.

@EwoutH
Copy link
Copy Markdown
Member

EwoutH commented Dec 10, 2025

@quaquel your formal review status is still “requested changes”, blocking merging.

@Sahil-Chhoker ready to merge, right?

@EwoutH
Copy link
Copy Markdown
Member

EwoutH commented Dec 10, 2025

Thanks Jan!

@Sahil-Chhoker, we're good?

@Sahil-Chhoker
Copy link
Copy Markdown
Collaborator Author

@EwoutH, CI seems to be failing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Release notes label example Changes the examples or adds to them. visualisation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants