Skip to content

Commit 68db2ba

Browse files
committed
Fix runfiles interacting badly with bazel_tools runfiles; user site flag
1 parent 7c7531e commit 68db2ba

File tree

7 files changed

+84
-29
lines changed

7 files changed

+84
-29
lines changed

py/private/py_venv/py_venv.bzl

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,10 @@ def _py_venv_base_impl(ctx):
101101
rfs = _py_library.make_merged_runfiles(
102102
ctx,
103103
extra_depsets = [
104-
py_toolchain.files,
105-
srcs_depset,
106-
] + virtual_resolution.srcs + virtual_resolution.runfiles +
107-
([py_toolchain.files] if py_toolchain.runfiles_interpreter else []),
104+
py_toolchain.files,
105+
srcs_depset,
106+
] + virtual_resolution.srcs + virtual_resolution.runfiles +
107+
([py_toolchain.files] if py_toolchain.runfiles_interpreter else []),
108108
extra_runfiles_depsets = [
109109
ctx.attr._runfiles_lib[DefaultInfo].default_runfiles,
110110
],
@@ -137,6 +137,10 @@ def _py_venv_base_impl(ctx):
137137
True: "true",
138138
False: "false"
139139
}[ctx.attr.include_system_site_packages]),
140+
"--include-user-site-packages=" + ({
141+
True: "true",
142+
False: "false"
143+
}[ctx.attr.include_system_site_packages]),
140144
] + (["--debug"] if ctx.attr.debug else []),
141145
inputs = rfs.merge_all([
142146
ctx.runfiles(files = [
@@ -299,7 +303,7 @@ A collision can occur when multiple packages providing the same file are install
299303
default = False,
300304
),
301305
"include_system_site_packages": attr.bool(
302-
default = True,
306+
default = False,
303307
doc = """`pyvenv.cfg` feature flag for the `include-system-site-packages` key.
304308
305309
When `True`, the user's site directory AND the interpreter's site directory will
@@ -314,6 +318,24 @@ to be unusable. Many libraries assume these packages will always be available
314318
and may not reliably declare their dependencies such that Bazel will satisfy
315319
them, so choosing isolation could expose packaging errors.
316320
321+
"""
322+
),
323+
"include_user_site_packages": attr.bool(
324+
default = False,
325+
doc = """`pyvenv.cfg` feature flag for the `aspect-include-user-site-packages` extension key.
326+
327+
When `True`, the user's site directory directory will be included into the
328+
runtime pythonpath.
329+
330+
When `False`, only the virtualenv's site directory and the interpreter's core
331+
libraries will be included into the runtime pythonpath.
332+
333+
`False` is obviously preferable as it increases hermeticity, but the choice of
334+
`False` cases for instance a `pip` or `setuptools` bundled into the interpreter
335+
to be unusable. Many libraries assume these packages will always be available
336+
and may not reliably declare their dependencies such that Bazel will satisfy
337+
them, so choosing isolation could expose packaging errors.
338+
317339
"""
318340
),
319341
})

py/tools/py/src/activate.tmpl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ deactivate () {
4949
fi
5050
}
5151

52-
{{DEBUG}}
53-
5452
# unset irrelevant variables
5553
deactivate nondestructive
5654

py/tools/py/src/pyvenv.cfg.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ home = ./bin/python3
22
implementation = CPython
33
version_info = {{MAJOR}}.{{MINOR}}.{{PATCH}}
44
include-system-site-packages = {{INCLUDE_SYSTEM_SITE}}
5+
aspect-include-user-site-packages = {{INCLUDE_USER_SITE}}
56
relocatable = true
67
{{INTERPRETER}}

py/tools/py/src/venv.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ pub fn create_empty_venv<'a>(
211211
venv_shim: &Option<PathBuf>,
212212
debug: bool,
213213
include_system_site_packages: bool,
214+
include_user_site_packages: bool,
214215
) -> miette::Result<Virtualenv> {
215216
let build_dir = current_dir().into_diagnostic()?;
216217
let home_dir = &build_dir.join(location.to_path_buf());
@@ -245,17 +246,17 @@ pub fn create_empty_venv<'a>(
245246
let using_runfiles_interpreter = !python.exists() && venv_shim != &None;
246247

247248
let interpreter_cfg_snippet = if using_runfiles_interpreter {
248-
&format!(
249+
format!(
249250
"
250251
# Non-standard extension keys used by the Aspect shim
251-
aspect_runfiles_interpreter = {0}
252-
aspect_runfiles_repo = {1}
252+
aspect-runfiles-interpreter = {0}
253+
aspect-runfiles-repo = {1}
253254
",
254255
python.display(),
255256
repo
256257
)
257258
} else {
258-
""
259+
"".to_owned()
259260
};
260261

261262
// Create the `pyvenv.cfg` file
@@ -266,10 +267,14 @@ aspect_runfiles_repo = {1}
266267
.replace("{{MAJOR}}", &venv.version_info.major.to_string())
267268
.replace("{{MINOR}}", &venv.version_info.minor.to_string())
268269
.replace("{{PATCH}}", &venv.version_info.patch.to_string())
269-
.replace("{{INTERPRETER}}", interpreter_cfg_snippet)
270+
.replace("{{INTERPRETER}}", &interpreter_cfg_snippet)
270271
.replace(
271272
"{{INCLUDE_SYSTEM_SITE}}",
272273
&include_system_site_packages.to_string(),
274+
)
275+
.replace(
276+
"{{INCLUDE_USER_SITE}}",
277+
&include_user_site_packages.to_string(),
273278
),
274279
)
275280
.into_diagnostic()?;

py/tools/runfiles/src/lib.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,17 @@ impl Runfiles {
155155
/// RUNFILES_MANIFEST_FILE environment variable is present,
156156
/// or a directory based Runfiles object otherwise.
157157
pub fn create() -> Result<Self> {
158-
let mode = if let Some(manifest_file) = std::env::var_os(MANIFEST_FILE_ENV_VAR) {
159-
Self::create_manifest_based(Path::new(&manifest_file))?
160-
} else {
161-
let dir = find_runfiles_dir()?;
162-
let manifest_path = dir.join("MANIFEST");
163-
match manifest_path.exists() {
164-
true => Self::create_manifest_based(&manifest_path)?,
165-
false => Mode::DirectoryBased(dir),
158+
let mode = match std::env::var_os(MANIFEST_FILE_ENV_VAR) {
159+
Some(manifest_file) if (!manifest_file.is_empty()) => {
160+
Self::create_manifest_based(Path::new(&manifest_file))?
161+
}
162+
_ => {
163+
let dir = find_runfiles_dir()?;
164+
let manifest_path = dir.join("MANIFEST");
165+
match manifest_path.exists() {
166+
true => Self::create_manifest_based(&manifest_path)?,
167+
false => Mode::DirectoryBased(dir),
168+
}
166169
}
167170
};
168171

@@ -179,6 +182,7 @@ impl Runfiles {
179182
}
180183

181184
fn create_manifest_based(manifest_path: &Path) -> Result<Mode> {
185+
eprintln!("{manifest_path:?}");
182186
let manifest_content = std::fs::read_to_string(manifest_path)
183187
.map_err(RunfilesError::RunfilesManifestIoError)?;
184188
let path_mapping = manifest_content
@@ -266,11 +270,11 @@ fn parse_repo_mapping(path: PathBuf) -> Result<RepoMapping> {
266270

267271
/// Returns the .runfiles directory for the currently executing binary.
268272
pub fn find_runfiles_dir() -> Result<PathBuf> {
269-
assert!(
270-
std::env::var_os(MANIFEST_FILE_ENV_VAR).is_none(),
271-
"Unexpected call when {} exists",
272-
MANIFEST_FILE_ENV_VAR
273-
);
273+
// assert!(
274+
// std::env::var_os(MANIFEST_FILE_ENV_VAR).is_none(),
275+
// "Unexpected call when {} exists",
276+
// MANIFEST_FILE_ENV_VAR
277+
// );
274278

275279
// If Bazel told us about the runfiles dir, use that without looking further.
276280
if let Some(runfiles_dir) = std::env::var_os(RUNFILES_DIR_ENV_VAR).map(PathBuf::from) {

py/tools/venv_bin/src/main.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,23 @@ struct VenvArgs {
9494

9595
#[clap(
9696
long,
97-
default_missing_value("true"),
98-
default_value("true"),
97+
default_missing_value("false"),
98+
default_value("false"),
9999
num_args(0..=1),
100100
require_equals(true),
101101
action = ArgAction::Set,
102102
)]
103103
include_system_site_packages: bool,
104+
105+
#[clap(
106+
long,
107+
default_missing_value("false"),
108+
default_value("false"),
109+
num_args(0..=1),
110+
require_equals(true),
111+
action = ArgAction::Set,
112+
)]
113+
include_user_site_packages: bool,
104114
}
105115

106116
fn venv_cmd_handler(args: VenvArgs) -> miette::Result<()> {
@@ -131,6 +141,7 @@ fn venv_cmd_handler(args: VenvArgs) -> miette::Result<()> {
131141
&args.venv_shim,
132142
args.debug,
133143
args.include_system_site_packages,
144+
args.include_user_site_packages,
134145
)?;
135146

136147
py::venv::populate_venv_with_copies(
@@ -160,6 +171,7 @@ fn venv_cmd_handler(args: VenvArgs) -> miette::Result<()> {
160171
&args.venv_shim,
161172
args.debug,
162173
args.include_system_site_packages,
174+
args.include_user_site_packages,
163175
)?;
164176

165177
py::venv::populate_venv_with_pth(

py/tools/venv_shim/src/main.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ struct PyCfg {
4545
cfg: PathBuf,
4646
version_info: String,
4747
interpreter: InterpreterConfig,
48+
user_site: bool,
4849
}
4950

5051
fn parse_venv_cfg(venv_root: &Path, cfg_path: &Path) -> miette::Result<PyCfg> {
@@ -53,6 +54,7 @@ fn parse_venv_cfg(venv_root: &Path, cfg_path: &Path) -> miette::Result<PyCfg> {
5354
let mut version: Option<String> = None;
5455
let mut bazel_interpreter: Option<String> = None;
5556
let mut bazel_repo: Option<String> = None;
57+
let mut user_site: Option<bool> = None;
5658

5759
// FIXME: Errors possible here?
5860
let reader = io::BufReader::new(file);
@@ -66,12 +68,15 @@ fn parse_venv_cfg(venv_root: &Path, cfg_path: &Path) -> miette::Result<PyCfg> {
6668
"version_info" => {
6769
version = Some(value.to_string());
6870
}
69-
"aspect_runfiles_interpreter" => {
71+
"aspect-runfiles-interpreter" => {
7072
bazel_interpreter = Some(value.to_string());
7173
}
72-
"aspect_runfiles_repo" => {
74+
"aspect-runfiles-repo" => {
7375
bazel_repo = Some(value.to_string());
7476
}
77+
"aspect-include-user-site-packages" => {
78+
user_site = Some(value == "true");
79+
}
7580
// We don't care about other keys
7681
&_ => {}
7782
}
@@ -87,6 +92,7 @@ fn parse_venv_cfg(venv_root: &Path, cfg_path: &Path) -> miette::Result<PyCfg> {
8792
rpath: rloc,
8893
repo: repo,
8994
},
95+
user_site: user_site.expect("User site flag not set!"),
9096
}),
9197
// FIXME: Kinda useless copy in this case
9298
(Some(version), None, None) => Ok(PyCfg {
@@ -96,6 +102,7 @@ fn parse_venv_cfg(venv_root: &Path, cfg_path: &Path) -> miette::Result<PyCfg> {
96102
interpreter: InterpreterConfig::External {
97103
version: parse_version_info(&version).unwrap(),
98104
},
105+
user_site: user_site.expect("User site flag not set!"),
99106
}),
100107
(None, _, _) => Err(miette!(
101108
"Invalid pyvenv.cfg file! no interpreter version specified!"
@@ -230,6 +237,12 @@ fn main() -> miette::Result<()> {
230237
// Set the executable pointer for MacOS, but we do it consistently
231238
cmd.env("PYTHONEXECUTABLE", &venv_interpreter);
232239

240+
// Similar to `-s` but this avoids us having to muck with the argv in ways
241+
// that could be visible to the called program.
242+
if !venv_config.user_site {
243+
cmd.env("PYTHONNOUSERSITE", "1");
244+
}
245+
233246
// Set the interpreter home to the resolved install base. This works around
234247
// the home = property in the pyvenv.cfg being wrong because we don't
235248
// (currently) have a good way to map the interpreter rlocation to a

0 commit comments

Comments
 (0)