mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 13:51:37 +00:00
Add a subcommand to generate dependency graphs (#13402)
## Summary This PR adds an experimental Ruff subcommand to generate dependency graphs based on module resolution. A few highlights: - You can generate either dependency or dependent graphs via the `--direction` command-line argument. - Like Pants, we also provide an option to identify imports from string literals (`--detect-string-imports`). - Users can also provide additional dependency data via the `include-dependencies` key under `[tool.ruff.import-map]`. This map uses file paths as keys, and lists of strings as values. Those strings can be file paths or globs. The dependency resolution uses the red-knot module resolver which is intended to be fully spec compliant, so it's also a chance to expose the module resolver in a real-world setting. The CLI is, e.g., `ruff graph build ../autobot`, which will output a JSON map from file to files it depends on for the `autobot` project.
This commit is contained in:
parent
260c2ecd15
commit
4e935f7d7d
30 changed files with 1339 additions and 45 deletions
39
crates/ruff_graph/src/resolver.rs
Normal file
39
crates/ruff_graph/src/resolver.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use red_knot_python_semantic::SemanticModel;
|
||||
use ruff_db::files::FilePath;
|
||||
|
||||
use crate::collector::CollectedImport;
|
||||
|
||||
/// Collect all imports for a given Python file.
|
||||
pub(crate) struct Resolver<'a> {
|
||||
semantic: &'a SemanticModel<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Resolver<'a> {
|
||||
/// Initialize a [`Resolver`] with a given [`SemanticModel`].
|
||||
pub(crate) fn new(semantic: &'a SemanticModel<'a>) -> Self {
|
||||
Self { semantic }
|
||||
}
|
||||
|
||||
/// Resolve the [`CollectedImport`] into a [`FilePath`].
|
||||
pub(crate) fn resolve(&self, import: CollectedImport) -> Option<&'a FilePath> {
|
||||
match import {
|
||||
CollectedImport::Import(import) => self
|
||||
.semantic
|
||||
.resolve_module(import)
|
||||
.map(|module| module.file().path(self.semantic.db())),
|
||||
CollectedImport::ImportFrom(import) => {
|
||||
// Attempt to resolve the member (e.g., given `from foo import bar`, look for `foo.bar`).
|
||||
let parent = import.parent();
|
||||
self.semantic
|
||||
.resolve_module(import)
|
||||
.map(|module| module.file().path(self.semantic.db()))
|
||||
.or_else(|| {
|
||||
// Attempt to resolve the module (e.g., given `from foo import bar`, look for `foo`).
|
||||
self.semantic
|
||||
.resolve_module(parent?)
|
||||
.map(|module| module.file().path(self.semantic.db()))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue