Changed code to support older Python versions

This commit is contained in:
Malasaur 2025-12-01 23:27:09 +01:00
parent eb92d2d36f
commit 582458cdd0
5027 changed files with 794942 additions and 4 deletions

View file

@ -0,0 +1,157 @@
"""
Unified getchar implementation for all platforms.
Combines approaches from:
- Textual (Unix/Linux): Copyright (c) 2023 Textualize Inc., MIT License
- Click (Windows fallback): Copyright 2014 Pallets, BSD-3-Clause License
"""
import os
import sys
from codecs import getincrementaldecoder
from typing import Optional, TextIO
def getchar() -> str:
"""
Read input from stdin with support for longer pasted text.
On Windows:
- Uses msvcrt for native Windows console input
- Handles special keys that send two-byte sequences
- Reads up to 4096 characters for paste support
On Unix/Linux:
- Uses Textual's approach with manual termios configuration
- Reads up to 4096 bytes with proper UTF-8 decoding
- Provides fine-grained terminal control
Returns:
str: The input character(s) read from stdin
Raises:
KeyboardInterrupt: When CTRL+C is pressed
"""
if sys.platform == "win32":
# Windows implementation
try:
import msvcrt
except ImportError:
# Fallback if msvcrt is not available
return sys.stdin.read(1)
# Use getwch for Unicode support
func = msvcrt.getwch # type: ignore
# Read first character
rv = func()
# Check for special keys (they send two characters)
if rv in ("\x00", "\xe0"):
# Special key, read the second character
rv += func()
return rv
# Check if more input is available (for paste support)
chars = [rv]
max_chars = 4096
# Keep reading while characters are available
while len(chars) < max_chars and msvcrt.kbhit(): # type: ignore
next_char = func()
# Handle special keys during paste
if next_char in ("\x00", "\xe0"):
# Stop here, let this be handled in next call
break
chars.append(next_char)
# Check for CTRL+C
if next_char == "\x03":
raise KeyboardInterrupt()
result = "".join(chars)
# Check for CTRL+C in the full result
if "\x03" in result:
raise KeyboardInterrupt()
return result
else:
# Unix/Linux implementation (Textual approach)
import termios
import tty
f: Optional[TextIO] = None
fd: int
# Get the file descriptor
if not sys.stdin.isatty():
f = open("/dev/tty")
fd = f.fileno()
else:
fd = sys.stdin.fileno()
try:
# Save current terminal settings
attrs_before = termios.tcgetattr(fd)
try:
# Configure terminal settings (Textual-style)
newattr = termios.tcgetattr(fd)
# Patch LFLAG (local flags)
# Disable:
# - ECHO: Don't echo input characters
# - ICANON: Disable canonical mode (line-by-line input)
# - IEXTEN: Disable extended processing
# - ISIG: Disable signal generation
newattr[tty.LFLAG] &= ~(
termios.ECHO | termios.ICANON | termios.IEXTEN | termios.ISIG
)
# Patch IFLAG (input flags)
# Disable:
# - IXON/IXOFF: XON/XOFF flow control
# - ICRNL/INLCR/IGNCR: Various newline translations
newattr[tty.IFLAG] &= ~(
termios.IXON
| termios.IXOFF
| termios.ICRNL
| termios.INLCR
| termios.IGNCR
)
# Set VMIN to 1 (minimum number of characters to read)
# This ensures we get at least 1 character
newattr[tty.CC][termios.VMIN] = 1
# Apply the new terminal settings
termios.tcsetattr(fd, termios.TCSANOW, newattr)
# Read up to 4096 bytes (same as Textual)
raw_data = os.read(fd, 1024 * 4)
# Use incremental UTF-8 decoder for proper Unicode handling
decoder = getincrementaldecoder("utf-8")()
result = decoder.decode(raw_data, final=True)
# Check for CTRL+C (ASCII 3)
if "\x03" in result:
raise KeyboardInterrupt()
return result
finally:
# Restore original terminal settings
termios.tcsetattr(fd, termios.TCSANOW, attrs_before)
sys.stdout.flush()
if f is not None:
f.close()
except termios.error:
# If we can't control the terminal, fall back to simple read
return sys.stdin.read(1)