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
30 changes: 30 additions & 0 deletions crates/ty/docs/rules.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions crates/ty/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ pub(crate) struct CheckCommand {
#[arg(long, conflicts_with("fix"))]
pub(crate) add_ignore: bool,

/// Path to a `uv workspace metadata` JSON snapshot used instead of automatic uv discovery.
#[arg(long, value_name = "PATH", hide = true)]
pub(crate) dependency_metadata: Option<SystemPathBuf>,

/// Run the command within the given project directory.
///
/// All `pyproject.toml` files will be discovered by walking up the directory tree from the given project directory,
Expand Down
58 changes: 55 additions & 3 deletions crates/ty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod version;

use std::io::{BufWriter, Write};
use std::process::{ExitCode, Termination};
use std::sync::Mutex;
use std::sync::{Arc, Mutex};

use anyhow::Result;
use anyhow::{Context, anyhow};
Expand All @@ -24,10 +24,14 @@ use ruff_db::system::{OsSystem, System, SystemPath, SystemPathBuf};
use ruff_db::{STACK_SIZE, max_parallelism};
use ruff_diagnostics::Applicability;
use salsa::Database;
use ty_project::dependency_metadata::{
enrich_dependency_metadata_with_editables, parse_uv_workspace_metadata,
};
use ty_project::metadata::settings::TerminalSettings;
use ty_project::watch::ProjectWatcher;
use ty_project::{CollectReporter, Db, watch};
use ty_project::{ProjectDatabase, ProjectMetadata};
use ty_python_semantic::dependency::DependencyMetadata;
use ty_python_semantic::{fix_all_diagnostics, suppress_all_diagnostics};
use ty_server::run_server;
use ty_static::EnvVars;
Expand Down Expand Up @@ -133,6 +137,10 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
.iter()
.map(|path| SystemPath::absolute(path, &cwd))
.collect();
let dependency_metadata_path = args
.dependency_metadata
.as_ref()
.map(|path| SystemPath::absolute(path, &cwd));

let mode = if args.fix {
MainLoopMode::Fix(FixMode::ApplyFixes)
Expand Down Expand Up @@ -170,15 +178,24 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
));
}

let dependency_metadata = match dependency_metadata_path.as_deref() {
Some(path) => Some(load_dependency_metadata(&system, path)?),
None => project_metadata.uv_dependency_metadata().cloned(),
};

project_metadata.apply_configuration_files(&system)?;

project_metadata.apply_override_options(args.into_options());

let mut db = ProjectDatabase::fallible(project_metadata, system)?;
let project = db.project();
let dependency_metadata = dependency_metadata.map(Arc::new);
let enriched_dependency_metadata =
enrich_dependency_metadata(&db, dependency_metadata.as_ref());

project.set_verbose(&mut db, verbosity >= VerbosityLevel::Verbose);
project.set_force_exclude(&mut db, force_exclude);
project.set_dependency_metadata(&mut db, enriched_dependency_metadata.as_ref());

if !check_paths.is_empty() {
project.set_included_paths(&mut db, check_paths);
Expand All @@ -201,7 +218,8 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
db.freeze();
}

let (main_loop, main_loop_cancellation_token) = MainLoop::new(mode, printer);
let (main_loop, main_loop_cancellation_token) =
MainLoop::new(mode, dependency_metadata, printer);

// Listen to Ctrl+C and abort the watch mode.
let main_loop_cancellation_token = Mutex::new(Some(main_loop_cancellation_token));
Expand Down Expand Up @@ -245,6 +263,29 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
}
}

fn load_dependency_metadata(system: &dyn System, path: &SystemPath) -> Result<DependencyMetadata> {
let source = system
.read_to_string(path)
.with_context(|| format!("Failed to read dependency metadata `{path}`"))?;

let metadata = parse_uv_workspace_metadata(&source)
.with_context(|| format!("Failed to load dependency metadata `{path}`"))?;

Ok(metadata)
}

fn enrich_dependency_metadata(
db: &ProjectDatabase,
dependency_metadata: Option<&Arc<DependencyMetadata>>,
) -> Option<Arc<DependencyMetadata>> {
dependency_metadata.map(|metadata| {
Arc::new(enrich_dependency_metadata_with_editables(
db,
(**metadata).clone(),
))
})
}

#[derive(Copy, Clone)]
pub enum ExitStatus {
/// Checking was successful and there were no errors.
Expand Down Expand Up @@ -291,14 +332,20 @@ struct MainLoop {
/// Interface for displaying information to the user.
printer: Printer,

dependency_metadata: Option<Arc<DependencyMetadata>>,

/// Cancellation token that gets set by Ctrl+C.
/// Used for long-running operations on the main thread. Operations on background threads
/// use Salsa's cancellation mechanism.
cancellation_token: CancellationToken,
}

impl MainLoop {
fn new(mode: MainLoopMode, printer: Printer) -> (Self, MainLoopCancellationToken) {
fn new(
mode: MainLoopMode,
dependency_metadata: Option<Arc<DependencyMetadata>>,
printer: Printer,
) -> (Self, MainLoopCancellationToken) {
let (sender, receiver) = crossbeam_channel::bounded(10);

let cancellation_token_source = CancellationTokenSource::new();
Expand All @@ -310,6 +357,7 @@ impl MainLoop {
sender: sender.clone(),
receiver,
watcher: None,
dependency_metadata,
printer,
cancellation_token,
},
Expand Down Expand Up @@ -481,6 +529,10 @@ impl MainLoop {
revision += 1;
// Automatically cancels any pending queries and waits for them to complete.
db.apply_changes(&changes);
let dependency_metadata =
enrich_dependency_metadata(db, self.dependency_metadata.as_ref());
db.project()
.set_dependency_metadata(db, dependency_metadata.as_ref());
if let Some(watcher) = self.watcher.as_mut() {
watcher.update(db);
}
Expand Down
Loading
Loading