use std::borrow::Cow;
use std::io::stdout;
use std::path::{Path, PathBuf};
use std::time::Duration;
use std::{fmt::Write, process::ExitCode};
use anstream::AutoStream;
use anyhow::Context;
use owo_colors::OwoColorize;
use tracing::debug;
pub(crate) use auth::dir::dir as auth_dir;
pub(crate) use auth::helper::helper as auth_helper;
pub(crate) use auth::login::login as auth_login;
pub(crate) use auth::logout::logout as auth_logout;
pub(crate) use auth::token::token as auth_token;
pub(crate) use build_frontend::build_frontend;
pub(crate) use cache_clean::cache_clean;
pub(crate) use cache_dir::cache_dir;
pub(crate) use cache_prune::cache_prune;
pub(crate) use cache_size::cache_size;
pub(crate) use help::help;
pub(crate) use pip::check::pip_check;
pub(crate) use pip::compile::pip_compile;
pub(crate) use pip::freeze::pip_freeze;
pub(crate) use pip::install::pip_install;
pub(crate) use pip::list::pip_list;
pub(crate) use pip::show::pip_show;
pub(crate) use pip::sync::pip_sync;
pub(crate) use pip::tree::pip_tree;
pub(crate) use pip::uninstall::pip_uninstall;
pub(crate) use project::add::add;
pub(crate) use project::audit::audit;
pub(crate) use project::export::export;
pub(crate) use project::format::format;
pub(crate) use project::init::{InitKind, InitProjectKind, init};
pub(crate) use project::lock::lock;
pub(crate) use project::remove::remove;
pub(crate) use project::run::{ParsedRunCommand, RunCommand, run};
pub(crate) use project::sync::sync;
pub(crate) use project::tree::tree;
pub(crate) use project::version::{project_version, self_version};
pub(crate) use publish::publish;
pub(crate) use python::dir::dir as python_dir;
pub(crate) use python::find::find as python_find;
pub(crate) use python::find::find_script as python_find_script;
pub(crate) use python::install::install as python_install;
pub(crate) use python::install::{PythonUpgrade, PythonUpgradeSource};
pub(crate) use python::list::list as python_list;
pub(crate) use python::pin::pin as python_pin;
pub(crate) use python::uninstall::uninstall as python_uninstall;
pub(crate) use python::update_shell::update_shell as python_update_shell;
#[cfg(feature = "self-update")]
pub(crate) use self_update::self_update;
pub(crate) use tool::dir::dir as tool_dir;
pub(crate) use tool::install::install as tool_install;
pub(crate) use tool::list::list as tool_list;
pub(crate) use tool::run::ToolRunCommand;
pub(crate) use tool::run::run as tool_run;
pub(crate) use tool::uninstall::uninstall as tool_uninstall;
pub(crate) use tool::update_shell::update_shell as tool_update_shell;
pub(crate) use tool::upgrade::upgrade as tool_upgrade;
use uv_cache::Cache;
use uv_configuration::Concurrency;
pub(crate) use uv_console::human_readable_bytes;
use uv_fs::{CWD, Simplified};
use uv_installer::compile_tree;
use uv_python::PythonEnvironment;
use uv_scripts::Pep723Script;
pub(crate) use venv::venv;
pub(crate) use workspace::dir::dir;
pub(crate) use workspace::list::list;
pub(crate) use workspace::metadata::metadata;
use crate::commands::pip::operations::ChangedDist;
use crate::printer::Printer;
mod auth;
pub(crate) mod build_backend;
mod build_frontend;
mod cache_clean;
mod cache_dir;
mod cache_prune;
mod cache_size;
mod diagnostics;
mod editable;
mod help;
pub(crate) mod pip;
mod project;
mod publish;
mod pylock;
mod python;
pub(crate) mod reporters;
#[cfg(feature = "self-update")]
mod self_update;
mod tool;
mod venv;
mod workspace;
#[derive(Copy, Clone)]
pub(crate) enum ExitStatus {
Success,
Failure,
Error,
External(u8),
}
impl From<ExitStatus> for ExitCode {
fn from(status: ExitStatus) -> Self {
match status {
ExitStatus::Success => Self::from(0),
ExitStatus::Failure => Self::from(1),
ExitStatus::Error => Self::from(2),
ExitStatus::External(code) => Self::from(code),
}
}
}
pub(super) fn elapsed(duration: Duration) -> String {
let secs = duration.as_secs();
let ms = duration.subsec_millis();
if secs >= 60 {
format!("{}m {:02}s", secs / 60, secs % 60)
} else if secs > 0 {
format!("{}.{:02}s", secs, duration.subsec_nanos() / 10_000_000)
} else if ms > 0 {
format!("{ms}ms")
} else {
format!("0.{:02}ms", duration.subsec_nanos() / 10_000)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub(super) enum ChangeEventKind {
Removed,
Added,
Reinstalled,
}
#[derive(Debug)]
pub(super) struct ChangeEvent<'a> {
dist: &'a ChangedDist,
kind: ChangeEventKind,
}
pub(super) async fn compile_bytecode(
venv: &PythonEnvironment,
concurrency: &Concurrency,
cache: &Cache,
printer: Printer,
) -> anyhow::Result<()> {
let start = std::time::Instant::now();
let mut files = 0;
for site_packages in venv.site_packages() {
let site_packages = CWD.join(site_packages);
if !site_packages.exists() {
debug!(
"Skipping non-existent site-packages directory: {}",
site_packages.display()
);
continue;
}
files += compile_tree(
&site_packages,
venv.python_executable(),
concurrency,
cache.root(),
)
.await
.with_context(|| {
format!(
"Failed to bytecode-compile Python file in: {}",
site_packages.user_display()
)
})?;
}
let s = if files == 1 { "" } else { "s" };
writeln!(
printer.stderr(),
"{}",
format!(
"Bytecode compiled {} {}",
format!("{files} file{s}").bold(),
format!("in {}", elapsed(start.elapsed())).dimmed()
)
.dimmed()
)?;
Ok(())
}
struct OutputWriter<'a> {
stdout: Option<AutoStream<std::io::Stdout>>,
output_file: Option<&'a Path>,
buffer: Vec<u8>,
}
impl<'a> OutputWriter<'a> {
fn new(include_stdout: bool, output_file: Option<&'a Path>) -> Self {
let stdout = include_stdout.then(|| AutoStream::<std::io::Stdout>::auto(stdout()));
Self {
stdout,
output_file,
buffer: Vec::new(),
}
}
async fn commit(self) -> std::io::Result<()> {
if let Some(output_file) = self.output_file {
if let Some(parent_dir) = output_file.parent() {
fs_err::create_dir_all(parent_dir)?;
}
let output_file = fs_err::read_link(output_file)
.map(Cow::Owned)
.unwrap_or(Cow::Borrowed(output_file));
let stream = anstream::adapter::strip_bytes(&self.buffer).into_vec();
uv_fs::write_atomic(output_file, &stream).await?;
}
Ok(())
}
}
impl std::io::Write for OutputWriter<'_> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
if self.output_file.is_some() {
self.buffer.write_all(buf)?;
}
if let Some(stdout) = &mut self.stdout {
stdout.write_all(buf)?;
}
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
if let Some(stdout) = &mut self.stdout {
stdout.flush()?;
}
Ok(())
}
}
pub(super) fn conjunction(names: Vec<String>) -> String {
let mut names = names.into_iter();
let first = names.next();
let last = names.next_back();
match (first, last) {
(Some(first), Some(last)) => {
let mut result = first;
let mut comma = false;
for name in names {
result.push_str(", ");
result.push_str(&name);
comma = true;
}
if comma {
result.push_str(", and ");
} else {
result.push_str(" and ");
}
result.push_str(&last);
result
}
(Some(first), None) => first,
_ => String::new(),
}
}
pub(super) fn capitalize(s: &str) -> String {
let mut chars = s.chars();
match chars.next() {
None => String::new(),
Some(c) => c.to_uppercase().collect::<String>() + chars.as_str(),
}
}
#[derive(Debug)]
#[expect(clippy::large_enum_variant)]
pub(crate) enum ScriptPath {
Script(Pep723Script),
Path(PathBuf),
}