jlevy / jlevy/repren

Global mutable state (_tally, _fail) prevents clean library reuse

Open
#43 0 comments 0 reactions 0 assignees View on GitHub
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.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.