Global mutable state (_tally, _fail) prevents clean library reuse
- Dominant language
- Python
- Stars
- 374
- Forks
- 39
- Avg merge
- 3m
- Merged PRs (30d)
- 1
Description
## Summary
The module uses global mutable state for tallying (`_tally`) and error handling (`_fail`), which prevents clean reuse as a library and makes the code non-reentrant/non-threadsafe.
## Details
In `repren.py`:
- **`_tally`** (line 579): A module-level `_Tally()` instance that gets mutated by `multi_replace()` and `transform_file()`. This means calling these functions from library code accumulates state across calls with no way to reset it.
- **`_fail`** (line 527): A module-level function pointer that defaults to `_fail_with_exception` but gets reassigned to `_fail_with_exit` inside `_run_cli()`. This means library callers who import after `main()` has run get different behavior.
- **`_tally` mutation inside `multi_replace()`** (lines 669-673): The core replacement function has a side effect of mutating global state, mixing pure computation with I/O bookkeeping.
## Suggestion
Pass a `Tally` (or context) object through the call chain instead of relying on module-level globals. This would make the functions pure, reentrant, and safe for concurrent use. The Rust port (`repren-rs`) already uses this pattern — each function returns counts as part of its return value.
## Context
Found during the `repren-rs` Rust port review, where this pattern was identified as a porting friction point.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.