mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
parent
b818199403
commit
59c7a10c4b
35 changed files with 65 additions and 184 deletions
44
crates/uv-virtualenv/Cargo.toml
Normal file
44
crates/uv-virtualenv/Cargo.toml
Normal file
|
@ -0,0 +1,44 @@
|
|||
[package]
|
||||
name = "uv-virtualenv"
|
||||
version = "0.0.4"
|
||||
publish = false
|
||||
description = "virtualenv creation implemented in rust"
|
||||
keywords = ["virtualenv", "venv", "python"]
|
||||
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
homepage = { workspace = true }
|
||||
documentation = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
license = { workspace = true }
|
||||
|
||||
[[bin]]
|
||||
name = "uv-virtualenv"
|
||||
required-features = ["cli"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
platform-host = { path = "../platform-host" }
|
||||
uv-cache = { path = "../uv-cache" }
|
||||
uv-fs = { path = "../uv-fs" }
|
||||
uv-interpreter = { path = "../uv-interpreter" }
|
||||
|
||||
anstream = { workspace = true }
|
||||
cachedir = { workspace = true }
|
||||
camino = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive"], optional = true }
|
||||
directories = { workspace = true }
|
||||
fs-err = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true, optional = true }
|
||||
which = { workspace = true }
|
||||
|
||||
[features]
|
||||
cli = ["clap", "tracing-subscriber"]
|
3
crates/uv-virtualenv/README.md
Normal file
3
crates/uv-virtualenv/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# uv-virtualenv
|
||||
|
||||
`uv-virtualenv` is a rust library to create Python virtual environments. It also has a CLI.
|
103
crates/uv-virtualenv/src/_virtualenv.py
Normal file
103
crates/uv-virtualenv/src/_virtualenv.py
Normal file
|
@ -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())
|
87
crates/uv-virtualenv/src/activate
Normal file
87
crates/uv-virtualenv/src/activate
Normal file
|
@ -0,0 +1,87 @@
|
|||
# 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='{{ VIRTUAL_ENV_TEMPLATE_STRING }}'
|
||||
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
|
1
crates/uv-virtualenv/src/activator/.gitattributes
vendored
Normal file
1
crates/uv-virtualenv/src/activator/.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.bat text eol=crlf
|
108
crates/uv-virtualenv/src/activator/activate
Normal file
108
crates/uv-virtualenv/src/activator/activate
Normal file
|
@ -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='{{ VIRTUAL_ENV_DIR }}'
|
||||
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_NAME }}:$PATH"
|
||||
export PATH
|
||||
|
||||
if [ "x{{ VIRTUAL_PROMPT }}" != x ] ; then
|
||||
VIRTUAL_ENV_PROMPT="{{ VIRTUAL_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
|
59
crates/uv-virtualenv/src/activator/activate.bat
Normal file
59
crates/uv-virtualenv/src/activator/activate.bat
Normal file
|
@ -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.
|
||||
|
||||
@set "VIRTUAL_ENV={{ VIRTUAL_ENV_DIR }}"
|
||||
|
||||
@set "VIRTUAL_ENV_PROMPT={{ VIRTUAL_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_NAME }};%PATH%"
|
76
crates/uv-virtualenv/src/activator/activate.csh
Normal file
76
crates/uv-virtualenv/src/activator/activate.csh
Normal file
|
@ -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 '{{ VIRTUAL_ENV_DIR }}'
|
||||
|
||||
set _OLD_VIRTUAL_PATH="$PATH:q"
|
||||
setenv PATH "$VIRTUAL_ENV:q/{{ BIN_NAME }}:$PATH:q"
|
||||
|
||||
|
||||
|
||||
if ('{{ VIRTUAL_PROMPT }}' != "") then
|
||||
setenv VIRTUAL_ENV_PROMPT '{{ VIRTUAL_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
|
124
crates/uv-virtualenv/src/activator/activate.fish
Normal file
124
crates/uv-virtualenv/src/activator/activate.fish
Normal file
|
@ -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 '{{ VIRTUAL_ENV_DIR }}'
|
||||
|
||||
# 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_NAME }}' $PATH
|
||||
|
||||
# Prompt override provided?
|
||||
# If not, just use the environment name.
|
||||
if test -n '{{ VIRTUAL_PROMPT }}'
|
||||
set -gx VIRTUAL_ENV_PROMPT '{{ VIRTUAL_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
|
117
crates/uv-virtualenv/src/activator/activate.nu
Normal file
117
crates/uv-virtualenv/src/activator/activate.nu
Normal file
|
@ -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 = '{{ VIRTUAL_ENV_DIR }}'
|
||||
let bin = '{{ BIN_NAME }}'
|
||||
|
||||
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 ('{{ VIRTUAL_PROMPT }}' | is-empty) {
|
||||
($virtual_env | path basename)
|
||||
} else {
|
||||
'{{ VIRTUAL_PROMPT }}'
|
||||
})
|
||||
|
||||
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
|
82
crates/uv-virtualenv/src/activator/activate.ps1
Normal file
82
crates/uv-virtualenv/src/activator/activate.ps1
Normal file
|
@ -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 ("{{ VIRTUAL_PROMPT }}" -ne "") {
|
||||
$env:VIRTUAL_ENV_PROMPT = "{{ VIRTUAL_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_NAME }};" + $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)
|
||||
}
|
||||
}
|
57
crates/uv-virtualenv/src/activator/activate_this.py
Normal file
57
crates/uv-virtualenv/src/activator/activate_this.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
# 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:
|
||||
|
||||
Use exec(open(this_file).read(), {'__file__': 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 exec(open(this_file).read(), {'__file__': this_file}))"
|
||||
raise AssertionError(msg) from exc
|
||||
|
||||
bin_dir = os.path.dirname(abs_file)
|
||||
base = bin_dir[: -len("{{ BIN_NAME }}") - 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 "{{ RELATIVE_SITE_PACKAGES }}".split(os.pathsep):
|
||||
path = os.path.realpath(os.path.join(bin_dir, lib))
|
||||
site.addsitedir(path.decode("utf-8") if "" else path)
|
||||
sys.path[:] = sys.path[prev_length:] + sys.path[0:prev_length]
|
||||
|
||||
sys.real_prefix = sys.prefix
|
||||
sys.prefix = base
|
39
crates/uv-virtualenv/src/activator/deactivate.bat
Normal file
39
crates/uv-virtualenv/src/activator/deactivate.bat
Normal file
|
@ -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
|
22
crates/uv-virtualenv/src/activator/pydoc.bat
Normal file
22
crates/uv-virtualenv/src/activator/pydoc.bat
Normal file
|
@ -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 %*
|
323
crates/uv-virtualenv/src/bare.rs
Normal file
323
crates/uv-virtualenv/src/bare.rs
Normal file
|
@ -0,0 +1,323 @@
|
|||
//! Create a bare virtualenv without any packages install
|
||||
|
||||
use std::env;
|
||||
use std::env::consts::EXE_SUFFIX;
|
||||
use std::io;
|
||||
use std::io::{BufWriter, Write};
|
||||
|
||||
use camino::{FromPathBufError, Utf8Path, Utf8PathBuf};
|
||||
use fs_err as fs;
|
||||
use fs_err::File;
|
||||
use tracing::info;
|
||||
use uv_fs::Simplified;
|
||||
|
||||
use uv_interpreter::{Interpreter, SysconfigPaths, Virtualenv};
|
||||
|
||||
use crate::{Error, Prompt};
|
||||
|
||||
/// The bash activate scripts with the venv dependent paths patches out
|
||||
const ACTIVATE_TEMPLATES: &[(&str, &str)] = &[
|
||||
("activate", include_str!("activator/activate")),
|
||||
("activate.csh", include_str!("activator/activate.csh")),
|
||||
("activate.fish", include_str!("activator/activate.fish")),
|
||||
("activate.nu", include_str!("activator/activate.nu")),
|
||||
("activate.ps1", include_str!("activator/activate.ps1")),
|
||||
("activate.bat", include_str!("activator/activate.bat")),
|
||||
("deactivate.bat", include_str!("activator/deactivate.bat")),
|
||||
("pydoc.bat", include_str!("activator/pydoc.bat")),
|
||||
(
|
||||
"activate_this.py",
|
||||
include_str!("activator/activate_this.py"),
|
||||
),
|
||||
];
|
||||
const VIRTUALENV_PATCH: &str = include_str!("_virtualenv.py");
|
||||
|
||||
/// Very basic `.cfg` file format writer.
|
||||
fn write_cfg(f: &mut impl Write, data: &[(String, String)]) -> io::Result<()> {
|
||||
for (key, value) in data {
|
||||
writeln!(f, "{key} = {value}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write all the files that belong to a venv without any packages installed.
|
||||
pub fn create_bare_venv(
|
||||
location: &Utf8Path,
|
||||
interpreter: &Interpreter,
|
||||
prompt: Prompt,
|
||||
system_site_packages: bool,
|
||||
extra_cfg: Vec<(String, String)>,
|
||||
) -> Result<Virtualenv, Error> {
|
||||
// We have to canonicalize the interpreter path, otherwise the home is set to the venv dir instead of the real root.
|
||||
// This would make python-build-standalone fail with the encodings module not being found because its home is wrong.
|
||||
let base_python: Utf8PathBuf = fs_err::canonicalize(interpreter.sys_executable())?
|
||||
.try_into()
|
||||
.map_err(|err: FromPathBufError| err.into_io_error())?;
|
||||
|
||||
// Validate the existing location.
|
||||
match location.metadata() {
|
||||
Ok(metadata) => {
|
||||
if metadata.is_file() {
|
||||
return Err(Error::IO(io::Error::new(
|
||||
io::ErrorKind::AlreadyExists,
|
||||
format!("File exists at `{location}`"),
|
||||
)));
|
||||
} else if metadata.is_dir() {
|
||||
if location.join("pyvenv.cfg").is_file() {
|
||||
info!("Removing existing directory");
|
||||
fs::remove_dir_all(location)?;
|
||||
fs::create_dir_all(location)?;
|
||||
} else if location
|
||||
.read_dir()
|
||||
.is_ok_and(|mut dir| dir.next().is_none())
|
||||
{
|
||||
info!("Ignoring empty directory");
|
||||
} else {
|
||||
return Err(Error::IO(io::Error::new(
|
||||
io::ErrorKind::AlreadyExists,
|
||||
format!("The directory `{location}` exists, but it's not a virtualenv"),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => {
|
||||
fs::create_dir_all(location)?;
|
||||
}
|
||||
Err(err) => return Err(Error::IO(err)),
|
||||
}
|
||||
|
||||
let location = location.canonicalize_utf8()?;
|
||||
|
||||
let bin_name = if cfg!(unix) {
|
||||
"bin"
|
||||
} else if cfg!(windows) {
|
||||
"Scripts"
|
||||
} else {
|
||||
unimplemented!("Only Windows and Unix are supported")
|
||||
};
|
||||
let scripts = location.join(bin_name);
|
||||
let prompt = match prompt {
|
||||
Prompt::CurrentDirectoryName => env::current_dir()?
|
||||
.file_name()
|
||||
.map(|name| name.to_string_lossy().to_string()),
|
||||
Prompt::Static(value) => Some(value),
|
||||
Prompt::None => None,
|
||||
};
|
||||
|
||||
// Add the CACHEDIR.TAG.
|
||||
cachedir::ensure_tag(&location)?;
|
||||
|
||||
// Create a `.gitignore` file to ignore all files in the venv.
|
||||
fs::write(location.join(".gitignore"), "*")?;
|
||||
|
||||
// Different names for the python interpreter
|
||||
fs::create_dir(&scripts)?;
|
||||
let executable = scripts.join(format!("python{EXE_SUFFIX}"));
|
||||
|
||||
// No symlinking on Windows, at least not on a regular non-dev non-admin Windows install.
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use fs_err::os::unix::fs::symlink;
|
||||
|
||||
symlink(&base_python, &executable)?;
|
||||
symlink(
|
||||
"python",
|
||||
scripts.join(format!("python{}", interpreter.python_major())),
|
||||
)?;
|
||||
symlink(
|
||||
"python",
|
||||
scripts.join(format!(
|
||||
"python{}.{}",
|
||||
interpreter.python_major(),
|
||||
interpreter.python_minor(),
|
||||
)),
|
||||
)?;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// https://github.com/python/cpython/blob/d457345bbc6414db0443819290b04a9a4333313d/Lib/venv/__init__.py#L261-L267
|
||||
// https://github.com/pypa/virtualenv/blob/d9fdf48d69f0d0ca56140cf0381edbb5d6fe09f5/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py#L78-L83
|
||||
// There's two kinds of applications on windows: Those that allocate a console (python.exe) and those that
|
||||
// don't because they use window(s) (pythonw.exe).
|
||||
for python_exe in ["python.exe", "pythonw.exe"] {
|
||||
let shim = interpreter
|
||||
.stdlib()
|
||||
.join("venv")
|
||||
.join("scripts")
|
||||
.join("nt")
|
||||
.join(python_exe);
|
||||
fs_err::copy(shim, scripts.join(python_exe))?;
|
||||
}
|
||||
}
|
||||
#[cfg(not(any(unix, windows)))]
|
||||
{
|
||||
compile_error!("Only Windows and Unix are supported")
|
||||
}
|
||||
|
||||
// Add all the activate scripts for different shells
|
||||
for (name, template) in ACTIVATE_TEMPLATES {
|
||||
let relative_site_packages = if cfg!(unix) {
|
||||
format!(
|
||||
"../lib/{}{}.{}/site-packages",
|
||||
interpreter.site_packages_python(),
|
||||
interpreter.python_major(),
|
||||
interpreter.python_minor(),
|
||||
)
|
||||
} else if cfg!(windows) {
|
||||
"../Lib/site-packages".to_string()
|
||||
} else {
|
||||
unimplemented!("Only Windows and Unix are supported")
|
||||
};
|
||||
let activator = template
|
||||
.replace(
|
||||
"{{ VIRTUAL_ENV_DIR }}",
|
||||
// SAFETY: `unwrap` is guaranteed to succeed because `location` is an `Utf8PathBuf`.
|
||||
location.simplified().to_str().unwrap(),
|
||||
)
|
||||
.replace("{{ BIN_NAME }}", bin_name)
|
||||
.replace(
|
||||
"{{ VIRTUAL_PROMPT }}",
|
||||
prompt.as_deref().unwrap_or_default(),
|
||||
)
|
||||
.replace(
|
||||
"{{ RELATIVE_SITE_PACKAGES }}",
|
||||
relative_site_packages.as_str(),
|
||||
);
|
||||
fs::write(scripts.join(name), activator)?;
|
||||
}
|
||||
|
||||
// pyvenv.cfg
|
||||
let python_home = if cfg!(unix) {
|
||||
// On Linux and Mac, Python is symlinked so the base home is the parent of the resolved-by-canonicalize path.
|
||||
base_python
|
||||
.parent()
|
||||
.ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"The python interpreter needs to have a parent directory",
|
||||
)
|
||||
})?
|
||||
.to_string()
|
||||
} else if cfg!(windows) {
|
||||
// `virtualenv` seems to rely on the undocumented, private `sys._base_executable`. When I tried,
|
||||
// `sys.base_prefix` was the same as the parent of `sys._base_executable`, but a much simpler logic and
|
||||
// documented.
|
||||
// https://github.com/pypa/virtualenv/blob/d9fdf48d69f0d0ca56140cf0381edbb5d6fe09f5/src/virtualenv/discovery/py_info.py#L136-L156
|
||||
interpreter.base_prefix().display().to_string()
|
||||
} else {
|
||||
unimplemented!("Only Windows and Unix are supported")
|
||||
};
|
||||
|
||||
// Validate extra_cfg
|
||||
let reserved_keys = [
|
||||
"home",
|
||||
"implementation",
|
||||
"version_info",
|
||||
"include-system-site-packages",
|
||||
"base-prefix",
|
||||
"base-exec-prefix",
|
||||
"base-executable",
|
||||
"prompt",
|
||||
];
|
||||
for (key, _) in &extra_cfg {
|
||||
if reserved_keys.contains(&key.as_str()) {
|
||||
return Err(Error::ReservedConfigKey(key.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
let mut pyvenv_cfg_data: Vec<(String, String)> = vec![
|
||||
("home".to_string(), python_home),
|
||||
(
|
||||
"implementation".to_string(),
|
||||
interpreter.markers().platform_python_implementation.clone(),
|
||||
),
|
||||
(
|
||||
"version_info".to_string(),
|
||||
interpreter.markers().python_full_version.string.clone(),
|
||||
),
|
||||
(
|
||||
"include-system-site-packages".to_string(),
|
||||
if system_site_packages {
|
||||
"true".to_string()
|
||||
} else {
|
||||
"false".to_string()
|
||||
},
|
||||
),
|
||||
(
|
||||
"base-prefix".to_string(),
|
||||
interpreter.base_prefix().to_string_lossy().to_string(),
|
||||
),
|
||||
(
|
||||
"base-exec-prefix".to_string(),
|
||||
interpreter.base_exec_prefix().to_string_lossy().to_string(),
|
||||
),
|
||||
("base-executable".to_string(), base_python.to_string()),
|
||||
]
|
||||
.into_iter()
|
||||
.chain(extra_cfg)
|
||||
.collect();
|
||||
|
||||
if let Some(prompt) = prompt {
|
||||
pyvenv_cfg_data.push(("prompt".to_string(), prompt));
|
||||
}
|
||||
|
||||
let mut pyvenv_cfg = BufWriter::new(File::create(location.join("pyvenv.cfg"))?);
|
||||
write_cfg(&mut pyvenv_cfg, &pyvenv_cfg_data)?;
|
||||
drop(pyvenv_cfg);
|
||||
|
||||
// Construct the path to the `site-packages` directory.
|
||||
let site_packages = if cfg!(unix) {
|
||||
location
|
||||
.join("lib")
|
||||
.join(format!(
|
||||
"{}{}.{}",
|
||||
interpreter.site_packages_python(),
|
||||
interpreter.python_major(),
|
||||
interpreter.python_minor(),
|
||||
))
|
||||
.join("site-packages")
|
||||
} else if cfg!(windows) {
|
||||
location.join("Lib").join("site-packages")
|
||||
} else {
|
||||
unimplemented!("Only Windows and Unix are supported")
|
||||
};
|
||||
|
||||
// Construct the path to the `platstdlib` directory.
|
||||
let platstdlib = if cfg!(windows) {
|
||||
location.join("Lib")
|
||||
} else {
|
||||
location
|
||||
.join("lib")
|
||||
.join(format!(
|
||||
"{}{}.{}",
|
||||
interpreter.site_packages_python(),
|
||||
interpreter.python_major(),
|
||||
interpreter.python_minor()
|
||||
))
|
||||
.join("site-packages")
|
||||
};
|
||||
|
||||
// Populate `site-packages` with a `_virtualenv.py` file.
|
||||
fs::create_dir_all(&site_packages)?;
|
||||
fs::write(site_packages.join("_virtualenv.py"), VIRTUALENV_PATCH)?;
|
||||
fs::write(site_packages.join("_virtualenv.pth"), "import _virtualenv")?;
|
||||
|
||||
Ok(Virtualenv {
|
||||
root: location.clone().into_std_path_buf(),
|
||||
executable: executable.into_std_path_buf(),
|
||||
sysconfig_paths: SysconfigPaths {
|
||||
// Paths that were already constructed above.
|
||||
scripts: scripts.into_std_path_buf(),
|
||||
platstdlib: platstdlib.into_std_path_buf(),
|
||||
// Set `purelib` and `platlib` to the same value.
|
||||
purelib: site_packages.clone().into_std_path_buf(),
|
||||
platlib: site_packages.into_std_path_buf(),
|
||||
// Inherited from the interpreter.
|
||||
stdlib: interpreter.stdlib().to_path_buf(),
|
||||
include: interpreter.include().to_path_buf(),
|
||||
platinclude: interpreter.platinclude().to_path_buf(),
|
||||
data: location.into_std_path_buf(),
|
||||
},
|
||||
})
|
||||
}
|
73
crates/uv-virtualenv/src/lib.rs
Normal file
73
crates/uv-virtualenv/src/lib.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
use camino::{FromPathError, Utf8Path};
|
||||
use thiserror::Error;
|
||||
|
||||
use platform_host::PlatformError;
|
||||
use uv_interpreter::{Interpreter, PythonEnvironment};
|
||||
|
||||
pub use crate::bare::create_bare_venv;
|
||||
|
||||
mod bare;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
IO(#[from] io::Error),
|
||||
#[error("Failed to determine python interpreter to use")]
|
||||
InterpreterError(#[from] uv_interpreter::Error),
|
||||
#[error(transparent)]
|
||||
Platform(#[from] PlatformError),
|
||||
#[error("Reserved key used for pyvenv.cfg: {0}")]
|
||||
ReservedConfigKey(String),
|
||||
}
|
||||
|
||||
/// The value to use for the shell prompt when inside a virtual environment.
|
||||
#[derive(Debug)]
|
||||
pub enum Prompt {
|
||||
/// Use the current directory name as the prompt.
|
||||
CurrentDirectoryName,
|
||||
/// Use the fixed string as the prompt.
|
||||
Static(String),
|
||||
/// Default to no prompt. The prompt is then set by the activator script
|
||||
/// to the virtual environment's directory name.
|
||||
None,
|
||||
}
|
||||
|
||||
impl Prompt {
|
||||
/// Determine the prompt value to be used from the command line arguments.
|
||||
pub fn from_args(prompt: Option<String>) -> Self {
|
||||
match prompt {
|
||||
Some(prompt) if prompt == "." => Self::CurrentDirectoryName,
|
||||
Some(prompt) => Self::Static(prompt),
|
||||
None => Self::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a virtualenv.
|
||||
pub fn create_venv(
|
||||
location: &Path,
|
||||
interpreter: Interpreter,
|
||||
prompt: Prompt,
|
||||
system_site_packages: bool,
|
||||
extra_cfg: Vec<(String, String)>,
|
||||
) -> Result<PythonEnvironment, Error> {
|
||||
// Create the virtualenv at the given location.
|
||||
let location: &Utf8Path = location
|
||||
.try_into()
|
||||
.map_err(|err: FromPathError| err.into_io_error())?;
|
||||
let virtualenv = create_bare_venv(
|
||||
location,
|
||||
&interpreter,
|
||||
prompt,
|
||||
system_site_packages,
|
||||
extra_cfg,
|
||||
)?;
|
||||
|
||||
// Create the corresponding `PythonEnvironment`.
|
||||
let interpreter = interpreter.with_virtualenv(virtualenv);
|
||||
let root = interpreter.prefix().to_path_buf();
|
||||
Ok(PythonEnvironment::from_interpreter(interpreter, root))
|
||||
}
|
77
crates/uv-virtualenv/src/main.rs
Normal file
77
crates/uv-virtualenv/src/main.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use std::error::Error;
|
||||
use std::process::ExitCode;
|
||||
use std::time::Instant;
|
||||
|
||||
use anstream::eprintln;
|
||||
use camino::Utf8PathBuf;
|
||||
use clap::Parser;
|
||||
use directories::ProjectDirs;
|
||||
use tracing::info;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
|
||||
use platform_host::Platform;
|
||||
use uv_cache::Cache;
|
||||
use uv_interpreter::{find_default_python, find_requested_python};
|
||||
use uv_virtualenv::{create_bare_venv, Prompt};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
path: Option<Utf8PathBuf>,
|
||||
#[clap(short, long)]
|
||||
python: Option<String>,
|
||||
#[clap(long)]
|
||||
prompt: Option<String>,
|
||||
#[clap(long)]
|
||||
system_site_packages: bool,
|
||||
}
|
||||
|
||||
fn run() -> Result<(), uv_virtualenv::Error> {
|
||||
let cli = Cli::parse();
|
||||
let location = cli.path.unwrap_or(Utf8PathBuf::from(".venv"));
|
||||
let platform = Platform::current()?;
|
||||
let cache = if let Some(project_dirs) = ProjectDirs::from("", "", "uv-virtualenv") {
|
||||
Cache::from_path(project_dirs.cache_dir())?
|
||||
} else {
|
||||
Cache::from_path(".cache")?
|
||||
};
|
||||
let interpreter = if let Some(python_request) = &cli.python {
|
||||
find_requested_python(python_request, &platform, &cache)?.ok_or(
|
||||
uv_interpreter::Error::NoSuchPython(python_request.to_string()),
|
||||
)?
|
||||
} else {
|
||||
find_default_python(&platform, &cache)?
|
||||
};
|
||||
create_bare_venv(
|
||||
&location,
|
||||
&interpreter,
|
||||
Prompt::from_args(cli.prompt),
|
||||
cli.system_site_packages,
|
||||
Vec::new(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
tracing_subscriber::registry()
|
||||
.with(fmt::layer())
|
||||
.with(EnvFilter::from_default_env())
|
||||
.init();
|
||||
|
||||
let start = Instant::now();
|
||||
let result = run();
|
||||
info!("Took {}ms", start.elapsed().as_millis());
|
||||
if let Err(err) = result {
|
||||
eprintln!("💥 virtualenv creator failed");
|
||||
|
||||
let mut last_error: Option<&(dyn Error + 'static)> = Some(&err);
|
||||
while let Some(err) = last_error {
|
||||
eprintln!(" Caused by: {err}");
|
||||
last_error = err.source();
|
||||
}
|
||||
ExitCode::FAILURE
|
||||
} else {
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue