Skip to content

Commit

Permalink
Read package name from pyproject.toml in uv run (#3496)
Browse files Browse the repository at this point in the history
## Summary

Right now, the project name is hard-coded.

Closes #3491.
  • Loading branch information
charliermarsh authored May 9, 2024
1 parent 51f4ab1 commit 3dd34e2
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 16 deletions.
59 changes: 55 additions & 4 deletions crates/uv/src/commands/project/discovery.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,66 @@
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() {
debug!(
"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.
Expand All @@ -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>,
}
2 changes: 1 addition & 1 deletion crates/uv/src/commands/project/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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."
));
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/src/commands/project/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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."
));
Expand Down
25 changes: 15 additions & 10 deletions crates/uv/src/commands/project/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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())
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 3dd34e2

Please sign in to comment.