Skip to content

Commit a1fe6ce

Browse files
committed
feat(runtime): standardize on 'stitched' runtime mode naming
- Rename runtime mode from 'singlefile' to 'stitched' throughout codebase - Update pyproject.toml task names (build:script -> build:stitched, test:pytest:script -> test:pytest:stitched) - Rename BUNDLER_SCRIPT constant to STITCH_SCRIPT - Replace __STANDALONE__ marker with __STITCHED__ for stitched script detection - Update tagline from 'When stdlib just isn't enough' to 'When stdlib is almost enough' - Update all documentation, tests, and CI workflows to use new naming
1 parent d8ab854 commit a1fe6ce

30 files changed

+223
-223
lines changed

.github/workflows/release_github.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ jobs:
4545
- name: Run checks
4646
run: poetry run poe check
4747

48-
- name: Build single-file script
48+
- name: Build stitched script
4949
continue-on-error: true
50-
run: poetry run poe build:script
50+
run: poetry run poe build:stitched
5151

5252
- name: Build zipapp
5353
continue-on-error: true

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ This file provides a quick reference for common development tasks.
1919
| `poetry run poe check:fix` | Auto-fix issues, re-format, type-check, and re-test. |
2020
| `poetry run poe check` | Run linting (`ruff`), type checks (`mypy`), and tests (`pytest`). |
2121
| `poetry run poe fix` | Run all auto-fixers (`ruff`). |
22-
| `poetry run poe build:script` | Bundle the project into a single portable script in `dist/`. |
22+
| `poetry run poe build:stitched` | Bundle the project into a single portable script in `dist/`. |
2323

2424
### Setup
2525

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
📘 **[Roadmap](./ROADMAP.md)** · 📝 **[Release Notes](https://github.com/apathetic-tools/python-utils/releases)**
88

99
**Grab bag of helpers for Apathetic projects.**
10-
*When stdlib just isn't enough.*
10+
*When stdlib is almost enough.*
1111

1212
*Apathetic Python Utils* provides a lightweight, dependency-free collection of utility functions designed for CLI tools. It includes helpers for file loading, path manipulation, system detection, text processing, type checking, pattern matching, and more.
1313

@@ -29,7 +29,7 @@ pyproject = load_toml(Path("pyproject.toml"))
2929
if is_ci():
3030
print("Running in CI")
3131

32-
# Detect runtime mode (installed, standalone, zipapp, frozen)
32+
# Detect runtime mode (package, stitched, zipapp, frozen)
3333
mode = detect_runtime_mode("my_package")
3434
print(f"Running in {mode} mode")
3535
```
@@ -58,7 +58,7 @@ For installation guides, API reference, examples, and more, visit our documentat
5858
- 🔍 **Pattern matching** — Portable glob pattern matching with recursive `**` support
5959
- 🧩 **Module detection** — Detect Python packages from file paths
6060
- 🧪 **System detection** — Detect CI environments, pytest execution, and runtime modes
61-
- ⚙️ **Runtime utilities** — Build and test utilities for standalone scripts and zipapps
61+
- ⚙️ **Runtime utilities** — Build and test utilities for stitched scripts and zipapps
6262
- 🔧 **Subprocess utilities** — Capture and forward subprocess output
6363
- 📝 **Text processing** — Pluralization and error message cleanup utilities
6464
- 🔧 **Type utilities** — Safe isinstance checks for TypedDicts and generics

docs/api.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -470,8 +470,8 @@ Detect how the package is being executed.
470470

471471
**Returns:**
472472
- `str`: Runtime mode, one of:
473-
- `"installed"`: Package installed via pip/poetry
474-
- `"standalone"`: Single-file standalone script
473+
- `"package"`: Package installed via pip/poetry
474+
- `"stitched"`: Single-file stitched script
475475
- `"zipapp"`: Python zipapp (`.pyz` file)
476476
- `"frozen"`: Frozen executable (PyInstaller, etc.)
477477

@@ -593,21 +593,21 @@ ensure_standalone_script_up_to_date(
593593
) -> Path
594594
```
595595

596-
Rebuild standalone script if missing or outdated.
596+
Rebuild stitched script if missing or outdated.
597597

598-
Checks if the standalone script exists and is newer than all source files. If not, rebuilds it using either the provided bundler script or the Poetry-installed `serger` module.
598+
Checks if the stitched script exists and is newer than all source files. If not, rebuilds it using either the provided bundler script or the Poetry-installed `serger` module.
599599

600600
**Parameters:**
601601

602602
| Parameter | Type | Description |
603603
|-----------|------|-------------|
604604
| `root` | `Path` | Project root directory |
605-
| `script_name` | `str \| None` | Optional name of the standalone script (without .py extension). If None, defaults to `package_name`. |
605+
| `script_name` | `str \| None` | Optional name of the stitched script (without .py extension). If None, defaults to `package_name`. |
606606
| `package_name` | `str` | Name of the package (e.g., "apathetic_utils") |
607607
| `bundler_script` | `str \| None` | Optional path to bundler script (relative to root). If provided and exists, uses `python {bundler_script}`. Otherwise, uses `python -m serger --config .serger.jsonc`. |
608608

609609
**Returns:**
610-
- `Path`: Path to the standalone script
610+
- `Path`: Path to the stitched script
611611

612612
**Raises:**
613613
- `RuntimeError`: If the script generation fails
@@ -703,8 +703,8 @@ runtime_swap(
703703
Pre-import hook — runs before any tests or plugins are imported.
704704

705705
Swaps in the appropriate runtime module based on `RUNTIME_MODE`:
706-
- `installed` (default): uses `src/{package_name}` (no swap needed)
707-
- `singlefile`: uses `dist/{script_name}.py` (serger-built single file)
706+
- `package` (default): uses `src/{package_name}` (no swap needed)
707+
- `stitched`: uses `dist/{script_name}.py` (serger-built single file)
708708
- `zipapp`: uses `dist/{script_name}.pyz` (zipbundler-built zipapp)
709709

710710
This ensures all test imports work transparently regardless of runtime mode.
@@ -715,12 +715,12 @@ This ensures all test imports work transparently regardless of runtime mode.
715715
|-----------|------|-------------|
716716
| `root` | `Path` | Project root directory |
717717
| `package_name` | `str` | Name of the package (e.g., "apathetic_utils") |
718-
| `script_name` | `str \| None` | Optional name of the standalone script (without extension). If None, defaults to `package_name`. |
718+
| `script_name` | `str \| None` | Optional name of the stitched script (without extension). If None, defaults to `package_name`. |
719719
| `bundler_script` | `str \| None` | Optional path to bundler script (relative to root). If provided and exists, uses `python {bundler_script}`. Otherwise, uses `python -m serger --config .serger.jsonc`. |
720720
| `mode` | `str \| None` | Runtime mode override. If None, reads from `RUNTIME_MODE` env var. |
721721

722722
**Returns:**
723-
- `bool`: `True` if swap was performed, `False` if in installed mode
723+
- `bool`: `True` if swap was performed, `False` if in package mode
724724

725725
**Raises:**
726726
- `pytest.UsageError`: If mode is invalid or build fails
@@ -735,15 +735,15 @@ from pathlib import Path
735735
runtime_swap(
736736
root=Path(__file__).parent.parent,
737737
package_name="my_package",
738-
mode="singlefile" # or None to read from RUNTIME_MODE env var
738+
mode="stitched" # or None to read from RUNTIME_MODE env var
739739
)
740740

741741
# Using a custom script name
742742
runtime_swap(
743743
root=Path(__file__).parent.parent,
744744
package_name="my_package",
745745
script_name="my_script",
746-
mode="singlefile"
746+
mode="stitched"
747747
)
748748

749749
# Using a local bundler script
@@ -752,7 +752,7 @@ runtime_swap(
752752
package_name="my_package",
753753
script_name="my_script",
754754
bundler_script="bin/serger.py",
755-
mode="singlefile"
755+
mode="stitched"
756756
)
757757
```
758758

@@ -1224,7 +1224,7 @@ Works in both package and stitched single-file runtimes. Walks `sys.modules` onc
12241224
| `func_name` | `str` | Name of the function to patch |
12251225
| `replacement_func` | `Callable[..., object]` | Function to replace the original with |
12261226
| `package_prefix` | `str \| Sequence[str]` | Package name prefix(es) to filter modules. Can be a single string (e.g., "apathetic_utils") or a sequence of strings (e.g., ["apathetic_utils", "my_package"]) to patch across multiple packages. |
1227-
| `stitch_hints` | `set[str] \| None` | Set of path hints to identify stitched modules. Defaults to `{"/dist/", "standalone"}`. When providing custom hints, you must be certain of the path attributes of your stitched file, as this uses substring matching on the module's `__file__` path. This is a heuristic fallback when identity checks fail (e.g., when modules are reloaded). |
1227+
| `stitch_hints` | `set[str] \| None` | Set of path hints to identify stitched modules. Defaults to `{"/dist/", "stitched"}`. When providing custom hints, you must be certain of the path attributes of your stitched file, as this uses substring matching on the module's `__file__` path. This is a heuristic fallback when identity checks fail (e.g., when modules are reloaded). |
12281228
| `create_if_missing` | `bool` | If True, create the attribute if it doesn't exist. If False (default), raise TypeError if the function doesn't exist. |
12291229
| `caller_func_name` | `str \| None` | If provided, only patch `__globals__` for this specific function to handle direct calls. If None (default), patch `__globals__` for all functions in stitched modules that reference the original function. |
12301230

docs/contributing.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ All key workflows are defined in **`[tool.poe.tasks]`** inside `pyproject.toml`.
5050
| `poetry run poe check:fix` | Auto-fix issues, re-format, type-check, and re-test. |
5151
| `poetry run poe check` | Run linting (`ruff`), type checks (`mypy`), and tests (`pytest`). |
5252
| `poetry run poe fix` | Run all auto-fixers (`ruff`). |
53-
| `poetry run poe build:script` | Bundle the project into a single portable script in `dist/`. |
53+
| `poetry run poe build:stitched` | Bundle the project into a single portable script in `dist/`. |
5454
| `poetry run poe build:zipapp` | Create a cross-platform zipapp (`.pyz`) with dependency support. |
5555
| `poetry run poe release:version` | Check what version would be released (dry run). |
5656
| `poetry run poe release:publish` | Publish a release (creates tag, GitHub release, etc.). |
@@ -267,7 +267,7 @@ Apathetic Python Utils ships in three forms:
267267

268268
| Target | Command | Output |
269269
|--------|---------|--------|
270-
| **Single-file script** | `poetry run poe build:script` | Creates `dist/apathetic_utils.py` (human-readable, stitched source) |
270+
| **Stitched script** | `poetry run poe build:stitched` | Creates `dist/apathetic_utils.py` (human-readable, stitched source) |
271271
| **Zipapp** | `poetry run poe build:zipapp` | Creates `dist/apathetic_utils.pyz` (dependency-aware, importable) |
272272
| **PyPI package** | `poetry build && poetry publish` | Builds and uploads wheel & sdist |
273273

docs/examples.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,10 @@ from apathetic_utils import detect_runtime_mode
183183
def get_build_info():
184184
mode = detect_runtime_mode("my_package")
185185

186-
if mode == "installed":
187-
print("Running as installed package")
188-
elif mode == "standalone":
189-
print("Running as standalone script")
186+
if mode == "package":
187+
print("Running as package")
188+
elif mode == "stitched":
189+
print("Running as stitched script")
190190
elif mode == "zipapp":
191191
print("Running as zipapp")
192192
elif mode == "frozen":
@@ -451,12 +451,12 @@ from pathlib import Path
451451
def get_build_config():
452452
mode = detect_runtime_mode("my_package")
453453

454-
if mode == "standalone":
455-
# Load standalone-specific config
454+
if mode == "stitched":
455+
# Load stitched-specific config
456456
config = load_jsonc(Path(".serger.jsonc"))
457-
return config.get("standalone", {})
457+
return config.get("stitched", {})
458458
else:
459-
# Use installed package config
459+
# Use package config
460460
return {}
461461
```
462462

docs/index.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ permalink: /
77
# Apathetic Python Utils ⚙️
88

99
**Grab bag of helpers for Apathetic projects.**
10-
*When stdlib just isn't enough.*
10+
*When stdlib is almost enough.*
1111

1212
*Apathetic Python Utils* provides a lightweight, dependency-free collection of utility functions designed for CLI tools. It includes helpers for file loading, path manipulation, system detection, text processing, type checking, pattern matching, and more.
1313

@@ -18,7 +18,7 @@ permalink: /
1818
- 🔍 **Pattern matching** — Portable glob pattern matching with recursive `**` support
1919
- 🧩 **Module detection** — Detect Python packages from file paths
2020
- 🧪 **System detection** — Detect CI environments, pytest execution, and runtime modes
21-
- ⚙️ **Runtime utilities** — Build and test utilities for standalone scripts and zipapps
21+
- ⚙️ **Runtime utilities** — Build and test utilities for stitched scripts and zipapps
2222
- 🔧 **Subprocess utilities** — Capture and forward subprocess output
2323
- 📝 **Text processing** — Pluralization and error message cleanup utilities
2424
- 🔧 **Type utilities** — Safe isinstance checks for TypedDicts and generics
@@ -42,7 +42,7 @@ pyproject = load_toml(Path("pyproject.toml"))
4242
if is_ci():
4343
print("Running in CI")
4444

45-
# Detect runtime mode (installed, standalone, zipapp, frozen)
45+
# Detect runtime mode (package, stitched, zipapp, frozen)
4646
mode = detect_runtime_mode("my_package")
4747
print(f"Running in {mode} mode")
4848

docs/installation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ This installation method provides:
3131

3232
## Alternative: Single-File Distribution
3333

34-
For projects that prefer a single-file dependency, we also distribute a standalone `apathetic_utils.py` file that you can download directly from [releases](https://github.com/apathetic-tools/python-utils/releases).
34+
For projects that prefer a single-file dependency, we also distribute a stitched `apathetic_utils.py` file that you can download directly from [releases](https://github.com/apathetic-tools/python-utils/releases).
3535

3636
### Download and Use
3737

docs/quickstart.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Detect how your package is being executed:
9393
from apathetic_utils import detect_runtime_mode
9494

9595
mode = detect_runtime_mode("my_package")
96-
# Returns: "installed", "standalone", "zipapp", or "frozen"
96+
# Returns: "package", "stitched", "zipapp", or "frozen"
9797
```
9898

9999
## Path Utilities

pyproject.toml

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[project]
44
name = "apathetic-utils"
55
version = "0.2.2"
6-
description = "When stdlib isn't enough."
6+
description = "When stdlib is almost enough."
77
authors = [
88
{ name = "Apathetic Tools" }
99
]
@@ -51,21 +51,21 @@ typecheck = ["typecheck:mypy", "typecheck:pyright"]
5151
# run pyright to simulate pylance for AI CLI auto-fix
5252
"typecheck:pyright" = "bash dev/pyright-quiet.sh src tests"
5353

54-
# 🧪 Tests (runs all three: installed + singlefile + zipapp)
54+
# 🧪 Tests (runs all three: package + stitched + zipapp)
5555
test = [
56-
"test:pytest:installed",
57-
# "test:pytest:script",
56+
"test:pytest:package",
57+
# "test:pytest:stitched",
5858
# "test:pytest:zipapp"
5959
]
60-
"test:pytest:installed" = "pytest"
61-
"test:pytest:installed:parallel" = "pytest -n auto"
62-
"test:pytest:script" = [
63-
"build:script",
64-
{ cmd = "pytest", env = { RUNTIME_MODE="singlefile" } }
60+
"test:pytest:package" = "pytest"
61+
"test:pytest:package:parallel" = "pytest -n auto"
62+
"test:pytest:stitched" = [
63+
"build:stitched",
64+
{ cmd = "pytest", env = { RUNTIME_MODE="stitched" } }
6565
]
66-
"test:pytest:script:parallel" = [
67-
"build:script",
68-
{ cmd = "pytest -n auto", env = { RUNTIME_MODE="singlefile" } }
66+
"test:pytest:stitched:parallel" = [
67+
"build:stitched",
68+
{ cmd = "pytest -n auto", env = { RUNTIME_MODE="stitched" } }
6969
]
7070
"test:pytest:zipapp" = [
7171
"build:zipapp",
@@ -77,41 +77,41 @@ test = [
7777
]
7878

7979
# 🚀 Fast test runs (for development)
80-
# Fast: installed mode only, parallel, skip slow tests
80+
# Fast: package mode only, parallel, skip slow tests
8181
"test:fast" = "pytest -n auto -m 'not slow'"
8282
# Fast: all three modes, parallel, skip slow tests
83-
"test:fast:installed" = "pytest -n auto -m 'not slow'"
84-
"test:fast:script" = [
85-
"build:script",
86-
{ cmd = "pytest -n auto -m 'not slow'", env = { RUNTIME_MODE="singlefile" } }
83+
"test:fast:package" = "pytest -n auto -m 'not slow'"
84+
"test:fast:stitched" = [
85+
"build:stitched",
86+
{ cmd = "pytest -n auto -m 'not slow'", env = { RUNTIME_MODE="stitched" } }
8787
]
8888
"test:fast:zipapp" = [
8989
"build:zipapp",
9090
{ cmd = "pytest -n auto -m 'not slow'", env = { RUNTIME_MODE="zipapp" } }
9191
]
9292
"test:fast:all" = [
93-
"test:fast:installed",
94-
# "test:fast:script",
93+
"test:fast:package",
94+
# "test:fast:stitched",
9595
# "test:fast:zipapp"
9696
]
9797
# Parallel: all three modes with parallel execution
9898
"test:parallel" = [
99-
"test:pytest:installed:parallel",
100-
# "test:pytest:script:parallel",
99+
"test:pytest:package:parallel",
100+
# "test:pytest:stitched:parallel",
101101
# "test:pytest:zipapp:parallel"
102102
]
103103

104104
# 📊 Coverage reporting
105105
"coverage:clean" = { shell = "rm -f .coverage .coverage.*" }
106-
"coverage:run:installed" = { cmd = "pytest --cov=src --cov-report= --cov-context=test", env = { COVERAGE_FILE=".coverage.installed" } }
107-
"coverage:run:singlefile" = { cmd = "pytest --cov=src --cov-report= --cov-context=test", env = { RUNTIME_MODE="singlefile", COVERAGE_FILE=".coverage.singlefile" } }
106+
"coverage:run:package" = { cmd = "pytest --cov=src --cov-report= --cov-context=test", env = { COVERAGE_FILE=".coverage.package" } }
107+
"coverage:run:stitched" = { cmd = "pytest --cov=src --cov-report= --cov-context=test", env = { RUNTIME_MODE="stitched", COVERAGE_FILE=".coverage.stitched" } }
108108
"coverage:run:zipapp" = { cmd = "pytest --cov=src --cov-report= --cov-context=test", env = { RUNTIME_MODE="zipapp", COVERAGE_FILE=".coverage.zipapp" } }
109-
"coverage:combine" = { cmd = "coverage combine .coverage.installed .coverage.singlefile .coverage.zipapp" }
109+
"coverage:combine" = { cmd = "coverage combine .coverage.package .coverage.stitched .coverage.zipapp" }
110110
"coverage:base" = [
111111
"coverage:clean",
112-
"coverage:run:installed",
113-
"build:script",
114-
"coverage:run:singlefile",
112+
"coverage:run:package",
113+
"build:stitched",
114+
"coverage:run:stitched",
115115
"build:zipapp",
116116
"coverage:run:zipapp",
117117
"coverage:combine",
@@ -128,16 +128,16 @@ test = [
128128
]
129129

130130
# 🧹 Automated fixing / formatting
131-
fix = ["sync:ai:guidance", "fix:ruff:installed", "fix:format:installed"]
131+
fix = ["sync:ai:guidance", "fix:ruff:package", "fix:format:package"]
132132
"fix:dist" = ["fix:ruff:dist", "fix:format:dist"]
133-
"fix:ruff:installed" = "ruff check . --fix -q"
133+
"fix:ruff:package" = "ruff check . --fix -q"
134134
"fix:ruff:dist" = { shell = "if [ -d dist ]; then ruff check dist --fix -q; fi" }
135-
"fix:format:installed" = "ruff format . -q"
135+
"fix:format:package" = "ruff format . -q"
136136
"fix:format:dist" = { shell = "if [ -d dist ]; then ruff format dist -q; fi" }
137137

138138
# 🧩 Build and license
139139
"sync:ai:guidance" = "python -m sync_ai_guidance --quiet"
140-
"build:script" = "python -m serger"
140+
"build:stitched" = "python -m serger"
141141
"build:zipapp" = "python -m zipbundler -m apathetic_utils -o dist/apathetic_utils.pyz . -q"
142142

143143
# 🚀 Release management (semantic-release)

0 commit comments

Comments
 (0)