Rules for Python host code (templates/*.py, root launchers, tests) so every change runs identically on Windows, Ubuntu, and macOS the first time — host detection, path flavor, subprocess discipline, UTF-8, process replacement, and platform-portable test expectations.
Scanned 9/3/2026
Install to Claude Code
npx -y skills add FoxsterDev/xuunity-mcp --skill cross_platform_python --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Cross Platform Python?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/foxsterdev-cross-platform-python)More formats (shields.io, HTML) on the badges page.
---
name: cross-platform-python
description: Rules for Python host code (templates/*.py, root launchers, tests) so every change runs identically on Windows, Ubuntu, and macOS the first time — host detection, path flavor, subprocess discipline, UTF-8, process replacement, and platform-portable test expectations.
---
# Cross-Platform Python Guidelines
The Python host runs on macOS daily and on Windows/Linux only when a colleague or CI executes it. Every rule below was earned from a real Windows failure that macOS could not reproduce (2026-07 deep review: `docs/archive/reports/2026-07-10_windows_platform_deep_review.md`). Load this skill before writing or editing any Python in `templates/`, the root `*.py` launchers, or tests.
---
## 1. Host Detection Goes Through `server_core`
- `server_core.is_windows_like_host()` for user-facing flavor decisions (rendered commands, launcher names, quoting). It is env-based and covers native cmd/PowerShell, MSYS/Git Bash, and Cygwin — `os.name` alone does not.
- `server_host_platform.host_platform_kind()` for adapter routing (process listing, liveness).
- Never branch on `sys.platform == "darwin"` as an implicit "POSIX else Windows" split; Linux must land in the POSIX branch deliberately.
## 2. Path Flavor Is Part of the Contract
- Shell-facing strings: `server_core.quoted_shell_path()` / `render_launcher_cli()` — native form + quotes on Windows-like hosts, `as_posix()` elsewhere. Never embed bare `str(path)` in a command template (see cross-platform-shell skill rule 8).
- Preserve `PurePath` flavor in helpers: wrapping an incoming `PureWindowsPath` in `Path(...)` on a POSIX host silently rewrites separators.
- Durable values read later by native Windows tools must be host-native; values read by Git Bash must be POSIX (cross-platform-shell skill rule 7).
## 3. Subprocess Discipline (enforced by AST sweep)
Every helper spawn (`tasklist`, `taskkill`, PowerShell, `ps`, `lsof`, `wslpath`, `git`, `open`, refresh helpers) must pass:
- `timeout=` — a hung probe otherwise blocks the MCP server forever with zero diagnostics;
- explicit `subprocess.TimeoutExpired` handling — it is **not** an `OSError`; an `except OSError` guard silently lets the timeout escape and crash the caller (this bug shipped in the PowerShell process-listing path);
- `encoding="utf-8", errors="replace"` — never locale/ANSI decode (cp1251/cp866 hosts corrupt output);
- `**server_core.hidden_window_subprocess_kwargs()` when the spawn can run on a Windows host — GUI-hosted servers must not flash console windows.
The only legitimate no-timeout spawns are children that ARE the workload (delegated server run, batch self-invocations). `tests/test_subprocess_timeout_contract.py` sweeps every `subprocess.run` call via AST and holds the explicit allowlist — extend the allowlist consciously; never drop `timeout=` silently.
## 4. UTF-8 End to End
- Call `server_core.reconfigure_stdio_utf8()` first in every Python entrypoint; Windows pipes default to the ANSI codepage and a Cyrillic project path kills stdio mid-decode otherwise.
- Launchers default `PYTHONUTF8=1`; keep protocol JSON `ensure_ascii=True`.
- File reads accept BOM/UTF-16 where the writer may be PowerShell (`server_core.read_json`).
## 5. `os.execv` Is POSIX-Only Behavior
On Windows `os.execv` spawns a new process and kills the parent — an MCP client watching the parent sees its server die. On `os.name == "nt"` use `subprocess.run` and propagate the exit code (`exec_python_script`, refresh `exec_run` are the reference shape).
## 6. Files Other Processes Poll Are Published Atomically
Use `server_core.write_json` (temp + `os.replace`) for anything the editor or another process polls. Never ad-hoc `open(...).write` for polled files. Full co-design rules: atomic-ipc-files skill.
## 7. POSIX-Only Tools Need a Windows Answer
`lsof`, `ps`, `open` have no Windows presence. Any feature built on them needs a Windows counterpart (`tasklist`, CIM listing, Hub `secondaryInstallPath.json` discovery) or a documented safe degradation — silent `[]` on Windows turns a safety check into a no-op.
## 8. Platform-Portable Test Expectations
Windows CI failures from this repo's history were mostly test-pinning bugs, not product bugs. When writing tests:
- Pin the flavor when asserting rendered commands: `mock.patch.object(server_core, "is_windows_like_host", return_value=False)` — otherwise the Windows leg correctly renders `.cmd` + native paths and the assert breaks.
- Derive path expectations from the platform: expect `str(Path("/x/y"))`, not a hard-coded `"/x/y"`.
- Case-insensitive usage asserts: the wrapper prints `Usage:`, argparse prints `usage:` — compare on `.lower()`.
- Never compare full path strings (cross-platform-shell skill rule 9).
- Patch through the facade, call through the facade: `server_editor_host` re-syncs owner modules per call; patches applied via the facade leak into owner modules until the next facade call, so direct owner-module calls in later tests see stale mocks (`tests/test_subprocess_timeout_contract.py` TaskkillContractTest documents the shape).
- New cross-module name in a star-import module: verify it resolves in the defining module (`hasattr` probe) AND add a test that executes the new call path — a latent `NameError` on an untested branch passed a 340-test suite here once (`render_launcher_cli` in the lifecycle module).
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!