cadi_core/atomizer/languages/
python.rs

1//! Python-specific atomizer
2
3use crate::atomizer::{AtomizerConfig, ExtractedAtom};
4use crate::error::CadiResult;
5
6/// Python atomizer
7pub struct PythonAtomizer {
8    config: AtomizerConfig,
9}
10
11impl PythonAtomizer {
12    pub fn new(config: AtomizerConfig) -> Self {
13        Self { config }
14    }
15
16    /// Extract atoms from Python
17    pub fn extract(&self, source: &str) -> CadiResult<Vec<ExtractedAtom>> {
18        use crate::atomizer::AtomExtractor;
19        AtomExtractor::new("python", self.config.clone()).extract(source)
20    }
21}
22
23/// Common Python patterns
24pub struct PythonPatterns;
25
26impl PythonPatterns {
27    /// Check if a function is a test
28    pub fn is_test(name: &str, decorators: &[String]) -> bool {
29        name.starts_with("test_") 
30            || decorators.iter().any(|d| d.contains("@pytest") || d.contains("@test"))
31    }
32    
33    /// Check if this is a private function/class
34    pub fn is_private(name: &str) -> bool {
35        name.starts_with('_') && !name.starts_with("__")
36    }
37    
38    /// Check if this is a dunder method
39    pub fn is_dunder(name: &str) -> bool {
40        name.starts_with("__") && name.ends_with("__")
41    }
42    
43    /// Check if this is a dataclass
44    pub fn is_dataclass(decorators: &[String]) -> bool {
45        decorators.iter().any(|d| d.contains("@dataclass"))
46    }
47    
48    /// Extract decorator names
49    pub fn extract_decorators(source: &str) -> Vec<String> {
50        let mut decorators = Vec::new();
51        
52        for line in source.lines() {
53            let trimmed = line.trim();
54            if trimmed.starts_with('@') {
55                decorators.push(trimmed.to_string());
56            } else if !trimmed.is_empty() && !trimmed.starts_with('#') {
57                break;
58            }
59        }
60        
61        decorators
62    }
63}