Last active
May 20, 2025 20:33
-
-
Save panzi/b4a51b3968f67b9ff4c99459fb9c5b3d to your computer and use it in GitHub Desktop.
Python argparse help formatter that keeps new lines and doesn't break words (so URLs are preserved), but still wraps lines. Use with: `argparse.ArgumentParser(formatter_class=SmartFormatter)`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Version 2: | |
# This version preserves space, including indentation. | |
# | |
# Also if you have lists like this: | |
# | |
# foo ....... bla bla bla | |
# bar baz ... bla bla bla bla | |
# | |
# It will re-formated it to e.g. this: | |
# | |
# foo ....... bla bla | |
# bla | |
# bar baz ... bla bla | |
# bla bla | |
# | |
# This detects a sequence of >=3 periods or >=3 spaces. | |
# | |
# LICNESE: Public Domain/CC0 | |
import argparse | |
import re | |
SPACE = re.compile(r'\s') | |
NON_SPACE = re.compile(r'\S') | |
class SmartFormatter(argparse.HelpFormatter): | |
def _split_lines(self, text: str, width: int) -> list[str]: | |
lines: list[str] = [] | |
for line_str in text.split('\n'): | |
match = NON_SPACE.search(line_str) | |
if not match: | |
lines.append('') | |
continue | |
prefix = line_str[:match.start()] | |
if len(prefix) >= width: | |
lines.append('') | |
prefix = '' | |
line_len = prefix_len = len(prefix) | |
line: list[str] = [prefix] | |
pos = match.start() | |
while pos < len(line_str): | |
match = NON_SPACE.search(line_str, pos) | |
if not match: | |
break | |
next_pos = match.start() | |
space = line_str[pos:next_pos] | |
line_len += len(space) | |
if line_len >= width: | |
lines.append(''.join(line)) | |
line.clear() | |
line.append(prefix) | |
line_len = prefix_len | |
else: | |
line.append(space) | |
pos = next_pos | |
match = SPACE.search(line_str, pos) | |
if not match: | |
next_pos = len(line_str) | |
else: | |
next_pos = match.start() | |
word = line_str[pos:next_pos] | |
word_len = len(word) | |
line_len += word_len | |
if line_len > width: | |
lines.append(''.join(line)) | |
line.clear() | |
line.append(prefix) | |
line_len = prefix_len + word_len | |
elif word_len >= 3: | |
if all(c == '.' for c in word) and line_str[next_pos:next_pos + 1].isspace(): | |
prefix_len = line_len + 1 | |
prefix = ' ' * prefix_len | |
elif all(c == ' ' for c in word): | |
prefix_len = line_len | |
prefix = ' ' * prefix_len | |
line.append(word) | |
pos = next_pos | |
lines.append(''.join(line)) | |
return lines | |
def _fill_text(self, text: str, width: int, indent: str) -> str: | |
return '\n'.join(indent + line for line in self._split_lines(text, width - len(indent))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for sharing this! You just saved me a ton of time!