added magic methods
This commit is contained in:
parent
60568933e6
commit
f6eee30f17
@ -43,6 +43,12 @@ b = StreamNumber(literal="1337")
|
|||||||
|
|
||||||
result = mul(a, b)
|
result = mul(a, b)
|
||||||
print("".join(result.stream()))
|
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:
|
Available operations:
|
||||||
@ -50,6 +56,8 @@ Available operations:
|
|||||||
- Introspection & helpers: `is_even`, `is_odd`, `free_stream`, `active_streams`, `tracked_files`
|
- Introspection & helpers: `is_even`, `is_odd`, `free_stream`, `active_streams`, `tracked_files`
|
||||||
- Lifecycle control: `collect_garbage`, `set_manual_free_only`, `manual_free_only_enabled`
|
- Lifecycle control: `collect_garbage`, `set_manual_free_only`, `manual_free_only_enabled`
|
||||||
- Environment helpers: `clear_logs`, `StreamNumber.write_stage`, `engine.LOG_DIR`
|
- 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
|
## How It Works
|
||||||
|
|
||||||
@ -73,6 +81,7 @@ Available operations:
|
|||||||
### Common Pitfalls & Recoveries
|
### 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.
|
- **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.
|
- **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.
|
- **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).
|
- **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
|
import weakref
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from pathlib import Path
|
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 (
|
from .utils import (
|
||||||
register_log_file,
|
register_log_file,
|
||||||
@ -117,6 +118,41 @@ class StreamNumber:
|
|||||||
def __exit__(self, exc_type, exc, tb):
|
def __exit__(self, exc_type, exc, tb):
|
||||||
self.free()
|
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()
|
_ACTIVE_COUNTER: Counter[str] = Counter()
|
||||||
|
|
||||||
@ -163,3 +199,19 @@ def set_manual_free_only(enabled: bool) -> None:
|
|||||||
def manual_free_only_enabled() -> bool:
|
def manual_free_only_enabled() -> bool:
|
||||||
"""Return the current manual-free-only toggle."""
|
"""Return the current manual-free-only toggle."""
|
||||||
return _MANUAL_FREE_ONLY
|
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