Rewrap Python # comments to a specified line length
Project description
octowrap
A CLI tool that rewraps octothorpe (#) Python comments to a specified line length while preserving commented-out code, section dividers, list items, and tool directives. TODO/FIXME markers are intelligently rewrapped with continuation indentation.
Features
- Rewraps comment blocks to a configurable line length (default 88)
- Keeps hyphenated words intact (never breaks
command-line-interfaceat hyphens) - Keeps long words and URLs intact (they overflow the line length rather than being broken mid-word)
- Heals previously broken hyphenated words on rewrap (e.g.
re-/validate->re-validate) - Preserves commented-out Python code (detected via 21 heuristic patterns with a prose disqualifier to avoid false positives on natural English)
- Preserves section dividers (
# --------,# ========, etc.) - Preserves list items (bullets, numbered items)
- Rewraps TODO/FIXME markers with proper continuation indent, with configurable patterns, case sensitivity, and multi-line collection
- Preserves tool directives (
type: ignore,noqa,fmt: off,pragma: no cover,pylint: disable, etc.) - Supports
# octowrap: off/# octowrap: onpragma comments to disable rewrapping for regions of a file - Applies changes automatically by default, or use
-ifor interactive per block approval with colorized diffs (aaccept,Aaccept all,eexclude,fflag,sskip,qquit). Flagging inserts a FIXME marker above the block for later human attention. Quitting stops all processing, including remaining files. - Reads from stdin when
-is passed as the path (like black/ruff/isort) - Auto-detects color support; respects
--no-color,--color, and theNO_COLORenv var - Atomic file writes (temp file + rename) to protect against interruptions and power loss
- Project-level configuration via
[tool.octowrap]inpyproject.toml
Development Setup
git clone https://github.com/camUrban/octowrap.git
cd octowrap
uv venv # uses .python-version (3.13)
uv pip install -e ".[dev]"
Note: The dev environment is pinned to Python 3.13 via
.python-versionbecause docformatter'suntokenizedependency doesn't build on 3.14. The runtime itself supports 3.11+.
Usage
octowrap <files_or_dirs> [--line-length 88] [--config PATH] [--stdin-filename PATH] [--dry-run] [--diff] [--check] [--no-recursive] [-i] [--color | --no-color]
Stdin/stdout
Pass - as the path to read from stdin and write to stdout:
echo "# A very long comment that needs rewrapping to a shorter width." | octowrap -
cat file.py | octowrap - --diff # show diff
cat file.py | octowrap - --check # exit 1 if changes needed
cat file.py | octowrap - -l 79 # custom line length
Use --stdin-filename to provide the original file path for config discovery and diff labels (useful for editor integrations like VS Code and Vim that pipe buffers via stdin):
cat file.py | octowrap - --stdin-filename src/app.py --diff
Note: - cannot be mixed with other paths and is incompatible with -i (interactive mode). --stdin-filename requires -.
Example
Before:
# This is a long comment that has been written without much regard for line length and really should be wrapped to fit within a reasonable number of columns.
After (--line-length 88):
# This is a long comment that has been written without much regard for line
# length and really should be wrapped to fit within a reasonable number of
# columns.
TODO/FIXME Rewrapping
By default, TODO and FIXME markers are detected (case-insensitive, no colon required) and rewrapped with the marker on the first line and a one-space continuation indent on subsequent lines:
Before:
# TODO: Refactor this function to use the new async API instead of the old synchronous one, and update all callers.
After (--line-length 88):
# TODO: Refactor this function to use the new async API instead of the old
# synchronous one, and update all callers.
Multi-line TODOs (continuation lines starting with exactly one space) are collected and rewrapped together:
# TODO: This is a long todo
# that continues on the next line
Configure TODO handling via pyproject.toml:
[tool.octowrap]
todo-patterns = ["todo", "fixme", "hack"] # replace default patterns
extend-todo-patterns = ["note"] # add to effective patterns
todo-case-sensitive = true # match patterns literally
todo-multiline = false # don't collect continuations
Setting todo-patterns = [] disables TODO detection entirely, causing former TODO lines to be rewrapped as regular prose.
Disabling Rewrapping
Use pragma comments to protect regions of a file from rewrapping, similar to # fmt: off/on in black/ruff:
# octowrap: off
# This comment will not be rewrapped,
# no matter how long or short
# the lines are.
# octowrap: on
# This comment will be rewrapped normally.
- Directives are case-insensitive (
# OCTOWRAP: OFFworks) - Must be a standalone comment line (inline
x = 1 # octowrap: offis ignored) # octowrap: offwithout a matchingondisables rewrapping through end of file- Pragma lines themselves are always preserved as-is
Editor Integration
PyCharm
Settings -> Tools -> File Watchers -> Add:
- File type: Python
- Program:
$ProjectFileDir$/.venv/Scripts/octowrap.exe(or.venv/bin/octowrapon Unix) - Arguments:
$FilePath$ - Output paths to refresh:
$FilePath$ - Working directory:
$ProjectFileDir$
Pre-commit Hook
Add octowrap to your .pre-commit-config.yaml:
- repo: https://github.com/camUrban/octowrap
rev: v0.3.1
hooks:
- id: octowrap
# args: [-l, "79"] # custom line length
# args: [--check] # fail without modifying (useful for CI)
GitHub Actions
Use --check in CI to fail if any comments would be rewrapped:
- name: Install octowrap
run: pip install octowrap
- name: Check comment wrapping
run: octowrap --check .
Configuration
Add a [tool.octowrap] section to your pyproject.toml to set project-level defaults:
[tool.octowrap]
line-length = 120
recursive = false
exclude = ["migrations", "generated"]
extend-exclude = ["vendor"]
| Key | Type | Default | CLI equivalent |
|---|---|---|---|
line-length |
int | 88 | --line-length |
recursive |
bool | true | --no-recursive |
exclude |
list[str] | — | — |
extend-exclude |
list[str] | — | — |
todo-patterns |
list[str] | ["todo", "fixme"] |
— |
extend-todo-patterns |
list[str] | — | — |
todo-case-sensitive |
bool | false | — |
todo-multiline |
bool | true | — |
CLI flags always take precedence over config values. Use --config PATH to point to a specific pyproject.toml instead of relying on auto-discovery.
exclude replaces the built-in default exclude list entirely. extend-exclude adds patterns to the defaults (or to exclude if set). Default excludes: .git, .hg, .svn, .bzr, .venv, venv, .tox, .nox, .mypy_cache, .ruff_cache, .pytest_cache, __pycache__, __pypackages__, _build, build, dist, node_modules, .eggs. Patterns are matched against individual path components using fnmatch.
todo-patterns replaces the default TODO marker patterns (["todo", "fixme"]). extend-todo-patterns adds to the effective list. Both can be combined. Setting todo-patterns = [] disables TODO detection entirely.
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file octowrap-0.3.1.tar.gz.
File metadata
- Download URL: octowrap-0.3.1.tar.gz
- Upload date:
- Size: 38.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5c89ca511c52ada6f7037ce0ff351c0366f7444bd60fecaafbee770692f5cc92
|
|
| MD5 |
f9f6809b103fd6e53aa654737b9104cf
|
|
| BLAKE2b-256 |
f0c005d308c836a5471fa1266d1b3dda97cba69453dbb298c4ce3617c306526a
|
Provenance
The following attestation bundles were made for octowrap-0.3.1.tar.gz:
Publisher:
publish.yml on camUrban/octowrap
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
octowrap-0.3.1.tar.gz -
Subject digest:
5c89ca511c52ada6f7037ce0ff351c0366f7444bd60fecaafbee770692f5cc92 - Sigstore transparency entry: 936117188
- Sigstore integration time:
-
Permalink:
camUrban/octowrap@09f92afe7f6fa74ea5d8ae333ea3c825d4f617f6 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/camUrban
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@09f92afe7f6fa74ea5d8ae333ea3c825d4f617f6 -
Trigger Event:
release
-
Statement type:
File details
Details for the file octowrap-0.3.1-py3-none-any.whl.
File metadata
- Download URL: octowrap-0.3.1-py3-none-any.whl
- Upload date:
- Size: 16.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3cead9308e3f8e0ebe67c62c73d2925b6f95a921313e94e81893739114a4aff
|
|
| MD5 |
0f38abac7804e774c9c0aadae1464698
|
|
| BLAKE2b-256 |
5449f2c48f1ebc9340923520bb402be6639155ab638c2b68356d7373956a7be4
|
Provenance
The following attestation bundles were made for octowrap-0.3.1-py3-none-any.whl:
Publisher:
publish.yml on camUrban/octowrap
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
octowrap-0.3.1-py3-none-any.whl -
Subject digest:
c3cead9308e3f8e0ebe67c62c73d2925b6f95a921313e94e81893739114a4aff - Sigstore transparency entry: 936117208
- Sigstore integration time:
-
Permalink:
camUrban/octowrap@09f92afe7f6fa74ea5d8ae333ea3c825d4f617f6 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/camUrban
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@09f92afe7f6fa74ea5d8ae333ea3c825d4f617f6 -
Trigger Event:
release
-
Statement type: