mirror of https://github.com/astral-sh/ruff
[red-knot] Derive `site-packages` from a venv path (#12716)
This commit is contained in:
parent
14dd6d980e
commit
7fa76a2b2b
|
|
@ -1971,6 +1971,7 @@ dependencies = [
|
||||||
"ruff_python_ast",
|
"ruff_python_ast",
|
||||||
"rustc-hash 2.0.0",
|
"rustc-hash 2.0.0",
|
||||||
"salsa",
|
"salsa",
|
||||||
|
"thiserror",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use std::sync::Mutex;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use crossbeam::channel as crossbeam_channel;
|
use crossbeam::channel as crossbeam_channel;
|
||||||
|
use red_knot_workspace::site_packages::site_packages_dirs_of_venv;
|
||||||
use tracing::subscriber::Interest;
|
use tracing::subscriber::Interest;
|
||||||
use tracing::{Level, Metadata};
|
use tracing::{Level, Metadata};
|
||||||
use tracing_subscriber::filter::LevelFilter;
|
use tracing_subscriber::filter::LevelFilter;
|
||||||
|
|
@ -41,6 +42,17 @@ struct Args {
|
||||||
)]
|
)]
|
||||||
current_directory: Option<SystemPathBuf>,
|
current_directory: Option<SystemPathBuf>,
|
||||||
|
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Path to the virtual environment the project uses",
|
||||||
|
long_help = "\
|
||||||
|
Path to the virtual environment the project uses. \
|
||||||
|
If provided, red-knot will use the `site-packages` directory of this virtual environment \
|
||||||
|
to resolve type information for the project's third-party dependencies.",
|
||||||
|
value_name = "PATH"
|
||||||
|
)]
|
||||||
|
venv_path: Option<SystemPathBuf>,
|
||||||
|
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
value_name = "DIRECTORY",
|
value_name = "DIRECTORY",
|
||||||
|
|
@ -87,6 +99,7 @@ pub fn main() -> anyhow::Result<()> {
|
||||||
current_directory,
|
current_directory,
|
||||||
custom_typeshed_dir,
|
custom_typeshed_dir,
|
||||||
extra_search_path: extra_paths,
|
extra_search_path: extra_paths,
|
||||||
|
venv_path,
|
||||||
target_version,
|
target_version,
|
||||||
verbosity,
|
verbosity,
|
||||||
watch,
|
watch,
|
||||||
|
|
@ -120,6 +133,17 @@ pub fn main() -> anyhow::Result<()> {
|
||||||
let workspace_metadata =
|
let workspace_metadata =
|
||||||
WorkspaceMetadata::from_path(system.current_directory(), &system).unwrap();
|
WorkspaceMetadata::from_path(system.current_directory(), &system).unwrap();
|
||||||
|
|
||||||
|
let site_packages = if let Some(venv_path) = venv_path {
|
||||||
|
let venv_path = system.canonicalize_path(&venv_path).unwrap_or(venv_path);
|
||||||
|
assert!(
|
||||||
|
system.is_directory(&venv_path),
|
||||||
|
"Provided venv-path {venv_path} is not a directory!"
|
||||||
|
);
|
||||||
|
site_packages_dirs_of_venv(&venv_path, &system).unwrap()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: Respect the settings from the workspace metadata. when resolving the program settings.
|
// TODO: Respect the settings from the workspace metadata. when resolving the program settings.
|
||||||
let program_settings = ProgramSettings {
|
let program_settings = ProgramSettings {
|
||||||
target_version: target_version.into(),
|
target_version: target_version.into(),
|
||||||
|
|
@ -127,7 +151,7 @@ pub fn main() -> anyhow::Result<()> {
|
||||||
extra_paths,
|
extra_paths,
|
||||||
src_root: workspace_metadata.root().to_path_buf(),
|
src_root: workspace_metadata.root().to_path_buf(),
|
||||||
custom_typeshed: custom_typeshed_dir,
|
custom_typeshed: custom_typeshed_dir,
|
||||||
site_packages: vec![],
|
site_packages,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -446,6 +446,12 @@ impl SearchPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new search path pointing to the `site-packages` directory on disk
|
/// Create a new search path pointing to the `site-packages` directory on disk
|
||||||
|
///
|
||||||
|
/// TODO: the validation done here is somewhat redundant given that `site-packages`
|
||||||
|
/// are already validated at a higher level by the time we get here.
|
||||||
|
/// However, removing the validation here breaks some file-watching tests -- and
|
||||||
|
/// ultimately we'll probably want all search paths to be validated before a
|
||||||
|
/// `Program` is instantiated, so it doesn't seem like a huge priority right now.
|
||||||
pub(crate) fn site_packages(
|
pub(crate) fn site_packages(
|
||||||
system: &dyn System,
|
system: &dyn System,
|
||||||
root: SystemPathBuf,
|
root: SystemPathBuf,
|
||||||
|
|
|
||||||
|
|
@ -128,12 +128,16 @@ fn try_resolve_module_resolution_settings(
|
||||||
site_packages,
|
site_packages,
|
||||||
} = program.search_paths(db.upcast());
|
} = program.search_paths(db.upcast());
|
||||||
|
|
||||||
|
if !extra_paths.is_empty() {
|
||||||
|
tracing::info!("Extra search paths: {extra_paths:?}");
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(custom_typeshed) = custom_typeshed {
|
if let Some(custom_typeshed) = custom_typeshed {
|
||||||
tracing::info!("Custom typeshed directory: {custom_typeshed}");
|
tracing::info!("Custom typeshed directory: {custom_typeshed}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !extra_paths.is_empty() {
|
if !site_packages.is_empty() {
|
||||||
tracing::info!("extra search paths: {extra_paths:?}");
|
tracing::info!("Site-packages directories: {site_packages:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let system = db.system();
|
let system = db.system();
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ crossbeam = { workspace = true }
|
||||||
notify = { workspace = true }
|
notify = { workspace = true }
|
||||||
rustc-hash = { workspace = true }
|
rustc-hash = { workspace = true }
|
||||||
salsa = { workspace = true }
|
salsa = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
*
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Signature: 8a477f597d28d172789f06886806bc55
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
# Copyright (c) 2020-202x The virtualenv developers
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
# a copy of this software and associated documentation files (the
|
||||||
|
# "Software"), to deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
# the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be
|
||||||
|
# included in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
# This file must be used with "source bin/activate" *from bash*
|
||||||
|
# you cannot run it directly
|
||||||
|
|
||||||
|
|
||||||
|
if [ "${BASH_SOURCE-}" = "$0" ]; then
|
||||||
|
echo "You must source this script: \$ source $0" >&2
|
||||||
|
exit 33
|
||||||
|
fi
|
||||||
|
|
||||||
|
deactivate () {
|
||||||
|
unset -f pydoc >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# reset old environment variables
|
||||||
|
# ! [ -z ${VAR+_} ] returns true if VAR is declared at all
|
||||||
|
if ! [ -z "${_OLD_VIRTUAL_PATH:+_}" ] ; then
|
||||||
|
PATH="$_OLD_VIRTUAL_PATH"
|
||||||
|
export PATH
|
||||||
|
unset _OLD_VIRTUAL_PATH
|
||||||
|
fi
|
||||||
|
if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then
|
||||||
|
PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME"
|
||||||
|
export PYTHONHOME
|
||||||
|
unset _OLD_VIRTUAL_PYTHONHOME
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The hash command must be called to get it to forget past
|
||||||
|
# commands. Without forgetting past commands the $PATH changes
|
||||||
|
# we made may not be respected
|
||||||
|
hash -r 2>/dev/null
|
||||||
|
|
||||||
|
if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then
|
||||||
|
PS1="$_OLD_VIRTUAL_PS1"
|
||||||
|
export PS1
|
||||||
|
unset _OLD_VIRTUAL_PS1
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset VIRTUAL_ENV
|
||||||
|
unset VIRTUAL_ENV_PROMPT
|
||||||
|
if [ ! "${1-}" = "nondestructive" ] ; then
|
||||||
|
# Self destruct!
|
||||||
|
unset -f deactivate
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# unset irrelevant variables
|
||||||
|
deactivate nondestructive
|
||||||
|
|
||||||
|
VIRTUAL_ENV='/Users/alexw/dev/ruff/crates/red_knot_workspace/resources/test/empty-test-venv'
|
||||||
|
if ([ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]) && $(command -v cygpath &> /dev/null) ; then
|
||||||
|
VIRTUAL_ENV=$(cygpath -u "$VIRTUAL_ENV")
|
||||||
|
fi
|
||||||
|
export VIRTUAL_ENV
|
||||||
|
|
||||||
|
_OLD_VIRTUAL_PATH="$PATH"
|
||||||
|
PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
if [ "x" != x ] ; then
|
||||||
|
VIRTUAL_ENV_PROMPT=""
|
||||||
|
else
|
||||||
|
VIRTUAL_ENV_PROMPT=$(basename "$VIRTUAL_ENV")
|
||||||
|
fi
|
||||||
|
export VIRTUAL_ENV_PROMPT
|
||||||
|
|
||||||
|
# unset PYTHONHOME if set
|
||||||
|
if ! [ -z "${PYTHONHOME+_}" ] ; then
|
||||||
|
_OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME"
|
||||||
|
unset PYTHONHOME
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then
|
||||||
|
_OLD_VIRTUAL_PS1="${PS1-}"
|
||||||
|
PS1="(${VIRTUAL_ENV_PROMPT}) ${PS1-}"
|
||||||
|
export PS1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make sure to unalias pydoc if it's already there
|
||||||
|
alias pydoc 2>/dev/null >/dev/null && unalias pydoc || true
|
||||||
|
|
||||||
|
pydoc () {
|
||||||
|
python -m pydoc "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# The hash command must be called to get it to forget past
|
||||||
|
# commands. Without forgetting past commands the $PATH changes
|
||||||
|
# we made may not be respected
|
||||||
|
hash -r 2>/dev/null
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
@REM Copyright (c) 2020-202x The virtualenv developers
|
||||||
|
@REM
|
||||||
|
@REM Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
@REM a copy of this software and associated documentation files (the
|
||||||
|
@REM "Software"), to deal in the Software without restriction, including
|
||||||
|
@REM without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
@REM distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
@REM permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
@REM the following conditions:
|
||||||
|
@REM
|
||||||
|
@REM The above copyright notice and this permission notice shall be
|
||||||
|
@REM included in all copies or substantial portions of the Software.
|
||||||
|
@REM
|
||||||
|
@REM THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
@REM EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
@REM MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
@REM NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
@REM LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
@REM OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
@REM WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
@for %%i in ("/Users/alexw/dev/ruff/crates/red_knot_workspace/resources/test/empty-test-venv") do @set "VIRTUAL_ENV=%%~fi"
|
||||||
|
|
||||||
|
@set "VIRTUAL_ENV_PROMPT="
|
||||||
|
@if NOT DEFINED VIRTUAL_ENV_PROMPT (
|
||||||
|
@for %%d in ("%VIRTUAL_ENV%") do @set "VIRTUAL_ENV_PROMPT=%%~nxd"
|
||||||
|
)
|
||||||
|
|
||||||
|
@if defined _OLD_VIRTUAL_PROMPT (
|
||||||
|
@set "PROMPT=%_OLD_VIRTUAL_PROMPT%"
|
||||||
|
) else (
|
||||||
|
@if not defined PROMPT (
|
||||||
|
@set "PROMPT=$P$G"
|
||||||
|
)
|
||||||
|
@if not defined VIRTUAL_ENV_DISABLE_PROMPT (
|
||||||
|
@set "_OLD_VIRTUAL_PROMPT=%PROMPT%"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@if not defined VIRTUAL_ENV_DISABLE_PROMPT (
|
||||||
|
@set "PROMPT=(%VIRTUAL_ENV_PROMPT%) %PROMPT%"
|
||||||
|
)
|
||||||
|
|
||||||
|
@REM Don't use () to avoid problems with them in %PATH%
|
||||||
|
@if defined _OLD_VIRTUAL_PYTHONHOME @goto ENDIFVHOME
|
||||||
|
@set "_OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME%"
|
||||||
|
:ENDIFVHOME
|
||||||
|
|
||||||
|
@set PYTHONHOME=
|
||||||
|
|
||||||
|
@REM if defined _OLD_VIRTUAL_PATH (
|
||||||
|
@if not defined _OLD_VIRTUAL_PATH @goto ENDIFVPATH1
|
||||||
|
@set "PATH=%_OLD_VIRTUAL_PATH%"
|
||||||
|
:ENDIFVPATH1
|
||||||
|
@REM ) else (
|
||||||
|
@if defined _OLD_VIRTUAL_PATH @goto ENDIFVPATH2
|
||||||
|
@set "_OLD_VIRTUAL_PATH=%PATH%"
|
||||||
|
:ENDIFVPATH2
|
||||||
|
|
||||||
|
@set "PATH=%VIRTUAL_ENV%\bin;%PATH%"
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
# Copyright (c) 2020-202x The virtualenv developers
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
# a copy of this software and associated documentation files (the
|
||||||
|
# "Software"), to deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
# the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be
|
||||||
|
# included in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
# This file must be used with "source bin/activate.csh" *from csh*.
|
||||||
|
# You cannot run it directly.
|
||||||
|
# Created by Davide Di Blasi <davidedb@gmail.com>.
|
||||||
|
|
||||||
|
set newline='\
|
||||||
|
'
|
||||||
|
|
||||||
|
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH:q" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT:q" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc'
|
||||||
|
|
||||||
|
# Unset irrelevant variables.
|
||||||
|
deactivate nondestructive
|
||||||
|
|
||||||
|
setenv VIRTUAL_ENV '/Users/alexw/dev/ruff/crates/red_knot_workspace/resources/test/empty-test-venv'
|
||||||
|
|
||||||
|
set _OLD_VIRTUAL_PATH="$PATH:q"
|
||||||
|
setenv PATH "$VIRTUAL_ENV:q/bin:$PATH:q"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if ('' != "") then
|
||||||
|
setenv VIRTUAL_ENV_PROMPT ''
|
||||||
|
else
|
||||||
|
setenv VIRTUAL_ENV_PROMPT "$VIRTUAL_ENV:t:q"
|
||||||
|
endif
|
||||||
|
|
||||||
|
if ( $?VIRTUAL_ENV_DISABLE_PROMPT ) then
|
||||||
|
if ( $VIRTUAL_ENV_DISABLE_PROMPT == "" ) then
|
||||||
|
set do_prompt = "1"
|
||||||
|
else
|
||||||
|
set do_prompt = "0"
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
set do_prompt = "1"
|
||||||
|
endif
|
||||||
|
|
||||||
|
if ( $do_prompt == "1" ) then
|
||||||
|
# Could be in a non-interactive environment,
|
||||||
|
# in which case, $prompt is undefined and we wouldn't
|
||||||
|
# care about the prompt anyway.
|
||||||
|
if ( $?prompt ) then
|
||||||
|
set _OLD_VIRTUAL_PROMPT="$prompt:q"
|
||||||
|
if ( "$prompt:q" =~ *"$newline:q"* ) then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
set prompt = '('"$VIRTUAL_ENV_PROMPT:q"') '"$prompt:q"
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
unset env_name
|
||||||
|
unset do_prompt
|
||||||
|
|
||||||
|
alias pydoc python -m pydoc
|
||||||
|
|
||||||
|
rehash
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
# Copyright (c) 2020-202x The virtualenv developers
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
# a copy of this software and associated documentation files (the
|
||||||
|
# "Software"), to deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
# the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be
|
||||||
|
# included in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
# This file must be used using `source bin/activate.fish` *within a running fish ( http://fishshell.com ) session*.
|
||||||
|
# Do not run it directly.
|
||||||
|
|
||||||
|
function _bashify_path -d "Converts a fish path to something bash can recognize"
|
||||||
|
set fishy_path $argv
|
||||||
|
set bashy_path $fishy_path[1]
|
||||||
|
for path_part in $fishy_path[2..-1]
|
||||||
|
set bashy_path "$bashy_path:$path_part"
|
||||||
|
end
|
||||||
|
echo $bashy_path
|
||||||
|
end
|
||||||
|
|
||||||
|
function _fishify_path -d "Converts a bash path to something fish can recognize"
|
||||||
|
echo $argv | tr ':' '\n'
|
||||||
|
end
|
||||||
|
|
||||||
|
function deactivate -d 'Exit virtualenv mode and return to the normal environment.'
|
||||||
|
# reset old environment variables
|
||||||
|
if test -n "$_OLD_VIRTUAL_PATH"
|
||||||
|
# https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling
|
||||||
|
if test (echo $FISH_VERSION | head -c 1) -lt 3
|
||||||
|
set -gx PATH (_fishify_path "$_OLD_VIRTUAL_PATH")
|
||||||
|
else
|
||||||
|
set -gx PATH $_OLD_VIRTUAL_PATH
|
||||||
|
end
|
||||||
|
set -e _OLD_VIRTUAL_PATH
|
||||||
|
end
|
||||||
|
|
||||||
|
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
|
||||||
|
set -gx PYTHONHOME "$_OLD_VIRTUAL_PYTHONHOME"
|
||||||
|
set -e _OLD_VIRTUAL_PYTHONHOME
|
||||||
|
end
|
||||||
|
|
||||||
|
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
|
||||||
|
and functions -q _old_fish_prompt
|
||||||
|
# Set an empty local `$fish_function_path` to allow the removal of `fish_prompt` using `functions -e`.
|
||||||
|
set -l fish_function_path
|
||||||
|
|
||||||
|
# Erase virtualenv's `fish_prompt` and restore the original.
|
||||||
|
functions -e fish_prompt
|
||||||
|
functions -c _old_fish_prompt fish_prompt
|
||||||
|
functions -e _old_fish_prompt
|
||||||
|
set -e _OLD_FISH_PROMPT_OVERRIDE
|
||||||
|
end
|
||||||
|
|
||||||
|
set -e VIRTUAL_ENV
|
||||||
|
set -e VIRTUAL_ENV_PROMPT
|
||||||
|
|
||||||
|
if test "$argv[1]" != 'nondestructive'
|
||||||
|
# Self-destruct!
|
||||||
|
functions -e pydoc
|
||||||
|
functions -e deactivate
|
||||||
|
functions -e _bashify_path
|
||||||
|
functions -e _fishify_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Unset irrelevant variables.
|
||||||
|
deactivate nondestructive
|
||||||
|
|
||||||
|
set -gx VIRTUAL_ENV '/Users/alexw/dev/ruff/crates/red_knot_workspace/resources/test/empty-test-venv'
|
||||||
|
|
||||||
|
# https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling
|
||||||
|
if test (echo $FISH_VERSION | head -c 1) -lt 3
|
||||||
|
set -gx _OLD_VIRTUAL_PATH (_bashify_path $PATH)
|
||||||
|
else
|
||||||
|
set -gx _OLD_VIRTUAL_PATH $PATH
|
||||||
|
end
|
||||||
|
set -gx PATH "$VIRTUAL_ENV"'/bin' $PATH
|
||||||
|
|
||||||
|
# Prompt override provided?
|
||||||
|
# If not, just use the environment name.
|
||||||
|
if test -n ''
|
||||||
|
set -gx VIRTUAL_ENV_PROMPT ''
|
||||||
|
else
|
||||||
|
set -gx VIRTUAL_ENV_PROMPT (basename "$VIRTUAL_ENV")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Unset `$PYTHONHOME` if set.
|
||||||
|
if set -q PYTHONHOME
|
||||||
|
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
|
||||||
|
set -e PYTHONHOME
|
||||||
|
end
|
||||||
|
|
||||||
|
function pydoc
|
||||||
|
python -m pydoc $argv
|
||||||
|
end
|
||||||
|
|
||||||
|
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
|
||||||
|
# Copy the current `fish_prompt` function as `_old_fish_prompt`.
|
||||||
|
functions -c fish_prompt _old_fish_prompt
|
||||||
|
|
||||||
|
function fish_prompt
|
||||||
|
# Run the user's prompt first; it might depend on (pipe)status.
|
||||||
|
set -l prompt (_old_fish_prompt)
|
||||||
|
|
||||||
|
printf '(%s) ' $VIRTUAL_ENV_PROMPT
|
||||||
|
|
||||||
|
string join -- \n $prompt # handle multi-line prompts
|
||||||
|
end
|
||||||
|
|
||||||
|
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
# Copyright (c) 2020-202x The virtualenv developers
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
# a copy of this software and associated documentation files (the
|
||||||
|
# "Software"), to deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
# the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be
|
||||||
|
# included in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
# virtualenv activation module
|
||||||
|
# Activate with `overlay use activate.nu`
|
||||||
|
# Deactivate with `deactivate`, as usual
|
||||||
|
#
|
||||||
|
# To customize the overlay name, you can call `overlay use activate.nu as foo`,
|
||||||
|
# but then simply `deactivate` won't work because it is just an alias to hide
|
||||||
|
# the "activate" overlay. You'd need to call `overlay hide foo` manually.
|
||||||
|
|
||||||
|
export-env {
|
||||||
|
def is-string [x] {
|
||||||
|
($x | describe) == 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
def has-env [...names] {
|
||||||
|
$names | each {|n|
|
||||||
|
$n in $env
|
||||||
|
} | all {|i| $i == true}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Emulates a `test -z`, but btter as it handles e.g 'false'
|
||||||
|
def is-env-true [name: string] {
|
||||||
|
if (has-env $name) {
|
||||||
|
# Try to parse 'true', '0', '1', and fail if not convertible
|
||||||
|
let parsed = (do -i { $env | get $name | into bool })
|
||||||
|
if ($parsed | describe) == 'bool' {
|
||||||
|
$parsed
|
||||||
|
} else {
|
||||||
|
not ($env | get -i $name | is-empty)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let virtual_env = '/Users/alexw/dev/ruff/crates/red_knot_workspace/resources/test/empty-test-venv'
|
||||||
|
let bin = 'bin'
|
||||||
|
|
||||||
|
let is_windows = ($nu.os-info.family) == 'windows'
|
||||||
|
let path_name = (if (has-env 'Path') {
|
||||||
|
'Path'
|
||||||
|
} else {
|
||||||
|
'PATH'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let venv_path = ([$virtual_env $bin] | path join)
|
||||||
|
let new_path = ($env | get $path_name | prepend $venv_path)
|
||||||
|
|
||||||
|
# If there is no default prompt, then use the env name instead
|
||||||
|
let virtual_env_prompt = (if ('' | is-empty) {
|
||||||
|
($virtual_env | path basename)
|
||||||
|
} else {
|
||||||
|
''
|
||||||
|
})
|
||||||
|
|
||||||
|
let new_env = {
|
||||||
|
$path_name : $new_path
|
||||||
|
VIRTUAL_ENV : $virtual_env
|
||||||
|
VIRTUAL_ENV_PROMPT : $virtual_env_prompt
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_env = (if (is-env-true 'VIRTUAL_ENV_DISABLE_PROMPT') {
|
||||||
|
$new_env
|
||||||
|
} else {
|
||||||
|
# Creating the new prompt for the session
|
||||||
|
let virtual_prefix = $'(char lparen)($virtual_env_prompt)(char rparen) '
|
||||||
|
|
||||||
|
# Back up the old prompt builder
|
||||||
|
let old_prompt_command = (if (has-env 'PROMPT_COMMAND') {
|
||||||
|
$env.PROMPT_COMMAND
|
||||||
|
} else {
|
||||||
|
''
|
||||||
|
})
|
||||||
|
|
||||||
|
let new_prompt = (if (has-env 'PROMPT_COMMAND') {
|
||||||
|
if 'closure' in ($old_prompt_command | describe) {
|
||||||
|
{|| $'($virtual_prefix)(do $old_prompt_command)' }
|
||||||
|
} else {
|
||||||
|
{|| $'($virtual_prefix)($old_prompt_command)' }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
{|| $'($virtual_prefix)' }
|
||||||
|
})
|
||||||
|
|
||||||
|
$new_env | merge {
|
||||||
|
PROMPT_COMMAND : $new_prompt
|
||||||
|
VIRTUAL_PREFIX : $virtual_prefix
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Environment variables that will be loaded as the virtual env
|
||||||
|
load-env $new_env
|
||||||
|
}
|
||||||
|
|
||||||
|
export alias pydoc = python -m pydoc
|
||||||
|
export alias deactivate = overlay hide activate
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
# Copyright (c) 2020-202x The virtualenv developers
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
# a copy of this software and associated documentation files (the
|
||||||
|
# "Software"), to deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
# the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be
|
||||||
|
# included in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
$script:THIS_PATH = $myinvocation.mycommand.path
|
||||||
|
$script:BASE_DIR = Split-Path (Resolve-Path "$THIS_PATH/..") -Parent
|
||||||
|
|
||||||
|
function global:deactivate([switch] $NonDestructive) {
|
||||||
|
if (Test-Path variable:_OLD_VIRTUAL_PATH) {
|
||||||
|
$env:PATH = $variable:_OLD_VIRTUAL_PATH
|
||||||
|
Remove-Variable "_OLD_VIRTUAL_PATH" -Scope global
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path function:_old_virtual_prompt) {
|
||||||
|
$function:prompt = $function:_old_virtual_prompt
|
||||||
|
Remove-Item function:\_old_virtual_prompt
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($env:VIRTUAL_ENV) {
|
||||||
|
Remove-Item env:VIRTUAL_ENV -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($env:VIRTUAL_ENV_PROMPT) {
|
||||||
|
Remove-Item env:VIRTUAL_ENV_PROMPT -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$NonDestructive) {
|
||||||
|
# Self destruct!
|
||||||
|
Remove-Item function:deactivate
|
||||||
|
Remove-Item function:pydoc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function global:pydoc {
|
||||||
|
python -m pydoc $args
|
||||||
|
}
|
||||||
|
|
||||||
|
# unset irrelevant variables
|
||||||
|
deactivate -nondestructive
|
||||||
|
|
||||||
|
$VIRTUAL_ENV = $BASE_DIR
|
||||||
|
$env:VIRTUAL_ENV = $VIRTUAL_ENV
|
||||||
|
|
||||||
|
if ("" -ne "") {
|
||||||
|
$env:VIRTUAL_ENV_PROMPT = ""
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$env:VIRTUAL_ENV_PROMPT = $( Split-Path $env:VIRTUAL_ENV -Leaf )
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Variable -Scope global -Name _OLD_VIRTUAL_PATH -Value $env:PATH
|
||||||
|
|
||||||
|
$env:PATH = "$env:VIRTUAL_ENV/bin:" + $env:PATH
|
||||||
|
if (!$env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
||||||
|
function global:_old_virtual_prompt {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
$function:_old_virtual_prompt = $function:prompt
|
||||||
|
|
||||||
|
function global:prompt {
|
||||||
|
# Add the custom prefix to the existing prompt
|
||||||
|
$previous_prompt_value = & $function:_old_virtual_prompt
|
||||||
|
("(" + $env:VIRTUAL_ENV_PROMPT + ") " + $previous_prompt_value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
# Copyright (c) 2020-202x The virtualenv developers
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
# a copy of this software and associated documentation files (the
|
||||||
|
# "Software"), to deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
# the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be
|
||||||
|
# included in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Activate virtualenv for current interpreter:
|
||||||
|
|
||||||
|
import runpy
|
||||||
|
runpy.run_path(this_file)
|
||||||
|
|
||||||
|
This can be used when you must use an existing Python interpreter, not the virtualenv bin/python.
|
||||||
|
""" # noqa: D415
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import site
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
abs_file = os.path.abspath(__file__)
|
||||||
|
except NameError as exc:
|
||||||
|
msg = "You must use import runpy; runpy.run_path(this_file)"
|
||||||
|
raise AssertionError(msg) from exc
|
||||||
|
|
||||||
|
bin_dir = os.path.dirname(abs_file)
|
||||||
|
base = bin_dir[: -len("bin") - 1] # strip away the bin part from the __file__, plus the path separator
|
||||||
|
|
||||||
|
# prepend bin to PATH (this file is inside the bin directory)
|
||||||
|
os.environ["PATH"] = os.pathsep.join([bin_dir, *os.environ.get("PATH", "").split(os.pathsep)])
|
||||||
|
os.environ["VIRTUAL_ENV"] = base # virtual env is right above bin directory
|
||||||
|
os.environ["VIRTUAL_ENV_PROMPT"] = "" or os.path.basename(base) # noqa: SIM222
|
||||||
|
|
||||||
|
# add the virtual environments libraries to the host python import mechanism
|
||||||
|
prev_length = len(sys.path)
|
||||||
|
for lib in "../lib/python3.12/site-packages".split(os.pathsep):
|
||||||
|
path = os.path.realpath(os.path.join(bin_dir, lib))
|
||||||
|
site.addsitedir(path)
|
||||||
|
sys.path[:] = sys.path[prev_length:] + sys.path[0:prev_length]
|
||||||
|
|
||||||
|
sys.real_prefix = sys.prefix
|
||||||
|
sys.prefix = base
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
@REM Copyright (c) 2020-202x The virtualenv developers
|
||||||
|
@REM
|
||||||
|
@REM Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
@REM a copy of this software and associated documentation files (the
|
||||||
|
@REM "Software"), to deal in the Software without restriction, including
|
||||||
|
@REM without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
@REM distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
@REM permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
@REM the following conditions:
|
||||||
|
@REM
|
||||||
|
@REM The above copyright notice and this permission notice shall be
|
||||||
|
@REM included in all copies or substantial portions of the Software.
|
||||||
|
@REM
|
||||||
|
@REM THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
@REM EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
@REM MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
@REM NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
@REM LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
@REM OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
@REM WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
@set VIRTUAL_ENV=
|
||||||
|
@set VIRTUAL_ENV_PROMPT=
|
||||||
|
|
||||||
|
@REM Don't use () to avoid problems with them in %PATH%
|
||||||
|
@if not defined _OLD_VIRTUAL_PROMPT @goto ENDIFVPROMPT
|
||||||
|
@set "PROMPT=%_OLD_VIRTUAL_PROMPT%"
|
||||||
|
@set _OLD_VIRTUAL_PROMPT=
|
||||||
|
:ENDIFVPROMPT
|
||||||
|
|
||||||
|
@if not defined _OLD_VIRTUAL_PYTHONHOME @goto ENDIFVHOME
|
||||||
|
@set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%"
|
||||||
|
@set _OLD_VIRTUAL_PYTHONHOME=
|
||||||
|
:ENDIFVHOME
|
||||||
|
|
||||||
|
@if not defined _OLD_VIRTUAL_PATH @goto ENDIFVPATH
|
||||||
|
@set "PATH=%_OLD_VIRTUAL_PATH%"
|
||||||
|
@set _OLD_VIRTUAL_PATH=
|
||||||
|
:ENDIFVPATH
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
@REM Copyright (c) 2020-202x The virtualenv developers
|
||||||
|
@REM
|
||||||
|
@REM Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
@REM a copy of this software and associated documentation files (the
|
||||||
|
@REM "Software"), to deal in the Software without restriction, including
|
||||||
|
@REM without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
@REM distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
@REM permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
@REM the following conditions:
|
||||||
|
@REM
|
||||||
|
@REM The above copyright notice and this permission notice shall be
|
||||||
|
@REM included in all copies or substantial portions of the Software.
|
||||||
|
@REM
|
||||||
|
@REM THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
@REM EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
@REM MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
@REM NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
@REM LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
@REM OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
@REM WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
python.exe -m pydoc %*
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
/Users/alexw/.pyenv/versions/3.12.4/bin/python3.12
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
python
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
python
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
import _virtualenv
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
"""Patches that are applied at runtime to the virtual environment."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
VIRTUALENV_PATCH_FILE = os.path.join(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
def patch_dist(dist):
|
||||||
|
"""
|
||||||
|
Distutils allows user to configure some arguments via a configuration file:
|
||||||
|
https://docs.python.org/3/install/index.html#distutils-configuration-files.
|
||||||
|
|
||||||
|
Some of this arguments though don't make sense in context of the virtual environment files, let's fix them up.
|
||||||
|
""" # noqa: D205
|
||||||
|
# we cannot allow some install config as that would get packages installed outside of the virtual environment
|
||||||
|
old_parse_config_files = dist.Distribution.parse_config_files
|
||||||
|
|
||||||
|
def parse_config_files(self, *args, **kwargs):
|
||||||
|
result = old_parse_config_files(self, *args, **kwargs)
|
||||||
|
install = self.get_option_dict("install")
|
||||||
|
|
||||||
|
if "prefix" in install: # the prefix governs where to install the libraries
|
||||||
|
install["prefix"] = VIRTUALENV_PATCH_FILE, os.path.abspath(sys.prefix)
|
||||||
|
for base in ("purelib", "platlib", "headers", "scripts", "data"):
|
||||||
|
key = f"install_{base}"
|
||||||
|
if key in install: # do not allow global configs to hijack venv paths
|
||||||
|
install.pop(key, None)
|
||||||
|
return result
|
||||||
|
|
||||||
|
dist.Distribution.parse_config_files = parse_config_files
|
||||||
|
|
||||||
|
|
||||||
|
# Import hook that patches some modules to ignore configuration values that break package installation in case
|
||||||
|
# of virtual environments.
|
||||||
|
_DISTUTILS_PATCH = "distutils.dist", "setuptools.dist"
|
||||||
|
# https://docs.python.org/3/library/importlib.html#setting-up-an-importer
|
||||||
|
|
||||||
|
|
||||||
|
class _Finder:
|
||||||
|
"""A meta path finder that allows patching the imported distutils modules."""
|
||||||
|
|
||||||
|
fullname = None
|
||||||
|
|
||||||
|
# lock[0] is threading.Lock(), but initialized lazily to avoid importing threading very early at startup,
|
||||||
|
# because there are gevent-based applications that need to be first to import threading by themselves.
|
||||||
|
# See https://github.com/pypa/virtualenv/issues/1895 for details.
|
||||||
|
lock = [] # noqa: RUF012
|
||||||
|
|
||||||
|
def find_spec(self, fullname, path, target=None): # noqa: ARG002
|
||||||
|
if fullname in _DISTUTILS_PATCH and self.fullname is None:
|
||||||
|
# initialize lock[0] lazily
|
||||||
|
if len(self.lock) == 0:
|
||||||
|
import threading
|
||||||
|
|
||||||
|
lock = threading.Lock()
|
||||||
|
# there is possibility that two threads T1 and T2 are simultaneously running into find_spec,
|
||||||
|
# observing .lock as empty, and further going into hereby initialization. However due to the GIL,
|
||||||
|
# list.append() operation is atomic and this way only one of the threads will "win" to put the lock
|
||||||
|
# - that every thread will use - into .lock[0].
|
||||||
|
# https://docs.python.org/3/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe
|
||||||
|
self.lock.append(lock)
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
from importlib.util import find_spec
|
||||||
|
|
||||||
|
with self.lock[0]:
|
||||||
|
self.fullname = fullname
|
||||||
|
try:
|
||||||
|
spec = find_spec(fullname, path)
|
||||||
|
if spec is not None:
|
||||||
|
# https://www.python.org/dev/peps/pep-0451/#how-loading-will-work
|
||||||
|
is_new_api = hasattr(spec.loader, "exec_module")
|
||||||
|
func_name = "exec_module" if is_new_api else "load_module"
|
||||||
|
old = getattr(spec.loader, func_name)
|
||||||
|
func = self.exec_module if is_new_api else self.load_module
|
||||||
|
if old is not func:
|
||||||
|
try: # noqa: SIM105
|
||||||
|
setattr(spec.loader, func_name, partial(func, old))
|
||||||
|
except AttributeError:
|
||||||
|
pass # C-Extension loaders are r/o such as zipimporter with <3.7
|
||||||
|
return spec
|
||||||
|
finally:
|
||||||
|
self.fullname = None
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def exec_module(old, module):
|
||||||
|
old(module)
|
||||||
|
if module.__name__ in _DISTUTILS_PATCH:
|
||||||
|
patch_dist(module)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_module(old, name):
|
||||||
|
module = old(name)
|
||||||
|
if module.__name__ in _DISTUTILS_PATCH:
|
||||||
|
patch_dist(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
sys.meta_path.insert(0, _Finder())
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
home = /Users/alexw/.pyenv/versions/3.12.4/bin
|
||||||
|
implementation = CPython
|
||||||
|
uv = 0.2.32
|
||||||
|
version_info = 3.12.4
|
||||||
|
include-system-site-packages = false
|
||||||
|
relocatable = false
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod lint;
|
pub mod lint;
|
||||||
|
pub mod site_packages;
|
||||||
pub mod watch;
|
pub mod watch;
|
||||||
pub mod workspace;
|
pub mod workspace;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
//! Utilities for finding the `site-packages` directory,
|
||||||
|
//! into which third-party packages are installed.
|
||||||
|
//!
|
||||||
|
//! The routines exposed by this module have different behaviour depending
|
||||||
|
//! on the platform of the *host machine*, which may be
|
||||||
|
//! different from the *target platform for type checking*. (A user
|
||||||
|
//! might be running red-knot on a Windows machine, but might
|
||||||
|
//! reasonably ask us to type-check code assuming that the code runs
|
||||||
|
//! on Linux.)
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
use ruff_db::system::{System, SystemPath, SystemPathBuf};
|
||||||
|
|
||||||
|
/// Attempt to retrieve the `site-packages` directory
|
||||||
|
/// associated with a given Python installation.
|
||||||
|
///
|
||||||
|
/// `sys_prefix_path` is equivalent to the value of [`sys.prefix`]
|
||||||
|
/// at runtime in Python. For the case of a virtual environment, where a
|
||||||
|
/// Python binary is at `/.venv/bin/python`, `sys.prefix` is the path to
|
||||||
|
/// the virtual environment the Python binary lies inside, i.e. `/.venv`,
|
||||||
|
/// and `site-packages` will be at `.venv/Lib/site-packages`. System
|
||||||
|
/// Python installations generally work the same way: if a system Python
|
||||||
|
/// installation lies at `/opt/homebrew/bin/python`, `sys.prefix` will be
|
||||||
|
/// `/opt/homebrew`, and `site-packages` will be at
|
||||||
|
/// `/opt/homebrew/Lib/site-packages`.
|
||||||
|
///
|
||||||
|
/// This routine does not verify that `sys_prefix_path` points
|
||||||
|
/// to an existing directory on disk; it is assumed that this has already
|
||||||
|
/// been checked.
|
||||||
|
///
|
||||||
|
/// [`sys.prefix`]: https://docs.python.org/3/library/sys.html#sys.prefix
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn site_packages_dir_from_sys_prefix(
|
||||||
|
sys_prefix_path: &SystemPath,
|
||||||
|
system: &dyn System,
|
||||||
|
) -> Result<SystemPathBuf, SitePackagesDiscoveryError> {
|
||||||
|
let site_packages = venv_path.join("Lib/site-packages");
|
||||||
|
system
|
||||||
|
.is_directory(&site_packages)
|
||||||
|
.then_some(site_packages)
|
||||||
|
.ok_or(SitePackagesDiscoveryError::NoSitePackagesDirFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to retrieve the `site-packages` directory
|
||||||
|
/// associated with a given Python installation.
|
||||||
|
///
|
||||||
|
/// `sys_prefix_path` is equivalent to the value of [`sys.prefix`]
|
||||||
|
/// at runtime in Python. For the case of a virtual environment, where a
|
||||||
|
/// Python binary is at `/.venv/bin/python`, `sys.prefix` is the path to
|
||||||
|
/// the virtual environment the Python binary lies inside, i.e. `/.venv`,
|
||||||
|
/// and `site-packages` will be at `.venv/lib/python3.X/site-packages`.
|
||||||
|
/// System Python installations generally work the same way: if a system
|
||||||
|
/// Python installation lies at `/opt/homebrew/bin/python`, `sys.prefix`
|
||||||
|
/// will be `/opt/homebrew`, and `site-packages` will be at
|
||||||
|
/// `/opt/homebrew/lib/python3.X/site-packages`.
|
||||||
|
///
|
||||||
|
/// This routine does not verify that `sys_prefix_path` points
|
||||||
|
/// to an existing directory on disk; it is assumed that this has already
|
||||||
|
/// been checked.
|
||||||
|
///
|
||||||
|
/// [`sys.prefix`]: https://docs.python.org/3/library/sys.html#sys.prefix
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn site_packages_dir_from_sys_prefix(
|
||||||
|
sys_prefix_path: &SystemPath,
|
||||||
|
system: &dyn System,
|
||||||
|
) -> Result<SystemPathBuf, SitePackagesDiscoveryError> {
|
||||||
|
// In the Python standard library's `site.py` module (used for finding `site-packages`
|
||||||
|
// at runtime), we can find this in [the non-Windows branch]:
|
||||||
|
//
|
||||||
|
// ```py
|
||||||
|
// libdirs = [sys.platlibdir]
|
||||||
|
// if sys.platlibdir != "lib":
|
||||||
|
// libdirs.append("lib")
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// Pyright therefore searches for both a `lib/python3.X/site-packages` directory
|
||||||
|
// and a `lib64/python3.X/site-packages` directory on non-MacOS Unix systems,
|
||||||
|
// since `sys.platlibdir` can sometimes be equal to `"lib64"`.
|
||||||
|
//
|
||||||
|
// However, we only care about the `site-packages` directory insofar as it allows
|
||||||
|
// us to discover Python source code that can be used for inferring type
|
||||||
|
// information regarding third-party dependencies. That means that we don't need
|
||||||
|
// to care about any possible `lib64/site-packages` directories, since
|
||||||
|
// [the `sys`-module documentation] states that `sys.platlibdir` is *only* ever
|
||||||
|
// used for C extensions, never for pure-Python modules.
|
||||||
|
//
|
||||||
|
// [the non-Windows branch]: https://github.com/python/cpython/blob/a8be8fc6c4682089be45a87bd5ee1f686040116c/Lib/site.py#L401-L410
|
||||||
|
// [the `sys`-module documentation]: https://docs.python.org/3/library/sys.html#sys.platlibdir
|
||||||
|
for entry_result in system.read_directory(&sys_prefix_path.join("lib"))? {
|
||||||
|
let Ok(entry) = entry_result else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if !entry.file_type().is_directory() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = entry.path();
|
||||||
|
|
||||||
|
// The `python3.x` part of the `site-packages` path can't be computed from
|
||||||
|
// the `--target-version` the user has passed, as they might be running Python 3.12 locally
|
||||||
|
// even if they've requested that we type check their code "as if" they're running 3.8.
|
||||||
|
//
|
||||||
|
// The `python3.x` part of the `site-packages` path *could* be computed
|
||||||
|
// by parsing the virtual environment's `pyvenv.cfg` file.
|
||||||
|
// Right now that seems like overkill, but in the future we may need to parse
|
||||||
|
// the `pyvenv.cfg` file anyway, in which case we could switch to that method
|
||||||
|
// rather than iterating through the whole directory until we find
|
||||||
|
// an entry where the last component of the path starts with `python3.`
|
||||||
|
if !path
|
||||||
|
.components()
|
||||||
|
.next_back()
|
||||||
|
.is_some_and(|last_part| last_part.as_str().starts_with("python3."))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let site_packages_candidate = path.join("site-packages");
|
||||||
|
if system.is_directory(&site_packages_candidate) {
|
||||||
|
return Ok(site_packages_candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(SitePackagesDiscoveryError::NoSitePackagesDirFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum SitePackagesDiscoveryError {
|
||||||
|
#[error("Failed to search the virtual environment directory for `site-packages` due to {0}")]
|
||||||
|
CouldNotReadLibDirectory(#[from] io::Error),
|
||||||
|
#[error("Could not find the `site-packages` directory in the virtual environment")]
|
||||||
|
NoSitePackagesDirFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a validated, canonicalized path to a virtual environment,
|
||||||
|
/// return a list of `site-packages` directories that are available from that environment.
|
||||||
|
///
|
||||||
|
/// See the documentation for `site_packages_dir_from_sys_prefix` for more details.
|
||||||
|
///
|
||||||
|
/// TODO: Currently we only ever return 1 path from this function:
|
||||||
|
/// the `site-packages` directory that is actually inside the virtual environment.
|
||||||
|
/// Some `site-packages` directories are able to also access system `site-packages` and
|
||||||
|
/// user `site-packages`, however.
|
||||||
|
pub fn site_packages_dirs_of_venv(
|
||||||
|
venv_path: &SystemPath,
|
||||||
|
system: &dyn System,
|
||||||
|
) -> Result<Vec<SystemPathBuf>, SitePackagesDiscoveryError> {
|
||||||
|
Ok(vec![site_packages_dir_from_sys_prefix(venv_path, system)?])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use ruff_db::system::{OsSystem, System, SystemPath};
|
||||||
|
|
||||||
|
use crate::site_packages::site_packages_dirs_of_venv;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Windows venvs have different layouts, and we only have a Unix venv committed for now.
|
||||||
|
// This test is skipped on Windows until we commit a Windows venv.
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn can_find_site_packages_dir_in_committed_venv() {
|
||||||
|
let path_to_venv = SystemPath::new("resources/test/empty-unix-venv");
|
||||||
|
let system = OsSystem::default();
|
||||||
|
|
||||||
|
// if this doesn't hold true, the premise of the test is incorrect.
|
||||||
|
assert!(system.is_directory(path_to_venv));
|
||||||
|
|
||||||
|
let site_packages_dirs = site_packages_dirs_of_venv(path_to_venv, &system).unwrap();
|
||||||
|
assert_eq!(site_packages_dirs.len(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue