Skip to content
Merged
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
106 changes: 50 additions & 56 deletions py/tools/py/src/venv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,12 +480,12 @@ pub enum Command {
}

pub trait PthEntryHandler {
fn plan<A: AsRef<Path>, B: AsRef<Path>>(
fn plan(
&self,
venv: &Virtualenv,
bin_dir: A,
bin_dir: &Path,
entry_repo: &str,
entry_path: B,
entry_path: &Path,
) -> miette::Result<Vec<Command>>;
}

Expand All @@ -497,12 +497,12 @@ pub trait PthEntryHandler {
/// the default `rules_python` $PYTHONPATH behavior is effectively emulated.
pub struct PthStrategy;
impl PthEntryHandler for PthStrategy {
fn plan<A: AsRef<Path>, B: AsRef<Path>>(
fn plan(
&self,
venv: &Virtualenv,
bin_dir: A,
bin_dir: &Path,
entry_repo: &str,
entry_path: B,
entry_path: &Path,
) -> miette::Result<Vec<Command>> {
let action_src_dir = current_dir().into_diagnostic()?;
let action_bin_dir = action_src_dir.join(bin_dir);
Expand All @@ -526,12 +526,12 @@ impl PthEntryHandler for PthStrategy {
#[derive(Copy, Clone)]
pub struct CopyStrategy;
impl PthEntryHandler for CopyStrategy {
fn plan<A: AsRef<Path>, B: AsRef<Path>>(
fn plan(
&self,
venv: &Virtualenv,
bin_dir: A,
bin_dir: &Path,
entry_repo: &str,
entry_path: B,
entry_path: &Path,
) -> miette::Result<Vec<Command>> {
// Assumes that `create_empty_venv` has already been called to build out the virtualenv.
let dest = &venv.site_dir;
Expand Down Expand Up @@ -568,12 +568,12 @@ impl PthEntryHandler for CopyStrategy {
#[derive(Clone)]
pub struct CopyAndPatchStrategy;
impl PthEntryHandler for CopyAndPatchStrategy {
fn plan<A: AsRef<Path>, B: AsRef<Path>>(
fn plan(
&self,
venv: &Virtualenv,
bin_dir: A,
bin_dir: &Path,
entry_repo: &str,
entry_path: B,
entry_path: &Path,
) -> miette::Result<Vec<Command>> {
// Assumes that `create_empty_venv` has already been called to build out the virtualenv.
let dest = &venv.site_dir;
Expand Down Expand Up @@ -611,12 +611,12 @@ impl PthEntryHandler for CopyAndPatchStrategy {
#[derive(Clone)]
pub struct SymlinkStrategy;
impl PthEntryHandler for SymlinkStrategy {
fn plan<A: AsRef<Path>, B: AsRef<Path>>(
fn plan(
&self,
venv: &Virtualenv,
bin_dir: A,
bin_dir: &Path,
entry_repo: &str,
entry_path: B,
entry_path: &Path,
) -> miette::Result<Vec<Command>> {
// Assumes that `create_empty_venv` has already been called to build out the virtualenv.
let dest = &venv.site_dir;
Expand Down Expand Up @@ -659,20 +659,21 @@ pub struct FirstpartyThirdpartyStrategy<A: PthEntryHandler, B: PthEntryHandler>
impl<A: PthEntryHandler, B: PthEntryHandler> PthEntryHandler
for FirstpartyThirdpartyStrategy<A, B>
{
fn plan<C: AsRef<Path>, D: AsRef<Path>>(
fn plan(
&self,
venv: &Virtualenv,
bin_dir: C,
bin_dir: &Path,
entry_repo: &str,
entry_path: D,
entry_path: &Path,
) -> miette::Result<Vec<Command>> {
let action_src_dir = current_dir().into_diagnostic()?;
let main_repo = action_src_dir.file_name().unwrap();
if entry_repo != main_repo {
return self.thirdparty.plan(venv, bin_dir, entry_repo, entry_path);
let strategy: &dyn PthEntryHandler = if entry_repo != main_repo {
&self.thirdparty
} else {
return self.firstparty.plan(venv, bin_dir, entry_repo, entry_path);
}
&self.firstparty
};
strategy.plan(venv, bin_dir, entry_repo, entry_path)
}
}

Expand All @@ -685,18 +686,14 @@ pub struct SrcSiteStrategy<A: PthEntryHandler, B: PthEntryHandler, C: AsRef<Path
impl<A: PthEntryHandler, B: PthEntryHandler, C: AsRef<Path>> PthEntryHandler
for SrcSiteStrategy<A, B, C>
{
fn plan<D: AsRef<Path>, E: AsRef<Path>>(
fn plan(
&self,
venv: &Virtualenv,
bin_dir: D,
bin_dir: &Path,
entry_repo: &str,
entry_path: E,
entry_path: &Path,
) -> miette::Result<Vec<Command>> {
if self
.site_suffixes
.iter()
.any(|it| entry_path.as_ref().ends_with(it))
{
if self.site_suffixes.iter().any(|it| entry_path.ends_with(it)) {
return self
.site_strategy
.plan(venv, bin_dir, entry_repo, entry_path);
Expand All @@ -714,12 +711,12 @@ pub struct StrategyWithBindir<A: PthEntryHandler, B: PthEntryHandler> {
pub bin_strategy: B,
}
impl<A: PthEntryHandler, B: PthEntryHandler> PthEntryHandler for StrategyWithBindir<A, B> {
fn plan<C: AsRef<Path>, D: AsRef<Path>>(
fn plan(
&self,
venv: &Virtualenv,
bin_dir: C,
bin_dir: &Path,
entry_repo: &str,
entry_path: D,
entry_path: &Path,
) -> miette::Result<Vec<Command>> {
// Assumes that `create_empty_venv` has already been called to build out the virtualenv.
let action_src_dir = current_dir().into_diagnostic()?;
Expand All @@ -732,34 +729,28 @@ impl<A: PthEntryHandler, B: PthEntryHandler> PthEntryHandler for StrategyWithBin
.plan(venv, &bin_dir, entry_repo, &entry_path)?,
);

let mut flag = false;
for prefix in [&action_src_dir, &action_bin_dir] {
let bin_dir = prefix
.join(entry_repo)
.join(&entry_path.as_ref().parent().unwrap())
.join("bin");
if bin_dir.exists() {
flag |= true;
}
}
if flag {
plan.append(&mut self.bin_strategy.plan(
venv,
bin_dir,
entry_repo,
&entry_path.as_ref().parent().unwrap().join("bin"),
)?);
let entry_bin = entry_path.parent().unwrap().join("bin");
let found_bin_dir = [&action_src_dir, &action_bin_dir]
.iter()
.map(|pfx| pfx.join(entry_repo).join(&entry_bin))
.any(|p| p.exists());
if found_bin_dir {
plan.append(
&mut self
.bin_strategy
.plan(venv, bin_dir, entry_repo, &entry_bin)?,
);
}

Ok(plan)
}
}

pub fn populate_venv<A: PthEntryHandler>(
pub fn populate_venv(
venv: Virtualenv,
pth_file: PthFile,
bin_dir: PathBuf,
population_strategy: A,
bin_dir: impl AsRef<Path>,
population_strategy: &dyn PthEntryHandler,
collision_strategy: CollisionResolutionStrategy,
) -> miette::Result<()> {
let mut plan: Vec<Command> = Vec::new();
Expand All @@ -783,9 +774,12 @@ pub fn populate_venv<A: PthEntryHandler>(
return Err(miette!("Invalid path file entry!"));
};

let entry = PathBuf::from(entry_path);

plan.append(&mut population_strategy.plan(&venv, bin_dir.clone(), entry_repo, entry)?);
plan.append(&mut population_strategy.plan(
&venv,
bin_dir.as_ref(),
entry_repo,
entry_path.as_ref(),
)?);
}

let mut planned_destinations: HashMap<PathBuf, Vec<Command>> = HashMap::new();
Expand Down
113 changes: 52 additions & 61 deletions py/tools/venv_bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,76 +115,67 @@ struct VenvArgs {

fn venv_cmd_handler(args: VenvArgs) -> miette::Result<()> {
let pth_file = py::PthFile::new(&args.pth_file, args.pth_entry_prefix);
match args.mode {
// FIXME: Does this need to care about the repo?
VenvMode::DynamicSymlink => py::create_venv(
if let VenvMode::DynamicSymlink = args.mode {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

By switching from a match with one arm acting and the other arm doing-work-then-matching-again to if let with a return, I took out one level of indentation with no other code changes. The second match still needs the unreachable! in it because rustc doesn't have the ability to statically strip enum variants in cases like these. Annoying but c'est la vie. Beats writing Haskal

return py::create_venv(
&args.python,
&args.location,
Some(pth_file),
args.collision_strategy.unwrap_or_default().into(),
&args.venv_name,
),
);
}

it => {
let Some(version) = args.version else {
return Err(miette!("Version must be provided for static venv modes"));
let version = args
.version
.ok_or_else(|| miette!("Version must be provided for static venv modes"))?;

let venv = py::venv::create_empty_venv(
args.repo
.as_deref()
.ok_or_else(|| miette!("The --repo argument is required for static venvs!"))?,
&args.python,
py::venv::PythonVersionInfo::from_str(&version)?,
&args.location,
args.env_file.as_deref(),
args.venv_shim.as_deref(),
args.debug,
args.include_system_site_packages,
args.include_user_site_packages,
)?;

let strategy: Box<dyn py::venv::PthEntryHandler> = match args.mode {
VenvMode::DynamicSymlink => unreachable!(),
VenvMode::StaticPth => Box::new(py::venv::PthStrategy),
// TODO: This is much more a "prod" strategy than a "symlink" strategy
// but here we are. Better naming or user-facing extension/strategy
// options would be a good get.
VenvMode::StaticSymlink => {
let thirdparty_strategy = py::venv::StrategyWithBindir {
root_strategy: py::venv::SymlinkStrategy,
bin_strategy: py::venv::CopyAndPatchStrategy,
};

let venv = py::venv::create_empty_venv(
args.repo
.as_deref()
.expect("The --repo argument is required for static venvs!"),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

quit calling Option::expect() in -> Result functions. that is what .ok_or()? and .ok_or_else()? are for.

&args.python,
py::venv::PythonVersionInfo::from_str(&version)?,
&args.location,
args.env_file.as_deref(),
args.venv_shim.as_deref(),
args.debug,
args.include_system_site_packages,
args.include_user_site_packages,
)?;

// Because the strategy type is dyn-incompatible we have to do this
// so that each call is monomorphic. Oh well.
match it {
VenvMode::DynamicSymlink => unreachable!(),
VenvMode::StaticPth => py::venv::populate_venv(
venv,
pth_file,
args.bin_dir.unwrap(),
py::venv::PthStrategy {},
args.collision_strategy.unwrap_or_default().into(),
)?,
VenvMode::StaticSymlink => {
let thirdparty_strategy = py::venv::StrategyWithBindir {
root_strategy: py::venv::SymlinkStrategy,
bin_strategy: py::venv::CopyAndPatchStrategy,
};

py::venv::populate_venv(
venv,
pth_file,
args.bin_dir.unwrap(),
py::venv::FirstpartyThirdpartyStrategy {
firstparty: py::venv::SrcSiteStrategy {
src_strategy: py::venv::PthStrategy {},
site_suffixes: vec!["site-packages", "dist-packages"],
site_strategy: thirdparty_strategy.clone(),
},
thirdparty: py::venv::SrcSiteStrategy {
src_strategy: py::venv::SymlinkStrategy {},
site_suffixes: vec!["site-packages", "dist-packages"],
site_strategy: thirdparty_strategy.clone(),
},
},
args.collision_strategy.unwrap_or_default().into(),
)?
}
}

Ok(())
Box::new(py::venv::FirstpartyThirdpartyStrategy {
firstparty: py::venv::SrcSiteStrategy {
src_strategy: py::venv::PthStrategy {},
site_suffixes: vec!["site-packages", "dist-packages"],
site_strategy: thirdparty_strategy.clone(),
},
thirdparty: py::venv::SrcSiteStrategy {
src_strategy: py::venv::SymlinkStrategy {},
site_suffixes: vec!["site-packages", "dist-packages"],
site_strategy: thirdparty_strategy.clone(),
},
})
}
}
};
py::venv::populate_venv(
venv,
pth_file,
args.bin_dir.unwrap(),
&*strategy,
args.collision_strategy.unwrap_or_default().into(),
)
}

fn main() -> miette::Result<()> {
Expand Down
Loading