Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions crates/codebook/src/queries/python.scm
Original file line number Diff line number Diff line change
@@ -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)
122 changes: 122 additions & 0 deletions crates/codebook/tests/test_python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,52 @@ 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 misspelled words used are distinct,
// otherwise the test could fail to properly test location sensitive properties
for word in expected_misspellings.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::<Vec<&str>>();
misspelled.sort();
println!("Misspelled words: {misspelled:?}");

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:?}");
assert!(!misspelled.iter().any(|w| *w == word));
}
}

#[test]
fn test_python_simple() {
utils::init_logging();
Expand Down Expand Up @@ -267,3 +313,79 @@ 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, wrngdefaultparam=1, correct_default=2):
pass
"#;
assert_simple_misspellings(
&processor,
simple_function,
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, correct_default: Nons | int = 2) -> Wrngret:
pass
"#;
assert_simple_misspellings(
&processor,
simple_typed_function,
vec!["wrngfunction", "wrngparam"],
vec![
"simple",
"correct",
"str",
"Wrngtype",
"wrngmod",
"Wrngmodtype",
"Wrngret",
"def",
"Nons",
"default",
],
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,
);
}