mirror of https://github.com/astral-sh/ruff
[`flake8-use-pathlib`] Implement `glob` (`PTH207`) (#5939)
Discovered that the usage of `glob.glob` is [widespread](https://grep.app/search?current=7&q=glob.glob%28&filter%5Blang%5D%5B0%5D=Python) when working on the previous lints for `flake8-use-pathlib`.
This commit is contained in:
parent
132f07c27b
commit
ffdd653c54
|
|
@ -0,0 +1,11 @@
|
|||
import os
|
||||
import glob
|
||||
from glob import glob as search
|
||||
|
||||
|
||||
extensions_dir = "./extensions"
|
||||
|
||||
# PTH207
|
||||
glob.glob(os.path.join(extensions_dir, "ops", "autograd", "*.cpp"))
|
||||
list(glob.iglob(os.path.join(extensions_dir, "ops", "autograd", "*.cpp")))
|
||||
search("*.png")
|
||||
|
|
@ -849,6 +849,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
|||
Rule::OsPathGetatime,
|
||||
Rule::OsPathGetmtime,
|
||||
Rule::OsPathGetctime,
|
||||
Rule::Glob,
|
||||
]) {
|
||||
flake8_use_pathlib::rules::replaceable_by_pathlib(checker, func);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -760,6 +760,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
|||
(Flake8UsePathlib, "204") => (RuleGroup::Unspecified, rules::flake8_use_pathlib::rules::OsPathGetmtime),
|
||||
(Flake8UsePathlib, "205") => (RuleGroup::Unspecified, rules::flake8_use_pathlib::rules::OsPathGetctime),
|
||||
(Flake8UsePathlib, "206") => (RuleGroup::Unspecified, rules::flake8_use_pathlib::rules::OsSepSplit),
|
||||
(Flake8UsePathlib, "207") => (RuleGroup::Unspecified, rules::flake8_use_pathlib::rules::Glob),
|
||||
|
||||
// flake8-logging-format
|
||||
(Flake8LoggingFormat, "001") => (RuleGroup::Unspecified, rules::flake8_logging_format::violations::LoggingStringFormat),
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ mod tests {
|
|||
#[test_case(Rule::OsPathGetmtime, Path::new("PTH204.py"))]
|
||||
#[test_case(Rule::OsPathGetctime, Path::new("PTH205.py"))]
|
||||
#[test_case(Rule::OsSepSplit, Path::new("PTH206.py"))]
|
||||
#[test_case(Rule::Glob, Path::new("PTH207.py"))]
|
||||
fn rules_pypath(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
use ruff_diagnostics::Violation;
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the use of `glob` and `iglob`.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// `pathlib` offers a high-level API for path manipulation, as compared to
|
||||
/// the lower-level API offered by `os` and `glob`.
|
||||
///
|
||||
/// When possible, using `Path` object methods such as `Path.glob()` can
|
||||
/// improve readability over their low-level counterparts (e.g.,
|
||||
/// `glob.glob()`).
|
||||
///
|
||||
/// Note that `glob.glob` and `Path.glob` are not exact equivalents:
|
||||
///
|
||||
/// | | `glob` | `Path.glob` |
|
||||
/// |-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
/// | Hidden files | Excludes hidden files by default. From Python 3.11 onwards, the `include_hidden` keyword can used to include hidden directories. | Includes hidden files by default. |
|
||||
/// | Iterator | `iglob` returns an iterator. Under the hood, `glob` simply converts the iterator to a list. | `Path.glob` returns an iterator. |
|
||||
/// | Working directory | `glob` takes a `root_dir` keyword to set the current working directory. | `Path.rglob` can be used to return the relative path. |
|
||||
/// | Globstar (`**`) | `glob` requires the `recursive` flag to be set to `True` for the `**` pattern to match any files and zero or more directories, subdirectories, and symbolic links. | The `**` pattern in `Path.glob` means "this directory and all subdirectories, recursively". In other words, it enables recursive globbing. |
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// import glob
|
||||
/// import os
|
||||
///
|
||||
/// glob.glob(os.path.join(path, "requirements*.txt"))
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// from pathlib import Path
|
||||
///
|
||||
/// Path(path).glob("requirements*.txt")
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `Path.glob`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob)
|
||||
/// - [Python documentation: `Path.rglob`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.rglob)
|
||||
/// - [Python documentation: `glob.glob`](https://docs.python.org/3/library/glob.html#glob.glob)
|
||||
/// - [Python documentation: `glob.iglob`](https://docs.python.org/3/library/glob.html#glob.iglob)
|
||||
#[violation]
|
||||
pub struct Glob {
|
||||
pub function: String,
|
||||
}
|
||||
|
||||
impl Violation for Glob {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Glob { function } = self;
|
||||
format!("Replace `{function}` with `Path.glob` or `Path.rglob`")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
pub(crate) use glob_rule::*;
|
||||
pub(crate) use os_path_getatime::*;
|
||||
pub(crate) use os_path_getctime::*;
|
||||
pub(crate) use os_path_getmtime::*;
|
||||
|
|
@ -6,6 +7,7 @@ pub(crate) use os_sep_split::*;
|
|||
pub(crate) use path_constructor_current_directory::*;
|
||||
pub(crate) use replaceable_by_pathlib::*;
|
||||
|
||||
mod glob_rule;
|
||||
mod os_path_getatime;
|
||||
mod os_path_getctime;
|
||||
mod os_path_getmtime;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
|||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
use crate::rules::flake8_use_pathlib::rules::{
|
||||
OsPathGetatime, OsPathGetctime, OsPathGetmtime, OsPathGetsize,
|
||||
Glob, OsPathGetatime, OsPathGetctime, OsPathGetmtime, OsPathGetsize,
|
||||
};
|
||||
use crate::rules::flake8_use_pathlib::violations::{
|
||||
BuiltinOpen, OsChmod, OsGetcwd, OsMakedirs, OsMkdir, OsPathAbspath, OsPathBasename,
|
||||
|
|
@ -89,6 +89,19 @@ pub(crate) fn replaceable_by_pathlib(checker: &mut Checker, expr: &Expr) {
|
|||
["" | "builtin", "open"] => Some(BuiltinOpen.into()),
|
||||
// PTH124
|
||||
["py", "path", "local"] => Some(PyPath.into()),
|
||||
// PTH207
|
||||
["glob", "glob"] => Some(
|
||||
Glob {
|
||||
function: "glob".to_string(),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
["glob", "iglob"] => Some(
|
||||
Glob {
|
||||
function: "iglob".to_string(),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
// PTH115
|
||||
// Python 3.9+
|
||||
["os", "readlink"] if checker.settings.target_version >= PythonVersion::Py39 => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_use_pathlib/mod.rs
|
||||
---
|
||||
PTH207.py:9:1: PTH207 Replace `glob` with `Path.glob` or `Path.rglob`
|
||||
|
|
||||
8 | # PTH207
|
||||
9 | glob.glob(os.path.join(extensions_dir, "ops", "autograd", "*.cpp"))
|
||||
| ^^^^^^^^^ PTH207
|
||||
10 | list(glob.iglob(os.path.join(extensions_dir, "ops", "autograd", "*.cpp")))
|
||||
11 | search("*.png")
|
||||
|
|
||||
|
||||
PTH207.py:10:6: PTH207 Replace `iglob` with `Path.glob` or `Path.rglob`
|
||||
|
|
||||
8 | # PTH207
|
||||
9 | glob.glob(os.path.join(extensions_dir, "ops", "autograd", "*.cpp"))
|
||||
10 | list(glob.iglob(os.path.join(extensions_dir, "ops", "autograd", "*.cpp")))
|
||||
| ^^^^^^^^^^ PTH207
|
||||
11 | search("*.png")
|
||||
|
|
||||
|
||||
PTH207.py:11:1: PTH207 Replace `glob` with `Path.glob` or `Path.rglob`
|
||||
|
|
||||
9 | glob.glob(os.path.join(extensions_dir, "ops", "autograd", "*.cpp"))
|
||||
10 | list(glob.iglob(os.path.join(extensions_dir, "ops", "autograd", "*.cpp")))
|
||||
11 | search("*.png")
|
||||
| ^^^^^^ PTH207
|
||||
|
|
||||
|
||||
|
||||
|
|
@ -2346,6 +2346,7 @@
|
|||
"PTH204",
|
||||
"PTH205",
|
||||
"PTH206",
|
||||
"PTH207",
|
||||
"PYI",
|
||||
"PYI0",
|
||||
"PYI00",
|
||||
|
|
|
|||
Loading…
Reference in New Issue