ncr demo · astral-sh/uv#19605 on GitHub · ← all examples ·
what to notice The adapter GitHub showed you first is chapter 5 here — right where a reader finally has the context for it.

Add `uv check` to run `ty` from uv · #19605

49/49 blocks placed ✓

This PR adds uv check, a new subcommand that downloads and runs ty (Astral's Python type checker) against the current project. The implementation follows the same pattern as uv format/uv audit: discover the project, optionally sync the environment, fetch the binary from GitHub/Astral mirror, then exec it. Read outside-in: CLI contract → settings resolution → command dispatch → core implementation → binary download support → preview feature flag → tests.

uv check — CLI contract and user-facing args

The new ProjectCommand::Check(CheckArgs) variant and its full CheckArgs struct define what users can pass to uv check.

0 Contract ProjectCommand::Check Registers check as a project-level subcommand with the uv help check after-help link.
Registers check as a project-level subcommand with the uv help check after-help link.
crates/uv-cli/src/lib.rs · b012 · Outermost public contract — the Clap subcommand declaration
         after_long_help = ""     )]     Format(FormatArgs),+    /// Type check the project.+    ///+    /// Type checks Python code using ty. By default, all Python files in the project+    /// are checked. This command has the same behavior as running `ty check` in the project+    /// root.+    ///+    /// Additional arguments can be passed to ty after `--`.+    #[command(+        after_help = "Use `uv help check` for more details.",+        after_long_help = ""+    )]+    Check(CheckArgs),     /// Audit the project's dependencies.     ///     /// Dependencies are audited for known vulnerabilities, as well as 'adverse' statuses such as
0 Contract CheckArgs Full set of flags: extras/groups for env sync, --locked/--frozen/--no-sync, --python, --version (ty version), --extra-args (pass-through to ty), --no-project, --show-version. Mirrors the shape of FormatArgs but adds sync-related flags since check needs a live environment.
Full set of flags: extras/groups for env sync, --locked/--frozen/--no-sync, --python, --version (ty version), --extra-args (pass-through to ty), --no-project, --show-version. Mirrors the shape of FormatArgs but adds sync-related flags since check needs a live environment.
crates/uv-cli/src/lib.rs · b013 · CLI argument struct — the full user-visible API surface
     pub show_version: bool, }  +#[derive(Args)]+pub struct CheckArgs {+    /// Include optional dependencies from the specified extra name.+    ///+    /// May be provided more than once.+    ///+    /// When multiple extras or groups are specified that appear in `tool.uv.conflicts`, uv will+    /// report an error.+    ///+    /// Note that all optional dependencies are always included in the resolution; this option only+    /// affects the selection of packages to install.+    #[arg(+        long,+        conflicts_with = "all_extras",+        conflicts_with = "only_group",+        value_delimiter = ',',+        value_parser = extra_name_with_clap_error,+        value_hint = ValueHint::Other,+    )]+    pub extra: Option<Vec<ExtraName>>,+ +    /// Include all optional dependencies.+    ///+    /// When two or more extras are declared as conflicting in `tool.uv.conflicts`, using this flag+    /// will always result in an error.+    ///+    /// Note that all optional dependencies are always included in the resolution; this option only+    /// affects the selection of packages to install.+    #[arg(long, conflicts_with = "extra", conflicts_with = "only_group")]+    pub all_extras: bool,+ +    /// Exclude the specified optional dependencies, if `--all-extras` is supplied.+    ///+    /// May be provided multiple times.+    #[arg(long, value_hint = ValueHint::Other)]+    pub no_extra: Vec<ExtraName>,+ +    #[arg(long, overrides_with("all_extras"), hide = true)]+    pub no_all_extras: bool,+ +    /// Include the development dependency group [env: UV_DEV=]+    ///+    /// This option is an alias for `--group dev`.+    #[arg(long, overrides_with("no_dev"), hide = true, value_parser = clap::builder::BoolishValueParser::new())]+    pub dev: bool,+ +    /// Disable the development dependency group [env: UV_NO_DEV=]+    ///+    /// This option is an alias of `--no-group dev`.+    /// See `--no-default-groups` to disable all default groups instead.+    #[arg(long, overrides_with("dev"), value_parser = clap::builder::BoolishValueParser::new())]+    pub no_dev: bool,+ +    /// Only include the development dependency group.+    ///+    /// The project and its dependencies will be omitted.+    ///+    /// This option is an alias for `--only-group dev`. Implies `--no-default-groups`.+    #[arg(long, conflicts_with_all = ["group", "all_groups", "no_dev"])]+    pub only_dev: bool,+ +    /// Include dependencies from the specified dependency group.+    ///+    /// When multiple extras or groups are specified that appear in+    /// `tool.uv.conflicts`, uv will report an error.+    ///+    /// May be provided multiple times.+    #[arg(long, conflicts_with_all = ["only_group", "only_dev"], value_hint = ValueHint::Other)]+    pub group: Vec<GroupName>,+ +    /// Disable the specified dependency group [env: `UV_NO_GROUP`=]+    ///+    /// This option always takes precedence over default groups,+    /// `--all-groups`, and `--group`.+    ///+    /// May be provided multiple times.+    #[arg(long, value_delimiter = ' ', value_hint = ValueHint::Other)]+    pub no_group: Vec<GroupName>,+ +    /// Ignore the default dependency groups.+    ///+    /// uv includes the groups defined in `tool.uv.default-groups` by default.+    /// This disables that option, however, specific groups can still be included with `--group`.+    #[arg(long, env = EnvVars::UV_NO_DEFAULT_GROUPS, value_parser = clap::builder::BoolishValueParser::new())]+    pub no_default_groups: bool,+ +    /// Only include dependencies from the specified dependency group.+    ///+    /// The project and its dependencies will be omitted.+    ///+    /// May be provided multiple times. Implies `--no-default-groups`.+    #[arg(long, conflicts_with_all = ["group", "dev", "all_groups"], value_hint = ValueHint::Other)]+    pub only_group: Vec<GroupName>,+ +    /// Include dependencies from all dependency groups.+    ///+    /// `--no-group` can be used to exclude specific groups.+    #[arg(long, conflicts_with_all = ["only_group", "only_dev"])]+    pub all_groups: bool,+ +    /// Assert that the `uv.lock` will remain unchanged [env: UV_LOCKED=]+    ///+    /// Requires that the lockfile is up-to-date. If the lockfile is missing or needs to be updated,+    /// uv will exit with an error.+    #[arg(long, conflicts_with_all = ["frozen", "upgrade"])]+    pub locked: bool,+ +    /// Sync without updating the `uv.lock` file [env: UV_FROZEN=]+    ///+    /// Instead of checking if the lockfile is up-to-date, uses the versions in the lockfile as the+    /// source of truth. If the lockfile is missing, uv will exit with an error. If the+    /// `pyproject.toml` includes changes to dependencies that have not been included in the+    /// lockfile yet, they will not be present in the environment.+    #[arg(long, conflicts_with_all = ["locked", "upgrade", "no_sources"])]+    pub frozen: bool,+ +    /// Avoid syncing the virtual environment [env: UV_NO_SYNC=]+    ///+    /// Implies `--frozen`, as the project dependencies will be ignored (i.e., the lockfile will not+    /// be updated, since the environment will not be synced regardless).+    #[arg(long)]+    pub no_sync: bool,+ +    /// The Python interpreter to use for the project environment.+    ///+    /// By default, the first interpreter that meets the project's+    /// `requires-python` constraint is used.+    ///+    /// See `uv python` for more details on Python discovery and requests.+    #[arg(+        long,+        short,+        env = EnvVars::UV_PYTHON,+        value_parser = parse_maybe_string,+        value_hint = ValueHint::Other,+    )]+    pub python: Option<Maybe<String>>,+ +    /// The version of ty to use for type checking.+    ///+    /// Accepts either a version (e.g., `0.0.1`) which will be treated as an exact pin,+    /// a version specifier (e.g., `>=0.0.1`), or `latest` to use the latest available version.+    ///+    /// By default, a constrained version range of ty will be used (e.g., `>=0.0,<0.1`).+    #[arg(long, value_hint = ValueHint::Other)]+    pub version: Option<String>,+ +    /// Additional arguments to pass to ty.+    ///+    /// For example, use `uv check -- --warn-on error` to treat warnings as errors or+    /// `uv check -- src/module/foo.py` to check a specific file.+    #[arg(last = true, value_hint = ValueHint::Other)]+    pub extra_args: Vec<String>,+ +    /// Avoid discovering a project or workspace.+    ///+    /// Instead of running the type checker in the context of the current project, run it in the+    /// context of the current directory. This is useful when the current directory is not a+    /// project.+    #[arg(+        long,+        env = EnvVars::UV_NO_PROJECT,+        value_parser = clap::builder::BoolishValueParser::new()+    )]+    pub no_project: bool,+ +    /// Display the version of ty that will be used for type checking.+    #[arg(long, hide = true)]+    pub show_version: bool,+ +    #[command(flatten)]+    pub installer: ResolverInstallerArgs,+ +    #[command(flatten)]+    pub build: BuildOptionsArgs,+ +    #[command(flatten)]+    pub refresh: RefreshArgs,+}+  #[derive(Args)] pub struct AuditArgs {     /// Don't audit the specified optional dependencies.

uv check — settings resolution (CLI → typed struct)

CheckSettings resolves the raw Clap args into a typed, normalized struct that the command handler consumes.

1 Entrypoint CheckSettings Introduces CheckSettings and its resolve() impl. Handles flag conflicts (locked vs frozen), dev/no-dev pair resolution, and propagates install mirrors and malware settings — the same resolution pattern used by sync/run.
Introduces CheckSettings and its resolve() impl. Handles flag conflicts (locked vs frozen), dev/no-dev pair resolution, and propagates install mirrors and malware settings — the same resolution pattern used by sync/run.
crates/uv/src/settings.rs · b029 b030 b031 · Settings layer — translates CLI flags into resolved, conflict-checked values
     ToolUninstallArgs, TreeArgs, VenvArgs, VersionArgs, VersionBumpSpec, VersionFormat, }; use uv_cli::{-    AuthorFrom, BuildArgs, ExportArgs, FormatArgs, PublishArgs, PythonDirArgs,+    AuthorFrom, BuildArgs, CheckArgs, ExportArgs, FormatArgs, PublishArgs, PythonDirArgs,     ResolverInstallerArgs, ToolUpgradeArgs,     options::{         Flag, FlagSource, check_conflicts, flag, resolve_flag, resolve_flag_pair,⋯     } }  +/// The resolved settings to use for a `check` invocation.+#[derive(Debug, Clone)]+pub(crate) struct CheckSettings {+    pub(crate) extras: ExtrasSpecification,+    pub(crate) groups: DependencyGroups,+    pub(crate) lock_check: LockCheck,+    pub(crate) frozen: Option<FrozenSource>,+    pub(crate) no_sync: bool,+    pub(crate) python: Option<String>,+    pub(crate) install_mirrors: PythonInstallMirrors,+    pub(crate) refresh: Refresh,+    pub(crate) settings: ResolverInstallerSettings,+    pub(crate) extra_args: Vec<String>,+    pub(crate) version: Option<String>,+    pub(crate) no_project: bool,+    pub(crate) show_version: bool,+    pub(crate) malware_settings: MalwareCheckSettings,+}+ ⋯+impl CheckSettings {+    /// Resolve the [`CheckSettings`] from the CLI and filesystem configuration.+    pub(crate) fn resolve(+        args: CheckArgs,+        filesystem: Option<FilesystemOptions>,+        environment: EnvironmentOptions,+    ) -> Self {+        let CheckArgs {+            extra,+            all_extras,+            no_extra,+            no_all_extras,+            dev,+            no_dev,+            only_dev,+            group,+            no_group,+            no_default_groups,+            only_group,+            all_groups,+            locked,+            frozen,+            no_sync,+            python,+            version,+            extra_args,+            no_project,+            show_version,+            installer,+            build,+            refresh,+        } = args;+ +        let filesystem_install_mirrors = filesystem+            .clone()+            .map(|fs| fs.install_mirrors.clone())+            .unwrap_or_default();+ +        let locked = resolve_flag(locked, "locked", environment.locked);+        let frozen = resolve_flag(frozen, "frozen", environment.frozen);+        let no_sync = resolve_flag(no_sync, "no-sync", environment.no_sync);+        check_conflicts(locked, frozen);+ +        let (dev, no_dev) = resolve_flag_pair(+            dev,+            no_dev,+            "dev",+            "no-dev",+            Some(environment.dev),+            Some(environment.no_dev),+        );+        let settings = ResolverInstallerSettings::combine(+            resolver_installer_options(installer, build),+            filesystem,+            &environment,+        );+        let malware_settings = MalwareCheckSettings::from(&environment);+ +        Self {+            extras: ExtrasSpecification::from_args(+                extra.unwrap_or_default(),+                no_extra,+                false,+                vec![],+                flag(all_extras, no_all_extras, "all-extras").unwrap_or_default(),+            ),+            groups: DependencyGroups::from_args(+                dev.into(),+                no_dev.into(),+                only_dev,+                group,+                if no_group.is_empty() {+                    environment.no_group.clone().unwrap_or_default()+                } else {+                    no_group+                },+                no_default_groups,+                only_group,+                all_groups,+            ),+            lock_check: resolve_lock_check(locked),+            frozen: resolve_frozen(frozen),+            no_sync: no_sync.is_enabled(),+            python: python.and_then(Maybe::into_option),+            install_mirrors: environment+                .install_mirrors+                .combine(filesystem_install_mirrors),+            refresh: Refresh::from(refresh),+            settings,+            extra_args,+            version,+            no_project,+            show_version,+            malware_settings,+        }+    }+}+  /// The resolved settings to use for an `audit` invocation. #[derive(Debug, Clone)] pub(crate) struct AuditSettings {

uv check — command dispatch in run_project

The match arm in run_project wires CheckSettings into the check() command call.

2 Application ProjectCommand::Check dispatch Resolves settings, checks refresh/offline conflicts, initialises the cache with combined refresh, then calls commands::check with all resolved arguments. Passes client_builder.subcommand(["check"]) so network attribution is correct.
Resolves settings, checks refresh/offline conflicts, initialises the cache with combined refresh, then calls commands::check with all resolved arguments. Passes client_builder.subcommand(["check"]) so network attribution is correct.
crates/uv/src/lib.rs · b028 · Entrypoint dispatch — bridges CLI to the async command function
             ))             .await         }+        ProjectCommand::Check(args) => {+            // Resolve the settings from the command-line arguments and workspace configuration.+            let args = settings::CheckSettings::resolve(args, filesystem, environment);+            show_settings!(args);+ +            // Check for conflicts between offline and refresh.+            globals+                .network_settings+                .check_refresh_conflict(&args.refresh);+ +            // Initialize the cache.+            let cache = cache.init().await?.with_refresh(+                args.refresh+                    .combine(Refresh::from(args.settings.reinstall.clone()))+                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),+            );+ +            Box::pin(commands::check(+                project_dir,+                args.lock_check,+                args.frozen,+                args.no_sync,+                args.extras,+                args.groups,+                args.python,+                args.install_mirrors,+                args.settings,+                args.extra_args,+                args.version,+                args.show_version,+                client_builder.subcommand(vec!["check".to_owned()]),+                globals.python_preference,+                globals.python_downloads,+                globals.installer_metadata,+                globals.concurrency,+                &cache,+                workspace_cache,+                printer,+                globals.preview,+                args.no_project,+                no_config,+                args.malware_settings,+            ))+            .await+        }         ProjectCommand::Audit(audit_args) => {             let args = settings::AuditSettings::resolve(audit_args, filesystem, environment);             show_settings!(args);
2 Application mod / pub(crate) use exports Declares and re-exports the check module so the dispatch arm can call commands::check.
Declares and re-exports the check module so the dispatch arm can call commands::check.
crates/uv/src/commands/mod.rs · b019 b027 · Module plumbing that makes the new check module visible
 pub(crate) use pip::uninstall::pip_uninstall; pub(crate) use project::add::add; pub(crate) use project::audit::audit;+pub(crate) use project::check::check; pub(crate) use project::export::export; pub(crate) use project::format::format; pub(crate) use project::init::{InitKind, InitProjectKind, init};⋯   pub(crate) mod add; pub(crate) mod audit;+pub(crate) mod check; pub(crate) mod environment; pub(crate) mod export; pub(crate) mod format;

uv check — core command implementation

The async check() function: project discovery, optional env sync, ty download, and exec.

3 Domain check() Implements the full check pipeline in one function: (1) warn if not in preview mode; (2) discover project or fall back to cwd; (3) warn when project-only flags are used without a project; (4) if a project was found, get-or-init the virtual env and run lock+sync (same path as uv run); (5) resolve ty version from BinVersion enum (default constraints >=0.0,<0.1, pinned, latest, or constraint); (6) download and cache ty via bin_install; (7) spawn ty check in the project root with VIRTUAL_ENV set, forwarding extra_args. The venv path is passed as VIRTUAL_ENV so ty can find installed stubs.
Implements the full check pipeline in one function: (1) warn if not in preview mode; (2) discover project or fall back to cwd; (3) warn when project-only flags are used without a project; (4) if a project was found, get-or-init the virtual env and run lock+sync (same path as uv run); (5) resolve ty version from BinVersion enum (default constraints >=0.0,<0.1, pinned, latest, or constraint); (6) download and cache ty via bin_install; (7) spawn ty check in the project root with VIRTUAL_ENV set, forwarding extra_args. The venv path is passed as VIRTUAL_ENV so ty can find installed stubs.
crates/uv/src/commands/project/check.rs · b020 b021 b022 b023 b024 b025 b026 · Application logic — the actual implementation of the capability
+use std::fmt::Write;+use std::path::Path;+use std::str::FromStr;+ ⋯+use anyhow::{Context, Result};+use tokio::process::Command;+use tracing::debug;+ ⋯+use uv_bin_install::{BinVersion, Binary, ResolvedVersion, bin_install, find_matching_version};+use uv_cache::Cache;+use uv_client::BaseClientBuilder;+use uv_configuration::{+    Concurrency, DependencyGroups, DryRun, ExtrasSpecification, InstallOptions,+};⋯+use uv_normalize::DefaultExtras;+use uv_preview::{Preview, PreviewFeature};+use uv_python::{PythonDownloads, PythonPreference, PythonRequest};+use uv_settings::{MalwareCheckSettings, PythonInstallMirrors};+use uv_warnings::warn_user;+use uv_workspace::{DiscoveryOptions, VirtualProject, WorkspaceCache, WorkspaceError};+ ⋯+use crate::child::run_to_completion;+use crate::commands::pip::loggers::{SummaryInstallLogger, SummaryResolveLogger};+use crate::commands::pip::operations::Modifications;+use crate::commands::project::install_target::InstallTarget;+use crate::commands::project::lock::LockMode;+use crate::commands::project::{+    ProjectEnvironment, ProjectError, UniversalState, default_dependency_groups,+};⋯+use crate::commands::reporters::BinaryDownloadReporter;+use crate::commands::{ExitStatus, diagnostics, project};+use crate::printer::Printer;+use crate::settings::{FrozenSource, LockCheck, ResolverInstallerSettings};+ ⋯+/// Run the type checker.+#[expect(clippy::fn_params_excessive_bools)]+pub(crate) async fn check(+    project_dir: &Path,+    lock_check: LockCheck,+    frozen: Option<FrozenSource>,+    no_sync: bool,+    extras: ExtrasSpecification,+    groups: DependencyGroups,+    python: Option<String>,+    install_mirrors: PythonInstallMirrors,+    settings: ResolverInstallerSettings,+    extra_args: Vec<String>,+    version: Option<String>,+    show_version: bool,+    client_builder: BaseClientBuilder<'_>,+    python_preference: PythonPreference,+    python_downloads: PythonDownloads,+    installer_metadata: bool,+    concurrency: Concurrency,+    cache: &Cache,+    workspace_cache: &WorkspaceCache,+    printer: Printer,+    preview: Preview,+    no_project: bool,+    no_config: bool,+    malware_settings: MalwareCheckSettings,+) -> Result<ExitStatus> {+    if !preview.is_enabled(PreviewFeature::Check) {+        warn_user!(+            "`uv check` is experimental and may change without warning. Pass `--preview-features {}` to disable this warning.",+            PreviewFeature::Check+        );+    }+ +    // Discover the project.+    let project = if no_project {+        None+    } else {+        match VirtualProject::discover(project_dir, &DiscoveryOptions::default(), workspace_cache)+            .await+        {+            Ok(project) => Some(project),+            Err(+                WorkspaceError::MissingPyprojectToml+                | WorkspaceError::MissingProject(_)+                | WorkspaceError::NonWorkspace(_),+            ) => None,+            Err(err) => return Err(err.into()),+        }+    };+ +    if no_project {+        for flag in extras.history().as_flags_pretty() {+            warn_user!("`{flag}` has no effect when used alongside `--no-project`");+        }+        for flag in groups.history().as_flags_pretty() {+            warn_user!("`{flag}` has no effect when used alongside `--no-project`");+        }+        if let LockCheck::Enabled(lock_check) = lock_check {+            warn_user!("`{lock_check}` has no effect when used alongside `--no-project`");+        }+        if frozen.is_some() {+            warn_user!("`--frozen` has no effect when used alongside `--no-project`");+        }+        if no_sync {+            warn_user!("`--no-sync` has no effect when used alongside `--no-project`");+        }+    } else if project.is_none() {+        for flag in extras.history().as_flags_pretty() {+            warn_user!("`{flag}` has no effect when used outside of a project");+        }+        for flag in groups.history().as_flags_pretty() {+            warn_user!("`{flag}` has no effect when used outside of a project");+        }+        if let LockCheck::Enabled(lock_check) = lock_check {+            warn_user!("`{lock_check}` has no effect when used outside of a project");+        }+        if frozen.is_some() {+            warn_user!("`--frozen` has no effect when used outside of a project");+        }+        if no_sync {+            warn_user!("`--no-sync` has no effect when used outside of a project");+        }+    }+ +    let target_dir = project+        .as_ref()+        .map(|p| p.root().to_owned())+        .unwrap_or_else(|| project_dir.to_owned());+ +    // If we found a project, sync the environment before type checking.+    let venv_path = if let Some(project) = &project {+        let default_groups = default_dependency_groups(project.pyproject_toml())?;+        let default_extras = DefaultExtras::default();+        let groups = groups.with_defaults(default_groups);+        let extras = extras.with_defaults(default_extras);+ +        let venv = ProjectEnvironment::get_or_init(+            project.workspace(),+            &groups,+            python.as_deref().map(PythonRequest::parse),+            &install_mirrors,+            &client_builder,+            python_preference,+            python_downloads,+            no_sync,+            no_config,+            None,+            cache,+            DryRun::Disabled,+            printer,+            preview,+        )+        .await?+        .into_environment()?;+ +        if no_sync {+            debug!("Skipping environment synchronization due to `--no-sync`");+        } else {+            let _lock = venv+                .lock()+                .await+                .inspect_err(|err| {+                    tracing::warn!("Failed to acquire environment lock: {err}");+                })+                .ok();+ +            let lock_state = UniversalState::default();+            let sync_state = lock_state.fork();+ +            let mode = if let Some(frozen_source) = frozen {+                LockMode::Frozen(frozen_source.into())+            } else if let LockCheck::Enabled(lock_check) = lock_check {+                LockMode::Locked(venv.interpreter(), lock_check)+            } else {+                LockMode::Write(venv.interpreter())+            };+ +            let result = match Box::pin(+                project::lock::LockOperation::new(+                    mode,+                    &settings.resolver,+                    &client_builder,+                    &lock_state,+                    Box::new(SummaryResolveLogger),+                    &concurrency,+                    cache,+                    workspace_cache,+                    printer,+                    preview,+                )+                .execute(project.workspace().into()),+            )+            .await+            {+                Ok(result) => result,+                Err(ProjectError::Operation(err)) => {+                    return diagnostics::OperationDiagnostic::with_system_certs(+                        client_builder.system_certs(),+                    )+                    .report(err)+                    .map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));+                }+                Err(err) => return Err(err.into()),+            };+ +            let target = match project {+                VirtualProject::Project(project) => InstallTarget::Project {+                    workspace: project.workspace(),+                    name: project.project_name(),+                    lock: result.lock(),+                },+                VirtualProject::NonProject(workspace) => InstallTarget::NonProjectWorkspace {+                    workspace,+                    lock: result.lock(),+                },+            };+ +            target.validate_extras(&extras)?;+            target.validate_groups(&groups)?;+ +            match project::sync::do_sync(+                target,+                &venv,+                &extras,+                &groups,+                None,+                InstallOptions::default(),+                Modifications::Sufficient,+                None,+                (&settings).into(),+                &client_builder,+                &sync_state,+                Box::new(SummaryInstallLogger),+                installer_metadata,+                &concurrency,+                cache,+                workspace_cache,+                DryRun::Disabled,+                printer,+                preview,+                &malware_settings,+            )+            .await+            {+                Ok(_) => {}+                Err(ProjectError::Operation(err)) => {+                    return diagnostics::OperationDiagnostic::with_system_certs(+                        client_builder.system_certs(),+                    )+                    .report(err)+                    .map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));+                }+                Err(err) => return Err(err.into()),+            }+        }+ +        Some(venv.root().to_owned())+    } else {+        None+    };+ +    // Download and install ty.+    let exclude_newer = settings+        .resolver+        .exclude_newer+        .global+        .map(|v| v.timestamp());+    let retry_policy = client_builder.retry_policy();+    let ty_client = client_builder.clone().retries(0).build()?;+ +    let reporter = BinaryDownloadReporter::single(printer);+    let bin_version = version+        .as_deref()+        .map(BinVersion::from_str)+        .transpose()?+        .unwrap_or(BinVersion::Default);+ +    let resolved = match bin_version {+        BinVersion::Default => {+            let constraints = Binary::Ty.default_constraints();+            let resolved = find_matching_version(+                Binary::Ty,+                Some(&constraints),+                exclude_newer,+                &ty_client,+                &retry_policy,+            )+            .await+            .with_context(|| {+                format!("Failed to find ty version matching default constraints: {constraints}")+            })?;+            debug!("Resolved `ty@{constraints}` to `ty=={}`", resolved.version);+            resolved+        }+        BinVersion::Pinned(version) => {+            if exclude_newer.is_some() {+                debug!("`--exclude-newer` is ignored for pinned version `{version}`");+            }+            ResolvedVersion::from_version(Binary::Ty, version)?+        }+        BinVersion::Latest => {+            let resolved =+                find_matching_version(Binary::Ty, None, exclude_newer, &ty_client, &retry_policy)+                    .await+                    .with_context(|| "Failed to find latest ty version")?;+            debug!("Resolved `ty@latest` to `ty=={}`", resolved.version);+            resolved+        }+        BinVersion::Constraint(constraints) => {+            let resolved = find_matching_version(+                Binary::Ty,+                Some(&constraints),+                exclude_newer,+                &ty_client,+                &retry_policy,+            )+            .await+            .with_context(|| format!("Failed to find ty version matching: {constraints}"))?;+            debug!("Resolved `ty@{constraints}` to `ty=={}`", resolved.version);+            resolved+        }+    };+ +    if show_version {+        writeln!(printer.stderr(), "ty {}", resolved.version)?;+    }+ +    let ty_path = bin_install(+        Binary::Ty,+        &resolved,+        &ty_client,+        &retry_policy,+        cache,+        &reporter,+    )+    .await+    .with_context(|| format!("Failed to install ty {}", resolved.version))?;+ +    let mut command = Command::new(&ty_path);+    command.current_dir(&target_dir);+    command.arg("check");+ +    if let Some(venv_path) = &venv_path {+        command.env("VIRTUAL_ENV", venv_path);+    }+ +    command.args(extra_args.iter());+ +    let handle = command.spawn().context("Failed to spawn `ty check`")?;+    run_to_completion(handle).await+}

Binary download support for ty (uv-bin-install)

Teaches the uv-bin-install crate about the ty binary: URL patterns, default version constraints, and mirror support.

4 Adapter Binary::Ty — enum variant and constants Adds the Ty variant to the Binary enum, its name ("ty"), default constraints (>=0.0,<0.1), and the two URL constants TY_GITHUB_URL_PREFIX and TY_MIRROR_SUFFIX. The default constraint range mirrors how Ruff is pinned: a narrow range that allows patch updates without a uv release.
Adds the Ty variant to the Binary enum, its name ("ty"), default constraints (>=0.0,<0.1), and the two URL constants TY_GITHUB_URL_PREFIX and TY_MIRROR_SUFFIX. The default constraint range mirrors how Ruff is pinned: a narrow range that allows patch updates without a uv release.
crates/uv-bin-install/src/lib.rs · b001 b002 b003 b006 b007 · Adapter/infrastructure — binary download plumbing
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Binary {     Ruff,+    Ty,     Uv, }  ⋯             ]             .into_iter()             .collect(),+            Self::Ty => [+                VersionSpecifier::greater_than_equal_version(Version::new([0, 0])),+                VersionSpecifier::less_than_version(Version::new([0, 1])),+            ]+            .into_iter()+            .collect(),             Self::Uv => VersionSpecifiers::empty(),         }     }⋯     pub fn name(&self) -> &'static str {         match self {             Self::Ruff => "ruff",+            Self::Ty => "ty",             Self::Uv => "uv",         }     }⋯ /// The canonical GitHub URL prefix for Ruff releases. const RUFF_GITHUB_URL_PREFIX: &str = "https://github.com/astral-sh/ruff/releases/download/";  +/// The canonical GitHub URL prefix for ty releases.+const TY_GITHUB_URL_PREFIX: &str = "https://github.com/astral-sh/ty/releases/download/";+  /// The canonical GitHub URL prefix for uv releases. const UV_GITHUB_URL_PREFIX: &str = "https://github.com/astral-sh/uv/releases/download/";  ⋯ /// The suffix appended to the Astral mirror base for Ruff releases. const RUFF_MIRROR_SUFFIX: &str = "/github/ruff/releases/download/";  +/// The suffix appended to the Astral mirror base for ty releases.+const TY_MIRROR_SUFFIX: &str = "/github/ty/releases/download/";+  /// The suffix appended to the Astral mirror base for the versions manifest. const VERSIONS_MANIFEST_MIRROR_SUFFIX: &str = "/github/versions/main/v1";  
4 Adapter download_urls / mirror_urls for Ty Implements the same mirror-first, GitHub-fallback URL pattern that Ruff uses, but pointed at astral-sh/ty releases.
Implements the same mirror-first, GitHub-fallback URL pattern that Ruff uses, but pointed at astral-sh/ty releases.
crates/uv-bin-install/src/lib.rs · b004 b005 · Adapter — URL generation logic for ty artifacts
                 }                 Ok(urls)             }+            Self::Ty => {+                let suffix = format!("{version}/ty-{platform}.{}", format.extension());+                let mirror_base = astral_mirror_base_url(astral_mirror_url);+                let mirror = format!("{mirror_base}{TY_MIRROR_SUFFIX}{suffix}");+                let mut urls = vec![parse_url(mirror)?];+                // When using the default mirror, also fall back to GitHub.+                if astral_mirror_url.is_none() {+                    let canonical = format!("{TY_GITHUB_URL_PREFIX}{suffix}");+                    urls.push(parse_url(canonical)?);+                }+                Ok(urls)+            }             Self::Uv => {                 let canonical = format!(                     "{UV_GITHUB_URL_PREFIX}{version}/uv-{platform}.{}",⋯                 }                 Ok(vec![canonical_url])             }+            Self::Ty => {+                if let Some(suffix) = canonical_url.as_str().strip_prefix(TY_GITHUB_URL_PREFIX) {+                    let mirror_base = astral_mirror_base_url(astral_mirror_url);+                    let mirror = format!("{mirror_base}{TY_MIRROR_SUFFIX}{suffix}");+                    let mirror_url = parse_url(mirror)?;+                    if astral_mirror_url.is_some() {+                        return Ok(vec![mirror_url]);+                    }+                    return Ok(vec![mirror_url, canonical_url]);+                }+                Ok(vec![canonical_url])+            }             Self::Uv => Ok(vec![canonical_url]),         }     }
6 Tests/Docs uv-bin-install tests for ty Unit tests for ty download URL generation (custom mirror, default mirror + GitHub fallback) and for mirror URL rewriting, matching the existing test coverage for Ruff.
Unit tests for ty download URL generation (custom mirror, default mirror + GitHub fallback) and for mirror URL rewriting, matching the existing test coverage for Ruff.
crates/uv-bin-install/src/lib.rs · b008 b009 b010 b011 · Tests for the binary download layer
         assert_eq!(default_urls, empty_urls);     }  +    #[test]+    fn test_ty_download_urls_custom_astral_mirror() {+        let urls = Binary::Ty+            .download_urls_with_astral_mirror(+                &Version::new([0, 0, 1]),+                "x86_64-unknown-linux-gnu",+                ArchiveFormat::TarGz,+                Some("https://nexus.example.com/repository/releases.astral.sh/"),+            )+            .expect("ty download URLs should be valid");+ +        let urls = urls+            .into_iter()+            .map(|url| url.to_string())+            .collect::<Vec<_>>();+        assert_eq!(+            urls,+            vec![+                "https://nexus.example.com/repository/releases.astral.sh/github/ty/releases/download/0.0.1/ty-x86_64-unknown-linux-gnu.tar.gz"+                    .to_string(),+            ]+        );+    }+ +    #[test]+    fn test_ty_download_urls_use_default_astral_mirror_then_github() {+        let default_urls = Binary::Ty+            .download_urls_with_astral_mirror(+                &Version::new([0, 0, 1]),+                "x86_64-unknown-linux-gnu",+                ArchiveFormat::TarGz,+                None,+            )+            .expect("ty download URLs should be valid");+        let empty_urls = Binary::Ty+            .download_urls_with_astral_mirror(+                &Version::new([0, 0, 1]),+                "x86_64-unknown-linux-gnu",+                ArchiveFormat::TarGz,+                Some(""),+            )+            .expect("ty download URLs should be valid");+ +        assert_eq!(default_urls, empty_urls);+        assert_eq!(+            default_urls,+            vec![+                DisplaySafeUrl::parse(+                    "https://releases.astral.sh/github/ty/releases/download/0.0.1/ty-x86_64-unknown-linux-gnu.tar.gz",+                )+                .expect("default Astral mirror ty URL should be valid"),+                DisplaySafeUrl::parse(+                    "https://github.com/astral-sh/ty/releases/download/0.0.1/ty-x86_64-unknown-linux-gnu.tar.gz",+                )+                .expect("canonical ty URL should be valid"),+            ]+        );+    }+      #[test]     fn test_manifest_urls_custom_astral_mirror() {⋯     #[test]     fn test_manifest_urls_custom_astral_mirror() {-        for (binary, filename) in [(Binary::Ruff, "ruff.ndjson"), (Binary::Uv, "uv.ndjson")] {+        for (binary, filename) in [+            (Binary::Ruff, "ruff.ndjson"),+            (Binary::Ty, "ty.ndjson"),+            (Binary::Uv, "uv.ndjson"),+        ] {             let urls = binary                 .manifest_urls_with_astral_mirror(Some(                     "https://nexus.example.com/repository/releases.astral.sh/",⋯         );     }  +    #[test]+    fn test_ty_mirror_urls_custom_astral_mirror() {+        let canonical_url = DisplaySafeUrl::parse(+            "https://github.com/astral-sh/ty/releases/download/0.0.1/ty-x86_64-unknown-linux-gnu.tar.gz",+        )+        .expect("canonical ty URL should be valid");+        let urls = Binary::Ty+            .mirror_urls_with_astral_mirror(+                canonical_url,+                Some("https://nexus.example.com/repository/releases.astral.sh/"),+            )+            .expect("mirror URLs should be valid");+ +        let urls = urls+            .into_iter()+            .map(|url| url.to_string())+            .collect::<Vec<_>>();+        assert_eq!(+            urls,+            vec![+                "https://nexus.example.com/repository/releases.astral.sh/github/ty/releases/download/0.0.1/ty-x86_64-unknown-linux-gnu.tar.gz"+                    .to_string(),+            ]+        );+    }+ +    #[test]+    fn test_ty_mirror_urls_use_default_astral_mirror_then_github() {+        let canonical_url = DisplaySafeUrl::parse(+            "https://github.com/astral-sh/ty/releases/download/0.0.1/ty-x86_64-unknown-linux-gnu.tar.gz",+        )+        .expect("canonical ty URL should be valid");+        let default_urls = Binary::Ty+            .mirror_urls_with_astral_mirror(canonical_url.clone(), None)+            .expect("mirror URLs should be valid");+        let empty_urls = Binary::Ty+            .mirror_urls_with_astral_mirror(canonical_url.clone(), Some(""))+            .expect("mirror URLs should be valid");+ +        assert_eq!(default_urls, empty_urls);+        assert_eq!(+            default_urls,+            vec![+                DisplaySafeUrl::parse(+                    "https://releases.astral.sh/github/ty/releases/download/0.0.1/ty-x86_64-unknown-linux-gnu.tar.gz",+                )+                .expect("default Astral mirror ty URL should be valid"),+                canonical_url,+            ]+        );+    }+      #[test]     fn test_manifest_urls_empty_astral_mirror_uses_default() {⋯     #[test]     fn test_manifest_urls_empty_astral_mirror_uses_default() {-        for binary in [Binary::Ruff, Binary::Uv] {+        for binary in [Binary::Ruff, Binary::Ty, Binary::Uv] {             let default_urls = binary                 .manifest_urls_with_astral_mirror(None)                 .expect("manifest URLs should be valid");

PreviewFeature::Check — feature flag gate

Registers 'check' as a named preview feature so the experimental warning can be suppressed with --preview-features check.

5 Cross-cutting PreviewFeature::Check Adds Check = 1 << 33 to the PreviewFeature bitflag enum, maps it to the string "check" in as_str/FromStr, and tests the round-trip. Without this flag the command still works but prints an experimental warning.
Adds Check = 1 << 33 to the PreviewFeature bitflag enum, maps it to the string "check" in as_str/FromStr, and tests the round-trip. Without this flag the command still works but prints an experimental warning.
crates/uv-preview/src/lib.rs · b014 b015 b016 b017 · Cross-cutting — preview gating infrastructure
     TomlBackwardsCompatibility = 1 << 30,     MalwareCheck = 1 << 31,     VenvSafeClear = 1 << 32,+    Check = 1 << 33, }   impl PreviewFeature {⋯             Self::TomlBackwardsCompatibility => "toml-backwards-compatibility",             Self::MalwareCheck => "malware-check",             Self::VenvSafeClear => "venv-safe-clear",+            Self::Check => "check",         }     } }⋯             "toml-backwards-compatibility" => Self::TomlBackwardsCompatibility,             "malware-check" => Self::MalwareCheck,             "venv-safe-clear" => Self::VenvSafeClear,+            "check" => Self::Check,             _ => return Err(PreviewFeatureParseError),         })     }⋯         );         assert_eq!(PreviewFeature::MalwareCheck.as_str(), "malware-check");         assert_eq!(PreviewFeature::VenvSafeClear.as_str(), "venv-safe-clear");+        assert_eq!(PreviewFeature::Check.as_str(), "check");     }       #[test]

Test infrastructure and integration tests for uv check

TestContext::check() helper and the full integration test suite covering the main scenarios.

6 Tests/Docs TestContext::check Adds context.check() mirroring context.format(): sets UV_EXCLUDE_NEWER to a future date so ty versions are resolvable in tests.
Adds context.check() mirroring context.format(): sets UV_EXCLUDE_NEWER to a future date so ty versions are resolvable in tests.
crates/uv-test/src/lib.rs · b018 · Test helper — shared test setup
         command     }  +    /// Create a `uv check` command with options shared across scenarios.+    pub fn check(&self) -> Command {+        let mut command = self.new_command();+        command.arg("check");+        self.add_shared_options(&mut command, false);+        // Override to a more recent date for ty version resolution+        command.env(EnvVars::UV_EXCLUDE_NEWER, "2026-02-15T00:00:00Z");+        command+    }+      /// Create a `uv build` command with options shared across scenarios.     pub fn build(&self) -> Command {         let mut command = self.new_command();
6 Tests/Docs mod check in main.rs Gates the check test module behind #[cfg(feature = "test-python")], matching the level of the audit tests.
Gates the check test module behind #[cfg(feature = "test-python")], matching the level of the audit tests.
crates/uv/tests/it/main.rs · b047 · Test registration
 #[cfg(all(feature = "test-python", feature = "test-pypi"))] mod export;  +#[cfg(feature = "test-python")]+mod check;+  #[cfg(all(feature = "test-python", feature = "test-pypi"))] mod format;  
6 Tests/Docs check_project Basic success case: project with pyproject.toml, valid Python code, expects exit 0 and the experimental warning.
Basic success case: project with pyproject.toml, valid Python code, expects exit 0 and the experimental warning.
crates/uv/tests/it/check.rs · b032 b033 b034 · Integration test — happy path
+use anyhow::Result;+use assert_fs::prelude::*;+use indoc::indoc;+ ⋯+use uv_test::uv_snapshot;+ ⋯+#[test]+fn check_project() -> Result<()> {+    let context = uv_test::test_context!("3.12");+ +    let pyproject_toml = context.temp_dir.child("pyproject.toml");+    pyproject_toml.write_str(indoc! {r#"+        [project]+        name = "project"+        version = "0.1.0"+        requires-python = ">=3.12"+        dependencies = []+    "#})?;+ +    let main_py = context.temp_dir.child("main.py");+    main_py.write_str(indoc! {r"+        x: int = 1+    "})?;+ +    uv_snapshot!(context.filters(), context.check(), @"+    success: true+    exit_code: 0+    ----- stdout -----+    All checks passed!+ +    ----- stderr -----+    warning: `uv check` is experimental and may change without warning. Pass `--preview-features check` to disable this warning.+    ");+ +    Ok(())+}+ 
6 Tests/Docs check_missing_pyproject_toml Verifies that check still runs (against cwd) when no pyproject.toml exists, and that project-only flags (--group, --frozen, --no-sync) are warned about and ignored.
Verifies that check still runs (against cwd) when no pyproject.toml exists, and that project-only flags (--group, --frozen, --no-sync) are warned about and ignored.
crates/uv/tests/it/check.rs · b035 · Integration test — no-project fallback
+#[test]+fn check_missing_pyproject_toml() -> Result<()> {+    let context = uv_test::test_context_with_versions!(&[]);+ +    let main_py = context.temp_dir.child("main.py");+    main_py.write_str(indoc! {r"+        x: int = 1+    "})?;+ +    uv_snapshot!(context.filters(), context.check(), @"+    success: true+    exit_code: 0+    ----- stdout -----+    All checks passed!+ +    ----- stderr -----+    warning: `uv check` is experimental and may change without warning. Pass `--preview-features check` to disable this warning.+    ");+ +    // Project-only settings are ignored without a discovered project.+    uv_snapshot!(context.filters(), context.check().arg("--group").arg("dev").arg("--frozen").arg("--no-sync"), @"+    success: true+    exit_code: 0+    ----- stdout -----+    All checks passed!+ +    ----- stderr -----+    warning: `uv check` is experimental and may change without warning. Pass `--preview-features check` to disable this warning.+    warning: `--group dev` has no effect when used outside of a project+    warning: `--frozen` has no effect when used outside of a project+    warning: `--no-sync` has no effect when used outside of a project+    ");+ +    Ok(())+}+ 
6 Tests/Docs check_no_project Tests that --no-project skips project discovery even when a pyproject.toml is present with an unsatisfiable Python requirement, and that project-only flags generate warnings.
Tests that --no-project skips project discovery even when a pyproject.toml is present with an unsatisfiable Python requirement, and that project-only flags generate warnings.
crates/uv/tests/it/check.rs · b036 · Integration test — --no-project flag
+#[test]+fn check_no_project() -> Result<()> {+    let context = uv_test::test_context_with_versions!(&[]).with_filtered_python_sources();+ +    let pyproject_toml = context.temp_dir.child("pyproject.toml");+    pyproject_toml.write_str(indoc! {r#"+        [project]+        name = "project"+        version = "0.1.0"+        requires-python = ">=4.0"+        dependencies = []+    "#})?;+ +    let main_py = context.temp_dir.child("main.py");+    main_py.write_str(indoc! {r"+        x: int = 1+    "})?;+ +    uv_snapshot!(context.filters(), context.check(), @"+    success: false+    exit_code: 2+    ----- stdout -----+ +    ----- stderr -----+    warning: `uv check` is experimental and may change without warning. Pass `--preview-features check` to disable this warning.+    error: No interpreter found for Python >=4.0 in [PYTHON SOURCES]+    ");+ +    // The unavailable project environment is not initialized when project discovery is disabled.+    uv_snapshot!(context.filters(), context.check().arg("--no-project"), @"+    success: true+    exit_code: 0+    ----- stdout -----+    All checks passed!+ +    ----- stderr -----+    warning: `uv check` is experimental and may change without warning. Pass `--preview-features check` to disable this warning.+    ");+ +    // Project-only settings are ignored when project discovery is disabled.+    uv_snapshot!(+        context.filters(),+        context+            .check()+            .arg("--no-project")+            .arg("--extra")+            .arg("foo")+            .arg("--group")+            .arg("bar")+            .arg("--locked")+            .arg("--no-sync"),+        @"+    success: true+    exit_code: 0+    ----- stdout -----+    All checks passed!+ +    ----- stderr -----+    warning: `uv check` is experimental and may change without warning. Pass `--preview-features check` to disable this warning.+    warning: `--extra foo` has no effect when used alongside `--no-project`+    warning: `--group bar` has no effect when used alongside `--no-project`+    warning: `--locked` has no effect when used alongside `--no-project`+    warning: `--no-sync` has no effect when used alongside `--no-project`+    "+    );+ +    Ok(())+}+ 
6 Tests/Docs check_type_error Verifies that ty's actual error output (invalid-assignment) is surfaced with exit code 1, confirming end-to-end integration with ty.
Verifies that ty's actual error output (invalid-assignment) is surfaced with exit code 1, confirming end-to-end integration with ty.
crates/uv/tests/it/check.rs · b037 · Integration test — ty diagnostic output
+#[test]+fn check_type_error() -> Result<()> {+    let context = uv_test::test_context_with_versions!(&[]);+ +    let main_py = context.temp_dir.child("main.py");+    main_py.write_str(indoc! {r#"+        name: str = "project"+        version: int = name+    "#})?;+ +    uv_snapshot!(context.filters(), context.check(), @r#"+    success: false+    exit_code: 1+    ----- stdout -----+    error[invalid-assignment]: Object of type `Literal["project"]` is not assignable to `int`+     --> main.py:2:10+      |+    1 | name: str = "project"+    2 | version: int = name+      |          ---   ^^^^ Incompatible value of type `Literal["project"]`+      |          |+      |          Declared type+      |+    info: rule `invalid-assignment` is enabled by default+ +    Found 1 diagnostic+ +    ----- stderr -----+    warning: `uv check` is experimental and may change without warning. Pass `--preview-features check` to disable this warning.+    "#);+ +    Ok(())+}+ 
6 Tests/Docs check_with_declared_dependency Tests that uv syncs the declared dependency (iniconfig) into the venv and that ty resolves the import through VIRTUAL_ENV. Gated behind test-pypi.
Tests that uv syncs the declared dependency (iniconfig) into the venv and that ty resolves the import through VIRTUAL_ENV. Gated behind test-pypi.
crates/uv/tests/it/check.rs · b038 · Integration test — env sync + ty resolution
+#[test]+#[cfg(feature = "test-pypi")]+fn check_with_declared_dependency() -> Result<()> {+    let context = uv_test::test_context!("3.12");+ +    let pyproject_toml = context.temp_dir.child("pyproject.toml");+    pyproject_toml.write_str(indoc! {r#"+        [project]+        name = "project"+        version = "0.1.0"+        requires-python = ">=3.12"+        dependencies = ["iniconfig"]+    "#})?;+ +    let main_py = context.temp_dir.child("main.py");+    main_py.write_str(indoc! {r"+        import iniconfig+    "})?;+ +    // ty should resolve the import via the synced virtual environment.+    uv_snapshot!(context.filters(), context.check(), @"+    success: true+    exit_code: 0+    ----- stdout -----+    All checks passed!+ +    ----- stderr -----+    warning: `uv check` is experimental and may change without warning. Pass `--preview-features check` to disable this warning.+    Installed 1 package in [TIME]+    ");+ +    context+        .assert_command(+            "from importlib.metadata import distribution; assert distribution('iniconfig').read_text('INSTALLER') == 'uv'",+        )+        .success();+ +    Ok(())+}+ 
6 Tests/Docs check_with_undeclared_dependency Verifies that an undeclared import produces a ty unresolved-import diagnostic, confirming the venv is correctly passed and ty inspects it.
Verifies that an undeclared import produces a ty unresolved-import diagnostic, confirming the venv is correctly passed and ty inspects it.
crates/uv/tests/it/check.rs · b039 · Integration test — ty unresolved-import diagnostic
+#[test]+fn check_with_undeclared_dependency() -> Result<()> {+    let context = uv_test::test_context!("3.12");+ +    let pyproject_toml = context.temp_dir.child("pyproject.toml");+    pyproject_toml.write_str(indoc! {r#"+        [project]+        name = "project"+        version = "0.1.0"+        requires-python = ">=3.12"+        dependencies = []+    "#})?;+ +    let main_py = context.temp_dir.child("main.py");+    main_py.write_str(indoc! {r"+        import iniconfig+    "})?;+ +    let filters = context+        .filters()+        .into_iter()+        .chain([(+            r"info:   \d+\. \[VENV\]/lib64/python3\.12/site-packages \(site-packages\)\n",+            "",+        )])+        .collect::<Vec<_>>();+ +    // ty should report a diagnostic for the unresolvable import.+    uv_snapshot!(filters, context.check(), @"+    success: false+    exit_code: 1+    ----- stdout -----+    error[unresolved-import]: Cannot resolve imported module `iniconfig`+     --> main.py:1:8+      |+    1 | import iniconfig+      |        ^^^^^^^^^+      |+    info: Searched in the following paths during module resolution:+    info:   1. [TEMP_DIR]/ (first-party code)+    info:   2. vendored://stdlib (stdlib typeshed stubs vendored by ty)+    info:   3. [SITE_PACKAGES]/ (site-packages)+    info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment+    info: rule `unresolved-import` is enabled by default+ +    Found 1 diagnostic+ +    ----- stderr -----+    warning: `uv check` is experimental and may change without warning. Pass `--preview-features check` to disable this warning.+    ");+ +    Ok(())+}
6 Tests/Docs help snapshot updates Snapshot updates adding 'check Type check the project' to all help output variants.
Snapshot updates adding 'check Type check the project' to all help output variants.
crates/uv/tests/it/help.rs · b040 b041 b042 b043 b044 b045 b046 · Snapshot test updates for CLI help text
       export                     Export the project's lockfile to an alternate format       tree                       Display the project's dependency tree       format                     Format Python code in the project+      check                      Type check the project       audit                      Audit the project's dependencies       tool                       Run and install commands provided by Python packages       python                     Manage Python versions and installations⋯       export     Export the project's lockfile to an alternate format       tree       Display the project's dependency tree       format     Format Python code in the project+      check      Type check the project       audit      Audit the project's dependencies       tool       Run and install commands provided by Python packages       python     Manage Python versions and installations⋯       export     Export the project's lockfile to an alternate format       tree       Display the project's dependency tree       format     Format Python code in the project+      check      Type check the project       audit      Audit the project's dependencies       tool       Run and install commands provided by Python packages       python     Manage Python versions and installations⋯         export         tree         format+        check         audit         tool         python⋯         export         tree         format+        check         audit         tool         python⋯       export                     Export the project's lockfile to an alternate format       tree                       Display the project's dependency tree       format                     Format Python code in the project+      check                      Type check the project       audit                      Audit the project's dependencies       tool                       Run and install commands provided by Python packages       python                     Manage Python versions and installations⋯       export                     Export the project's lockfile to an alternate format       tree                       Display the project's dependency tree       format                     Format Python code in the project+      check                      Type check the project       audit                      Audit the project's dependencies       tool                       Run and install commands provided by Python packages       python                     Manage Python versions and installations
6 Tests/Docs show_settings preview_features snapshot Adds Check to the preview features list in the show_settings snapshot.
Adds Check to the preview features list in the show_settings snapshot.
crates/uv/tests/it/show_settings.rs · b048 b049 · Snapshot test for preview feature enumeration
                 TomlBackwardsCompatibility,                 MalwareCheck,                 VenvSafeClear,+                Check,             ],         },         python_preference: Managed,⋯                 TomlBackwardsCompatibility,                 MalwareCheck,                 VenvSafeClear,+                Check,             ],         },         python_preference: Managed,