Another attempt at fixing external numpad detection. I *think* some of them send numlock state with every event. If that's the case, this should fix it.
This commit is contained in:
		| @@ -26,9 +26,20 @@ class command: | ||||
|             return | ||||
|  | ||||
|         # Only announce numlock changes if an actual numlock key was pressed | ||||
|         # This prevents spurious announcements from external numpad automatic state changes | ||||
|         # AND the LED state actually changed (some numpads send spurious NUMLOCK events) | ||||
|         current_input = self.env["input"]["currInput"] | ||||
|         if current_input and "KEY_NUMLOCK" in current_input: | ||||
|  | ||||
|         # Check if this is a genuine numlock key press by verifying: | ||||
|         # 1. KEY_NUMLOCK is in the current input sequence | ||||
|         # 2. The LED state has actually changed | ||||
|         # 3. This isn't just a side effect from a KP_ key (which some buggy numpads do) | ||||
|         is_genuine_numlock = ( | ||||
|             current_input and | ||||
|             "KEY_NUMLOCK" in current_input and | ||||
|             not any(key.startswith("KEY_KP") for key in current_input if isinstance(key, str)) | ||||
|         ) | ||||
|  | ||||
|         if is_genuine_numlock: | ||||
|             if self.env["input"]["newNumLock"]: | ||||
|                 self.env["runtime"]["OutputManager"].present_text( | ||||
|                     _("Numlock on"), interrupt=True | ||||
|   | ||||
| @@ -159,8 +159,7 @@ class command(config_command): | ||||
|  | ||||
|         except Exception as e: | ||||
|             self.present_text( | ||||
|                 f"Failed to create default configuration: { | ||||
|                     str(e)}", | ||||
|                 f"Failed to create default configuration: {str(e)}", | ||||
|                 interrupt=False, | ||||
|                 flush=False, | ||||
|             ) | ||||
|   | ||||
| @@ -45,8 +45,7 @@ class command: | ||||
|                     sound_driver.initialize(self.env) | ||||
|                 except Exception as e: | ||||
|                     print( | ||||
|                         f"revert_to_saved sound_driver: Error reinitializing sound driver: { | ||||
|                             str(e)}" | ||||
|                         f"revert_to_saved sound_driver: Error reinitializing sound driver: {str(e)}" | ||||
|                     ) | ||||
|  | ||||
|             self.env["runtime"]["OutputManager"].present_text( | ||||
|   | ||||
| @@ -189,9 +189,7 @@ class DynamicApplyVoiceCommand: | ||||
|  | ||||
|                     # Debug: verify what was actually set | ||||
|                     self.env["runtime"]["OutputManager"].present_text( | ||||
|                         f"Speech driver now has module: { | ||||
|                             SpeechDriver.module}, voice: { | ||||
|                             SpeechDriver.voice}", | ||||
|                         f"Speech driver now has module: {SpeechDriver.module}, voice: {SpeechDriver.voice}", | ||||
|                         interrupt=True, | ||||
|                     ) | ||||
|  | ||||
| @@ -233,8 +231,7 @@ class DynamicApplyVoiceCommand: | ||||
|                         ) | ||||
|  | ||||
|                 self.env["runtime"]["OutputManager"].present_text( | ||||
|                     f"Failed to apply voice, reverted: { | ||||
|                         str(e)}", | ||||
|                     f"Failed to apply voice, reverted: {str(e)}", | ||||
|                     interrupt=False, | ||||
|                     flush=False, | ||||
|                 ) | ||||
|   | ||||
| @@ -4,5 +4,5 @@ | ||||
| # Fenrir TTY screen reader | ||||
| # By Chrys, Storm Dragon, and contributors. | ||||
|  | ||||
| version = "2025.09.12" | ||||
| version = "2025.09.13" | ||||
| code_name = "testing" | ||||
|   | ||||
| @@ -43,12 +43,19 @@ if [ -n "$STAGED_PYTHON_FILES" ]; then | ||||
|      | ||||
|     for file in $STAGED_PYTHON_FILES; do | ||||
|         if [ -f "$file" ]; then | ||||
|             # Check for unterminated strings (the main issue from the email) | ||||
|             if grep -n 'f".*{$' "$file" >/dev/null 2>&1; then | ||||
|                 echo -e "${RED}✗ $file: Potential unterminated f-string${NC}" | ||||
|             # Check for broken f-strings (multiline issues that cause syntax errors) | ||||
|             # Pattern 1: f-string with opening brace at end of line (likely broken across lines) | ||||
|             if grep -n 'f"[^"]*{[^}]*$' "$file" >/dev/null 2>&1; then | ||||
|                 echo -e "${RED}✗ $file: Potential broken multiline f-string${NC}" | ||||
|                 grep -n 'f"[^"]*{[^}]*$' "$file" | head -3 | ||||
|                 ISSUES_FOUND=1 | ||||
|             fi | ||||
|  | ||||
|             # Pattern 2: Lines that end with just an opening brace (common in broken f-strings) | ||||
|             if grep -n '^\s*[^#]*{$' "$file" >/dev/null 2>&1; then | ||||
|                 echo -e "${YELLOW}⚠ $file: Lines ending with lone opening brace (check f-strings)${NC}" | ||||
|             fi | ||||
|              | ||||
|             # Check for missing imports that are commonly used | ||||
|             if grep -q 'debug\.DebugLevel\.' "$file" && ! grep -q 'from.*debug' "$file" && ! grep -q 'import.*debug' "$file"; then | ||||
|                 echo -e "${YELLOW}⚠ $file: Uses debug.DebugLevel but no debug import found${NC}" | ||||
| @@ -110,7 +117,7 @@ if [ -n "$STAGED_PYTHON_FILES" ]; then | ||||
|     for file in $STAGED_PYTHON_FILES; do | ||||
|         if [ -f "$file" ]; then | ||||
|             # Check for potential passwords, keys, tokens | ||||
|             if grep -i -E '(password|passwd|pwd|key|token|secret|api_key).*=.*["\'][^"\']{8,}["\']' "$file" >/dev/null 2>&1; then | ||||
|             if grep -i -E '(password|passwd|pwd|key|token|secret|api_key).*=.*["'"'"'][^"'"'"']{8,}["'"'"']' "$file" >/dev/null 2>&1; then | ||||
|                 echo -e "${RED}✗ $file: Potential hardcoded secret detected${NC}" | ||||
|                 SECRETS_FOUND=1 | ||||
|             fi | ||||
| @@ -126,7 +133,7 @@ else | ||||
| fi | ||||
|  | ||||
| # Summary | ||||
| echo -e "\n${'='*50}" | ||||
| echo -e "\n==================================================" | ||||
| if [ $VALIDATION_FAILED -eq 0 ]; then | ||||
|     echo -e "${GREEN}✓ All pre-commit validations passed${NC}" | ||||
|     echo -e "${GREEN}Commit allowed to proceed${NC}" | ||||
|   | ||||
| @@ -13,6 +13,7 @@ Usage: | ||||
|  | ||||
| import ast | ||||
| import os | ||||
| import re | ||||
| import sys | ||||
| import argparse | ||||
| import tempfile | ||||
| @@ -25,12 +26,33 @@ class SyntaxValidator: | ||||
|         self.warnings = [] | ||||
|         self.fixed = [] | ||||
|  | ||||
|     def check_fstring_issues(self, content): | ||||
|         """Check for common f-string formatting issues that cause syntax errors.""" | ||||
|         issues = [] | ||||
|         lines = content.split('\n') | ||||
|  | ||||
|         for i, line in enumerate(lines, 1): | ||||
|             # Check for f-strings that appear to be broken across lines | ||||
|             # Pattern: f"some text { | ||||
|             if re.search(r'f"[^"]*{[^}]*$', line.strip()): | ||||
|                 issues.append(f"Line {i}: Potential broken multiline f-string") | ||||
|  | ||||
|         return issues | ||||
|  | ||||
|     def validate_file(self, filepath): | ||||
|         """Validate syntax of a single Python file.""" | ||||
|         try: | ||||
|             with open(filepath, 'r', encoding='utf-8') as f: | ||||
|                 content = f.read() | ||||
|  | ||||
|             # Check for specific f-string issues before AST parsing | ||||
|             fstring_issues = self.check_fstring_issues(content) | ||||
|             if fstring_issues: | ||||
|                 # Create a synthetic syntax error for f-string issues | ||||
|                 error_msg = f"F-string issues: {'; '.join(fstring_issues)}" | ||||
|                 self.errors.append((filepath, SyntaxError(error_msg), content)) | ||||
|                 return False, content | ||||
|  | ||||
|             # Parse with AST (catches syntax errors) | ||||
|             ast.parse(content, filename=str(filepath)) | ||||
|             return True, content | ||||
|   | ||||
		Reference in New Issue
	
	Block a user