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:
@@ -24,11 +24,22 @@ class command:
|
|||||||
def run(self):
|
def run(self):
|
||||||
if self.env["input"]["oldNumLock"] == self.env["input"]["newNumLock"]:
|
if self.env["input"]["oldNumLock"] == self.env["input"]["newNumLock"]:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Only announce numlock changes if an actual numlock key was pressed
|
# 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"]
|
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"]:
|
if self.env["input"]["newNumLock"]:
|
||||||
self.env["runtime"]["OutputManager"].present_text(
|
self.env["runtime"]["OutputManager"].present_text(
|
||||||
_("Numlock on"), interrupt=True
|
_("Numlock on"), interrupt=True
|
||||||
|
@@ -159,8 +159,7 @@ class command(config_command):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.present_text(
|
self.present_text(
|
||||||
f"Failed to create default configuration: {
|
f"Failed to create default configuration: {str(e)}",
|
||||||
str(e)}",
|
|
||||||
interrupt=False,
|
interrupt=False,
|
||||||
flush=False,
|
flush=False,
|
||||||
)
|
)
|
||||||
|
@@ -45,8 +45,7 @@ class command:
|
|||||||
sound_driver.initialize(self.env)
|
sound_driver.initialize(self.env)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(
|
print(
|
||||||
f"revert_to_saved sound_driver: Error reinitializing sound driver: {
|
f"revert_to_saved sound_driver: Error reinitializing sound driver: {str(e)}"
|
||||||
str(e)}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.env["runtime"]["OutputManager"].present_text(
|
self.env["runtime"]["OutputManager"].present_text(
|
||||||
|
@@ -189,9 +189,7 @@ class DynamicApplyVoiceCommand:
|
|||||||
|
|
||||||
# Debug: verify what was actually set
|
# Debug: verify what was actually set
|
||||||
self.env["runtime"]["OutputManager"].present_text(
|
self.env["runtime"]["OutputManager"].present_text(
|
||||||
f"Speech driver now has module: {
|
f"Speech driver now has module: {SpeechDriver.module}, voice: {SpeechDriver.voice}",
|
||||||
SpeechDriver.module}, voice: {
|
|
||||||
SpeechDriver.voice}",
|
|
||||||
interrupt=True,
|
interrupt=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -233,8 +231,7 @@ class DynamicApplyVoiceCommand:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.env["runtime"]["OutputManager"].present_text(
|
self.env["runtime"]["OutputManager"].present_text(
|
||||||
f"Failed to apply voice, reverted: {
|
f"Failed to apply voice, reverted: {str(e)}",
|
||||||
str(e)}",
|
|
||||||
interrupt=False,
|
interrupt=False,
|
||||||
flush=False,
|
flush=False,
|
||||||
)
|
)
|
||||||
|
@@ -4,5 +4,5 @@
|
|||||||
# Fenrir TTY screen reader
|
# Fenrir TTY screen reader
|
||||||
# By Chrys, Storm Dragon, and contributors.
|
# By Chrys, Storm Dragon, and contributors.
|
||||||
|
|
||||||
version = "2025.09.12"
|
version = "2025.09.13"
|
||||||
code_name = "testing"
|
code_name = "testing"
|
||||||
|
@@ -43,11 +43,18 @@ if [ -n "$STAGED_PYTHON_FILES" ]; then
|
|||||||
|
|
||||||
for file in $STAGED_PYTHON_FILES; do
|
for file in $STAGED_PYTHON_FILES; do
|
||||||
if [ -f "$file" ]; then
|
if [ -f "$file" ]; then
|
||||||
# Check for unterminated strings (the main issue from the email)
|
# Check for broken f-strings (multiline issues that cause syntax errors)
|
||||||
if grep -n 'f".*{$' "$file" >/dev/null 2>&1; then
|
# Pattern 1: f-string with opening brace at end of line (likely broken across lines)
|
||||||
echo -e "${RED}✗ $file: Potential unterminated f-string${NC}"
|
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
|
ISSUES_FOUND=1
|
||||||
fi
|
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
|
# 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
|
if grep -q 'debug\.DebugLevel\.' "$file" && ! grep -q 'from.*debug' "$file" && ! grep -q 'import.*debug' "$file"; then
|
||||||
@@ -110,7 +117,7 @@ if [ -n "$STAGED_PYTHON_FILES" ]; then
|
|||||||
for file in $STAGED_PYTHON_FILES; do
|
for file in $STAGED_PYTHON_FILES; do
|
||||||
if [ -f "$file" ]; then
|
if [ -f "$file" ]; then
|
||||||
# Check for potential passwords, keys, tokens
|
# 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}"
|
echo -e "${RED}✗ $file: Potential hardcoded secret detected${NC}"
|
||||||
SECRETS_FOUND=1
|
SECRETS_FOUND=1
|
||||||
fi
|
fi
|
||||||
@@ -126,7 +133,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Summary
|
# Summary
|
||||||
echo -e "\n${'='*50}"
|
echo -e "\n=================================================="
|
||||||
if [ $VALIDATION_FAILED -eq 0 ]; then
|
if [ $VALIDATION_FAILED -eq 0 ]; then
|
||||||
echo -e "${GREEN}✓ All pre-commit validations passed${NC}"
|
echo -e "${GREEN}✓ All pre-commit validations passed${NC}"
|
||||||
echo -e "${GREEN}Commit allowed to proceed${NC}"
|
echo -e "${GREEN}Commit allowed to proceed${NC}"
|
||||||
|
@@ -13,6 +13,7 @@ Usage:
|
|||||||
|
|
||||||
import ast
|
import ast
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
import tempfile
|
import tempfile
|
||||||
@@ -24,13 +25,34 @@ class SyntaxValidator:
|
|||||||
self.errors = []
|
self.errors = []
|
||||||
self.warnings = []
|
self.warnings = []
|
||||||
self.fixed = []
|
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):
|
def validate_file(self, filepath):
|
||||||
"""Validate syntax of a single Python file."""
|
"""Validate syntax of a single Python file."""
|
||||||
try:
|
try:
|
||||||
with open(filepath, 'r', encoding='utf-8') as f:
|
with open(filepath, 'r', encoding='utf-8') as f:
|
||||||
content = f.read()
|
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)
|
# Parse with AST (catches syntax errors)
|
||||||
ast.parse(content, filename=str(filepath))
|
ast.parse(content, filename=str(filepath))
|
||||||
return True, content
|
return True, content
|
||||||
|
Reference in New Issue
Block a user