You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
add_trees({"a": x1, "b": x2}, {"a": y1, "b": y2}) # OK — same structure
356
364
add_trees({"a": x1}, [y1, y2]) # Raises — different structure
357
365
```
358
366
359
-
### Composite structures
367
+
### Multi-level structure matching
360
368
361
-
Use `S[T]` for nested structure matching — outer structure S, inner structure T:
369
+
Structure names are listed left-to-right from outer to inner. Without `...`, each name except the last captures ONE level; the last captures the full remaining structure. Trailing `...` makes all names one-level-only with inner levels unchecked. Leading `...` matches names from the bottom up.
@@ -412,36 +426,74 @@ A **dimension memo** maps dimension names to sizes (e.g., `N→4`, `C→3`). Eac
412
426
413
427
This happens **automatically** with `@beartype`. Shapix detects the beartype wrapper frame via `sys._getframe()` and associates a memo with it. No extra decorator needed.
414
428
415
-
### `@shapix.check` (optional)
429
+
### `@shapix.check` — explicit memo management
430
+
431
+
To understand `@shapix.check`, you need to understand the problem it solves.
416
432
417
-
`@shapix.check` provides **explicit**memo management. It's useful in two scenarios:
433
+
**The problem: sharing state across parameter checks.** When beartype validates `f(x, y)`, it checks `x` and `y` independently — it calls the `Is[...]` validator once per parameter. But shapix needs all those validators to share the same dimension memo, so that `N=4` bound by `x` is enforced on `y`. Something has to connect them.
418
434
419
-
1.**Combining memo with custom `BeartypeConf`** — a single decorator instead of stacking two:
435
+
**The automatic approach** (no extra decorator needed) is frame-based detection. Shapix walks up the call stack with `sys._getframe()` to find the beartype wrapper frame. Since all parameter checks within one `f(x, y)` call share the same wrapper frame, shapix can key a memo to it. This just works:
420
436
421
437
```python
422
-
from beartype import BeartypeConf
438
+
@beartype# Shapix auto-detects this frame — nothing else needed
**`@shapix.check`** takes a different approach: instead of detecting the frame, it explicitly pushes a fresh memo onto a stack before the call and pops it after. All validators see this explicit memo first (it takes priority over frame detection):
443
+
444
+
```python
445
+
@shapix.check# Pushes memo before call, pops after
2.**Exotic call stacks** where frame-based detection is unreliable (deep decorator chains, recursive wrappers).
450
+
Both approaches produce identical results in normal usage. You only need `@shapix.check` in specific situations:
451
+
452
+
#### 1. Extra decorators between beartype and the call site
429
453
430
-
For normal usage, just use `@beartype` — it works out of the box.
454
+
Frame-based detection counts a fixed number of frames up from the validator. If something adds extra frames between beartype's wrapper and the actual function call, the detection can land on the wrong frame:
455
+
456
+
```python
457
+
# This works — beartype is the outermost wrapper, frame detection is stable
If you want a guarantee that cross-argument checking works regardless of how your code is called (by test runners, async frameworks, deep middleware stacks), `@shapix.check` removes all dependence on call-stack structure.
476
+
477
+
**When you don't need it:** If you're using plain `@beartype` and your tests pass, the frame-based detection is working. Most applications never need `@shapix.check`.
431
478
432
479
### Manual checks with `check_context`
433
480
434
-
For `isinstance`-style checks outside of decorated functions, use `check_context` with beartype's `is_bearable`:
481
+
For `isinstance`-style checks outside of decorated functions, use `check_context` with beartype's `is_bearable`. Without it, each check gets an independent memo — dimensions aren't cross-checked:
435
482
436
483
```python
437
484
from beartype.door import is_bearable
438
485
import shapix
439
486
from shapix import N, C
440
487
from shapix.numpy import F32
441
488
489
+
# Without check_context — each check is independent, N is NOT cross-checked
490
+
is_bearable(x, F32[N, C]) # Binds N=4 in a temporary memo (discarded)
491
+
is_bearable(y, F32[N, C]) # Binds N=999 in a new memo — no error!
492
+
493
+
# With check_context — checks share a memo, N IS cross-checked
442
494
with shapix.check_context():
443
-
assert is_bearable(x, F32[N, C]) # Binds N and C
444
-
assert is_bearable(y, F32[N, C]) #Must match same N, C
495
+
assert is_bearable(x, F32[N, C]) # Binds N=4
496
+
assert is_bearable(y, F32[N, C]) #Checks N=4 — raises if y has N=999
0 commit comments