-
Notifications
You must be signed in to change notification settings - Fork 14
Ensure runtime type-checking with beartype #143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…ctor() parameter "data" PEP 585 type hint dict[str | typing.Any] not subscripted (indexed) by 2 child type hints (i.e., subscripted by 1 != 2 child type hints).
for more information, see https://pre-commit.ci
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThis update introduces comprehensive enhancements to type annotations, runtime type checking, and type alias definitions across the codebase. The Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ModelDF
participant AgentSetDF
participant SpaceDF
User->>ModelDF: create ModelDF()
ModelDF->>AgentSetDF: create AgentSetDF(model)
AgentSetDF->>SpaceDF: interact with SpaceDF (typed as concrete)
AgentSetDF->>AgentSetDF: add/discard/remove agents (runtime checked by @beartype)
AgentSetDF->>AgentSetDF: get/set agents, use advanced indexing
ModelDF->>AgentSetDF: get_agents_of_type(type) returns AgentSetDF
Poem
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
for more information, see https://pre-commit.ci
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this 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
🔭 Outside diff range comments (1)
mesa_frames/abstract/agents.py (1)
48-55
:⚠️ Potential issueImport guard for
Self
(build blocker)Same pattern required here:
-from typing import Self, overload, Any, Literal +from typing import overload, Any, Literal, TYPE_CHECKING +try: + from typing import Self +except ImportError: + from typing_extensions import Self +if TYPE_CHECKING: + from mesa_frames.concrete.model import ModelDF + from mesa_frames.concrete.agents import AgentSetDFThis removes the current CI failure and also lets you shorten many very long
annotations (mesa_frames.concrete.agents.AgentSetDF
) to the alias
AgentSetDF
, improving readability.
🧹 Nitpick comments (2)
mesa_frames/abstract/space.py (2)
100-108
: Constructor annotation may trigger circular import
beartype
will importmesa_frames.concrete.model
at runtime. To stay safe:
- Add the
TYPE_CHECKING
guarded import (see above).- Replace the long annotation with the local alias
ModelDF
.No behaviour change, fewer import cycles.
🧰 Tools
🪛 Ruff (0.8.2)
100-100: Undefined name
mesa_frames
(F821)
1108-1116
: Docstring no longer matches type
remaining_capacity
now returnsint | Infinity
but docstring still says
“None if the capacity is infinite”. Update wording to reflect the new sentinel
value (math.inf
/Infinity
) so users aren’t confused.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
uv.lock
is excluded by!**/*.lock
📒 Files selected for processing (16)
.gitignore
(1 hunks)mesa_frames/__init__.py
(1 hunks)mesa_frames/abstract/agents.py
(27 hunks)mesa_frames/abstract/mixin.py
(8 hunks)mesa_frames/abstract/space.py
(17 hunks)mesa_frames/concrete/agents.py
(12 hunks)mesa_frames/concrete/agentset.py
(2 hunks)mesa_frames/concrete/mixin.py
(15 hunks)mesa_frames/concrete/model.py
(3 hunks)mesa_frames/concrete/space.py
(4 hunks)mesa_frames/types_.py
(3 hunks)pyproject.toml
(1 hunks)tests/test_agents.py
(1 hunks)tests/test_agentset.py
(1 hunks)tests/test_grid.py
(2 hunks)tests/test_mixin.py
(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
tests/test_mixin.py (1)
mesa_frames/concrete/mixin.py (1)
PolarsMixin
(58-649)
mesa_frames/concrete/agentset.py (5)
mesa_frames/concrete/model.py (3)
agents
(163-181)agents
(184-187)ModelDF
(56-228)mesa_frames/abstract/agents.py (6)
agents
(698-704)agents
(708-716)agents
(1102-1103)agents
(1106-1114)AgentSetDF
(783-1135)model
(667-674)mesa_frames/concrete/agents.py (2)
agents
(558-559)agents
(562-570)mesa_frames/concrete/mixin.py (1)
PolarsMixin
(58-649)mesa_frames/utils.py (1)
copydoc
(4-18)
mesa_frames/abstract/agents.py (7)
mesa_frames/abstract/space.py (4)
random
(532-539)model
(522-529)agents
(512-519)SpaceDF
(89-539)mesa_frames/abstract/mixin.py (2)
CopyMixin
(66-184)_df_get_masked_df
(328-335)tests/test_grid.py (1)
model
(22-29)mesa_frames/concrete/model.py (5)
ModelDF
(56-228)agents
(163-181)agents
(184-187)space
(201-218)space
(221-228)mesa_frames/concrete/agents.py (12)
agents
(558-559)agents
(562-570)do
(147-155)do
(158-166)do
(168-203)inactive_agents
(605-606)index
(609-610)pos
(613-614)add
(87-120)remove
(216-260)active_agents
(573-574)active_agents
(577-580)mesa_frames/concrete/agentset.py (8)
agents
(498-499)agents
(502-505)inactive_agents
(516-517)index
(520-521)pos
(524-525)add
(100-149)active_agents
(508-509)active_agents
(512-513)mesa_frames/concrete/mixin.py (1)
_df_get_masked_df
(325-336)
🪛 Ruff (0.8.2)
mesa_frames/concrete/agentset.py
67-67: typing.Literal
imported but unused
Remove unused import: typing.Literal
(F401)
71-71: mesa_frames.concrete.model.ModelDF
imported but unused
Remove unused import: mesa_frames.concrete.model.ModelDF
(F401)
72-72: mesa_frames.types_.IdsLike
imported but unused
Remove unused import: mesa_frames.types_.IdsLike
(F401)
88-88: Undefined name mesa_frames
(F821)
mesa_frames/concrete/agents.py
75-75: Undefined name mesa_frames
(F821)
mesa_frames/abstract/space.py
100-100: Undefined name mesa_frames
(F821)
522-522: Undefined name mesa_frames
(F821)
555-555: Undefined name mesa_frames
(F821)
1157-1157: Undefined name mesa_frames
(F821)
mesa_frames/abstract/agents.py
71-71: Undefined name mesa_frames
(F821)
80-80: Undefined name mesa_frames
(F821)
81-81: Undefined name mesa_frames
(F821)
107-107: Undefined name mesa_frames
(F821)
108-108: Undefined name mesa_frames
(F821)
134-134: Undefined name mesa_frames
(F821)
139-139: Undefined name mesa_frames
(F821)
176-176: Undefined name mesa_frames
(F821)
187-187: Undefined name mesa_frames
(F821)
250-250: Undefined name mesa_frames
(F821)
251-251: Undefined name mesa_frames
(F821)
399-399: Undefined name mesa_frames
(F821)
400-400: Undefined name mesa_frames
(F821)
489-489: Undefined name mesa_frames
(F821)
490-490: Undefined name mesa_frames
(F821)
512-512: Undefined name mesa_frames
(F821)
513-513: Undefined name mesa_frames
(F821)
535-535: Undefined name mesa_frames
(F821)
536-536: Undefined name mesa_frames
(F821)
667-667: Undefined name mesa_frames
(F821)
687-687: Undefined name mesa_frames
(F821)
709-709: Undefined name mesa_frames
(F821)
747-747: Undefined name mesa_frames
(F821)
759-759: Undefined name mesa_frames
(F821)
772-772: Undefined name mesa_frames
(F821)
797-797: Undefined name mesa_frames
(F821)
801-801: Undefined name mesa_frames
(F821)
mesa_frames/concrete/mixin.py
54-54: mesa_frames.types_.DataFrame
imported but unused
Remove unused import: mesa_frames.types_.DataFrame
(F401)
🪛 GitHub Actions: build
mesa_frames/concrete/agents.py
[error] 51-51: ImportError: cannot import name 'Self' from 'typing'. This causes test collection failures in multiple test modules.
🔇 Additional comments (36)
mesa_frames/__init__.py (2)
44-45
: Good addition of postponed evaluation for annotations.Adding
from __future__ import annotations
is a good practice for modern Python type checking. This enables postponed evaluation of type annotations, which helps avoid circular import issues and aligns with PEP 563.
48-48
: Import reordering looks good.The reordering of the import for
ModelDF
frommesa_frames.concrete.model
maintains the correctness of imports while potentially resolving dependency ordering requirements.pyproject.toml (2)
81-81
: Good replacement of typeguard with beartype.Replacing typeguard with beartype for runtime type checking aligns with the PR objectives. Beartype is known for its high-performance runtime type checking with minimal overhead, which should provide better debugging experience as intended.
88-88
: Beneficial addition of pre-commit.Adding pre-commit as a dev dependency is a good practice. It will help enforce consistent code quality and potentially automate type checking before commits, supporting the runtime type checking objectives.
mesa_frames/types_.py (6)
3-13
: Good modernization of imports and dependencies.The additions of
from __future__ import annotations
and comprehensive imports from standard libraries and beartype improve type checking capabilities. The organization of imports is clean and follows best practices.
15-30
: Well-handled optional dependencies.The implementation of optional imports with fallback dummy classes is an excellent pattern. This ensures that type annotations remain valid even if optional dependencies like pandas or pyarrow aren't installed, improving the package's resilience.
41-55
: Enhanced type definitions for Polars types.The refined type definitions for Polars types are more precise and comprehensive. The union types properly capture the range of acceptable inputs, which will make the runtime type checking more accurate and helpful for developers.
56-70
: Comprehensive IntoExpr type definition.The new
IntoExpr
type alias is well-defined and covers a wide range of primitive and complex types that can be converted to expressions. This will provide better type checking for functions that accept expression-convertible inputs.
73-84
: Improved generic type aliases with special attention to Infinity.The refined generic type aliases improve type precision. The
Infinity
type usingAnnotated
withIsEqual[math.inf]
is particularly clever - it ensures that only mathematical infinity is accepted where this type is used, preventing potential logical errors.
117-117
: Enhanced DiscreteSpaceCapacity type.Adding
np.ndarray
to theDiscreteSpaceCapacity
union type improves flexibility when working with capacity specifications, allowing direct usage of NumPy arrays without conversion..gitignore (1)
155-156
: Appropriate additions to .gitignore.Adding
*.code-workspace
andllm_rules.md
to the ignore list prevents IDE-specific files and presumably LLM-related documentation from being tracked in version control, which is a good practice for keeping the repository clean.tests/test_agentset.py (1)
5-5
: Type checking update to beartype looks good.The change from typeguard to beartype for runtime type checking aligns with the PR objectives. Beartype is known for being more efficient with lower runtime overhead compared to typeguard.
Also applies to: 11-11
tests/test_grid.py (1)
4-4
: Good migration to beartype for runtime type checking.The decorator change from typeguard.typechecked to beartype.beartype is consistent with the overall migration strategy. This change maintains the runtime type checking functionality while potentially improving performance.
Also applies to: 19-19
tests/test_mixin.py (1)
4-4
: Consistent adoption of beartype for TestPolarsMixin.The change maintains consistency across the codebase by replacing typeguard with beartype. This is aligned with the project's overall direction to use beartype for runtime type validation.
Also applies to: 9-9
tests/test_agents.py (1)
510-510
: Improved Series creation approach.Changing from implicit column creation to using
pl.Series()
explicitly is a more type-safe approach. This change likely helps beartype better understand and validate the types at runtime.mesa_frames/concrete/model.py (5)
43-43
: Good addition of future importsAdding
from __future__ import annotations
enables postponed evaluation of type annotations, allowing for self-referential and forward references in type hints.
48-50
: Clean import organizationThe imports are well-organized with beartype added for runtime type checking and AgentSetDF moved from conditional to direct import to support runtime type validation.
55-55
: Runtime type checking enabled with beartypeAdding the
@beartype
decorator to the ModelDF class enables runtime type checking, which will help catch type-related errors early.
104-104
: Improved return type annotationChanging the return type from string literal
"AgentSetDF"
to a direct referenceAgentSetDF
improves type checking and IDE support.
222-227
: Good addition of docstringAdding a docstring to the space setter method improves code documentation and helps users understand the parameter requirements.
mesa_frames/concrete/agentset.py (2)
60-72
: Well-organized importsThe import organization is good with future annotations for postponed evaluation, beartype for runtime type checking, and clear typing imports. The removal of conditional TYPE_CHECKING imports makes types available at runtime.
🧰 Tools
🪛 Ruff (0.8.2)
67-67:
typing.Literal
imported but unusedRemove unused import:
typing.Literal
(F401)
71-71:
mesa_frames.concrete.model.ModelDF
imported but unusedRemove unused import:
mesa_frames.concrete.model.ModelDF
(F401)
72-72:
mesa_frames.types_.IdsLike
imported but unusedRemove unused import:
mesa_frames.types_.IdsLike
(F401)
76-77
: Runtime type checking with beartypeThe
@beartype
decorator properly implements runtime type checking while maintaining the class documentation with@copydoc
.mesa_frames/concrete/space.py (5)
45-45
: Good import organizationThe imports are well organized with direct import of
inf
from math, beartype for runtime type checking, and the newInfinity
type from mesa_frames.types_. This supports both the runtime type checking objective and improved type safety.Also applies to: 51-51, 55-55
59-59
: Runtime type checking enabled with beartypeAdding the
@beartype
decorator to the GridPolars class enables runtime type checking, which will help catch type-related errors early.
89-89
: More flexible parameter typeChanging the capacity parameter type from
int
toint | None
makes the API more flexible, allowing for optional capacity specification.
168-168
: Enhanced input type flexibilityChanging the agents parameter type from
pl.DataFrame
topl.DataFrame | pl.Series
provides more flexibility for function callers.
219-222
: Better infinity handlingThe changes to return type and implementation improve infinity handling by:
- Using a more precise return type with
Infinity
- Using Python's built-in
inf
instead of NumPy'snp.inf
- Casting the sum to an integer for consistent return types when not infinite
mesa_frames/abstract/mixin.py (4)
45-45
: Well-organized importsThe import organization is good with future annotations for postponed evaluation, beartype for runtime type checking, and consolidated type imports including the new DataFrameInput type.
Also applies to: 52-62
65-65
: Runtime type checking with beartypeAdding the
@beartype
decorator to both CopyMixin and DataFrameMixin classes provides runtime type checking which helps catch type errors early.Also applies to: 187-187
272-272
: More flexible type annotationsChanging specific collection types to more general Collection types allows for greater flexibility while maintaining type safety.
Also applies to: 478-480
343-343
: Enhanced type flexibilityMultiple parameter types have been updated to accept more general collections or broader ranges of input types, providing greater flexibility for function callers.
Also applies to: 467-467, 487-487, 496-497, 505-506
mesa_frames/concrete/agents.py (2)
75-83
: Avoid hard dependency on fully-qualified forward reference at runtime
beartype
eagerly evaluates annotations; therefore
mesa_frames.concrete.model.ModelDF
must resolve at import time, risking
circular-import failures.
After adding the aboveTYPE_CHECKING
guard you can safely shorten the hint:- def __init__(self, model: mesa_frames.concrete.model.ModelDF) -> None: + def __init__(self, model: ModelDF) -> None:Keep the long path only inside the
TYPE_CHECKING
block.
[ suggest_essential_refactor ]🧰 Tools
🪛 Ruff (0.8.2)
75-75: Undefined name
mesa_frames
(F821)
403-411
: Minor: rename loop variable to prevent shadowing but use it consistentlyGreat that you renamed
mask
→mask_value
to avoid confusion, yet inside
return_dictionary[agentset] = …
you’re already usingmask_value
, so the
comment above (“change inner loop variable”) can be deleted to reduce clutter.
[ suggest_nitpick ]mesa_frames/concrete/mixin.py (1)
343-355
: Good use of overloads to improve_df_index
typingSplitting scalar vs. multi-column overloads clarifies call sites and helps
static analysers. 👍mesa_frames/abstract/agents.py (2)
829-845
:discard()
delegates correctly—nice reuseDelegating to the parent implementation keeps behaviour consistent while
tightening the signature.
Looks good.
1129-1132
: PotentialNoneType
access whenspace
is not set
self.space
may beNone
until the model sets it.
self.space.agents
will then raiseAttributeError
.Consider:
if self.space is None: raise ValueError("Space not initialised for this model.")before accessing it, or document the pre-condition.
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🔭 Outside diff range comments (2)
mesa_frames/abstract/agents.py (2)
43-52
:⚠️ Potential issueMissing imports – unresolved names break
beartype
at import-time
@beartype
resolves annotations immediately when the decorator is executed.
Becausemesa_frames
and its sub-modules are never imported in this file, every
fully-qualified annotation such asmesa_frames.concrete.model.ModelDF
is
currently a dangling forward reference. This triggers aNameError
during
module import and is also the root-cause of the RuffF821
warnings.-from abc import abstractmethod -from collections.abc import Callable, Collection, Iterable, Iterator, Sequence +from abc import abstractmethod +from collections.abc import Callable, Collection, Iterable, Iterator, Sequence from contextlib import suppress from beartype import beartype from numpy.random import Generator from typing import Self, overload, Any, Literal + +# --------------------------------------------------------------------------- # +# Imports required only for annotations but *must* exist at runtime so that # +# `beartype` can resolve them via `typing.get_type_hints`. Using `# noqa` # +# silences the “unused import” warnings without disabling strict type checks. # +# --------------------------------------------------------------------------- # +from mesa_frames.concrete.model import ModelDF # noqa: F401 +from mesa_frames.concrete.agents import AgentSetDF # noqa: F401 +from mesa_frames.abstract.space import SpaceDF # noqa: F401This keeps the readable, fully-qualified annotations while guaranteeing that
names exist in the module namespace for both static analysis and runtime
inspection. (If circular-import concerns arise, move these imports inside a
if TYPE_CHECKING:
block and convert annotations back to strings, but that
negates the stated PR goal of avoidingTYPE_CHECKING
blocks.)
271-299
:⚠️ Potential issue
select()
lost its@abstractmethod
decorator – subclasses get a stub that returns...
The method body is only
...
, yet the decorator was removed. As a result:
AgentContainer
is no longer abstract with respect toselect
.- Any concrete subclass that does not override
select
will inherit a
method that returns anEllipsis
, leading to hard-to-trace runtime errors.- def select( + @abstractmethod + def select(Either re-apply
@abstractmethod
or provide a concrete, working implementation.
🧹 Nitpick comments (1)
mesa_frames/abstract/agents.py (1)
932-945
: Edge-case inremove()
– magic string & duplicated traversal
Overloading
agents="active"
mixes ID semantics with a command keyword and
can silently clash with a legitimate agent whose unique_id is the literal
string"active"
. Consider an explicit boolean flag instead, e.g.
remove_active: bool = False
.The loop that searches the returned
agentsdf
for anAgentSetDF
of the
same class isO(n)
per call. Ifagentsdf.agents
were keyed by the
agent-set name (or type) the lookup could be constant-time.🧰 Tools
🪛 Ruff (0.8.2)
941-941: Use
key in dict
instead ofkey in dict.keys()
Remove
.keys()
(SIM118)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
uv.lock
is excluded by!**/*.lock
📒 Files selected for processing (5)
.github/workflows/build.yml
(1 hunks).pre-commit-config.yaml
(1 hunks)mesa_frames/abstract/agents.py
(27 hunks)mesa_frames/concrete/mixin.py
(15 hunks)pyproject.toml
(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- .github/workflows/build.yml
🚧 Files skipped from review as they are similar to previous changes (2)
- pyproject.toml
- mesa_frames/concrete/mixin.py
🧰 Additional context used
🪛 Ruff (0.8.2)
mesa_frames/abstract/agents.py
71-71: Undefined name mesa_frames
(F821)
80-80: Undefined name mesa_frames
(F821)
81-81: Undefined name mesa_frames
(F821)
107-107: Undefined name mesa_frames
(F821)
108-108: Undefined name mesa_frames
(F821)
134-134: Undefined name mesa_frames
(F821)
139-139: Undefined name mesa_frames
(F821)
176-176: Undefined name mesa_frames
(F821)
187-187: Undefined name mesa_frames
(F821)
250-250: Undefined name mesa_frames
(F821)
251-251: Undefined name mesa_frames
(F821)
399-399: Undefined name mesa_frames
(F821)
400-400: Undefined name mesa_frames
(F821)
489-489: Undefined name mesa_frames
(F821)
490-490: Undefined name mesa_frames
(F821)
512-512: Undefined name mesa_frames
(F821)
513-513: Undefined name mesa_frames
(F821)
535-535: Undefined name mesa_frames
(F821)
536-536: Undefined name mesa_frames
(F821)
667-667: Undefined name mesa_frames
(F821)
687-687: Undefined name mesa_frames
(F821)
709-709: Undefined name mesa_frames
(F821)
747-747: Undefined name mesa_frames
(F821)
759-759: Undefined name mesa_frames
(F821)
772-772: Undefined name mesa_frames
(F821)
797-797: Undefined name mesa_frames
(F821)
801-801: Undefined name mesa_frames
(F821)
🔇 Additional comments (1)
.pre-commit-config.yaml (1)
20-20
: Ensurepyupgrade
supports the--py311-plus
flag
The--py311-plus
argument is only available in recentpyupgrade
releases. Please verify that the pinned version (v3.19.1
) indeed supports this flag to prevent pre-commit failures.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #143 +/- ##
==========================================
+ Coverage 91.78% 91.98% +0.19%
==========================================
Files 11 11
Lines 1680 1721 +41
==========================================
+ Hits 1542 1583 +41
Misses 138 138 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Would it make sense to structure this so that runtime type checking is enabled only in development or test environments? For example, we could use a wrapper like:
This way we could annotate functions with @implement_beartype, and control activation via environment variables. It would also allow more advanced users to opt-in to runtime type checking in production if they really want to i guess this will stop the over head from affecting everyone |
Yes @Ben-geo, I think the best thing would be to gate all runtime type-checking behind a single env var: import os
from beartype.claw import beartype_this_package
if os.getenv("MESA_FRAMES_RUNTIME_TYPECHECKING", "").lower() in ("1", "true", "yes"):
beartype_this_package()
This keeps our codebase clean, gives us type-safety in CI/dev, and preserves peak performance everywhere else. |
Awesome @adamamer20 I think this is the best solution. |
This PR adds beartype to enable runtime type checking across the codebase.
Given the extensive use of type hints already present, the integration should be straightforward and introduce minimal performance overhead. Preliminary testing on the sugarscape example showed negligible impact, though broader testing across the full codebase hasn't been performed yet.
Runtime type checking provides several benefits:
Some notes regarding this PR:
typing_extensions
and changed totyping
with the support of Python bumped up to 3.11. This is in line with the currentmesa
repository.if TYPE_CHECKING
blocks for forward imports. This in fact doesn't work well with beartype. Instead, usingfrom __future__ import annotations
at the start of the file allows for the postponed evaluation of typesPerformance considerations
I ran a side-by-side comparison of
performance_plot.py
with and withoutbeartype
(on Codespaces) to quantify the overhead. Below is the full picture:Key points (0 – 100 k agents)
Larger runs (100 k – 1 000 k agents)
Startup overhead at 0 agents is ~133 %, mostly due to module import and GC initialization.
For small→mid runs (10 k–90 k), expect +20–40 % on the pandas-concise path and +5–70 % on native-Polars.
A sharp spike at exactly 100 k agents (+113 % / +261 %) arises from crossing Python’s GC and list-resizing thresholds under heavy temporary-object churn.
Beyond 200 k, the relative penalty subsides to +2–30 % on concise and −7 %→+8 % on native.
Disable runtime checks with
-O
.When you run Python under the
-O
(optimize) flag, all@beartype
checks are skipped—so you recover essentially the same performance as the “no-beartype” build.(See the discussion in the Beartype issue tracker for details:
[FAQ] Document how to disable @beartype beartype/beartype#94)
Summary by CodeRabbit
Refactor
beartype
runtime type checking to core classes for improved type safety.Chores
typeguard
withbeartype
for runtime type checking in tests and dependencies..gitignore
and pre-commit configurations.Tests
beartype
for runtime type validation.