From fd4b5895f35c76f88871742fe9ba71a3c335b9b3 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Fri, 21 Feb 2025 12:48:06 +0900 Subject: [PATCH] feat: multiple files & glob input --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + README.md | 9 ++++++++- crates/pylyzer_core/analyze.rs | 6 +++--- src/config.rs | 22 ++++++++++++++++++++++ src/main.rs | 19 +++++++++++++++++-- 6 files changed, 58 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f055d6..5f8f596 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -245,6 +245,12 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + [[package]] name = "hashbrown" version = "0.14.5" @@ -726,6 +732,7 @@ dependencies = [ "els", "erg_common", "erg_compiler", + "glob", "pylyzer_core", ] diff --git a/Cargo.toml b/Cargo.toml index f919fb9..4a8284e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ traditional_chinese = ["erg_common/traditional_chinese", "els/traditional_chines pylyzer_core = { version = "0.0.80", path = "./crates/pylyzer_core" } erg_common = { workspace = true } els = { workspace = true } +glob = "0.3.2" [dev-dependencies] erg_compiler = { workspace = true } diff --git a/README.md b/README.md index debf377..0b62ba0 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,13 @@ Make sure that `cargo`/`rustc` is up-to-date, as pylyzer may be written with the pylyzer file.py ``` +## Check multiple files + +```sh +# glob patterns are supported +pylyzer file1.py file2.py dir/file*.py +``` + ### Check an entire package If you don't specify a file path, pylyzer will automatically search for the entry point. @@ -177,7 +184,7 @@ pylyzer converts Python ASTs to Erg ASTs and passes them to Erg's type checker. * [x] type assertion (`typing.cast`) * [x] type narrowing (`is`, `isinstance`) * [x] `pyi` (stub) files support -* [ ] glob pattern file check +* [x] glob pattern file check * [x] type comment (`# type: ...`) * [x] virtual environment support * [x] package manager support diff --git a/crates/pylyzer_core/analyze.rs b/crates/pylyzer_core/analyze.rs index 59cb5dd..b2ac6a8 100644 --- a/crates/pylyzer_core/analyze.rs +++ b/crates/pylyzer_core/analyze.rs @@ -295,7 +295,7 @@ impl PythonAnalyzer { res } - pub fn run(&mut self) { + pub fn run(&mut self) -> i32 { /*if self.cfg.dist_dir.is_some() { reserve_decl_er(self.cfg.input.clone()); }*/ @@ -317,7 +317,7 @@ impl PythonAnalyzer { dump_decl_package(&self.checker.shared().mod_cache); println!("A declaration file has been generated to __pycache__ directory."); } - std::process::exit(0); + 0 } Err(artifact) => { if !artifact.warns.is_empty() { @@ -345,7 +345,7 @@ impl PythonAnalyzer { dump_decl_package(&self.checker.shared().mod_cache); println!("A declaration file has been generated to __pycache__ directory."); } - std::process::exit(code); + code } } } diff --git a/src/config.rs b/src/config.rs index 65d7594..0f19bc6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -212,3 +212,25 @@ For more information try `pylyzer --help`" cfg.runtime_args = runtime_args.into(); cfg } + +pub(crate) fn files_to_be_checked() -> Vec { + let file_or_patterns = env::args() + .skip(1) + .rev() + .take_while(|arg| !arg.starts_with("-")); + let mut files = vec![]; + for file_or_pattern in file_or_patterns { + if PathBuf::from(&file_or_pattern).is_file() { + files.push(PathBuf::from(&file_or_pattern)); + } else { + for entry in glob::glob(&file_or_pattern).expect("Failed to read glob pattern") { + match entry { + Err(e) => eprintln!("err: {e}"), + Ok(path) if path.is_file() => files.push(path), + _ => {} + } + } + } + } + files +} diff --git a/src/main.rs b/src/main.rs index 0e91c63..2cc7515 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use erg_common::config::ErgMode; use erg_common::spawn::exec_new_thread; use pylyzer_core::{PythonAnalyzer, SimplePythonParser}; +use crate::config::files_to_be_checked; use crate::copy::copy_dot_erg; fn run() { @@ -15,8 +16,22 @@ fn run() { let lang_server = Server::::new(cfg, None); lang_server.run(); } else { - let mut analyzer = PythonAnalyzer::new(cfg); - analyzer.run(); + let mut code = 0; + let files = files_to_be_checked(); + if files.is_empty() { + let mut analyzer = PythonAnalyzer::new(cfg); + code = analyzer.run(); + } else { + for path in files { + let cfg = cfg.inherit(path); + let mut analyzer = PythonAnalyzer::new(cfg); + let c = analyzer.run(); + if c != 0 { + code = 1; + } + } + } + std::process::exit(code); } }