From 565583ef9fa684aadebbafde9d7a29f45bbb9afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Br=C3=B8nner?= Date: Sat, 15 Nov 2025 01:09:51 +0100 Subject: [PATCH 1/2] Test python function signatures with type hints --- crates/codebook/tests/test_python.rs | 119 +++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/crates/codebook/tests/test_python.rs b/crates/codebook/tests/test_python.rs index a0e18d0..1853b19 100644 --- a/crates/codebook/tests/test_python.rs +++ b/crates/codebook/tests/test_python.rs @@ -5,6 +5,51 @@ use codebook::{ mod utils; +/// Strategy: +/// Use distinct misspellings to test location sensitive checking. +/// This simpler to write than asserting exact locations. +/// Granted - it doesn't test that the spell_check return correct locations, +/// but should be sufficient that some tests tests this. +/// This can be used to test language-specific grammar rules with less effort. +/// +/// `not_expected` does not have to be exhaustive. +fn assert_simple_misspellings( + processor: &codebook::Codebook, + sample_text: &str, + expected_misspellings: Vec<&str>, + not_expected: Vec<&str>, + language: LanguageType, +) { + // Check that the misspellings and the expected correct words used are distinct, + // otherwise the test could fail to properly test location sensitive properties + for word in expected_misspellings.iter().chain(not_expected.iter()) { + let count = sample_text.matches(word).count(); + assert_eq!( + count, 1, + "Word '{}' should occur exactly once in sample_text, but found {} occurrences", + word, count + ); + } + + let binding = processor + .spell_check(sample_text, Some(language), None) + .to_vec(); + let mut misspelled = binding + .iter() + .map(|r| r.word.as_str()) + .collect::>(); + misspelled.sort(); + println!("Misspelled words: {misspelled:?}"); + + assert_eq!(misspelled, expected_misspellings); + + for word in not_expected { + println!("Not expecting: {word:?}"); + assert!(!misspelled.iter().any(|w| *w == word)); + } +} + + #[test] fn test_python_simple() { utils::init_logging(); @@ -267,3 +312,77 @@ simple = f'check these wordz {but} {not} {the} {variables}' assert!(!misspelled.iter().any(|r| r.word == word)); } } + +#[test] +fn test_python_functions() { + utils::init_logging(); + let processor = utils::get_processor(); + + // Test simple function - function name and parameter names should be checked + let simple_function = r#" +def simple_wrngfunction_name(wrngparam, correct): + pass + "#; + assert_simple_misspellings( + &processor, + simple_function, + vec!["wrngfunction", "wrngparam"], + vec!["simple", "correct", "def", "name"], + LanguageType::Python, + ); + + // Test typed function - function names and parameters should be checked, but not types or modules + let simple_typed_function = r#" +def simple_wrngfunction(wrngparam: str, correct: Wrngtype, other: wrngmod.Wrngmodtype) -> Wrngret: + pass + "#; + assert_simple_misspellings( + &processor, + simple_typed_function, + vec!["wrngfunction", "wrngparam"], + vec![ + "simple", + "correct", + "str", + "Wrngtype", + "wrngmod", + "Wrngmodtype", + "Wrngret", + "def", + ], + LanguageType::Python, + ); + + // Test generic function 1 - function names and parameters should be checked, but not types + let generic_function_1 = r#" +def simple_wrngfunction(wrngparam: str, correct: Wrngtype[Wrngtemplate]): + pass + "#; + assert_simple_misspellings( + &processor, + generic_function_1, + vec!["wrngfunction", "wrngparam"], + vec!["simple", "correct", "str", "Wrngtype", "Wrngtemplate"], + LanguageType::Python, + ); + + // Test generic function 2 - function names and parameters should be checked, but not type templates + let generic_function_2 = r#" +def simple_wrngfunction[Wrgtemplate](wrngparam: str, correct: Wrngtype[Wrngtemplate]): + pass + "#; + assert_simple_misspellings( + &processor, + generic_function_2, + vec!["wrngfunction", "wrngparam"], + vec![ + "simple", + "correct", + "str", + "Wrgtemplate", + "Wrngtype", + "Wrngtemplate", + ], + LanguageType::Python, + ); +} From a9f3d076c1668dbad763afe28aa9397b63bf822e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Br=C3=B8nner?= Date: Sat, 15 Nov 2025 01:28:01 +0100 Subject: [PATCH 2/2] Don't spell check type hints in function signatures (python) --- crates/codebook/src/queries/python.scm | 23 +++++++++++++++++++++-- crates/codebook/tests/test_python.rs | 23 +++++++++++++---------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/crates/codebook/src/queries/python.scm b/crates/codebook/src/queries/python.scm index ac9d590..4d5b125 100644 --- a/crates/codebook/src/queries/python.scm +++ b/crates/codebook/src/queries/python.scm @@ -1,10 +1,29 @@ (comment) @comment + (string_content) @string + (function_definition name: (identifier) @identifier) -(function_definition - parameters: (parameters) @identifier) + (class_definition name: (identifier) @identifier) + (assignment (identifier) @identifier) + +(parameters + (identifier) @identifier) + +; Matches typed parameters (e.g., "name: str") +; The identifier for the name is a *direct child* of typed_parameter, +; while the type identifier is nested inside a (type) node. +(typed_parameter + (identifier) @identifier) + +; Matches parameters with default values (e.g., "limit=10") +(default_parameter + (identifier) @identifier) + +; Matches typed parameters with default values (e.g., "limit: int = 10") +(typed_default_parameter + (identifier) @identifier) diff --git a/crates/codebook/tests/test_python.rs b/crates/codebook/tests/test_python.rs index 1853b19..f721c02 100644 --- a/crates/codebook/tests/test_python.rs +++ b/crates/codebook/tests/test_python.rs @@ -5,11 +5,11 @@ use codebook::{ mod utils; -/// Strategy: +/// Strategy: /// Use distinct misspellings to test location sensitive checking. /// This simpler to write than asserting exact locations. /// Granted - it doesn't test that the spell_check return correct locations, -/// but should be sufficient that some tests tests this. +/// but should be sufficient that some tests tests this. /// This can be used to test language-specific grammar rules with less effort. /// /// `not_expected` does not have to be exhaustive. @@ -20,9 +20,9 @@ fn assert_simple_misspellings( not_expected: Vec<&str>, language: LanguageType, ) { - // Check that the misspellings and the expected correct words used are distinct, + // Check that the misspelled words used are distinct, // otherwise the test could fail to properly test location sensitive properties - for word in expected_misspellings.iter().chain(not_expected.iter()) { + for word in expected_misspellings.iter() { let count = sample_text.matches(word).count(); assert_eq!( count, 1, @@ -41,7 +41,9 @@ fn assert_simple_misspellings( misspelled.sort(); println!("Misspelled words: {misspelled:?}"); - assert_eq!(misspelled, expected_misspellings); + let mut expected_misspellings_sorted = expected_misspellings.clone(); + expected_misspellings_sorted.sort(); + assert_eq!(misspelled, expected_misspellings_sorted); for word in not_expected { println!("Not expecting: {word:?}"); @@ -49,7 +51,6 @@ fn assert_simple_misspellings( } } - #[test] fn test_python_simple() { utils::init_logging(); @@ -320,20 +321,20 @@ fn test_python_functions() { // Test simple function - function name and parameter names should be checked let simple_function = r#" -def simple_wrngfunction_name(wrngparam, correct): +def simple_wrngfunction_name(wrngparam, correct, wrngdefaultparam=1, correct_default=2): pass "#; assert_simple_misspellings( &processor, simple_function, - vec!["wrngfunction", "wrngparam"], - vec!["simple", "correct", "def", "name"], + vec!["wrngfunction", "wrngparam", "wrngdefaultparam"], + vec!["simple", "correct", "def", "name", "default"], LanguageType::Python, ); // Test typed function - function names and parameters should be checked, but not types or modules let simple_typed_function = r#" -def simple_wrngfunction(wrngparam: str, correct: Wrngtype, other: wrngmod.Wrngmodtype) -> Wrngret: +def simple_wrngfunction(wrngparam: str, correct: Wrngtype, other: wrngmod.Wrngmodtype, correct_default: Nons | int = 2) -> Wrngret: pass "#; assert_simple_misspellings( @@ -349,6 +350,8 @@ def simple_wrngfunction(wrngparam: str, correct: Wrngtype, other: wrngmod.Wrngmo "Wrngmodtype", "Wrngret", "def", + "Nons", + "default", ], LanguageType::Python, );