Skip to content

replace AttributeDict with a dataclass#3138

Merged
quaquel merged 24 commits intomesa:mainfrom
quaquel:signal_dataclass
Jan 17, 2026
Merged

replace AttributeDict with a dataclass#3138
quaquel merged 24 commits intomesa:mainfrom
quaquel:signal_dataclass

Conversation

@quaquel
Copy link
Copy Markdown
Member

@quaquel quaquel commented Jan 15, 2026

Summary

Replace the generic AttributeDict helper class with a dedicated frozen dataclass Message for improved type safety and developer experience when working with Mesa signals.

Bug / Issue

The current signal implementation uses AttributeDict, a generic dictionary with attribute-like access. This has several limitations:

  • No IDE autocomplete support when writing signal handlers
  • No type safety - typos in field names fail at runtime
  • Less clear intent - the structure of signal data is implicit
  • Inconsistent field access patterns

Part of #3131 (addresses item 1)

Implementation

Replaced AttributeDict with a frozen dataclass Message in the mesa_signals module:

  1. New Message dataclass in signals_util.py with explicit fields: name, old, new, owner, signal_type, additional_kwargs
  2. Updated type hints throughout mesa_signal.py to accept str | SignalType | All where appropriate
  3. Changed signal.typesignal.signal_type (more explicit, avoids Python keyword)
  4. Additional kwargs now stored in additional_kwargs dict rather than spread into the signal object

Local benchmarking shows no measurable performance difference.

Testing

All existing tests pass with updated field names. Type checking and IDE autocomplete now work correctly for signal handlers:

def on_change(signal: Message):  # Type hint enables autocomplete
    assert signal.signal_type == SignalType.CHANGE
    assert signal.old == 10

Additional Notes

This is a breaking change for experimental code:

  • signal.typesignal.signal_type
  • Custom kwargs now in signal.additional_kwargs dict

Since mesa_signals is experimental, breaking changes are allowed per Mesa's policy.

@github-actions
Copy link
Copy Markdown

Performance benchmarks:

Model Size Init time [95% CI] Run time [95% CI]
BoltzmannWealth small 🔵 -0.6% [-1.4%, +0.4%] 🔵 -1.0% [-1.2%, -0.9%]
BoltzmannWealth large 🔵 -0.3% [-1.1%, +0.5%] 🔵 -1.0% [-3.1%, +1.5%]
Schelling small 🔵 -0.5% [-0.7%, -0.3%] 🔵 -0.7% [-0.8%, -0.7%]
Schelling large 🔵 +0.4% [+0.1%, +0.7%] 🔵 -0.8% [-1.6%, +0.2%]
WolfSheep small 🔵 -0.7% [-1.0%, -0.4%] 🔵 -0.1% [-0.3%, +0.1%]
WolfSheep large 🔵 -2.0% [-3.0%, -1.1%] 🟢 -4.0% [-4.6%, -3.4%]
BoidFlockers small 🔵 +0.3% [-0.1%, +0.6%] 🔵 +0.1% [-0.1%, +0.3%]
BoidFlockers large 🔵 +0.6% [+0.2%, +1.1%] 🔵 -0.4% [-0.6%, -0.2%]

@quaquel quaquel changed the title replace attributedict with a dataclass replace AttributeDict with a dataclass Jan 15, 2026
@EwoutH
Copy link
Copy Markdown
Member

EwoutH commented Jan 15, 2026

Quick API check: Are any of these used by downstream users or part of the public API?

  1. Direct attribute access on signals for extra kwargs (e.g., signal.index vs signal.additional_args["index"])
  2. AttributeDict class itself (was exported in __all__)
  3. Custom __dir__ behavior for introspection/autocomplete on signal objects
  4. The type field as a string vs SignalType enum

If any are user-facing, I think we need to document that properly.

@quaquel
Copy link
Copy Markdown
Member Author

quaquel commented Jan 15, 2026

Quick API check: Are any of these used by downstream users or part of the public API?

Its in experimental and we don''t even use it in our own examples at the moment. So I would not worry about it too much.

Custom dir behavior for introspection/autocomplete on signal objects

Not sure what you mean, can you elaborate?

The type field as a string vs SignalType enum

Yes, I merged that PR after opening this one, so I'll fix that

@quaquel quaquel added experimental Release notes label enhancement Release notes label labels Jan 16, 2026
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 working on this.

I didn't review it in-depth, but overal looks good, and it's the experimental space, which is meant to move fast.

@quaquel quaquel merged commit 8b2c38d into mesa:main Jan 17, 2026
14 checks passed
quaquel added a commit to quaquel/mesa that referenced this pull request Jan 18, 2026
@quaquel quaquel deleted the signal_dataclass branch January 19, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Release notes label experimental Release notes label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants