Claude Code for msvcrt: Python Windows Console and File Utilities — Claude Skills 360 Blog
Blog / AI / Claude Code for msvcrt: Python Windows Console and File Utilities
AI

Claude Code for msvcrt: Python Windows Console and File Utilities

Published: January 12, 2029
Read time: 5 min read
By: Claude Skills 360

Python’s msvcrt module (Windows only) exposes the Microsoft Visual C Runtime’s console I/O and file-management routines. import msvcrt. Keypress: msvcrt.kbhit() → True if a key is waiting; msvcrt.getch()bytes (1 byte, no echo, no newline wait); msvcrt.getwch()str (wide char); msvcrt.getche()bytes (echoes char). Write: msvcrt.putch(b'x') — one byte to stdout without newline; msvcrt.putwch('x') — wide char. Push back: msvcrt.ungetch(b'x'). File mode: msvcrt.setmode(fd, os.O_BINARY) — switch a file descriptor to binary mode; msvcrt.setmode(fd, os.O_TEXT) — text mode. File lock: msvcrt.locking(fd, msvcrt.LK_NBLCK, nbytes) — non-blocking lock; msvcrt.locking(fd, msvcrt.LK_UNLCK, nbytes) — unlock. Handle bridge: handle = msvcrt.get_osfhandle(fd) — C fd → Windows HANDLE; fd = msvcrt.open_osfhandle(handle, os.O_RDONLY) — HANDLE → C fd. Claude Code generates Windows CLI menus, keystroke-driven TUI apps, non-blocking keyboard loops, file-locking utilities, and console progress spinners.

CLAUDE.md for msvcrt

## msvcrt Stack
- Stdlib: import msvcrt, os   (Windows only)
- Poll:   msvcrt.kbhit()                      # True if key waiting
- Read:   msvcrt.getch()                       # bytes, no echo
-         msvcrt.getche()                      # bytes, with echo
-         msvcrt.getwch()                      # str wide char
- Write:  msvcrt.putch(b'.')                   # one byte, no newline
- Mode:   msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
- Lock:   msvcrt.locking(fd, msvcrt.LK_NBLCK, n)  # lock n bytes
-         msvcrt.locking(fd, msvcrt.LK_UNLCK, n)  # unlock
- Handle: msvcrt.get_osfhandle(fd)   # fd → Windows HANDLE

msvcrt Console and File Pipeline

# app/msvcrtutil.py — keypress, menu, progress, file locking, handle bridge
from __future__ import annotations

import os
import sys
import time
import threading
from contextlib import contextmanager
from dataclasses import dataclass, field
from typing import Callable, Iterator

# Only import msvcrt on Windows
_MSVCRT_AVAILABLE = False
if os.name == "nt":
    try:
        import msvcrt as _ms
        _MSVCRT_AVAILABLE = True
    except ImportError:
        pass


# ─────────────────────────────────────────────────────────────────────────────
# 1. Single-keypress helpers
# ─────────────────────────────────────────────────────────────────────────────

def kbhit() -> bool:
    """
    Return True if a keypress is waiting in the input buffer.
    Always returns False on non-Windows platforms.

    Example:
        if kbhit():
            ch = getch()
    """
    if _MSVCRT_AVAILABLE:
        return _ms.kbhit()
    return False


def getch() -> bytes | None:
    """
    Read one keypress without echo. Returns None on non-Windows.
    Extended keys (arrows, F-keys) return b'\\x00' or b'\\xe0' as first byte;
    call getch() a second time to get the scan code.

    Example:
        ch = getch()                  # b'a', b'\\r' for Enter, etc.
        if ch in (b'\\x00', b'\\xe0'):
            scan = getch()            # extended key scan code
    """
    if _MSVCRT_AVAILABLE:
        return _ms.getch()
    return None


def getche() -> bytes | None:
    """Read one keypress, echo it. Returns None on non-Windows."""
    if _MSVCRT_AVAILABLE:
        return _ms.getche()
    return None


def getwch() -> str | None:
    """Read one wide-character keypress without echo. Returns None on non-Windows."""
    if _MSVCRT_AVAILABLE:
        return _ms.getwch()
    return None


def putch(char: bytes) -> None:
    """
    Write one byte to the console without a trailing newline.

    Example:
        for c in b"Loading...":
            putch(bytes([c]))
            time.sleep(0.05)
    """
    if _MSVCRT_AVAILABLE:
        _ms.putch(char[:1])
    else:
        sys.stdout.write(char.decode("latin-1", errors="replace"))
        sys.stdout.flush()


# ─────────────────────────────────────────────────────────────────────────────
# 2. Interactive menu
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class MenuItem:
    key: str          # single character key
    label: str        # display label
    action: Callable[[], None]


def run_menu(title: str, items: list[MenuItem], quit_key: str = "q") -> None:
    """
    Display a keystroke-driven menu and dispatch actions.
    Press quit_key to exit. Falls back to input() on non-Windows.

    Example:
        run_menu("My App", [
            MenuItem("1", "Say hello", lambda: print("Hello!")),
            MenuItem("2", "Show time", lambda: print(time.ctime())),
        ])
    """
    while True:
        print(f"\n{'=' * 40}")
        print(f"  {title}")
        print(f"{'=' * 40}")
        for item in items:
            print(f"  [{item.key}]  {item.label}")
        print(f"  [{quit_key}]  Quit")
        print("Press a key: ", end="", flush=True)

        if _MSVCRT_AVAILABLE:
            ch = _ms.getch()
            key = ch.decode("latin-1", errors="replace")
        else:
            key = input().strip()[:1]

        print(key)

        if key == quit_key:
            break

        for item in items:
            if key == item.key:
                item.action()
                break


# ─────────────────────────────────────────────────────────────────────────────
# 3. Non-blocking keyboard loop
# ─────────────────────────────────────────────────────────────────────────────

def wait_for_key(timeout_s: float = 5.0, prompt: str = "Press any key...") -> bytes | None:
    """
    Wait up to timeout_s seconds for a keypress. Returns the key bytes
    or None if timed out. Non-blocking on Windows; blocking fallback elsewhere.

    Example:
        key = wait_for_key(3.0, "Press a key within 3 seconds: ")
        if key is None:
            print("Timed out")
    """
    print(prompt, end="", flush=True)
    if not _MSVCRT_AVAILABLE:
        return None  # no-op on non-Windows

    deadline = time.monotonic() + timeout_s
    while time.monotonic() < deadline:
        if _ms.kbhit():
            ch = _ms.getch()
            print()
            return ch
        time.sleep(0.05)
    print()
    return None


# ─────────────────────────────────────────────────────────────────────────────
# 4. Console spinner with keypress stop
# ─────────────────────────────────────────────────────────────────────────────

@contextmanager
def console_spinner(message: str = "Working") -> Iterator[None]:
    """
    Show a spinner in the console while a block runs.
    On Windows, pressing any key stops the spinner early.

    Example:
        with console_spinner("Processing"):
            time.sleep(3)
    """
    _stop = threading.Event()
    frames = ["|", "/", "-", "\\"]

    def _spin() -> None:
        idx = 0
        while not _stop.is_set():
            frame = frames[idx % len(frames)]
            sys.stdout.write(f"\r{message}... {frame} ")
            sys.stdout.flush()
            if _MSVCRT_AVAILABLE and _ms.kbhit():
                _ms.getch()      # consume the key
                _stop.set()
                break
            time.sleep(0.1)
            idx += 1
        sys.stdout.write(f"\r{message}... done\n")
        sys.stdout.flush()

    t = threading.Thread(target=_spin, daemon=True)
    t.start()
    try:
        yield
    finally:
        _stop.set()
        t.join(timeout=0.5)


# ─────────────────────────────────────────────────────────────────────────────
# 5. File mode + byte-range locking
# ─────────────────────────────────────────────────────────────────────────────

def set_binary_mode(fd: int) -> None:
    """
    Switch a file descriptor to binary mode (no CR/LF translation).
    No-op on non-Windows.

    Example:
        set_binary_mode(sys.stdout.fileno())
    """
    if _MSVCRT_AVAILABLE:
        _ms.setmode(fd, os.O_BINARY)


@contextmanager
def locked_region(fd: int, nbytes: int, offset: int = 0) -> Iterator[None]:
    """
    Lock a byte region of an open file descriptor for exclusive access,
    yield, then unlock. Uses LK_NBLCK (non-blocking, raises OSError if busy).
    No-op on non-Windows.

    Example:
        with open("data.bin", "r+b") as f:
            with locked_region(f.fileno(), 128, offset=0):
                f.seek(0)
                data = f.read(128)
    """
    if not _MSVCRT_AVAILABLE:
        yield
        return

    import msvcrt as _ms2
    # seek to offset before locking
    os.lseek(fd, offset, os.SEEK_SET)
    _ms2.locking(fd, _ms2.LK_NBLCK, nbytes)
    try:
        yield
    finally:
        os.lseek(fd, offset, os.SEEK_SET)
        _ms2.locking(fd, _ms2.LK_UNLCK, nbytes)


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== msvcrt demo ===")
    print(f"  Windows available: {_MSVCRT_AVAILABLE}")

    # ── putch ─────────────────────────────────────────────────────────────────
    print("\n--- putch progress bar ---")
    sys.stdout.write("  [")
    for _ in range(20):
        putch(b"#")
        time.sleep(0.02)
    sys.stdout.write("]\n")
    sys.stdout.flush()

    # ── wait_for_key ──────────────────────────────────────────────────────────
    print("\n--- wait_for_key (1s timeout) ---")
    key = wait_for_key(1.0, "  Press any key within 1s: ")
    if key is None:
        print("  Timed out (no key pressed or non-Windows)")
    else:
        print(f"  Got key: {key!r}")

    # ── console_spinner ───────────────────────────────────────────────────────
    print("\n--- console_spinner (1s work) ---")
    with console_spinner("  Computing"):
        time.sleep(1.0)

    # ── set_binary_mode ───────────────────────────────────────────────────────
    print("\n--- set_binary_mode ---")
    try:
        set_binary_mode(sys.stdout.fileno())
        print("  stdout switched to binary mode (Windows only)")
    except Exception as e:
        print(f"  set_binary_mode: {e}")

    # ── locked_region ─────────────────────────────────────────────────────────
    print("\n--- locked_region ---")
    import tempfile
    with tempfile.NamedTemporaryFile(delete=False, suffix=".bin") as tf:
        tf.write(b"\x00" * 256)
        tmp_path = tf.name

    try:
        with open(tmp_path, "r+b") as f:
            with locked_region(f.fileno(), 64, offset=0):
                f.seek(0)
                f.write(b"A" * 64)
        print(f"  wrote 64 bytes under lock to {tmp_path}")
        data = open(tmp_path, "rb").read(64)
        print(f"  first 8 bytes: {data[:8]!r}")
    finally:
        os.unlink(tmp_path)

    print("\n=== done ===")

For the keyboard (PyPI) alternative — keyboard.read_key() and keyboard.on_press(callback) provide cross-platform hotkey registration, key name resolution, and global keyboard hooks — use keyboard for cross-platform hotkeys, global shortcuts, or remapping keys; use msvcrt for lightweight Windows-only console input loops with zero dependencies. For the readchar (PyPI) alternative — readchar.readchar() and readchar.readkey() abstract over msvcrt.getch() on Windows and tty/termios on Unix — use readchar when you need a cross-platform single-keypress API; use msvcrt directly when you need the full Windows API (locking, setmode, get_osfhandle) or want zero dependencies. The Claude Skills 360 bundle includes msvcrt skill sets covering kbhit()/getch()/getche()/getwch() keypress helpers, putch() console writer, run_menu() keystroke dispatcher, wait_for_key() timed reader, console_spinner() threading context manager, set_binary_mode() fd helper, and locked_region() byte-range file lock. Start with the free tier to try Windows console patterns and msvcrt pipeline code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free