From python-engineering
Provides patterns for Python cross-platform script compatibility on Windows, Linux, macOS across CLI, TUI (Rich/Textual), GUI; fixes Unicode/encoding, Rich/Typer output breaks, paths, terminal detection, console colors.
How this skill is triggered — by the user, by Claude, or both
Slash command
/python-engineering:python-cross-platform-smoothingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Verified patterns for writing Python scripts that behave correctly on Windows, Linux, and macOS across CLI, TUI, and GUI contexts.
Verified patterns for writing Python scripts that behave correctly on Windows, Linux, and macOS across CLI, TUI, and GUI contexts.
Problem: Windows defaults stdout/stderr to a legacy code page (cp1252, cp437). Rich and Typer use Unicode characters (box-drawing, spinners, emoji) that these encodings cannot represent, causing UnicodeEncodeError at runtime.
Solution: Reconfigure streams before importing Rich or Typer.
import sys
from io import TextIOWrapper
# Ensure UTF-8 output on Windows (cp1252 default cannot encode emoji/spinner chars).
# reconfigure() is available on Python 3.7+ when stdout is a TextIOWrapper.
if isinstance(sys.stdout, TextIOWrapper):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
if isinstance(sys.stderr, TextIOWrapper):
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
# Rich/Typer imports come AFTER the reconfigure block
import typer
from rich.console import Console
Placement rule: Immediately after stdlib imports, before any third-party imports. Rich inspects stdout encoding at import time.
errors="replace" substitutes unencodable characters with ? instead of raising. Prefer this over errors="strict" for CLI tools where a crash is worse than a degraded character.
isinstance guard skips reconfigure when stdout is not a TextIOWrapper (e.g. redirected to BytesIO in tests, or already a custom wrapper). Safe to include unconditionally.
Rich Console variant: When targeting legacy Windows consoles (cmd.exe without Windows Terminal), also pass legacy_windows=False to force ANSI escape sequences instead of the Windows Console API:
console = Console(legacy_windows=False)
Why not env vars? PYTHONUTF8=1 and PYTHONIOENCODING=utf-8 require the caller to set them before the process starts — not self-contained in the script. The reconfigure() pattern is fully portable with no external configuration required.
Also applies to: pathlib.Path.write_text(), open(), and any file I/O that inherits the default encoding. Always pass encoding="utf-8" explicitly to file write calls on Windows:
path.write_text(content, encoding="utf-8")
open(path, "w", encoding="utf-8")
npx claudepluginhub jamie-bitflight/claude_skills --plugin python-engineeringGuides Typer and Rich best practices for Python CLI apps, covering non-TTY output, table rendering, testing patterns, exception handling, and integration pitfalls.
Guides Typer/Rich CLI development and review with patterns for non-TTY output, table rendering, pytest testing, and exception handling.
Provides Python CLI patterns using Typer for commands/groups/options and Rich for tables, progress bars, panels, and error handling in terminal apps.