added magic methods
This commit is contained in:
parent
60568933e6
commit
f6eee30f17
@ -43,6 +43,12 @@ b = StreamNumber(literal="1337")
|
||||
|
||||
result = mul(a, b)
|
||||
print("".join(result.stream()))
|
||||
|
||||
# same helpers are available via Python operators
|
||||
total = a + b # calls mathstream.add under the hood
|
||||
ratio = total / 2 # literal coercion is automatic
|
||||
with StreamNumber(literal="10") as temp:
|
||||
product = temp * ratio
|
||||
```
|
||||
|
||||
Available operations:
|
||||
@ -50,6 +56,8 @@ Available operations:
|
||||
- Introspection & helpers: `is_even`, `is_odd`, `free_stream`, `active_streams`, `tracked_files`
|
||||
- Lifecycle control: `collect_garbage`, `set_manual_free_only`, `manual_free_only_enabled`
|
||||
- Environment helpers: `clear_logs`, `StreamNumber.write_stage`, `engine.LOG_DIR`
|
||||
- Python sugar: `StreamNumber` implements `+`, `-`, `*`, `/`, `%`, `**`, and their reflected counterparts. Raw `int`, `str`, or `pathlib.Path` operands are coerced automatically.
|
||||
- Context manager support: `with StreamNumber(...) as sn:` ensures `.free()` is called at exit.
|
||||
|
||||
## How It Works
|
||||
|
||||
@ -73,6 +81,7 @@ Available operations:
|
||||
### Common Pitfalls & Recoveries
|
||||
|
||||
- **Accidentally freed files** – Automatic finalizers may delete staged outputs while you still hold the path elsewhere. Fix: call `set_manual_free_only(True)` at the start of long-lived workflows, or pass `delete_file=False` to `free_stream` when you need to keep the digits around manually.
|
||||
- **Operator coercion surprises** – Arithmetic operators turn `int`, `str`, or `Path` operands into streamed numbers. If a string happens to be a *file path* instead of a literal, the actual file will be wrapped. Fix: be explicit (`StreamNumber(literal="...")`) when in doubt.
|
||||
- **Literal churn** – Recreating the same `StreamNumber(literal="123")` millions of times hammers the filesystem. Fix: stash the first instance, or cache the `.path` and rely on `StreamNumber(existing_path)` in hot loops.
|
||||
- **GC too aggressive** – Running `collect_garbage(0)` after every operation removes recently written files. Fix: raise the threshold (e.g., `collect_garbage(1000)`) or run GC only after you’ve freed all references.
|
||||
- **Chunk mismatch** – Some editors save files with BOMs or commas. `_normalize_stream` will raise `ValueError("Non-digit characters found...")`. Fix: sanitise input files (only ASCII digits with optional leading sign).
|
||||
|
||||
@ -2,7 +2,8 @@ import hashlib
|
||||
import weakref
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional, Union
|
||||
from typing import Dict, Optional, Union, Any
|
||||
from .engine import add, sub, mul, div, mod, pow
|
||||
|
||||
from .utils import (
|
||||
register_log_file,
|
||||
@ -117,6 +118,41 @@ class StreamNumber:
|
||||
def __exit__(self, exc_type, exc, tb):
|
||||
self.free()
|
||||
|
||||
def __add__(self, other):
|
||||
return add(self, _coerce_operand(other))
|
||||
|
||||
def __sub__(self, other):
|
||||
return sub(self, _coerce_operand(other))
|
||||
|
||||
def __mul__(self, other):
|
||||
return mul(self, _coerce_operand(other))
|
||||
|
||||
def __truediv__(self, other):
|
||||
return div(self, _coerce_operand(other))
|
||||
|
||||
def __mod__(self, other):
|
||||
return mod(self, _coerce_operand(other))
|
||||
|
||||
def __pow__(self, other):
|
||||
return pow(self, _coerce_operand(other))
|
||||
|
||||
def __radd__(self, other):
|
||||
return add(_coerce_operand(other), self)
|
||||
|
||||
def __rsub__(self, other):
|
||||
return sub(_coerce_operand(other), self)
|
||||
|
||||
def __rmul__(self, other):
|
||||
return mul(_coerce_operand(other), self)
|
||||
|
||||
def __rtruediv__(self, other):
|
||||
return div(_coerce_operand(other), self)
|
||||
|
||||
def __rmod__(self, other):
|
||||
return mod(_coerce_operand(other), self)
|
||||
|
||||
def __rpow__(self, other):
|
||||
return pow(_coerce_operand(other), self)
|
||||
|
||||
_ACTIVE_COUNTER: Counter[str] = Counter()
|
||||
|
||||
@ -163,3 +199,19 @@ def set_manual_free_only(enabled: bool) -> None:
|
||||
def manual_free_only_enabled() -> bool:
|
||||
"""Return the current manual-free-only toggle."""
|
||||
return _MANUAL_FREE_ONLY
|
||||
|
||||
|
||||
def _coerce_operand(value: Any) -> StreamNumber:
|
||||
"""Convert supported operand types into a StreamNumber."""
|
||||
if isinstance(value, StreamNumber):
|
||||
return value
|
||||
if isinstance(value, (int,)):
|
||||
return StreamNumber(literal=str(value))
|
||||
if isinstance(value, Path):
|
||||
return StreamNumber(value)
|
||||
if isinstance(value, str):
|
||||
candidate = Path(value)
|
||||
if candidate.exists():
|
||||
return StreamNumber(candidate)
|
||||
return StreamNumber(literal=value)
|
||||
raise TypeError(f"Unsupported operand type for StreamNumber: {type(value)!r}")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user