Skip to content

Tighten exception types and wrap missing-cell lookups#3380

Merged
quaquel merged 15 commits intomesa:mainfrom
falloficarus22:exception-hierarchy-migration
Mar 2, 2026
Merged

Tighten exception types and wrap missing-cell lookups#3380
quaquel merged 15 commits intomesa:mainfrom
falloficarus22:exception-hierarchy-migration

Conversation

@falloficarus22
Copy link
Copy Markdown
Contributor

@falloficarus22 falloficarus22 commented Feb 26, 2026

Summary

This PR applies a targeted exception cleanup in discrete_space:

  • It keeps domain-specific errors only where they add API value:
    • DiscreteSpace.__getitem__ now raises CellMissingException instead of leaking internal KeyError.
  • It uses built-in Python exceptions for normal input validation:
    • TypeError for wrong argument types (Grid.torus, Grid.capacity, Network.layout).
    • ValueError for invalid-but-well-typed values (e.g., out-of-bounds non-torus grid lookups).

What changed

  • DiscreteSpace.__getitem__ now wraps internal KeyError as CellMissingException.
    • This hides internal dict details and exposes a domain-specific error to users.
  • Type-validation checks now use TypeError (instead of ValueError) where appropriate:
    • Grid._validate_parameters:
      • torus must be bool
      • capacity must be int | float | None
    • Network.__init__:
      • layout must be Mapping or Callable
  • Updated docstring in Grid.find_nearest_cell to match actual raised exception (ValueError for out-of-bounds non-torus positions).

Tests

  • Added coverage for CellMissingException on invalid coordinate access.
  • Added/updated validation tests to assert TypeError for type mismatches.
  • Updated spatial lookup test to expect TypeError for invalid Network(layout=...).

@github-actions
Copy link
Copy Markdown

Performance benchmarks:

Model Size Init time [95% CI] Run time [95% CI]
BoltzmannWealth small 🔵 -0.1% [-0.4%, +0.2%] 🔵 -0.6% [-0.9%, -0.4%]
BoltzmannWealth large 🔵 +0.3% [+0.0%, +0.5%] 🔵 -0.1% [-0.6%, +0.2%]
Schelling small 🔵 +0.9% [+0.6%, +1.1%] 🔵 -0.1% [-0.2%, +0.0%]
Schelling large 🔵 -0.9% [-1.2%, -0.5%] 🔵 +0.0% [-0.5%, +0.5%]
WolfSheep small 🔵 -1.1% [-1.3%, -0.9%] 🔵 +0.4% [+0.2%, +0.6%]
WolfSheep large 🔵 +0.3% [-0.0%, +0.6%] 🔵 +0.4% [+0.2%, +0.6%]
BoidFlockers small 🔵 -1.3% [-1.6%, -0.9%] 🔵 -0.1% [-0.3%, +0.1%]
BoidFlockers large 🔵 -1.0% [-1.3%, -0.7%] 🔵 +0.4% [+0.2%, +0.6%]

of recursion to avoid RecursionError on large radius values.
"""
if radius < 1:
raise ValueError("radius must be larger than one")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is an overarching comment: where normal python errors are clearly appropriate, I prefer to use those. So here, value error is fully appropriate and I fail to see a need to replace those with custom mesa errors.

Copy link
Copy Markdown
Member

@quaquel quaquel Feb 26, 2026

Choose a reason for hiding this comment

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

As far as I can tell, you only changed this line. Can you carefully review the PR and if you are replacing a value or type error with a custom error, indicate why you think that is appropriate?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've refined the exception migration based on your feedback. I've focused the custom MesaException hierarchy on cases where it provides domain-specific context or hides internal implementation details.

For standard argument validation (type checking, value ranges), I've reverted to using built-in ValueError and TypeError as they are more than sufficient for those tasks and follow standard Python conventions.

try:
return self._cells[key]
except KeyError as e:
raise CellMissingException(key) from e
Copy link
Copy Markdown
Member

@quaquel quaquel Feb 26, 2026

Choose a reason for hiding this comment

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

Here, I am fine with wrapping the key error. It is not obvious to the user that DiscretSpace uses a dictionary internally.

Comment on lines -171 to +173
raise ValueError("Torus must be a boolean.")
raise TypeError("Torus must be a boolean.")
if self.capacity is not None and not isinstance(self.capacity, float | int):
raise ValueError("Capacity must be a number or None.")
raise TypeError("Capacity must be a number or None.")
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed to TypeError because this is type validation, not value-range validation.

node_positions = layout
else:
raise ValueError(
raise TypeError(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is validating the type/form of layout (Mapping or Callable expected). Invalid type should be TypeError

G.add_node(1)

with pytest.raises(ValueError):
with pytest.raises(TypeError):
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

test must match production behavior after Network(..., layout=[]), changed to raise TypeError.

@falloficarus22 falloficarus22 force-pushed the exception-hierarchy-migration branch from 8ff6297 to f8140be Compare February 26, 2026 11:35
@falloficarus22 falloficarus22 changed the title Migrate discrete_space exceptions to MesaException hierarchy Tighten exception types and wrap missing-cell lookups Feb 26, 2026
@falloficarus22
Copy link
Copy Markdown
Contributor Author

I narrowed the scope of this PR:
Instead of broadly replacing ValueError/TypeError with Mesa-specific exceptions across discrete_space, this PR now does two focused things:

Keep built-in exceptions for standard argument validation (TypeError for wrong types, ValueError for invalid values).
Use a Mesa-specific exception only where it improves the public API (CellMissingException in DiscreteSpace.__getitem__, instead of exposing internal KeyError behavior).

@quaquel
Copy link
Copy Markdown
Member

quaquel commented Feb 26, 2026

@falloficarus22 thanks for the update. I'll try to review later today. Might it be an idea to write up something in the contributers guidlines sumarizing how to handle exceptions in Mesa?

@falloficarus22
Copy link
Copy Markdown
Contributor Author

Might it be an idea to write up something in the contributers guidlines sumarizing how to handle exceptions in Mesa?

This is indeed important. We can add a subsection under testing and code standards in CONTRIBUTING.md.
I'll address that in a seperate PR

@quaquel quaquel merged commit 6c29977 into mesa:main Mar 2, 2026
12 of 14 checks passed
@quaquel quaquel added the enhancement Release notes label label Mar 2, 2026
@falloficarus22 falloficarus22 deleted the exception-hierarchy-migration branch March 2, 2026 13:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Release notes label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants