mirror of https://github.com/astral-sh/uv
Read package name from `pyproject.toml` in `uv run` (#3496)
## Summary Right now, the project name is hard-coded. Closes https://github.com/astral-sh/uv/issues/3491.
This commit is contained in:
parent
51f4ab1c8d
commit
3dd34e218a
|
|
@ -1,19 +1,38 @@
|
|||
use serde::Deserialize;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use tracing::debug;
|
||||
use uv_fs::Simplified;
|
||||
use uv_normalize::PackageName;
|
||||
|
||||
use uv_requirements::RequirementsSource;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub(crate) enum ProjectError {
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Toml(#[from] toml::de::Error),
|
||||
|
||||
#[error("No `project` section found in: {}", _0.user_display())]
|
||||
MissingProject(PathBuf),
|
||||
|
||||
#[error("No `name` found in `project` section in: {}", _0.user_display())]
|
||||
MissingName(PathBuf),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Project {
|
||||
/// The name of the package.
|
||||
name: PackageName,
|
||||
/// The path to the `pyproject.toml` file.
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl Project {
|
||||
/// Find the current project.
|
||||
pub(crate) fn find(path: impl AsRef<Path>) -> Option<Self> {
|
||||
pub(crate) fn find(path: impl AsRef<Path>) -> Result<Option<Self>, ProjectError> {
|
||||
for ancestor in path.as_ref().ancestors() {
|
||||
let pyproject_path = ancestor.join("pyproject.toml");
|
||||
if pyproject_path.exists() {
|
||||
|
|
@ -21,13 +40,27 @@ impl Project {
|
|||
"Loading requirements from: {}",
|
||||
pyproject_path.user_display()
|
||||
);
|
||||
return Some(Self {
|
||||
|
||||
// Read the `pyproject.toml`.
|
||||
let contents = fs_err::read_to_string(&pyproject_path)?;
|
||||
let pyproject_toml: PyProjectToml = toml::from_str(&contents)?;
|
||||
|
||||
// Extract the package name.
|
||||
let Some(project) = pyproject_toml.project else {
|
||||
return Err(ProjectError::MissingProject(pyproject_path));
|
||||
};
|
||||
let Some(name) = project.name else {
|
||||
return Err(ProjectError::MissingName(pyproject_path));
|
||||
};
|
||||
|
||||
return Ok(Some(Self {
|
||||
name,
|
||||
path: pyproject_path,
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Return the requirements for the project.
|
||||
|
|
@ -37,4 +70,22 @@ impl Project {
|
|||
RequirementsSource::from_source_tree(self.path.parent().unwrap().to_path_buf()),
|
||||
]
|
||||
}
|
||||
|
||||
/// Return the [`PackageName`] for the project.
|
||||
pub(crate) fn name(&self) -> &PackageName {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
/// A pyproject.toml as specified in PEP 517.
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct PyProjectToml {
|
||||
project: Option<PyProjectProject>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct PyProjectProject {
|
||||
name: Option<PackageName>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ pub(crate) async fn lock(
|
|||
let venv = PythonEnvironment::from_virtualenv(cache)?;
|
||||
|
||||
// Find the project requirements.
|
||||
let Some(project) = Project::find(std::env::current_dir()?) else {
|
||||
let Some(project) = Project::find(std::env::current_dir()?)? else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Unable to find `pyproject.toml` for project."
|
||||
));
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ pub(crate) async fn run(
|
|||
} else {
|
||||
debug!("Syncing project environment.");
|
||||
|
||||
let Some(project) = Project::find(std::env::current_dir()?) else {
|
||||
let Some(project) = Project::find(std::env::current_dir()?)? else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Unable to find `pyproject.toml` for project."
|
||||
));
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ use uv_configuration::{ConfigSettings, NoBinary, NoBuild, PreviewMode, SetupPySt
|
|||
use uv_dispatch::BuildDispatch;
|
||||
use uv_installer::SitePackages;
|
||||
use uv_interpreter::PythonEnvironment;
|
||||
use uv_normalize::PackageName;
|
||||
use uv_resolver::{FlatIndex, InMemoryIndex, Lock};
|
||||
use uv_types::{BuildIsolation, HashStrategy, InFlight};
|
||||
use uv_warnings::warn_user;
|
||||
|
||||
use crate::commands::project::discovery::Project;
|
||||
use crate::commands::{project, ExitStatus};
|
||||
use crate::printer::Printer;
|
||||
|
||||
|
|
@ -32,6 +32,20 @@ pub(crate) async fn sync(
|
|||
let markers = venv.interpreter().markers();
|
||||
let tags = venv.interpreter().tags()?;
|
||||
|
||||
// Find the project requirements.
|
||||
let Some(project) = Project::find(std::env::current_dir()?)? else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Unable to find `pyproject.toml` for project."
|
||||
));
|
||||
};
|
||||
|
||||
// Read the lockfile.
|
||||
let resolution = {
|
||||
let encoded = fs_err::tokio::read_to_string("uv.lock").await?;
|
||||
let lock: Lock = toml::from_str(&encoded)?;
|
||||
lock.to_resolution(markers, tags, project.name())
|
||||
};
|
||||
|
||||
// Initialize the registry client.
|
||||
// TODO(zanieb): Support client options e.g. offline, tls, etc.
|
||||
let client = RegistryClientBuilder::new(cache.clone())
|
||||
|
|
@ -69,15 +83,6 @@ pub(crate) async fn sync(
|
|||
&no_binary,
|
||||
);
|
||||
|
||||
// Read the lockfile.
|
||||
let resolution = {
|
||||
// TODO(charlie): Read the project name from disk.
|
||||
let root = PackageName::new("project".to_string())?;
|
||||
let encoded = fs_err::tokio::read_to_string("uv.lock").await?;
|
||||
let lock: Lock = toml::from_str(&encoded)?;
|
||||
lock.to_resolution(markers, tags, &root)
|
||||
};
|
||||
|
||||
// Sync the environment.
|
||||
project::install(
|
||||
&resolution,
|
||||
|
|
|
|||
Loading…
Reference in New Issue