Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Support add/remove dependency to the given project #318

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 29 additions & 14 deletions rye/src/cli/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use url::Url;

use crate::bootstrap::ensure_self_venv;
use crate::consts::VENV_BIN;
use crate::pyproject::{BuildSystem, DependencyKind, ExpandedSources, PyProject};
use crate::pyproject::{
find_project_by_name, BuildSystem, DependencyKind, ExpandedSources, PyProject,
};
use crate::utils::{format_requirement, set_proxy_variables, CommandOutput};

const PACKAGE_FINDER_SCRIPT: &str = r#"
Expand Down Expand Up @@ -85,6 +87,9 @@ pub struct ReqExtras {
/// Adds a dependency with a specific feature.
#[arg(long)]
features: Vec<String>,
/// Add this as dependency to the given project if in workspace.
#[arg(short, long)]
project: Option<String>,
}

impl ReqExtras {
Expand Down Expand Up @@ -123,23 +128,31 @@ impl ReqExtras {
)),
};
} else if let Some(ref path) = self.path {
let mut pyproject_toml = PyProject::discover()?;
let project_dir = match self.project {
Some(ref name) => {
pyproject_toml = find_project_by_name(name)?;
pyproject_toml.root_path().as_ref().to_owned()
}
None => env::current_dir()?,
};
// For hatchling build backend, it use {root:uri} for file relative path,
// but this not supported by pip-tools,
// and use ${PROJECT_ROOT} will cause error in hatchling, so force absolute path.
let is_hatchling =
PyProject::discover()?.build_backend().unwrap() == BuildSystem::Hatchling;
let is_hatchling = pyproject_toml.build_backend().unwrap() == BuildSystem::Hatchling;
let file_url = if self.absolute || is_hatchling {
Url::from_file_path(env::current_dir()?.join(path))
Url::from_file_path(project_dir.join(path))
.map_err(|_| anyhow!("unable to interpret '{}' as path", path.display()))?
} else {
let base = env::current_dir()?;
let rv = pathdiff::diff_paths(base.join(path), &base).ok_or_else(|| {
anyhow!(
"unable to create relative path from {} to {}",
base.display(),
path.display()
)
})?;
let rv = pathdiff::diff_paths(project_dir.join(path), &project_dir).ok_or_else(
|| {
anyhow!(
"unable to create relative path from {} to {}",
project_dir.display(),
path.display()
)
},
)?;
Url::from_file_path(Path::new("/${PROJECT_ROOT}").join(rv)).unwrap()
};
req.version_or_url = match req.version_or_url {
Expand Down Expand Up @@ -191,8 +204,10 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
let mut added = Vec::new();
python_path.push(VENV_BIN);
python_path.push("python");

let mut pyproject_toml = PyProject::discover()?;
let mut pyproject_toml = match cmd.req_extras.project {
Some(ref name) => find_project_by_name(name)?,
None => PyProject::discover()?,
};
let py_ver = match pyproject_toml.target_python_version() {
Some(ver) => ver.format_simple(),
None => "".to_string(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I looked at it before for the --pyproject arg work:

Inside the requirement code in this same file, apply_to_requirement there is some logic about relative paths for path dependencies. That code uses the current working directory path as starting point. We might need to think about how that one should work together with this feature.

Copy link
Contributor Author

@ischaojie ischaojie Jun 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current messy way of adding local dependency is rooted in the limited support of pip-tools. I am looking for a proper way to make relative paths work properly. The main issue is that after adding dependencies for a specific project, lock and sync will fail due to the inability to find the path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just saw this PR #321, 👍

Expand Down
10 changes: 8 additions & 2 deletions rye/src/cli/remove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use anyhow::Error;
use clap::Parser;
use pep508_rs::Requirement;

use crate::pyproject::{DependencyKind, PyProject};
use crate::pyproject::{find_project_by_name, DependencyKind, PyProject};
use crate::utils::{format_requirement, CommandOutput};

/// Removes a package from this project.
Expand All @@ -18,6 +18,9 @@ pub struct Args {
/// Remove this from an optional dependency group.
#[arg(long, conflicts_with = "dev")]
optional: Option<String>,
/// Remove from a specific project.
#[arg(long)]
project: Option<String>,
/// Enables verbose diagnostics.
#[arg(short, long)]
verbose: bool,
Expand All @@ -30,7 +33,10 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
let output = CommandOutput::from_quiet_and_verbose(cmd.quiet, cmd.verbose);
let mut removed_packages = Vec::new();

let mut pyproject_toml = PyProject::discover()?;
let mut pyproject_toml = match cmd.project {
Some(name) => find_project_by_name(&name)?,
None => PyProject::discover()?,
};
for str_requirement in cmd.requirements {
let requirement = Requirement::from_str(&str_requirement)?;
if let Some(removed) = pyproject_toml.remove_dependency(
Expand Down
14 changes: 14 additions & 0 deletions rye/src/pyproject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,20 @@ fn is_rye_managed(doc: &Document) -> bool {
.unwrap_or(false)
}

pub fn find_project_by_name(name: &str) -> Result<PyProject, Error> {
let mut pyproject_toml = PyProject::discover()?;
if let Some(workspace) = pyproject_toml.workspace() {
if let Some(project) = workspace.get_project(&name)? {
pyproject_toml = project;
} else {
bail!("project {} not found", name);
}
} else {
bail!("no workspace found");
}
Ok(pyproject_toml)
}

/// Represents expanded sources.
#[derive(Debug, Clone, Serialize)]
pub struct ExpandedSources {
Expand Down