Update PEP 723 lockfile in uv add --script (#10145)

## Summary

`uv add --script main.py anyio` will now update the lockfile, _if_ it
already exists. (If no such lockfile exists, the behavior is unchanged.)
This commit is contained in:
Charlie Marsh 2025-01-08 16:19:12 -05:00 committed by GitHub
parent e22b728e3f
commit 31b2d3f988
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 585 additions and 121 deletions

View file

@ -3257,7 +3257,12 @@ pub struct AddArgs {
/// a new one will be created and added to the script. When executed via `uv run`, /// a new one will be created and added to the script. When executed via `uv run`,
/// uv will create a temporary environment for the script with all inline /// uv will create a temporary environment for the script with all inline
/// dependencies installed. /// dependencies installed.
#[arg(long, conflicts_with = "dev", conflicts_with = "optional")] #[arg(
long,
conflicts_with = "dev",
conflicts_with = "optional",
conflicts_with = "package"
)]
pub script: Option<PathBuf>, pub script: Option<PathBuf>,
/// The Python interpreter to use for resolving and syncing. /// The Python interpreter to use for resolving and syncing.

View file

@ -230,7 +230,7 @@ impl Pep723Script {
} }
/// Replace the existing metadata in the file with new metadata and write the updated content. /// Replace the existing metadata in the file with new metadata and write the updated content.
pub async fn write(&self, metadata: &str) -> Result<(), Pep723Error> { pub fn write(&self, metadata: &str) -> Result<(), io::Error> {
let content = format!( let content = format!(
"{}{}{}", "{}{}{}",
self.prelude, self.prelude,
@ -238,7 +238,7 @@ impl Pep723Script {
self.postlude self.postlude
); );
fs_err::tokio::write(&self.path, content).await?; fs_err::write(&self.path, content)?;
Ok(()) Ok(())
} }
@ -307,7 +307,7 @@ impl Pep723Metadata {
} }
impl FromStr for Pep723Metadata { impl FromStr for Pep723Metadata {
type Err = Pep723Error; type Err = toml::de::Error;
/// Parse `Pep723Metadata` from a raw TOML string. /// Parse `Pep723Metadata` from a raw TOML string.
fn from_str(raw: &str) -> Result<Self, Self::Err> { fn from_str(raw: &str) -> Result<Self, Self::Err> {

View file

@ -1,12 +1,14 @@
use anyhow::{bail, Context, Result};
use itertools::Itertools;
use owo_colors::OwoColorize;
use rustc_hash::{FxBuildHasher, FxHashMap};
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::fmt::Write; use std::fmt::Write;
use std::io; use std::io;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use anyhow::{bail, Context, Result};
use itertools::Itertools;
use owo_colors::OwoColorize;
use rustc_hash::{FxBuildHasher, FxHashMap};
use tracing::debug; use tracing::debug;
use url::Url; use url::Url;
@ -29,7 +31,7 @@ use uv_pypi_types::{redact_credentials, ParsedUrl, RequirementSource, VerbatimPa
use uv_python::{Interpreter, PythonDownloads, PythonEnvironment, PythonPreference, PythonRequest}; use uv_python::{Interpreter, PythonDownloads, PythonEnvironment, PythonPreference, PythonRequest};
use uv_requirements::{NamedRequirementsResolver, RequirementsSource, RequirementsSpecification}; use uv_requirements::{NamedRequirementsResolver, RequirementsSource, RequirementsSpecification};
use uv_resolver::FlatIndex; use uv_resolver::FlatIndex;
use uv_scripts::{Pep723ItemRef, Pep723Script}; use uv_scripts::{Pep723ItemRef, Pep723Metadata, Pep723Script};
use uv_settings::PythonInstallMirrors; use uv_settings::PythonInstallMirrors;
use uv_types::{BuildIsolation, HashStrategy}; use uv_types::{BuildIsolation, HashStrategy};
use uv_warnings::warn_user_once; use uv_warnings::warn_user_once;
@ -173,7 +175,7 @@ pub(crate) async fn add(
.await? .await?
.into_interpreter(); .into_interpreter();
Target::Script(script, Box::new(interpreter)) AddTarget::Script(script, Box::new(interpreter))
} else { } else {
// Find the project in the workspace. // Find the project in the workspace.
let project = if let Some(package) = package { let project = if let Some(package) = package {
@ -221,7 +223,7 @@ pub(crate) async fn add(
.await? .await?
.into_interpreter(); .into_interpreter();
Target::Project(project, Box::new(PythonTarget::Interpreter(interpreter))) AddTarget::Project(project, Box::new(PythonTarget::Interpreter(interpreter)))
} else { } else {
// Discover or create the virtual environment. // Discover or create the virtual environment.
let venv = project::get_or_init_environment( let venv = project::get_or_init_environment(
@ -239,7 +241,7 @@ pub(crate) async fn add(
) )
.await?; .await?;
Target::Project(project, Box::new(PythonTarget::Environment(venv))) AddTarget::Project(project, Box::new(PythonTarget::Environment(venv)))
} }
}; };
@ -361,7 +363,7 @@ pub(crate) async fn add(
// If any of the requirements are self-dependencies, bail. // If any of the requirements are self-dependencies, bail.
if matches!(dependency_type, DependencyType::Production) { if matches!(dependency_type, DependencyType::Production) {
if let Target::Project(project, _) = &target { if let AddTarget::Project(project, _) = &target {
if let Some(project_name) = project.project_name() { if let Some(project_name) = project.project_name() {
for requirement in &requirements { for requirement in &requirements {
if requirement.name == *project_name { if requirement.name == *project_name {
@ -389,10 +391,10 @@ pub(crate) async fn add(
// Add the requirements to the `pyproject.toml` or script. // Add the requirements to the `pyproject.toml` or script.
let mut toml = match &target { let mut toml = match &target {
Target::Script(script, _) => { AddTarget::Script(script, _) => {
PyProjectTomlMut::from_toml(&script.metadata.raw, DependencyTarget::Script) PyProjectTomlMut::from_toml(&script.metadata.raw, DependencyTarget::Script)
} }
Target::Project(project, _) => PyProjectTomlMut::from_toml( AddTarget::Project(project, _) => PyProjectTomlMut::from_toml(
&project.pyproject_toml().raw, &project.pyproject_toml().raw,
DependencyTarget::PyProjectToml, DependencyTarget::PyProjectToml,
), ),
@ -405,10 +407,10 @@ pub(crate) async fn add(
requirement.extras.dedup(); requirement.extras.dedup();
let (requirement, source) = match target { let (requirement, source) = match target {
Target::Script(_, _) | Target::Project(_, _) if raw_sources => { AddTarget::Script(_, _) | AddTarget::Project(_, _) if raw_sources => {
(uv_pep508::Requirement::from(requirement), None) (uv_pep508::Requirement::from(requirement), None)
} }
Target::Script(ref script, _) => { AddTarget::Script(ref script, _) => {
let script_path = std::path::absolute(&script.path)?; let script_path = std::path::absolute(&script.path)?;
let script_dir = script_path.parent().expect("script path has no parent"); let script_dir = script_path.parent().expect("script path has no parent");
resolve_requirement( resolve_requirement(
@ -422,7 +424,7 @@ pub(crate) async fn add(
script_dir, script_dir,
)? )?
} }
Target::Project(ref project, _) => { AddTarget::Project(ref project, _) => {
let workspace = project let workspace = project
.workspace() .workspace()
.packages() .packages()
@ -566,33 +568,17 @@ pub(crate) async fn add(
let content = toml.to_string(); let content = toml.to_string();
// Save the modified `pyproject.toml` or script. // Save the modified `pyproject.toml` or script.
let modified = match &target { let modified = target.write(&content)?;
Target::Script(script, _) => {
if content == script.metadata.raw {
debug!("No changes to dependencies; skipping update");
false
} else {
script.write(&content).await?;
true
}
}
Target::Project(project, _) => {
if content == *project.pyproject_toml().raw {
debug!("No changes to dependencies; skipping update");
false
} else {
let pyproject_path = project.root().join("pyproject.toml");
fs_err::write(pyproject_path, &content)?;
true
}
}
};
let (project, environment) = match target { // If `--frozen`, exit early. There's no reason to lock and sync, since we don't need a `uv.lock`
Target::Project(project, environment) => (project, environment), // to exist at all.
// If `--script`, exit early. There's no reason to lock and sync. if frozen {
Target::Script(script, _) => { return Ok(ExitStatus::Success);
// TODO(charlie): Lock the script, if a lockfile already exists. }
// If we're modifying a script, and lockfile doesn't exist, don't create it.
if let AddTarget::Script(ref script, _) = target {
if !LockTarget::from(script).lock_path().is_file() {
writeln!( writeln!(
printer.stderr(), printer.stderr(),
"Updated `{}`", "Updated `{}`",
@ -600,39 +586,20 @@ pub(crate) async fn add(
)?; )?;
return Ok(ExitStatus::Success); return Ok(ExitStatus::Success);
} }
};
// If `--frozen`, exit early. There's no reason to lock and sync, and we don't need a `uv.lock`
// to exist at all.
if frozen {
return Ok(ExitStatus::Success);
} }
// Store the content prior to any modifications. // Store the content prior to any modifications.
let project_root = project.root().to_path_buf(); let snapshot = target.snapshot().await?;
let workspace_root = project.workspace().install_path().clone();
let existing_pyproject_toml = project.pyproject_toml().as_ref().to_vec();
let existing_uv_lock = LockTarget::from(project.workspace()).read_bytes().await?;
// Update the `pypackage.toml` in-memory. // Update the `pypackage.toml` in-memory.
let project = project let target = target.update(&content)?;
.with_pyproject_toml(toml::from_str(&content).map_err(ProjectError::PyprojectTomlParse)?)
.ok_or(ProjectError::PyprojectTomlUpdate)?;
// Set the Ctrl-C handler to revert changes on exit. // Set the Ctrl-C handler to revert changes on exit.
let _ = ctrlc::set_handler({ let _ = ctrlc::set_handler({
let project_root = project_root.clone(); let snapshot = snapshot.clone();
let workspace_root = workspace_root.clone();
let existing_pyproject_toml = existing_pyproject_toml.clone();
let existing_uv_lock = existing_uv_lock.clone();
move || { move || {
if modified { if modified {
let _ = revert( let _ = snapshot.revert();
&project_root,
&workspace_root,
&existing_pyproject_toml,
existing_uv_lock.as_deref(),
);
} }
#[allow(clippy::exit, clippy::cast_possible_wrap)] #[allow(clippy::exit, clippy::cast_possible_wrap)]
@ -645,10 +612,9 @@ pub(crate) async fn add(
}); });
match lock_and_sync( match lock_and_sync(
project, target,
&mut toml, &mut toml,
&edits, &edits,
&environment,
state, state,
locked, locked,
&dependency_type, &dependency_type,
@ -668,12 +634,7 @@ pub(crate) async fn add(
Ok(()) => Ok(ExitStatus::Success), Ok(()) => Ok(ExitStatus::Success),
Err(err) => { Err(err) => {
if modified { if modified {
let _ = revert( let _ = snapshot.revert();
&project_root,
&workspace_root,
&existing_pyproject_toml,
existing_uv_lock.as_deref(),
);
} }
match err { match err {
ProjectError::Operation(err) => diagnostics::OperationDiagnostic::with_hint(format!("If you want to add the package regardless of the failed resolution, provide the `{}` flag to skip locking and syncing.", "--frozen".green())) ProjectError::Operation(err) => diagnostics::OperationDiagnostic::with_hint(format!("If you want to add the package regardless of the failed resolution, provide the `{}` flag to skip locking and syncing.", "--frozen".green()))
@ -688,10 +649,9 @@ pub(crate) async fn add(
/// Re-lock and re-sync the project after a series of edits. /// Re-lock and re-sync the project after a series of edits.
#[allow(clippy::fn_params_excessive_bools)] #[allow(clippy::fn_params_excessive_bools)]
async fn lock_and_sync( async fn lock_and_sync(
mut project: VirtualProject, mut target: AddTarget,
toml: &mut PyProjectTomlMut, toml: &mut PyProjectTomlMut,
edits: &[DependencyEdit], edits: &[DependencyEdit],
environment: &PythonTarget,
state: SharedState, state: SharedState,
locked: bool, locked: bool,
dependency_type: &DependencyType, dependency_type: &DependencyType,
@ -706,15 +666,13 @@ async fn lock_and_sync(
printer: Printer, printer: Printer,
preview: PreviewMode, preview: PreviewMode,
) -> Result<(), ProjectError> { ) -> Result<(), ProjectError> {
let mode = if locked {
LockMode::Locked(environment.interpreter())
} else {
LockMode::Write(environment.interpreter())
};
let mut lock = project::lock::do_safe_lock( let mut lock = project::lock::do_safe_lock(
mode, if locked {
project.workspace().into(), LockMode::Locked(target.interpreter())
} else {
LockMode::Write(target.interpreter())
},
(&target).into(),
settings.into(), settings.into(),
LowerBound::default(), LowerBound::default(),
&state, &state,
@ -811,17 +769,13 @@ async fn lock_and_sync(
let content = toml.to_string(); let content = toml.to_string();
// Write the updated `pyproject.toml` to disk. // Write the updated `pyproject.toml` to disk.
fs_err::write(project.root().join("pyproject.toml"), &content)?; target.write(&content)?;
// Update the `pypackage.toml` in-memory. // Update the `pypackage.toml` in-memory.
project = project target = target.update(&content)?;
.with_pyproject_toml(
toml::from_str(&content).map_err(ProjectError::PyprojectTomlParse)?,
)
.ok_or(ProjectError::PyprojectTomlUpdate)?;
// Invalidate the project metadata. // Invalidate the project metadata.
if let VirtualProject::Project(ref project) = project { if let AddTarget::Project(VirtualProject::Project(ref project), _) = target {
let url = Url::from_file_path(project.project_root()) let url = Url::from_file_path(project.project_root())
.expect("project root is a valid URL"); .expect("project root is a valid URL");
let version_id = VersionId::from_url(&url); let version_id = VersionId::from_url(&url);
@ -832,8 +786,12 @@ async fn lock_and_sync(
// If the file was modified, we have to lock again, though the only expected change is // If the file was modified, we have to lock again, though the only expected change is
// the addition of the minimum version specifiers. // the addition of the minimum version specifiers.
lock = project::lock::do_safe_lock( lock = project::lock::do_safe_lock(
mode, if locked {
project.workspace().into(), LockMode::Locked(target.interpreter())
} else {
LockMode::Write(target.interpreter())
},
(&target).into(),
settings.into(), settings.into(),
LowerBound::default(), LowerBound::default(),
&state, &state,
@ -851,7 +809,12 @@ async fn lock_and_sync(
} }
} }
let PythonTarget::Environment(venv) = environment else { let AddTarget::Project(project, environment) = target else {
// If we're not adding to a project, exit early.
return Ok(());
};
let PythonTarget::Environment(venv) = &*environment else {
// If we're not syncing, exit early. // If we're not syncing, exit early.
return Ok(()); return Ok(());
}; };
@ -918,25 +881,6 @@ async fn lock_and_sync(
Ok(()) Ok(())
} }
/// Revert the changes to the `pyproject.toml` and `uv.lock`, if necessary.
fn revert(
project_root: &Path,
workspace_root: &Path,
pyproject_toml: &[u8],
uv_lock: Option<&[u8]>,
) -> Result<(), io::Error> {
debug!("Reverting changes to `pyproject.toml`");
let () = fs_err::write(project_root.join("pyproject.toml"), pyproject_toml)?;
if let Some(uv_lock) = uv_lock.as_ref() {
debug!("Reverting changes to `uv.lock`");
let () = fs_err::write(workspace_root.join("uv.lock"), uv_lock)?;
} else {
debug!("Removing `uv.lock`");
let () = fs_err::remove_file(workspace_root.join("uv.lock"))?;
}
Ok(())
}
/// Augment a user-provided requirement by attaching any specification data that was provided /// Augment a user-provided requirement by attaching any specification data that was provided
/// separately from the requirement itself (e.g., `--branch main`). /// separately from the requirement itself (e.g., `--branch main`).
fn augment_requirement( fn augment_requirement(
@ -1047,8 +991,8 @@ fn resolve_requirement(
} }
/// Represents the destination where dependencies are added, either to a project or a script. /// Represents the destination where dependencies are added, either to a project or a script.
#[derive(Debug)] #[derive(Debug, Clone)]
enum Target { enum AddTarget {
/// A PEP 723 script, with inline metadata. /// A PEP 723 script, with inline metadata.
Script(Pep723Script, Box<Interpreter>), Script(Pep723Script, Box<Interpreter>),
@ -1056,7 +1000,16 @@ enum Target {
Project(VirtualProject, Box<PythonTarget>), Project(VirtualProject, Box<PythonTarget>),
} }
impl Target { impl<'lock> From<&'lock AddTarget> for LockTarget<'lock> {
fn from(value: &'lock AddTarget) -> Self {
match value {
AddTarget::Script(script, _) => LockTarget::Script(script),
AddTarget::Project(project, _) => LockTarget::Workspace(project.workspace()),
}
}
}
impl AddTarget {
/// Returns the [`Interpreter`] for the target. /// Returns the [`Interpreter`] for the target.
fn interpreter(&self) -> &Interpreter { fn interpreter(&self) -> &Interpreter {
match self { match self {
@ -1064,10 +1017,122 @@ impl Target {
Self::Project(_, venv) => venv.interpreter(), Self::Project(_, venv) => venv.interpreter(),
} }
} }
/// Write the updated content to the target.
///
/// Returns `true` if the content was modified.
fn write(&self, content: &str) -> Result<bool, io::Error> {
match self {
Self::Script(script, _) => {
if content == script.metadata.raw {
debug!("No changes to dependencies; skipping update");
Ok(false)
} else {
script.write(content)?;
Ok(true)
}
}
Self::Project(project, _) => {
if content == project.pyproject_toml().raw {
debug!("No changes to dependencies; skipping update");
Ok(false)
} else {
let pyproject_path = project.root().join("pyproject.toml");
fs_err::write(pyproject_path, content)?;
Ok(true)
}
}
}
}
/// Update the target in-memory to incorporate the new content.
#[allow(clippy::result_large_err)]
fn update(self, content: &str) -> Result<Self, ProjectError> {
match self {
Self::Script(mut script, interpreter) => {
script.metadata = Pep723Metadata::from_str(content)
.map_err(ProjectError::Pep723ScriptTomlParse)?;
Ok(Self::Script(script, interpreter))
}
Self::Project(project, venv) => {
let project = project
.with_pyproject_toml(
toml::from_str(content).map_err(ProjectError::PyprojectTomlParse)?,
)
.ok_or(ProjectError::PyprojectTomlUpdate)?;
Ok(Self::Project(project, venv))
}
}
}
/// Take a snapshot of the target.
async fn snapshot(&self) -> Result<AddTargetSnapshot, io::Error> {
// Read the lockfile into memory.
let target = match self {
Self::Script(script, _) => LockTarget::from(script),
Self::Project(project, _) => LockTarget::Workspace(project.workspace()),
};
let lock = target.read_bytes().await?;
// Clone the target.
match self {
Self::Script(script, _) => Ok(AddTargetSnapshot::Script(script.clone(), lock)),
Self::Project(project, _) => Ok(AddTargetSnapshot::Project(project.clone(), lock)),
}
}
}
#[derive(Debug, Clone)]
enum AddTargetSnapshot {
Script(Pep723Script, Option<Vec<u8>>),
Project(VirtualProject, Option<Vec<u8>>),
}
impl AddTargetSnapshot {
/// Write the snapshot back to disk (e.g., to a `pyproject.toml` and `uv.lock`).
fn revert(&self) -> Result<(), io::Error> {
match self {
Self::Script(script, lock) => {
// Write the PEP 723 script back to disk.
debug!("Reverting changes to PEP 723 script block");
script.write(&script.metadata.raw)?;
// Write the lockfile back to disk.
let target = LockTarget::from(script);
if let Some(lock) = lock {
debug!("Reverting changes to `uv.lock`");
fs_err::write(target.lock_path(), lock)?;
} else {
debug!("Removing `uv.lock`");
fs_err::remove_file(target.lock_path())?;
}
Ok(())
}
Self::Project(project, lock) => {
// Write the `pyproject.toml` back to disk.
debug!("Reverting changes to `pyproject.toml`");
fs_err::write(
project.root().join("pyproject.toml"),
project.pyproject_toml().as_ref(),
)?;
// Write the lockfile back to disk.
let target = LockTarget::from(project.workspace());
if let Some(lock) = lock {
debug!("Reverting changes to `uv.lock`");
fs_err::write(target.lock_path(), lock)?;
} else {
debug!("Removing `uv.lock`");
fs_err::remove_file(target.lock_path())?;
}
Ok(())
}
}
}
} }
/// A Python [`Interpreter`] or [`PythonEnvironment`] for a project. /// A Python [`Interpreter`] or [`PythonEnvironment`] for a project.
#[derive(Debug)] #[derive(Debug, Clone)]
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
enum PythonTarget { enum PythonTarget {
Interpreter(Interpreter), Interpreter(Interpreter),

View file

@ -235,11 +235,11 @@ impl<'lock> LockTarget<'lock> {
} }
/// Read the lockfile from the workspace as bytes. /// Read the lockfile from the workspace as bytes.
pub(crate) async fn read_bytes(self) -> Result<Option<Vec<u8>>, ProjectError> { pub(crate) async fn read_bytes(self) -> Result<Option<Vec<u8>>, std::io::Error> {
match fs_err::tokio::read(self.lock_path()).await { match fs_err::tokio::read(self.lock_path()).await {
Ok(encoded) => Ok(Some(encoded)), Ok(encoded) => Ok(Some(encoded)),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None), Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(err) => Err(err.into()), Err(err) => Err(err),
} }
} }

View file

@ -167,6 +167,9 @@ pub(crate) enum ProjectError {
#[error("Failed to update `pyproject.toml`")] #[error("Failed to update `pyproject.toml`")]
PyprojectTomlUpdate, PyprojectTomlUpdate,
#[error("Failed to parse PEP 723 script metadata")]
Pep723ScriptTomlParse(#[source] toml::de::Error),
#[error(transparent)] #[error(transparent)]
DependencyGroup(#[from] DependencyGroupError), DependencyGroup(#[from] DependencyGroupError),

View file

@ -164,7 +164,7 @@ pub(crate) async fn remove(
// Save the modified dependencies. // Save the modified dependencies.
match &target { match &target {
Target::Script(script) => { Target::Script(script) => {
script.write(&toml.to_string()).await?; script.write(&toml.to_string())?;
} }
Target::Project(project) => { Target::Project(project) => {
let pyproject_path = project.root().join("pyproject.toml"); let pyproject_path = project.root().join("pyproject.toml");

View file

@ -4929,6 +4929,10 @@ fn add_script() -> Result<()> {
"### "###
); );
}); });
// Adding to a script without a lockfile shouldn't create a lockfile.
assert!(!context.temp_dir.join("script.py.lock").exists());
Ok(()) Ok(())
} }
@ -5330,6 +5334,393 @@ fn remove_repeated() -> Result<()> {
Ok(()) Ok(())
} }
/// Add to a PEP 732 script with a lockfile.
#[test]
fn add_script_lock() -> Result<()> {
let context = TestContext::new("3.12");
let script = context.temp_dir.child("script.py");
script.write_str(indoc! {r#"
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests<3",
# "rich",
# ]
# ///
import requests
from rich.pretty import pprint
resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])
"#})?;
// Explicitly lock the script.
uv_snapshot!(context.filters(), context.lock().arg("--script").arg("script.py"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 9 packages in [TIME]
"###);
let lock = context.read("script.py.lock");
insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.11"
[options]
exclude-newer = "2024-03-25T00:00:00Z"
[manifest]
requirements = [
{ name = "requests", specifier = "<3" },
{ name = "rich" },
]
[[package]]
name = "certifi"
version = "2024.2.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/71/da/e94e26401b62acd6d91df2b52954aceb7f561743aa5ccc32152886c76c96/certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", size = 164886 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ba/06/a07f096c664aeb9f01624f858c3add0a4e913d6c96257acb4fce61e7de14/certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1", size = 163774 },
]
[[package]]
name = "charset-normalizer"
version = "3.3.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 },
{ url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 },
{ url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 },
{ url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 },
{ url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 },
{ url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 },
{ url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 },
{ url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 },
{ url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 },
{ url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 },
{ url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 },
{ url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 },
{ url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 },
{ url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 },
{ url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 },
{ url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 },
{ url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 },
{ url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 },
{ url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 },
{ url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 },
{ url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 },
{ url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 },
{ url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 },
{ url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 },
{ url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 },
{ url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 },
{ url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 },
{ url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 },
{ url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 },
{ url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 },
{ url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 },
]
[[package]]
name = "idna"
version = "3.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
]
[[package]]
name = "pygments"
version = "2.17.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", size = 4827772 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", size = 1179756 },
]
[[package]]
name = "requests"
version = "2.31.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "charset-normalizer" },
{ name = "idna" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1", size = 110794 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", size = 62574 },
]
[[package]]
name = "rich"
version = "13.7.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b3/01/c954e134dc440ab5f96952fe52b4fdc64225530320a910473c1fe270d9aa/rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432", size = 221248 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/87/67/a37f6214d0e9fe57f6ae54b2956d550ca8365857f42a1ce0392bb21d9410/rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", size = 240681 },
]
[[package]]
name = "urllib3"
version = "2.2.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7a/50/7fd50a27caa0652cd4caf224aa87741ea41d3265ad13f010886167cfcc79/urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19", size = 291020 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a2/73/a68704750a7679d0b6d3ad7aa8d4da8e14e151ae82e6fee774e6e0d05ec8/urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", size = 121067 },
]
"###
);
});
// Adding to a locked script should update the lockfile.
uv_snapshot!(context.filters(), context.add().arg("anyio").arg("--script").arg("script.py"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 11 packages in [TIME]
"###);
let script_content = context.read("script.py");
insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
script_content, @r###"
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "anyio>=4.3.0",
# "requests<3",
# "rich",
# ]
# ///
import requests
from rich.pretty import pprint
resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])
"###
);
});
let lock = context.read("script.py.lock");
insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.11"
[options]
exclude-newer = "2024-03-25T00:00:00Z"
[manifest]
requirements = [
{ name = "anyio", specifier = ">=4.3.0" },
{ name = "requests", specifier = "<3" },
{ name = "rich" },
]
[[package]]
name = "anyio"
version = "4.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna" },
{ name = "sniffio" },
]
sdist = { url = "https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6", size = 159642 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/14/fd/2f20c40b45e4fb4324834aea24bd4afdf1143390242c0b33774da0e2e34f/anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8", size = 85584 },
]
[[package]]
name = "certifi"
version = "2024.2.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/71/da/e94e26401b62acd6d91df2b52954aceb7f561743aa5ccc32152886c76c96/certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", size = 164886 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ba/06/a07f096c664aeb9f01624f858c3add0a4e913d6c96257acb4fce61e7de14/certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1", size = 163774 },
]
[[package]]
name = "charset-normalizer"
version = "3.3.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 },
{ url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 },
{ url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 },
{ url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 },
{ url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 },
{ url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 },
{ url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 },
{ url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 },
{ url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 },
{ url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 },
{ url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 },
{ url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 },
{ url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 },
{ url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 },
{ url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 },
{ url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 },
{ url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 },
{ url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 },
{ url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 },
{ url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 },
{ url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 },
{ url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 },
{ url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 },
{ url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 },
{ url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 },
{ url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 },
{ url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 },
{ url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 },
{ url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 },
{ url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 },
{ url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 },
]
[[package]]
name = "idna"
version = "3.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
]
[[package]]
name = "pygments"
version = "2.17.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", size = 4827772 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", size = 1179756 },
]
[[package]]
name = "requests"
version = "2.31.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "charset-normalizer" },
{ name = "idna" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1", size = 110794 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", size = 62574 },
]
[[package]]
name = "rich"
version = "13.7.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b3/01/c954e134dc440ab5f96952fe52b4fdc64225530320a910473c1fe270d9aa/rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432", size = 221248 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/87/67/a37f6214d0e9fe57f6ae54b2956d550ca8365857f42a1ce0392bb21d9410/rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", size = 240681 },
]
[[package]]
name = "sniffio"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
]
[[package]]
name = "urllib3"
version = "2.2.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7a/50/7fd50a27caa0652cd4caf224aa87741ea41d3265ad13f010886167cfcc79/urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19", size = 291020 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a2/73/a68704750a7679d0b6d3ad7aa8d4da8e14e151ae82e6fee774e6e0d05ec8/urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", size = 121067 },
]
"###
);
});
Ok(())
}
/// Remove from a PEP732 script, /// Remove from a PEP732 script,
#[test] #[test]
fn remove_script() -> Result<()> { fn remove_script() -> Result<()> {