This guide helps developers understand, extend, and contribute to the robust_input library.
- Python 3.8+ (recommended: 3.12+)
- Linux/Unix environment for full terminal features
- Git for version control
- Clone the repository:
git clone <repository-url>
cd robust_input- Set up development environment:
# Create virtual environment
python -m venv venv
source venv/bin/activate # Linux/Mac
# or
venv\\Scripts\\activate # Windows
# Install development dependencies
pip install coverage black isort flake8- Verify installation:
make validate
make testrobust_input/
├── robust_input.py # Main module implementation
├── test_robust_input.py # Comprehensive test suite
├── docs/ # Documentation
│ ├── api-reference.md # API documentation
│ ├── developer-guide.md # This file
│ └── architecture.md # Architecture documentation
├── .github/workflows/ # CI/CD configuration
├── Makefile # Development commands
├── README.md # Project overview
├── pyproject.toml # Package configuration
└── LICENSE # MIT license
- Separation of Concerns: Each class has a single, well-defined responsibility
- Configuration Object Pattern: All parameters encapsulated in
InputConfig - Strategy Pattern: Multiple validation strategies through
InputValidator - Graceful Degradation: Automatic fallback for non-terminal environments
- Resource Management: Proper terminal state cleanup
InputStyle # ANSI styling and color utilities
InputValidator # Static validation methods
InputConfig # Configuration container with validation
InputHandler # Terminal interaction and input processing
User Input → InputHandler → Validation Chain → Type Casting → Return Value
↓
Terminal Control ← InputConfig ← InputStyle
Purpose: Manages terminal interaction and character processing
Key Responsibilities:
- Raw terminal mode management
- Character-by-character input processing
- Cursor movement and buffer management
- Error display and screen management
- Validation orchestration
Critical Methods:
get_input(): Main input loop_read_char(): Safe character reading with timeouts_process_arrow_keys(): Handle navigation keys_validate_input(): Run validation chain
Purpose: Provides static validation methods
Validation Types:
- Length constraints (min/max)
- Type casting validation
- Regex pattern matching
- Choice selection
- IP address format
- Custom validator functions
Performance Features:
- Regex pattern caching
- Pre-compiled validation chains
- Optimized boolean validation
Purpose: Configuration container with parameter validation
Validation Features:
- Parameter consistency checking
- Regex pattern compilation testing
- Type validation
- Style list validation
Purpose: ANSI styling and color management
Features:
- Comprehensive ANSI color constants
- 256-color support
- RGB true color support
- Style application utilities
# Patterns are compiled once and cached
_pattern_cache = {}
@staticmethod
def validate_pattern(value: str, pattern: str) -> bool:
if pattern not in InputValidator._pattern_cache:
InputValidator._pattern_cache[pattern] = re.compile(pattern)
compiled_pattern = InputValidator._pattern_cache[pattern]
return bool(compiled_pattern.match(value))def _build_validator_chain(self):
"""Build optimized validation chain without lambda overhead."""
validators = []
if self.config.min_length is not None or self.config.max_length is not None:
validators.append(('length', self.config.min_length, self.config.max_length))
validators.append(('type', self.config.target_type))
# ... more validators
return validators- O(1) character insertion using list-based buffer
- Efficient redraw algorithms for middle insertions
- Minimal ANSI escape sequence usage
- Selective styling to reduce overhead
Raw Mode: Disables line buffering and echo for character-by-character input ANSI Escape Sequences: Used for cursor movement and styling Terminal State Management: Proper cleanup to prevent terminal corruption
def _read_char(self) -> str:
"""Read a single character with timeout protection."""
try:
tty.setraw(sys.stdin.fileno())
ready, _, _ = select.select([sys.stdin], [], [], 0.1)
if ready:
char = sys.stdin.read(1)
else:
char = ''
finally:
if self.old_settings:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.old_settings)
return charThe library handles various terminal escape sequences:
\\033[D/\\033[C: Left/Right arrow keys\\033[H/\\033[F: Home/End keys\\033[1~/\\033[4~: Alternative Home/End sequences
- Unit Tests: Individual class and method testing
- Integration Tests: Complete workflow testing
- Mock Tests: Terminal operations with proper isolation
- Edge Case Tests: Boundary conditions and error paths
@patch('robust_input.sys.stdin.isatty', return_value=False)
@patch('builtins.input', return_value='test_value')
def test_simple_input_fallback(self, mock_input, mock_isatty):
"""Test fallback to simple input for non-terminal environments."""
result = self.handler.get_input()
self.assertEqual(result, 'test_value')- Target: 80%+ overall coverage
- Focus: Business logic validation and configuration
- Exclusions: Platform-specific terminal operations
- Add static method to InputValidator:
@staticmethod
def validate_email(value: str) -> bool:
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'
return InputValidator.validate_pattern(value, pattern)- Update validation chain building:
def _build_validator_chain(self):
# ... existing validators
if self.config.validate_email:
validators.append(('email', None))- Add to InputConfig:
def __init__(self, ..., validate_email: bool = False):
self.validate_email = validate_email- Extend escape sequence handling:
def _handle_extended_keys(self, digit: str):
if final_char == "~":
if digit == "2": # Insert key
self._handle_insert_key()
elif digit == "3": # Delete key
self._handle_delete_key()- Implement key behavior:
def _handle_delete_key(self):
"""Handle Delete key (forward delete)."""
if self.cursor_pos < len(self.buffer):
self.buffer.pop(self.cursor_pos)
self._redraw_from_cursor()def get_email(
prompt: str,
default: Optional[str] = None,
allow_empty: bool = False,
**kwargs
) -> str:
"""Get a validated email address."""
return get_input(
prompt=prompt,
default=default,
allow_empty=allow_empty,
pattern=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$',
error_message="Please enter a valid email address",
**kwargs
)- Line Length: 88 characters (Black default)
- Import Sorting: isort with Black compatibility
- Quote Style: Double quotes preferred
- Docstrings: Google style for all public methods
- Type Hints: Required for all parameters and return values
- Comments: Explain complex logic, not obvious code
def validate_length(
value: str,
min_length: Optional[int] = None,
max_length: Optional[int] = None
) -> bool:
"""Validate the length of the input string.
Args:
value: The input string to validate.
min_length: The minimum allowed length (inclusive).
max_length: The maximum allowed length (inclusive).
Returns:
True if the length is valid, False otherwise.
"""# Run tests
make test
make test-verbose
# Check code quality
make lint
make format
# Generate coverage
make coverage
# Validate module
make validate- All tests pass (
make test) - Code is properly formatted (
make format) - Linting passes (
make lint) - Coverage is acceptable (
make coverage) - Documentation is updated
- CHANGELOG.md is updated (if applicable)
type(scope): brief description
Detailed description of the change, including:
- What was changed and why
- Any breaking changes
- Related issue numbers
Types: feat, fix, docs, style, refactor, test, chore
- Terminal State Corruption:
# Reset terminal if corrupted during development
reset
# or
stty sane- Debugging Character Input:
# Add debug prints to see raw character values
char = sys.stdin.read(1)
print(f"Debug: char={repr(char)}, ord={ord(char)}")- Testing in Different Environments:
# Test non-terminal mode
python script.py < input.txt
# Test with different TERM values
TERM=xterm python script.py
TERM=dumb python script.py- Import Errors: Ensure module is in Python path
- Terminal Features Missing: Check if running in actual terminal
- Test Failures: Verify mock setup for terminal operations
- Fork and Branch: Create feature branch from main
- Develop: Follow coding standards and add tests
- Test: Ensure all tests pass and coverage is maintained
- Document: Update relevant documentation
- Review: Submit PR with clear description
- Functionality works as intended
- Tests cover new functionality
- Code follows style guidelines
- Documentation is clear and complete
- Performance impact is acceptable
- Backward compatibility is maintained
- Version Update: Update version in
pyproject.toml - Changelog: Update
CHANGELOG.mdwith changes - Testing: Run full test suite across Python versions
- Documentation: Ensure docs are up to date
- Tag: Create git tag with version number
- Release: Create GitHub release with notes