init
This commit is contained in:
commit
38355d2442
9083 changed files with 1225834 additions and 0 deletions
10
.venv/lib/python3.8/site-packages/virtualenv/__init__.py
Normal file
10
.venv/lib/python3.8/site-packages/virtualenv/__init__.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from .run import cli_run, session_via_cli
|
||||
from .version import __version__
|
||||
|
||||
__all__ = (
|
||||
"__version__",
|
||||
"cli_run",
|
||||
"session_via_cli",
|
||||
)
|
||||
80
.venv/lib/python3.8/site-packages/virtualenv/__main__.py
Normal file
80
.venv/lib/python3.8/site-packages/virtualenv/__main__.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def run(args=None, options=None, env=None):
|
||||
env = os.environ if env is None else env
|
||||
start = datetime.now()
|
||||
from virtualenv.run import cli_run
|
||||
from virtualenv.util.error import ProcessCallFailed
|
||||
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
try:
|
||||
session = cli_run(args, options, env)
|
||||
logging.warning(LogSession(session, start))
|
||||
except ProcessCallFailed as exception:
|
||||
print("subprocess call failed for {} with code {}".format(exception.cmd, exception.code))
|
||||
print(exception.out, file=sys.stdout, end="")
|
||||
print(exception.err, file=sys.stderr, end="")
|
||||
raise SystemExit(exception.code)
|
||||
|
||||
|
||||
class LogSession(object):
|
||||
def __init__(self, session, start):
|
||||
self.session = session
|
||||
self.start = start
|
||||
|
||||
def __str__(self):
|
||||
from virtualenv.util.six import ensure_text
|
||||
|
||||
spec = self.session.creator.interpreter.spec
|
||||
elapsed = (datetime.now() - self.start).total_seconds() * 1000
|
||||
lines = [
|
||||
"created virtual environment {} in {:.0f}ms".format(spec, elapsed),
|
||||
" creator {}".format(ensure_text(str(self.session.creator))),
|
||||
]
|
||||
if self.session.seeder.enabled:
|
||||
lines += (
|
||||
" seeder {}".format(ensure_text(str(self.session.seeder))),
|
||||
" added seed packages: {}".format(
|
||||
", ".join(
|
||||
sorted(
|
||||
"==".join(i.stem.split("-"))
|
||||
for i in self.session.creator.purelib.iterdir()
|
||||
if i.suffix == ".dist-info"
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
if self.session.activators:
|
||||
lines.append(" activators {}".format(",".join(i.__class__.__name__ for i in self.session.activators)))
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def run_with_catch(args=None, env=None):
|
||||
from virtualenv.config.cli.parser import VirtualEnvOptions
|
||||
|
||||
env = os.environ if env is None else env
|
||||
options = VirtualEnvOptions()
|
||||
try:
|
||||
run(args, options, env)
|
||||
except (KeyboardInterrupt, SystemExit, Exception) as exception:
|
||||
try:
|
||||
if getattr(options, "with_traceback", False):
|
||||
raise
|
||||
else:
|
||||
if not (isinstance(exception, SystemExit) and exception.code == 0):
|
||||
logging.error("%s: %s", type(exception).__name__, exception)
|
||||
code = exception.code if isinstance(exception, SystemExit) else 1
|
||||
sys.exit(code)
|
||||
finally:
|
||||
logging.shutdown() # force flush of log messages before the trace is printed
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cov
|
||||
run_with_catch() # pragma: no cov
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,19 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from .bash import BashActivator
|
||||
from .batch import BatchActivator
|
||||
from .cshell import CShellActivator
|
||||
from .fish import FishActivator
|
||||
from .nushell import NushellActivator
|
||||
from .powershell import PowerShellActivator
|
||||
from .python import PythonActivator
|
||||
|
||||
__all__ = [
|
||||
"BashActivator",
|
||||
"PowerShellActivator",
|
||||
"CShellActivator",
|
||||
"PythonActivator",
|
||||
"BatchActivator",
|
||||
"FishActivator",
|
||||
"NushellActivator",
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,45 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import os
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
from six import add_metaclass
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class Activator(object):
|
||||
"""Generates an activate script for the virtual environment"""
|
||||
|
||||
def __init__(self, options):
|
||||
"""Create a new activator generator.
|
||||
|
||||
:param options: the parsed options as defined within :meth:`add_parser_arguments`
|
||||
"""
|
||||
self.flag_prompt = os.path.basename(os.getcwd()) if options.prompt == "." else options.prompt
|
||||
|
||||
@classmethod
|
||||
def supports(cls, interpreter):
|
||||
"""Check if the activation script is supported in the given interpreter.
|
||||
|
||||
:param interpreter: the interpreter we need to support
|
||||
:return: ``True`` if supported, ``False`` otherwise
|
||||
"""
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def add_parser_arguments(cls, parser, interpreter):
|
||||
"""
|
||||
Add CLI arguments for this activation script.
|
||||
|
||||
:param parser: the CLI parser
|
||||
:param interpreter: the interpreter this virtual environment is based of
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def generate(self, creator):
|
||||
"""Generate the activate script for the given creator.
|
||||
|
||||
:param creator: the creator (based of :class:`virtualenv.create.creator.Creator`) we used to create this \
|
||||
virtual environment
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from virtualenv.util.path import Path
|
||||
|
||||
from ..via_template import ViaTemplateActivator
|
||||
|
||||
|
||||
class BashActivator(ViaTemplateActivator):
|
||||
def templates(self):
|
||||
yield Path("activate.sh")
|
||||
|
||||
def as_name(self, template):
|
||||
return template.stem
|
||||
Binary file not shown.
|
|
@ -0,0 +1,83 @@
|
|||
# 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
|
||||
if [ ! "${1-}" = "nondestructive" ] ; then
|
||||
# Self destruct!
|
||||
unset -f deactivate
|
||||
fi
|
||||
}
|
||||
|
||||
# unset irrelevant variables
|
||||
deactivate nondestructive
|
||||
|
||||
VIRTUAL_ENV='__VIRTUAL_ENV__'
|
||||
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
|
||||
|
||||
# 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-}"
|
||||
if [ "x__VIRTUAL_PROMPT__" != x ] ; then
|
||||
PS1="(__VIRTUAL_PROMPT__) ${PS1-}"
|
||||
else
|
||||
PS1="(`basename \"$VIRTUAL_ENV\"`) ${PS1-}"
|
||||
fi
|
||||
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,23 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import os
|
||||
|
||||
from virtualenv.util.path import Path
|
||||
|
||||
from ..via_template import ViaTemplateActivator
|
||||
|
||||
|
||||
class BatchActivator(ViaTemplateActivator):
|
||||
@classmethod
|
||||
def supports(cls, interpreter):
|
||||
return interpreter.os == "nt"
|
||||
|
||||
def templates(self):
|
||||
yield Path("activate.bat")
|
||||
yield Path("deactivate.bat")
|
||||
yield Path("pydoc.bat")
|
||||
|
||||
def instantiate_template(self, replacements, template, creator):
|
||||
# ensure the text has all newlines as \r\n - required by batch
|
||||
base = super(BatchActivator, self).instantiate_template(replacements, template, creator)
|
||||
return base.replace(os.linesep, "\n").replace("\n", os.linesep)
|
||||
Binary file not shown.
|
|
@ -0,0 +1,39 @@
|
|||
@echo off
|
||||
|
||||
set "VIRTUAL_ENV=__VIRTUAL_ENV__"
|
||||
|
||||
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 (
|
||||
if "__VIRTUAL_PROMPT__" NEQ "" (
|
||||
set "PROMPT=(__VIRTUAL_PROMPT__) %PROMPT%"
|
||||
) else (
|
||||
for %%d in ("%VIRTUAL_ENV%") do set "PROMPT=(%%~nxd) %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%"
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
@echo off
|
||||
|
||||
set VIRTUAL_ENV=
|
||||
|
||||
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 @@
|
|||
python.exe -m pydoc %*
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from virtualenv.util.path import Path
|
||||
|
||||
from ..via_template import ViaTemplateActivator
|
||||
|
||||
|
||||
class CShellActivator(ViaTemplateActivator):
|
||||
@classmethod
|
||||
def supports(cls, interpreter):
|
||||
return interpreter.os != "nt"
|
||||
|
||||
def templates(self):
|
||||
yield Path("activate.csh")
|
||||
Binary file not shown.
|
|
@ -0,0 +1,55 @@
|
|||
# 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; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc'
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
setenv VIRTUAL_ENV '__VIRTUAL_ENV__'
|
||||
|
||||
set _OLD_VIRTUAL_PATH="$PATH:q"
|
||||
setenv PATH "$VIRTUAL_ENV:q/__BIN_NAME__:$PATH:q"
|
||||
|
||||
|
||||
|
||||
if ('__VIRTUAL_PROMPT__' != "") then
|
||||
set env_name = '(__VIRTUAL_PROMPT__) '
|
||||
else
|
||||
set env_name = '('"$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 = "$env_name:q$prompt:q"
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
unset env_name
|
||||
unset do_prompt
|
||||
|
||||
alias pydoc python -m pydoc
|
||||
|
||||
rehash
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from virtualenv.util.path import Path
|
||||
|
||||
from ..via_template import ViaTemplateActivator
|
||||
|
||||
|
||||
class FishActivator(ViaTemplateActivator):
|
||||
def templates(self):
|
||||
yield Path("activate.fish")
|
||||
Binary file not shown.
|
|
@ -0,0 +1,100 @@
|
|||
# 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
|
||||
|
||||
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__'
|
||||
|
||||
# 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
|
||||
|
||||
# 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)
|
||||
|
||||
# Prompt override provided?
|
||||
# If not, just prepend the environment name.
|
||||
if test -n '__VIRTUAL_PROMPT__'
|
||||
printf '(%s) ' '__VIRTUAL_PROMPT__'
|
||||
else
|
||||
printf '(%s) ' (basename "$VIRTUAL_ENV")
|
||||
end
|
||||
|
||||
string join -- \n $prompt # handle multi-line prompts
|
||||
end
|
||||
|
||||
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
|
||||
end
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import os
|
||||
|
||||
from virtualenv.util.path import Path
|
||||
from virtualenv.util.six import ensure_text
|
||||
|
||||
from ..via_template import ViaTemplateActivator
|
||||
|
||||
|
||||
class NushellActivator(ViaTemplateActivator):
|
||||
def templates(self):
|
||||
yield Path("activate.nu")
|
||||
yield Path("deactivate.nu")
|
||||
|
||||
def replacements(self, creator, dest_folder):
|
||||
# Due to nushell scoping, it isn't easy to create a function that will
|
||||
# deactivate the environment. For that reason a __DEACTIVATE_PATH__
|
||||
# replacement pointing to the deactivate.nu file is created
|
||||
|
||||
return {
|
||||
"__VIRTUAL_PROMPT__": "" if self.flag_prompt is None else self.flag_prompt,
|
||||
"__VIRTUAL_ENV__": ensure_text(str(creator.dest)),
|
||||
"__VIRTUAL_NAME__": creator.env_name,
|
||||
"__BIN_NAME__": ensure_text(str(creator.bin_dir.relative_to(creator.dest))),
|
||||
"__PATH_SEP__": ensure_text(os.pathsep),
|
||||
"__DEACTIVATE_PATH__": ensure_text(str(Path(dest_folder) / "deactivate.nu")),
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,92 @@
|
|||
# This command prepares the required environment variables
|
||||
def-env activate-virtualenv [] {
|
||||
def is-string [x] {
|
||||
($x | describe) == 'string'
|
||||
}
|
||||
|
||||
def has-env [name: string] {
|
||||
$name in (env).name
|
||||
}
|
||||
|
||||
let is-windows = ((sys).host.name | str downcase) == 'windows'
|
||||
let virtual-env = '__VIRTUAL_ENV__'
|
||||
let bin = '__BIN_NAME__'
|
||||
let path-sep = '__PATH_SEP__'
|
||||
let path-name = if $is-windows {
|
||||
if (has-env 'Path') {
|
||||
'Path'
|
||||
} else {
|
||||
'PATH'
|
||||
}
|
||||
} else {
|
||||
'PATH'
|
||||
}
|
||||
|
||||
let old-path = (
|
||||
if $is-windows {
|
||||
if (has-env 'Path') {
|
||||
$env.Path
|
||||
} else {
|
||||
$env.PATH
|
||||
}
|
||||
} else {
|
||||
$env.PATH
|
||||
} | if (is-string $in) {
|
||||
# if Path/PATH is a string, make it a list
|
||||
$in | split row $path-sep | path expand
|
||||
} else {
|
||||
$in
|
||||
}
|
||||
)
|
||||
|
||||
let venv-path = ([$virtual-env $bin] | path join)
|
||||
let new-path = ($old-path | prepend $venv-path | str collect $path-sep)
|
||||
|
||||
# Creating the new prompt for the session
|
||||
let virtual-prompt = if ('__VIRTUAL_PROMPT__' == '') {
|
||||
$'(char lparen)($virtual-env | path basename)(char rparen) '
|
||||
} else {
|
||||
'(__VIRTUAL_PROMPT__) '
|
||||
}
|
||||
|
||||
# Back up the old prompt builder
|
||||
let old-prompt-command = if (has-env 'VIRTUAL_ENV') && (has-env '_OLD_PROMPT_COMMAND') {
|
||||
$env._OLD_PROMPT_COMMAND
|
||||
} else {
|
||||
if (has-env 'PROMPT_COMMAND') {
|
||||
$env.PROMPT_COMMAND
|
||||
} else {
|
||||
''
|
||||
}
|
||||
}
|
||||
|
||||
# If there is no default prompt, then only the env is printed in the prompt
|
||||
let new-prompt = if (has-env 'PROMPT_COMMAND') {
|
||||
if ($old-prompt-command | describe) == 'block' {
|
||||
{ $'($virtual-prompt)(do $old-prompt-command)' }
|
||||
} else {
|
||||
{ $'($virtual-prompt)($old-prompt-command)' }
|
||||
}
|
||||
} else {
|
||||
{ $'($virtual-prompt)' }
|
||||
}
|
||||
|
||||
# Environment variables that will be batched loaded to the virtual env
|
||||
let new-env = {
|
||||
$path-name : $new-path
|
||||
VIRTUAL_ENV : $virtual-env
|
||||
_OLD_VIRTUAL_PATH : ($old-path | str collect $path-sep)
|
||||
_OLD_PROMPT_COMMAND : $old-prompt-command
|
||||
PROMPT_COMMAND : $new-prompt
|
||||
VIRTUAL_PROMPT : $virtual-prompt
|
||||
}
|
||||
|
||||
# Activate the environment variables
|
||||
load-env $new-env
|
||||
}
|
||||
|
||||
# Activate the virtualenv
|
||||
activate-virtualenv
|
||||
|
||||
alias pydoc = python -m pydoc
|
||||
alias deactivate = source '__DEACTIVATE_PATH__'
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
def-env deactivate-virtualenv [] {
|
||||
def has-env [name: string] {
|
||||
$name in (env).name
|
||||
}
|
||||
|
||||
let is-windows = ((sys).host.name | str downcase) == 'windows'
|
||||
|
||||
let path-name = if $is-windows {
|
||||
if (has-env 'Path') {
|
||||
'Path'
|
||||
} else {
|
||||
'PATH'
|
||||
}
|
||||
} else {
|
||||
'PATH'
|
||||
}
|
||||
|
||||
load-env { $path-name : $env._OLD_VIRTUAL_PATH }
|
||||
|
||||
let-env PROMPT_COMMAND = $env._OLD_PROMPT_COMMAND
|
||||
|
||||
# Hiding the environment variables that were created when activating the env
|
||||
hide _OLD_VIRTUAL_PATH
|
||||
hide _OLD_PROMPT_COMMAND
|
||||
hide VIRTUAL_ENV
|
||||
hide VIRTUAL_PROMPT
|
||||
}
|
||||
|
||||
deactivate-virtualenv
|
||||
|
||||
hide pydoc
|
||||
hide deactivate
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from virtualenv.util.path import Path
|
||||
|
||||
from ..via_template import ViaTemplateActivator
|
||||
|
||||
|
||||
class PowerShellActivator(ViaTemplateActivator):
|
||||
def templates(self):
|
||||
yield Path("activate.ps1")
|
||||
Binary file not shown.
|
|
@ -0,0 +1,60 @@
|
|||
$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 (!$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
|
||||
|
||||
New-Variable -Scope global -Name _OLD_VIRTUAL_PATH -Value $env:PATH
|
||||
|
||||
$env:PATH = "$env:VIRTUAL_ENV/__BIN_NAME____PATH_SEP__" + $env:PATH
|
||||
if (!$env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
||||
function global:_old_virtual_prompt {
|
||||
""
|
||||
}
|
||||
$function:_old_virtual_prompt = $function:prompt
|
||||
|
||||
if ("__VIRTUAL_PROMPT__" -ne "") {
|
||||
function global:prompt {
|
||||
# Add the custom prefix to the existing prompt
|
||||
$previous_prompt_value = & $function:_old_virtual_prompt
|
||||
("(__VIRTUAL_PROMPT__) " + $previous_prompt_value)
|
||||
}
|
||||
}
|
||||
else {
|
||||
function global:prompt {
|
||||
# Add a prefix to the current prompt, but don't discard it.
|
||||
$previous_prompt_value = & $function:_old_virtual_prompt
|
||||
$new_prompt_value = "($( Split-Path $env:VIRTUAL_ENV -Leaf )) "
|
||||
($new_prompt_value + $previous_prompt_value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import os
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
|
||||
from virtualenv.util.path import Path
|
||||
from virtualenv.util.six import ensure_text
|
||||
|
||||
from ..via_template import ViaTemplateActivator
|
||||
|
||||
|
||||
class PythonActivator(ViaTemplateActivator):
|
||||
def templates(self):
|
||||
yield Path("activate_this.py")
|
||||
|
||||
def replacements(self, creator, dest_folder):
|
||||
replacements = super(PythonActivator, self).replacements(creator, dest_folder)
|
||||
lib_folders = OrderedDict((os.path.relpath(str(i), str(dest_folder)), None) for i in creator.libs)
|
||||
win_py2 = creator.interpreter.platform == "win32" and creator.interpreter.version_info.major == 2
|
||||
replacements.update(
|
||||
{
|
||||
"__LIB_FOLDERS__": ensure_text(os.pathsep.join(lib_folders.keys())),
|
||||
"__DECODE_PATH__": ("yes" if win_py2 else ""),
|
||||
},
|
||||
)
|
||||
return replacements
|
||||
|
||||
@staticmethod
|
||||
def _repr_unicode(creator, value):
|
||||
py2 = creator.interpreter.version_info.major == 2
|
||||
if py2: # on Python 2 we need to encode this into explicit utf-8, py3 supports unicode literals
|
||||
start = 2 if sys.version_info[0] == 3 else 1
|
||||
value = ensure_text(repr(value.encode("utf-8"))[start:-1])
|
||||
return value
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""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.
|
||||
"""
|
||||
import os
|
||||
import site
|
||||
import sys
|
||||
|
||||
try:
|
||||
abs_file = os.path.abspath(__file__)
|
||||
except NameError:
|
||||
raise AssertionError("You must use exec(open(this_file).read(), {'__file__': this_file}))")
|
||||
|
||||
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
|
||||
|
||||
# add the virtual environments libraries to the host python import mechanism
|
||||
prev_length = len(sys.path)
|
||||
for lib in "__LIB_FOLDERS__".split(os.pathsep):
|
||||
path = os.path.realpath(os.path.join(bin_dir, lib))
|
||||
site.addsitedir(path.decode("utf-8") if "__DECODE_PATH__" else path)
|
||||
sys.path[:] = sys.path[prev_length:] + sys.path[0:prev_length]
|
||||
|
||||
sys.real_prefix = sys.prefix
|
||||
sys.prefix = base
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import os
|
||||
import sys
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
from six import add_metaclass
|
||||
|
||||
from virtualenv.util.six import ensure_text
|
||||
|
||||
from .activator import Activator
|
||||
|
||||
if sys.version_info >= (3, 7):
|
||||
from importlib.resources import read_binary
|
||||
else:
|
||||
from importlib_resources import read_binary
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class ViaTemplateActivator(Activator):
|
||||
@abstractmethod
|
||||
def templates(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def generate(self, creator):
|
||||
dest_folder = creator.bin_dir
|
||||
replacements = self.replacements(creator, dest_folder)
|
||||
generated = self._generate(replacements, self.templates(), dest_folder, creator)
|
||||
if self.flag_prompt is not None:
|
||||
creator.pyenv_cfg["prompt"] = self.flag_prompt
|
||||
return generated
|
||||
|
||||
def replacements(self, creator, dest_folder):
|
||||
return {
|
||||
"__VIRTUAL_PROMPT__": "" if self.flag_prompt is None else self.flag_prompt,
|
||||
"__VIRTUAL_ENV__": ensure_text(str(creator.dest)),
|
||||
"__VIRTUAL_NAME__": creator.env_name,
|
||||
"__BIN_NAME__": ensure_text(str(creator.bin_dir.relative_to(creator.dest))),
|
||||
"__PATH_SEP__": ensure_text(os.pathsep),
|
||||
}
|
||||
|
||||
def _generate(self, replacements, templates, to_folder, creator):
|
||||
generated = []
|
||||
for template in templates:
|
||||
text = self.instantiate_template(replacements, template, creator)
|
||||
dest = to_folder / self.as_name(template)
|
||||
# use write_bytes to avoid platform specific line normalization (\n -> \r\n)
|
||||
dest.write_bytes(text.encode("utf-8"))
|
||||
generated.append(dest)
|
||||
return generated
|
||||
|
||||
def as_name(self, template):
|
||||
return template.name
|
||||
|
||||
def instantiate_template(self, replacements, template, creator):
|
||||
# read content as binary to avoid platform specific line normalization (\n -> \r\n)
|
||||
binary = read_binary(self.__module__, str(template))
|
||||
text = binary.decode("utf-8", errors="strict")
|
||||
for key, value in replacements.items():
|
||||
value = self._repr_unicode(creator, value)
|
||||
text = text.replace(key, value)
|
||||
return text
|
||||
|
||||
@staticmethod
|
||||
def _repr_unicode(creator, value):
|
||||
# by default we just let it be unicode
|
||||
return value
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
"""
|
||||
Application data stored by virtualenv.
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from platformdirs import user_data_dir
|
||||
|
||||
from .na import AppDataDisabled
|
||||
from .read_only import ReadOnlyAppData
|
||||
from .via_disk_folder import AppDataDiskFolder
|
||||
from .via_tempdir import TempAppData
|
||||
|
||||
|
||||
def _default_app_data_dir(env):
|
||||
key = str("VIRTUALENV_OVERRIDE_APP_DATA")
|
||||
if key in env:
|
||||
return env[key]
|
||||
else:
|
||||
return user_data_dir(appname="virtualenv", appauthor="pypa")
|
||||
|
||||
|
||||
def make_app_data(folder, **kwargs):
|
||||
read_only = kwargs.pop("read_only")
|
||||
env = kwargs.pop("env")
|
||||
if kwargs: # py3+ kwonly
|
||||
raise TypeError("unexpected keywords: {}")
|
||||
|
||||
if folder is None:
|
||||
folder = _default_app_data_dir(env)
|
||||
folder = os.path.abspath(folder)
|
||||
|
||||
if read_only:
|
||||
return ReadOnlyAppData(folder)
|
||||
|
||||
if not os.path.isdir(folder):
|
||||
try:
|
||||
os.makedirs(folder)
|
||||
logging.debug("created app data folder %s", folder)
|
||||
except OSError as exception:
|
||||
logging.info("could not create app data folder %s due to %r", folder, exception)
|
||||
|
||||
if os.access(folder, os.W_OK):
|
||||
return AppDataDiskFolder(folder)
|
||||
else:
|
||||
logging.debug("app data folder %s has no write access", folder)
|
||||
return TempAppData()
|
||||
|
||||
|
||||
__all__ = (
|
||||
"AppDataDisabled",
|
||||
"AppDataDiskFolder",
|
||||
"ReadOnlyAppData",
|
||||
"TempAppData",
|
||||
"make_app_data",
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,95 @@
|
|||
"""
|
||||
Application data stored by virtualenv.
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from contextlib import contextmanager
|
||||
|
||||
import six
|
||||
|
||||
from virtualenv.info import IS_ZIPAPP
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class AppData(object):
|
||||
"""Abstract storage interface for the virtualenv application"""
|
||||
|
||||
@abstractmethod
|
||||
def close(self):
|
||||
"""called before virtualenv exits"""
|
||||
|
||||
@abstractmethod
|
||||
def reset(self):
|
||||
"""called when the user passes in the reset app data"""
|
||||
|
||||
@abstractmethod
|
||||
def py_info(self, path):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def py_info_clear(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def can_update(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def embed_update_log(self, distribution, for_py_version):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def house(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def transient(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def wheel_image(self, for_py_version, name):
|
||||
raise NotImplementedError
|
||||
|
||||
@contextmanager
|
||||
def ensure_extracted(self, path, to_folder=None):
|
||||
"""Some paths might be within the zipapp, unzip these to a path on the disk"""
|
||||
if IS_ZIPAPP:
|
||||
with self.extract(path, to_folder) as result:
|
||||
yield result
|
||||
else:
|
||||
yield path
|
||||
|
||||
@abstractmethod
|
||||
@contextmanager
|
||||
def extract(self, path, to_folder):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
@contextmanager
|
||||
def locked(self, path):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class ContentStore(object):
|
||||
@abstractmethod
|
||||
def exists(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def read(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def write(self, content):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def remove(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
@contextmanager
|
||||
def locked(self):
|
||||
pass
|
||||
66
.venv/lib/python3.8/site-packages/virtualenv/app_data/na.py
Normal file
66
.venv/lib/python3.8/site-packages/virtualenv/app_data/na.py
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
from .base import AppData, ContentStore
|
||||
|
||||
|
||||
class AppDataDisabled(AppData):
|
||||
"""No application cache available (most likely as we don't have write permissions)"""
|
||||
|
||||
transient = True
|
||||
can_update = False
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
error = RuntimeError("no app data folder available, probably no write access to the folder")
|
||||
|
||||
def close(self):
|
||||
"""do nothing"""
|
||||
|
||||
def reset(self):
|
||||
"""do nothing"""
|
||||
|
||||
def py_info(self, path):
|
||||
return ContentStoreNA()
|
||||
|
||||
def embed_update_log(self, distribution, for_py_version):
|
||||
return ContentStoreNA()
|
||||
|
||||
def extract(self, path, to_folder):
|
||||
raise self.error
|
||||
|
||||
@contextmanager
|
||||
def locked(self, path):
|
||||
"""do nothing"""
|
||||
yield
|
||||
|
||||
@property
|
||||
def house(self):
|
||||
raise self.error
|
||||
|
||||
def wheel_image(self, for_py_version, name):
|
||||
raise self.error
|
||||
|
||||
def py_info_clear(self):
|
||||
""" """
|
||||
|
||||
|
||||
class ContentStoreNA(ContentStore):
|
||||
def exists(self):
|
||||
return False
|
||||
|
||||
def read(self):
|
||||
""" """
|
||||
return None
|
||||
|
||||
def write(self, content):
|
||||
""" """
|
||||
|
||||
def remove(self):
|
||||
""" """
|
||||
|
||||
@contextmanager
|
||||
def locked(self):
|
||||
yield
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import os.path
|
||||
|
||||
from virtualenv.util.lock import NoOpFileLock
|
||||
|
||||
from .via_disk_folder import AppDataDiskFolder, PyInfoStoreDisk
|
||||
|
||||
|
||||
class ReadOnlyAppData(AppDataDiskFolder):
|
||||
can_update = False
|
||||
|
||||
def __init__(self, folder): # type: (str) -> None
|
||||
if not os.path.isdir(folder):
|
||||
raise RuntimeError("read-only app data directory {} does not exist".format(folder))
|
||||
self.lock = NoOpFileLock(folder)
|
||||
|
||||
def reset(self): # type: () -> None
|
||||
raise RuntimeError("read-only app data does not support reset")
|
||||
|
||||
def py_info_clear(self): # type: () -> None
|
||||
raise NotImplementedError
|
||||
|
||||
def py_info(self, path):
|
||||
return _PyInfoStoreDiskReadOnly(self.py_info_at, path)
|
||||
|
||||
def embed_update_log(self, distribution, for_py_version):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class _PyInfoStoreDiskReadOnly(PyInfoStoreDisk):
|
||||
def write(self, content):
|
||||
raise RuntimeError("read-only app data python info cannot be updated")
|
||||
|
||||
|
||||
__all__ = ("ReadOnlyAppData",)
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
A rough layout of the current storage goes as:
|
||||
|
||||
virtualenv-app-data
|
||||
├── py - <version> <cache information about python interpreters>
|
||||
│ └── *.json/lock
|
||||
├── wheel <cache wheels used for seeding>
|
||||
│ ├── house
|
||||
│ │ └── *.whl <wheels downloaded go here>
|
||||
│ └── <python major.minor> -> 3.9
|
||||
│ ├── img-<version>
|
||||
│ │ └── image
|
||||
│ │ └── <install class> -> CopyPipInstall / SymlinkPipInstall
|
||||
│ │ └── <wheel name> -> pip-20.1.1-py2.py3-none-any
|
||||
│ └── embed
|
||||
│ └── 3 -> json format versioning
|
||||
│ └── *.json -> for every distribution contains data about newer embed versions and releases
|
||||
└─── unzip <in zip app we cannot refer to some internal files, so first extract them>
|
||||
└── <virtualenv version>
|
||||
├── py_info.py
|
||||
├── debug.py
|
||||
└── _virtualenv.py
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import json
|
||||
import logging
|
||||
from abc import ABCMeta
|
||||
from contextlib import contextmanager
|
||||
from hashlib import sha256
|
||||
|
||||
import six
|
||||
|
||||
from virtualenv.util.lock import ReentrantFileLock
|
||||
from virtualenv.util.path import safe_delete
|
||||
from virtualenv.util.six import ensure_text
|
||||
from virtualenv.util.zipapp import extract
|
||||
from virtualenv.version import __version__
|
||||
|
||||
from .base import AppData, ContentStore
|
||||
|
||||
|
||||
class AppDataDiskFolder(AppData):
|
||||
"""
|
||||
Store the application data on the disk within a folder layout.
|
||||
"""
|
||||
|
||||
transient = False
|
||||
can_update = True
|
||||
|
||||
def __init__(self, folder):
|
||||
self.lock = ReentrantFileLock(folder)
|
||||
|
||||
def __repr__(self):
|
||||
return "{}({})".format(type(self).__name__, self.lock.path)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.lock.path)
|
||||
|
||||
def reset(self):
|
||||
logging.debug("reset app data folder %s", self.lock.path)
|
||||
safe_delete(self.lock.path)
|
||||
|
||||
def close(self):
|
||||
"""do nothing"""
|
||||
|
||||
@contextmanager
|
||||
def locked(self, path):
|
||||
path_lock = self.lock / path
|
||||
with path_lock:
|
||||
yield path_lock.path
|
||||
|
||||
@contextmanager
|
||||
def extract(self, path, to_folder):
|
||||
if to_folder is not None:
|
||||
root = ReentrantFileLock(to_folder())
|
||||
else:
|
||||
root = self.lock / "unzip" / __version__
|
||||
with root.lock_for_key(path.name):
|
||||
dest = root.path / path.name
|
||||
if not dest.exists():
|
||||
extract(path, dest)
|
||||
yield dest
|
||||
|
||||
@property
|
||||
def py_info_at(self):
|
||||
return self.lock / "py_info" / "1"
|
||||
|
||||
def py_info(self, path):
|
||||
return PyInfoStoreDisk(self.py_info_at, path)
|
||||
|
||||
def py_info_clear(self):
|
||||
""" """
|
||||
py_info_folder = self.py_info_at
|
||||
with py_info_folder:
|
||||
for filename in py_info_folder.path.iterdir():
|
||||
if filename.suffix == ".json":
|
||||
with py_info_folder.lock_for_key(filename.stem):
|
||||
if filename.exists():
|
||||
filename.unlink()
|
||||
|
||||
def embed_update_log(self, distribution, for_py_version):
|
||||
return EmbedDistributionUpdateStoreDisk(self.lock / "wheel" / for_py_version / "embed" / "3", distribution)
|
||||
|
||||
@property
|
||||
def house(self):
|
||||
path = self.lock.path / "wheel" / "house"
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
def wheel_image(self, for_py_version, name):
|
||||
return self.lock.path / "wheel" / for_py_version / "image" / "1" / name
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class JSONStoreDisk(ContentStore):
|
||||
def __init__(self, in_folder, key, msg, msg_args):
|
||||
self.in_folder = in_folder
|
||||
self.key = key
|
||||
self.msg = msg
|
||||
self.msg_args = msg_args + (self.file,)
|
||||
|
||||
@property
|
||||
def file(self):
|
||||
return self.in_folder.path / "{}.json".format(self.key)
|
||||
|
||||
def exists(self):
|
||||
return self.file.exists()
|
||||
|
||||
def read(self):
|
||||
data, bad_format = None, False
|
||||
try:
|
||||
data = json.loads(self.file.read_text())
|
||||
logging.debug("got {} from %s".format(self.msg), *self.msg_args)
|
||||
return data
|
||||
except ValueError:
|
||||
bad_format = True
|
||||
except Exception: # noqa
|
||||
pass
|
||||
if bad_format:
|
||||
try:
|
||||
self.remove()
|
||||
except OSError: # reading and writing on the same file may cause race on multiple processes
|
||||
pass
|
||||
return None
|
||||
|
||||
def remove(self):
|
||||
self.file.unlink()
|
||||
logging.debug("removed {} at %s".format(self.msg), *self.msg_args)
|
||||
|
||||
@contextmanager
|
||||
def locked(self):
|
||||
with self.in_folder.lock_for_key(self.key):
|
||||
yield
|
||||
|
||||
def write(self, content):
|
||||
folder = self.file.parent
|
||||
folder.mkdir(parents=True, exist_ok=True)
|
||||
self.file.write_text(ensure_text(json.dumps(content, sort_keys=True, indent=2)))
|
||||
logging.debug("wrote {} at %s".format(self.msg), *self.msg_args)
|
||||
|
||||
|
||||
class PyInfoStoreDisk(JSONStoreDisk):
|
||||
def __init__(self, in_folder, path):
|
||||
key = sha256(str(path).encode("utf-8") if six.PY3 else str(path)).hexdigest()
|
||||
super(PyInfoStoreDisk, self).__init__(in_folder, key, "python info of %s", (path,))
|
||||
|
||||
|
||||
class EmbedDistributionUpdateStoreDisk(JSONStoreDisk):
|
||||
def __init__(self, in_folder, distribution):
|
||||
super(EmbedDistributionUpdateStoreDisk, self).__init__(
|
||||
in_folder,
|
||||
distribution,
|
||||
"embed update of distribution %s",
|
||||
(distribution,),
|
||||
)
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import logging
|
||||
from tempfile import mkdtemp
|
||||
|
||||
from virtualenv.util.path import safe_delete
|
||||
|
||||
from .via_disk_folder import AppDataDiskFolder
|
||||
|
||||
|
||||
class TempAppData(AppDataDiskFolder):
|
||||
transient = True
|
||||
can_update = False
|
||||
|
||||
def __init__(self):
|
||||
super(TempAppData, self).__init__(folder=mkdtemp())
|
||||
logging.debug("created temporary app data folder %s", self.lock.path)
|
||||
|
||||
def reset(self):
|
||||
"""this is a temporary folder, is already empty to start with"""
|
||||
|
||||
def close(self):
|
||||
logging.debug("remove temporary app data folder %s", self.lock.path)
|
||||
safe_delete(self.lock.path)
|
||||
|
||||
def embed_update_log(self, distribution, for_py_version):
|
||||
raise NotImplementedError
|
||||
|
|
@ -0,0 +1 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,124 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import os
|
||||
from argparse import SUPPRESS, ArgumentDefaultsHelpFormatter, ArgumentParser, Namespace
|
||||
from collections import OrderedDict
|
||||
|
||||
from virtualenv.config.convert import get_type
|
||||
|
||||
from ..env_var import get_env_var
|
||||
from ..ini import IniConfig
|
||||
|
||||
|
||||
class VirtualEnvOptions(Namespace):
|
||||
def __init__(self, **kwargs):
|
||||
super(VirtualEnvOptions, self).__init__(**kwargs)
|
||||
self._src = None
|
||||
self._sources = {}
|
||||
|
||||
def set_src(self, key, value, src):
|
||||
setattr(self, key, value)
|
||||
if src.startswith("env var"):
|
||||
src = "env var"
|
||||
self._sources[key] = src
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
if getattr(self, "_src", None) is not None:
|
||||
self._sources[key] = self._src
|
||||
super(VirtualEnvOptions, self).__setattr__(key, value)
|
||||
|
||||
def get_source(self, key):
|
||||
return self._sources.get(key)
|
||||
|
||||
@property
|
||||
def verbosity(self):
|
||||
if not hasattr(self, "verbose") and not hasattr(self, "quiet"):
|
||||
return None
|
||||
return max(self.verbose - self.quiet, 0)
|
||||
|
||||
def __repr__(self):
|
||||
return "{}({})".format(
|
||||
type(self).__name__,
|
||||
", ".join("{}={}".format(k, v) for k, v in vars(self).items() if not k.startswith("_")),
|
||||
)
|
||||
|
||||
|
||||
class VirtualEnvConfigParser(ArgumentParser):
|
||||
"""
|
||||
Custom option parser which updates its defaults by checking the configuration files and environmental variables
|
||||
"""
|
||||
|
||||
def __init__(self, options=None, env=None, *args, **kwargs):
|
||||
env = os.environ if env is None else env
|
||||
self.file_config = IniConfig(env)
|
||||
self.epilog_list = []
|
||||
self.env = env
|
||||
kwargs["epilog"] = self.file_config.epilog
|
||||
kwargs["add_help"] = False
|
||||
kwargs["formatter_class"] = HelpFormatter
|
||||
kwargs["prog"] = "virtualenv"
|
||||
super(VirtualEnvConfigParser, self).__init__(*args, **kwargs)
|
||||
self._fixed = set()
|
||||
if options is not None and not isinstance(options, VirtualEnvOptions):
|
||||
raise TypeError("options must be of type VirtualEnvOptions")
|
||||
self.options = VirtualEnvOptions() if options is None else options
|
||||
self._interpreter = None
|
||||
self._app_data = None
|
||||
|
||||
def _fix_defaults(self):
|
||||
for action in self._actions:
|
||||
action_id = id(action)
|
||||
if action_id not in self._fixed:
|
||||
self._fix_default(action)
|
||||
self._fixed.add(action_id)
|
||||
|
||||
def _fix_default(self, action):
|
||||
if hasattr(action, "default") and hasattr(action, "dest") and action.default != SUPPRESS:
|
||||
as_type = get_type(action)
|
||||
names = OrderedDict((i.lstrip("-").replace("-", "_"), None) for i in action.option_strings)
|
||||
outcome = None
|
||||
for name in names:
|
||||
outcome = get_env_var(name, as_type, self.env)
|
||||
if outcome is not None:
|
||||
break
|
||||
if outcome is None and self.file_config:
|
||||
for name in names:
|
||||
outcome = self.file_config.get(name, as_type)
|
||||
if outcome is not None:
|
||||
break
|
||||
if outcome is not None:
|
||||
action.default, action.default_source = outcome
|
||||
else:
|
||||
outcome = action.default, "default"
|
||||
self.options.set_src(action.dest, *outcome)
|
||||
|
||||
def enable_help(self):
|
||||
self._fix_defaults()
|
||||
self.add_argument("-h", "--help", action="help", default=SUPPRESS, help="show this help message and exit")
|
||||
|
||||
def parse_known_args(self, args=None, namespace=None):
|
||||
if namespace is None:
|
||||
namespace = self.options
|
||||
elif namespace is not self.options:
|
||||
raise ValueError("can only pass in parser.options")
|
||||
self._fix_defaults()
|
||||
self.options._src = "cli"
|
||||
try:
|
||||
namespace.env = self.env
|
||||
return super(VirtualEnvConfigParser, self).parse_known_args(args, namespace=namespace)
|
||||
finally:
|
||||
self.options._src = None
|
||||
|
||||
|
||||
class HelpFormatter(ArgumentDefaultsHelpFormatter):
|
||||
def __init__(self, prog):
|
||||
super(HelpFormatter, self).__init__(prog, max_help_position=32, width=240)
|
||||
|
||||
def _get_help_string(self, action):
|
||||
# noinspection PyProtectedMember
|
||||
text = super(HelpFormatter, self)._get_help_string(action)
|
||||
if hasattr(action, "default_source"):
|
||||
default = " (default: %(default)s)"
|
||||
if text.endswith(default):
|
||||
text = "{} (default: %(default)s -> from %(default_source)s)".format(text[: -len(default)])
|
||||
return text
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
|
||||
class TypeData(object):
|
||||
def __init__(self, default_type, as_type):
|
||||
self.default_type = default_type
|
||||
self.as_type = as_type
|
||||
|
||||
def __repr__(self):
|
||||
return "{}(base={}, as={})".format(self.__class__.__name__, self.default_type, self.as_type)
|
||||
|
||||
def convert(self, value):
|
||||
return self.default_type(value)
|
||||
|
||||
|
||||
class BoolType(TypeData):
|
||||
BOOLEAN_STATES = {
|
||||
"1": True,
|
||||
"yes": True,
|
||||
"true": True,
|
||||
"on": True,
|
||||
"0": False,
|
||||
"no": False,
|
||||
"false": False,
|
||||
"off": False,
|
||||
}
|
||||
|
||||
def convert(self, value):
|
||||
if value.lower() not in self.BOOLEAN_STATES:
|
||||
raise ValueError("Not a boolean: %s" % value)
|
||||
return self.BOOLEAN_STATES[value.lower()]
|
||||
|
||||
|
||||
class NoneType(TypeData):
|
||||
def convert(self, value):
|
||||
if not value:
|
||||
return None
|
||||
return str(value)
|
||||
|
||||
|
||||
class ListType(TypeData):
|
||||
def _validate(self):
|
||||
""" """
|
||||
|
||||
def convert(self, value, flatten=True):
|
||||
values = self.split_values(value)
|
||||
result = []
|
||||
for value in values:
|
||||
sub_values = value.split(os.pathsep)
|
||||
result.extend(sub_values)
|
||||
converted = [self.as_type(i) for i in result]
|
||||
return converted
|
||||
|
||||
def split_values(self, value):
|
||||
"""Split the provided value into a list.
|
||||
|
||||
First this is done by newlines. If there were no newlines in the text,
|
||||
then we next try to split by comma.
|
||||
"""
|
||||
if isinstance(value, (str, bytes)):
|
||||
# Use `splitlines` rather than a custom check for whether there is
|
||||
# more than one line. This ensures that the full `splitlines()`
|
||||
# logic is supported here.
|
||||
values = value.splitlines()
|
||||
if len(values) <= 1:
|
||||
values = value.split(",")
|
||||
values = filter(None, [x.strip() for x in values])
|
||||
else:
|
||||
values = list(value)
|
||||
|
||||
return values
|
||||
|
||||
|
||||
def convert(value, as_type, source):
|
||||
"""Convert the value as a given type where the value comes from the given source"""
|
||||
try:
|
||||
return as_type.convert(value)
|
||||
except Exception as exception:
|
||||
logging.warning("%s failed to convert %r as %r because %r", source, value, as_type, exception)
|
||||
raise
|
||||
|
||||
|
||||
_CONVERT = {bool: BoolType, type(None): NoneType, list: ListType}
|
||||
|
||||
|
||||
def get_type(action):
|
||||
default_type = type(action.default)
|
||||
as_type = default_type if action.type is None else action.type
|
||||
return _CONVERT.get(default_type, TypeData)(default_type, as_type)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"convert",
|
||||
"get_type",
|
||||
)
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from virtualenv.util.six import ensure_str, ensure_text
|
||||
|
||||
from .convert import convert
|
||||
|
||||
|
||||
def get_env_var(key, as_type, env):
|
||||
"""Get the environment variable option.
|
||||
|
||||
:param key: the config key requested
|
||||
:param as_type: the type we would like to convert it to
|
||||
:param env: environment variables to use
|
||||
:return:
|
||||
"""
|
||||
environ_key = ensure_str("VIRTUALENV_{}".format(key.upper()))
|
||||
if env.get(environ_key):
|
||||
value = env[environ_key]
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
source = "env var {}".format(ensure_text(environ_key))
|
||||
as_type = convert(value, as_type, source)
|
||||
return as_type, source
|
||||
except Exception: # note the converter already logs a warning when failures happen
|
||||
pass
|
||||
|
||||
|
||||
__all__ = ("get_env_var",)
|
||||
84
.venv/lib/python3.8/site-packages/virtualenv/config/ini.py
Normal file
84
.venv/lib/python3.8/site-packages/virtualenv/config/ini.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from platformdirs import user_config_dir
|
||||
|
||||
from virtualenv.info import PY3
|
||||
from virtualenv.util import ConfigParser
|
||||
from virtualenv.util.path import Path
|
||||
from virtualenv.util.six import ensure_str
|
||||
|
||||
from .convert import convert
|
||||
|
||||
|
||||
class IniConfig(object):
|
||||
VIRTUALENV_CONFIG_FILE_ENV_VAR = ensure_str("VIRTUALENV_CONFIG_FILE")
|
||||
STATE = {None: "failed to parse", True: "active", False: "missing"}
|
||||
|
||||
section = "virtualenv"
|
||||
|
||||
def __init__(self, env=None):
|
||||
env = os.environ if env is None else env
|
||||
config_file = env.get(self.VIRTUALENV_CONFIG_FILE_ENV_VAR, None)
|
||||
self.is_env_var = config_file is not None
|
||||
config_file = (
|
||||
Path(config_file)
|
||||
if config_file is not None
|
||||
else Path(user_config_dir(appname="virtualenv", appauthor="pypa")) / "virtualenv.ini"
|
||||
)
|
||||
self.config_file = config_file
|
||||
self._cache = {}
|
||||
|
||||
exception = None
|
||||
self.has_config_file = None
|
||||
try:
|
||||
self.has_config_file = self.config_file.exists()
|
||||
except OSError as exc:
|
||||
exception = exc
|
||||
else:
|
||||
if self.has_config_file:
|
||||
self.config_file = self.config_file.resolve()
|
||||
self.config_parser = ConfigParser.ConfigParser()
|
||||
try:
|
||||
self._load()
|
||||
self.has_virtualenv_section = self.config_parser.has_section(self.section)
|
||||
except Exception as exc:
|
||||
exception = exc
|
||||
if exception is not None:
|
||||
logging.error("failed to read config file %s because %r", config_file, exception)
|
||||
|
||||
def _load(self):
|
||||
with self.config_file.open("rt") as file_handler:
|
||||
reader = getattr(self.config_parser, "read_file" if PY3 else "readfp")
|
||||
reader(file_handler)
|
||||
|
||||
def get(self, key, as_type):
|
||||
cache_key = key, as_type
|
||||
if cache_key in self._cache:
|
||||
return self._cache[cache_key]
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
source = "file"
|
||||
raw_value = self.config_parser.get(self.section, key.lower())
|
||||
value = convert(raw_value, as_type, source)
|
||||
result = value, source
|
||||
except Exception:
|
||||
result = None
|
||||
self._cache[cache_key] = result
|
||||
return result
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self.has_config_file) and bool(self.has_virtualenv_section)
|
||||
|
||||
@property
|
||||
def epilog(self):
|
||||
msg = "{}config file {} {} (change{} via env var {})"
|
||||
return msg.format(
|
||||
"\n",
|
||||
self.config_file,
|
||||
self.STATE[self.has_config_file],
|
||||
"d" if self.is_env_var else "",
|
||||
self.VIRTUALENV_CONFIG_FILE_ENV_VAR,
|
||||
)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
239
.venv/lib/python3.8/site-packages/virtualenv/create/creator.py
Normal file
239
.venv/lib/python3.8/site-packages/virtualenv/create/creator.py
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from argparse import ArgumentTypeError
|
||||
from ast import literal_eval
|
||||
from collections import OrderedDict
|
||||
from textwrap import dedent
|
||||
|
||||
from six import add_metaclass
|
||||
|
||||
from virtualenv.discovery.cached_py_info import LogCmd
|
||||
from virtualenv.info import WIN_CPYTHON_2
|
||||
from virtualenv.util.path import Path, safe_delete
|
||||
from virtualenv.util.six import ensure_str, ensure_text
|
||||
from virtualenv.util.subprocess import run_cmd
|
||||
from virtualenv.version import __version__
|
||||
|
||||
from .pyenv_cfg import PyEnvCfg
|
||||
|
||||
HERE = Path(os.path.abspath(__file__)).parent
|
||||
DEBUG_SCRIPT = HERE / "debug.py"
|
||||
|
||||
|
||||
class CreatorMeta(object):
|
||||
def __init__(self):
|
||||
self.error = None
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class Creator(object):
|
||||
"""A class that given a python Interpreter creates a virtual environment"""
|
||||
|
||||
def __init__(self, options, interpreter):
|
||||
"""Construct a new virtual environment creator.
|
||||
|
||||
:param options: the CLI option as parsed from :meth:`add_parser_arguments`
|
||||
:param interpreter: the interpreter to create virtual environment from
|
||||
"""
|
||||
self.interpreter = interpreter
|
||||
self._debug = None
|
||||
self.dest = Path(options.dest)
|
||||
self.clear = options.clear
|
||||
self.no_vcs_ignore = options.no_vcs_ignore
|
||||
self.pyenv_cfg = PyEnvCfg.from_folder(self.dest)
|
||||
self.app_data = options.app_data
|
||||
self.env = options.env
|
||||
|
||||
def __repr__(self):
|
||||
return ensure_str(self.__unicode__())
|
||||
|
||||
def __unicode__(self):
|
||||
return "{}({})".format(self.__class__.__name__, ", ".join("{}={}".format(k, v) for k, v in self._args()))
|
||||
|
||||
def _args(self):
|
||||
return [
|
||||
("dest", ensure_text(str(self.dest))),
|
||||
("clear", self.clear),
|
||||
("no_vcs_ignore", self.no_vcs_ignore),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def can_create(cls, interpreter):
|
||||
"""Determine if we can create a virtual environment.
|
||||
|
||||
:param interpreter: the interpreter in question
|
||||
:return: ``None`` if we can't create, any other object otherwise that will be forwarded to \
|
||||
:meth:`add_parser_arguments`
|
||||
"""
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def add_parser_arguments(cls, parser, interpreter, meta, app_data):
|
||||
"""Add CLI arguments for the creator.
|
||||
|
||||
:param parser: the CLI parser
|
||||
:param app_data: the application data folder
|
||||
:param interpreter: the interpreter we're asked to create virtual environment for
|
||||
:param meta: value as returned by :meth:`can_create`
|
||||
"""
|
||||
parser.add_argument(
|
||||
"dest",
|
||||
help="directory to create virtualenv at",
|
||||
type=cls.validate_dest,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--clear",
|
||||
dest="clear",
|
||||
action="store_true",
|
||||
help="remove the destination directory if exist before starting (will overwrite files otherwise)",
|
||||
default=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-vcs-ignore",
|
||||
dest="no_vcs_ignore",
|
||||
action="store_true",
|
||||
help="don't create VCS ignore directive in the destination directory",
|
||||
default=False,
|
||||
)
|
||||
|
||||
@abstractmethod
|
||||
def create(self):
|
||||
"""Perform the virtual environment creation."""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def validate_dest(cls, raw_value):
|
||||
"""No path separator in the path, valid chars and must be write-able"""
|
||||
|
||||
def non_write_able(dest, value):
|
||||
common = Path(*os.path.commonprefix([value.parts, dest.parts]))
|
||||
raise ArgumentTypeError(
|
||||
"the destination {} is not write-able at {}".format(dest.relative_to(common), common),
|
||||
)
|
||||
|
||||
# the file system must be able to encode
|
||||
# note in newer CPython this is always utf-8 https://www.python.org/dev/peps/pep-0529/
|
||||
encoding = sys.getfilesystemencoding()
|
||||
refused = OrderedDict()
|
||||
kwargs = {"errors": "ignore"} if encoding != "mbcs" else {}
|
||||
for char in ensure_text(raw_value):
|
||||
try:
|
||||
trip = char.encode(encoding, **kwargs).decode(encoding)
|
||||
if trip == char:
|
||||
continue
|
||||
raise ValueError(trip)
|
||||
except ValueError:
|
||||
refused[char] = None
|
||||
if refused:
|
||||
raise ArgumentTypeError(
|
||||
"the file system codec ({}) cannot handle characters {!r} within {!r}".format(
|
||||
encoding,
|
||||
"".join(refused.keys()),
|
||||
raw_value,
|
||||
),
|
||||
)
|
||||
if os.pathsep in raw_value:
|
||||
raise ArgumentTypeError(
|
||||
"destination {!r} must not contain the path separator ({}) as this would break "
|
||||
"the activation scripts".format(raw_value, os.pathsep),
|
||||
)
|
||||
|
||||
value = Path(raw_value)
|
||||
if value.exists() and value.is_file():
|
||||
raise ArgumentTypeError("the destination {} already exists and is a file".format(value))
|
||||
if (3, 3) <= sys.version_info <= (3, 6):
|
||||
# pre 3.6 resolve is always strict, aka must exists, sidestep by using os.path operation
|
||||
dest = Path(os.path.realpath(raw_value))
|
||||
else:
|
||||
dest = Path(os.path.abspath(str(value))).resolve() # on Windows absolute does not imply resolve so use both
|
||||
value = dest
|
||||
while dest:
|
||||
if dest.exists():
|
||||
if os.access(ensure_text(str(dest)), os.W_OK):
|
||||
break
|
||||
else:
|
||||
non_write_able(dest, value)
|
||||
base, _ = dest.parent, dest.name
|
||||
if base == dest:
|
||||
non_write_able(dest, value) # pragma: no cover
|
||||
dest = base
|
||||
return str(value)
|
||||
|
||||
def run(self):
|
||||
if self.dest.exists() and self.clear:
|
||||
logging.debug("delete %s", self.dest)
|
||||
safe_delete(self.dest)
|
||||
self.create()
|
||||
self.set_pyenv_cfg()
|
||||
if not self.no_vcs_ignore:
|
||||
self.setup_ignore_vcs()
|
||||
|
||||
def set_pyenv_cfg(self):
|
||||
self.pyenv_cfg.content = OrderedDict()
|
||||
self.pyenv_cfg["home"] = self.interpreter.system_exec_prefix
|
||||
self.pyenv_cfg["implementation"] = self.interpreter.implementation
|
||||
self.pyenv_cfg["version_info"] = ".".join(str(i) for i in self.interpreter.version_info)
|
||||
self.pyenv_cfg["virtualenv"] = __version__
|
||||
|
||||
def setup_ignore_vcs(self):
|
||||
"""Generate ignore instructions for version control systems."""
|
||||
# mark this folder to be ignored by VCS, handle https://www.python.org/dev/peps/pep-0610/#registered-vcs
|
||||
git_ignore = self.dest / ".gitignore"
|
||||
if not git_ignore.exists():
|
||||
git_ignore.write_text(
|
||||
dedent(
|
||||
"""
|
||||
# created by virtualenv automatically
|
||||
*
|
||||
""",
|
||||
).lstrip(),
|
||||
)
|
||||
# Mercurial - does not support the .hgignore file inside a subdirectory directly, but only if included via the
|
||||
# subinclude directive from root, at which point on might as well ignore the directory itself, see
|
||||
# https://www.selenic.com/mercurial/hgignore.5.html for more details
|
||||
# Bazaar - does not support ignore files in sub-directories, only at root level via .bzrignore
|
||||
# Subversion - does not support ignore files, requires direct manipulation with the svn tool
|
||||
|
||||
@property
|
||||
def debug(self):
|
||||
"""
|
||||
:return: debug information about the virtual environment (only valid after :meth:`create` has run)
|
||||
"""
|
||||
if self._debug is None and self.exe is not None:
|
||||
self._debug = get_env_debug_info(self.exe, self.debug_script(), self.app_data, self.env)
|
||||
return self._debug
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
def debug_script(self):
|
||||
return DEBUG_SCRIPT
|
||||
|
||||
|
||||
def get_env_debug_info(env_exe, debug_script, app_data, env):
|
||||
env = env.copy()
|
||||
env.pop(str("PYTHONPATH"), None)
|
||||
|
||||
with app_data.ensure_extracted(debug_script) as debug_script:
|
||||
cmd = [str(env_exe), str(debug_script)]
|
||||
if WIN_CPYTHON_2:
|
||||
cmd = [ensure_text(i) for i in cmd]
|
||||
logging.debug(str("debug via %r"), LogCmd(cmd))
|
||||
code, out, err = run_cmd(cmd)
|
||||
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
if code != 0:
|
||||
result = literal_eval(out)
|
||||
else:
|
||||
result = json.loads(out)
|
||||
if err:
|
||||
result["err"] = err
|
||||
except Exception as exception:
|
||||
return {"out": out, "err": err, "returncode": code, "exception": repr(exception)}
|
||||
if "sys" in result and "path" in result["sys"]:
|
||||
del result["sys"]["path"][0]
|
||||
return result
|
||||
110
.venv/lib/python3.8/site-packages/virtualenv/create/debug.py
Normal file
110
.venv/lib/python3.8/site-packages/virtualenv/create/debug.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
"""Inspect a target Python interpreter virtual environment wise"""
|
||||
import sys # built-in
|
||||
|
||||
PYPY2_WIN = hasattr(sys, "pypy_version_info") and sys.platform != "win32" and sys.version_info[0] == 2
|
||||
|
||||
|
||||
def encode_path(value):
|
||||
if value is None:
|
||||
return None
|
||||
if not isinstance(value, (str, bytes)):
|
||||
if isinstance(value, type):
|
||||
value = repr(value)
|
||||
else:
|
||||
value = repr(type(value))
|
||||
if isinstance(value, bytes) and not PYPY2_WIN:
|
||||
value = value.decode(sys.getfilesystemencoding())
|
||||
return value
|
||||
|
||||
|
||||
def encode_list_path(value):
|
||||
return [encode_path(i) for i in value]
|
||||
|
||||
|
||||
def run():
|
||||
"""print debug data about the virtual environment"""
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError: # pragma: no cover
|
||||
# this is possible if the standard library cannot be accessed
|
||||
# noinspection PyPep8Naming
|
||||
OrderedDict = dict # pragma: no cover
|
||||
result = OrderedDict([("sys", OrderedDict())])
|
||||
path_keys = (
|
||||
"executable",
|
||||
"_base_executable",
|
||||
"prefix",
|
||||
"base_prefix",
|
||||
"real_prefix",
|
||||
"exec_prefix",
|
||||
"base_exec_prefix",
|
||||
"path",
|
||||
"meta_path",
|
||||
)
|
||||
for key in path_keys:
|
||||
value = getattr(sys, key, None)
|
||||
if isinstance(value, list):
|
||||
value = encode_list_path(value)
|
||||
else:
|
||||
value = encode_path(value)
|
||||
result["sys"][key] = value
|
||||
result["sys"]["fs_encoding"] = sys.getfilesystemencoding()
|
||||
result["sys"]["io_encoding"] = getattr(sys.stdout, "encoding", None)
|
||||
result["version"] = sys.version
|
||||
|
||||
try:
|
||||
import sysconfig
|
||||
|
||||
# https://bugs.python.org/issue22199
|
||||
makefile = getattr(sysconfig, "get_makefile_filename", getattr(sysconfig, "_get_makefile_filename", None))
|
||||
result["makefile_filename"] = encode_path(makefile())
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
import os # landmark
|
||||
|
||||
result["os"] = repr(os)
|
||||
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
import site # site
|
||||
|
||||
result["site"] = repr(site)
|
||||
except ImportError as exception: # pragma: no cover
|
||||
result["site"] = repr(exception) # pragma: no cover
|
||||
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
import datetime # site
|
||||
|
||||
result["datetime"] = repr(datetime)
|
||||
except ImportError as exception: # pragma: no cover
|
||||
result["datetime"] = repr(exception) # pragma: no cover
|
||||
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
import math # site
|
||||
|
||||
result["math"] = repr(math)
|
||||
except ImportError as exception: # pragma: no cover
|
||||
result["math"] = repr(exception) # pragma: no cover
|
||||
|
||||
# try to print out, this will validate if other core modules are available (json in this case)
|
||||
try:
|
||||
import json
|
||||
|
||||
result["json"] = repr(json)
|
||||
except ImportError as exception:
|
||||
result["json"] = repr(exception)
|
||||
else:
|
||||
try:
|
||||
content = json.dumps(result, indent=2)
|
||||
sys.stdout.write(content)
|
||||
except (ValueError, TypeError) as exception: # pragma: no cover
|
||||
sys.stderr.write(repr(exception))
|
||||
sys.stdout.write(repr(result)) # pragma: no cover
|
||||
raise SystemExit(1) # pragma: no cover
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
117
.venv/lib/python3.8/site-packages/virtualenv/create/describe.py
Normal file
117
.venv/lib/python3.8/site-packages/virtualenv/create/describe.py
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
from abc import ABCMeta
|
||||
from collections import OrderedDict
|
||||
|
||||
from six import add_metaclass
|
||||
|
||||
from virtualenv.info import IS_WIN
|
||||
from virtualenv.util.path import Path
|
||||
from virtualenv.util.six import ensure_text
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class Describe(object):
|
||||
"""Given a host interpreter tell us information about what the created interpreter might look like"""
|
||||
|
||||
suffix = ".exe" if IS_WIN else ""
|
||||
|
||||
def __init__(self, dest, interpreter):
|
||||
self.interpreter = interpreter
|
||||
self.dest = dest
|
||||
self._stdlib = None
|
||||
self._stdlib_platform = None
|
||||
self._system_stdlib = None
|
||||
self._conf_vars = None
|
||||
|
||||
@property
|
||||
def bin_dir(self):
|
||||
return self.script_dir
|
||||
|
||||
@property
|
||||
def script_dir(self):
|
||||
return self.dest / self.interpreter.install_path("scripts")
|
||||
|
||||
@property
|
||||
def purelib(self):
|
||||
return self.dest / self.interpreter.install_path("purelib")
|
||||
|
||||
@property
|
||||
def platlib(self):
|
||||
return self.dest / self.interpreter.install_path("platlib")
|
||||
|
||||
@property
|
||||
def libs(self):
|
||||
return list(OrderedDict(((self.platlib, None), (self.purelib, None))).keys())
|
||||
|
||||
@property
|
||||
def stdlib(self):
|
||||
if self._stdlib is None:
|
||||
self._stdlib = Path(self.interpreter.sysconfig_path("stdlib", config_var=self._config_vars))
|
||||
return self._stdlib
|
||||
|
||||
@property
|
||||
def stdlib_platform(self):
|
||||
if self._stdlib_platform is None:
|
||||
self._stdlib_platform = Path(self.interpreter.sysconfig_path("platstdlib", config_var=self._config_vars))
|
||||
return self._stdlib_platform
|
||||
|
||||
@property
|
||||
def _config_vars(self):
|
||||
if self._conf_vars is None:
|
||||
self._conf_vars = self._calc_config_vars(ensure_text(str(self.dest)))
|
||||
return self._conf_vars
|
||||
|
||||
def _calc_config_vars(self, to):
|
||||
return {
|
||||
k: (to if v.startswith(self.interpreter.prefix) else v) for k, v in self.interpreter.sysconfig_vars.items()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def can_describe(cls, interpreter):
|
||||
"""Knows means it knows how the output will look"""
|
||||
return True
|
||||
|
||||
@property
|
||||
def env_name(self):
|
||||
return ensure_text(self.dest.parts[-1])
|
||||
|
||||
@property
|
||||
def exe(self):
|
||||
return self.bin_dir / "{}{}".format(self.exe_stem(), self.suffix)
|
||||
|
||||
@classmethod
|
||||
def exe_stem(cls):
|
||||
"""executable name without suffix - there seems to be no standard way to get this without creating it"""
|
||||
raise NotImplementedError
|
||||
|
||||
def script(self, name):
|
||||
return self.script_dir / "{}{}".format(name, self.suffix)
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class Python2Supports(Describe):
|
||||
@classmethod
|
||||
def can_describe(cls, interpreter):
|
||||
return interpreter.version_info.major == 2 and super(Python2Supports, cls).can_describe(interpreter)
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class Python3Supports(Describe):
|
||||
@classmethod
|
||||
def can_describe(cls, interpreter):
|
||||
return interpreter.version_info.major == 3 and super(Python3Supports, cls).can_describe(interpreter)
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class PosixSupports(Describe):
|
||||
@classmethod
|
||||
def can_describe(cls, interpreter):
|
||||
return interpreter.os == "posix" and super(PosixSupports, cls).can_describe(interpreter)
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class WindowsSupports(Describe):
|
||||
@classmethod
|
||||
def can_describe(cls, interpreter):
|
||||
return interpreter.os == "nt" and super(WindowsSupports, cls).can_describe(interpreter)
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
from virtualenv.util.six import ensure_text
|
||||
|
||||
|
||||
class PyEnvCfg(object):
|
||||
def __init__(self, content, path):
|
||||
self.content = content
|
||||
self.path = path
|
||||
|
||||
@classmethod
|
||||
def from_folder(cls, folder):
|
||||
return cls.from_file(folder / "pyvenv.cfg")
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, path):
|
||||
content = cls._read_values(path) if path.exists() else OrderedDict()
|
||||
return PyEnvCfg(content, path)
|
||||
|
||||
@staticmethod
|
||||
def _read_values(path):
|
||||
content = OrderedDict()
|
||||
for line in path.read_text(encoding="utf-8").splitlines():
|
||||
equals_at = line.index("=")
|
||||
key = line[:equals_at].strip()
|
||||
value = line[equals_at + 1 :].strip()
|
||||
content[key] = value
|
||||
return content
|
||||
|
||||
def write(self):
|
||||
logging.debug("write %s", ensure_text(str(self.path)))
|
||||
text = ""
|
||||
for key, value in self.content.items():
|
||||
line = "{} = {}".format(key, value)
|
||||
logging.debug("\t%s", line)
|
||||
text += line
|
||||
text += "\n"
|
||||
self.path.write_text(text, encoding="utf-8")
|
||||
|
||||
def refresh(self):
|
||||
self.content = self._read_values(self.path)
|
||||
return self.content
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.content[key] = value
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.content[key]
|
||||
|
||||
def __contains__(self, item):
|
||||
return item in self.content
|
||||
|
||||
def update(self, other):
|
||||
self.content.update(other)
|
||||
return self
|
||||
|
||||
def __repr__(self):
|
||||
return "{}(path={})".format(self.__class__.__name__, self.path)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,130 @@
|
|||
"""Patches that are applied at runtime to the virtual environment"""
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
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.
|
||||
"""
|
||||
# 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 = "install_{}".format(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"
|
||||
if sys.version_info > (3, 4):
|
||||
# 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 = []
|
||||
|
||||
def find_spec(self, fullname, path, target=None):
|
||||
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:
|
||||
setattr(spec.loader, func_name, partial(func, old))
|
||||
except AttributeError:
|
||||
pass # C-Extension loaders are r/o such as zipimporter with <python 3.7
|
||||
return spec
|
||||
finally:
|
||||
self.fullname = 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())
|
||||
else:
|
||||
# https://www.python.org/dev/peps/pep-0302/
|
||||
from imp import find_module
|
||||
from pkgutil import ImpImporter, ImpLoader
|
||||
|
||||
class _VirtualenvImporter(object, ImpImporter):
|
||||
def __init__(self, path=None):
|
||||
object.__init__(self)
|
||||
ImpImporter.__init__(self, path)
|
||||
|
||||
def find_module(self, fullname, path=None):
|
||||
if fullname in _DISTUTILS_PATCH:
|
||||
try:
|
||||
return _VirtualenvLoader(fullname, *find_module(fullname.split(".")[-1], path))
|
||||
except ImportError:
|
||||
pass
|
||||
return None
|
||||
|
||||
class _VirtualenvLoader(object, ImpLoader):
|
||||
def __init__(self, fullname, file, filename, etc):
|
||||
object.__init__(self)
|
||||
ImpLoader.__init__(self, fullname, file, filename, etc)
|
||||
|
||||
def load_module(self, fullname):
|
||||
module = super(_VirtualenvLoader, self).load_module(fullname)
|
||||
patch_dist(module)
|
||||
module.__loader__ = None # distlib fallback
|
||||
return module
|
||||
|
||||
sys.meta_path.append(_VirtualenvImporter())
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import logging
|
||||
import os
|
||||
from abc import ABCMeta
|
||||
|
||||
from six import add_metaclass
|
||||
|
||||
from virtualenv.info import fs_supports_symlink
|
||||
from virtualenv.util.path import Path
|
||||
from virtualenv.util.six import ensure_text
|
||||
|
||||
from ..creator import Creator, CreatorMeta
|
||||
|
||||
|
||||
class ViaGlobalRefMeta(CreatorMeta):
|
||||
def __init__(self):
|
||||
super(ViaGlobalRefMeta, self).__init__()
|
||||
self.copy_error = None
|
||||
self.symlink_error = None
|
||||
if not fs_supports_symlink():
|
||||
self.symlink_error = "the filesystem does not supports symlink"
|
||||
|
||||
@property
|
||||
def can_copy(self):
|
||||
return not self.copy_error
|
||||
|
||||
@property
|
||||
def can_symlink(self):
|
||||
return not self.symlink_error
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class ViaGlobalRefApi(Creator):
|
||||
def __init__(self, options, interpreter):
|
||||
super(ViaGlobalRefApi, self).__init__(options, interpreter)
|
||||
self.symlinks = self._should_symlink(options)
|
||||
self.enable_system_site_package = options.system_site
|
||||
|
||||
@staticmethod
|
||||
def _should_symlink(options):
|
||||
# Priority of where the option is set to follow the order: CLI, env var, file, hardcoded.
|
||||
# If both set at same level prefers copy over symlink.
|
||||
copies, symlinks = getattr(options, "copies", False), getattr(options, "symlinks", False)
|
||||
copy_src, sym_src = options.get_source("copies"), options.get_source("symlinks")
|
||||
for level in ["cli", "env var", "file", "default"]:
|
||||
s_opt = symlinks if sym_src == level else None
|
||||
c_opt = copies if copy_src == level else None
|
||||
if s_opt is True and c_opt is True:
|
||||
return False
|
||||
if s_opt is True:
|
||||
return True
|
||||
if c_opt is True:
|
||||
return False
|
||||
return False # fallback to copy
|
||||
|
||||
@classmethod
|
||||
def add_parser_arguments(cls, parser, interpreter, meta, app_data):
|
||||
super(ViaGlobalRefApi, cls).add_parser_arguments(parser, interpreter, meta, app_data)
|
||||
parser.add_argument(
|
||||
"--system-site-packages",
|
||||
default=False,
|
||||
action="store_true",
|
||||
dest="system_site",
|
||||
help="give the virtual environment access to the system site-packages dir",
|
||||
)
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
if not meta.can_symlink and not meta.can_copy:
|
||||
raise RuntimeError("neither symlink or copy method supported")
|
||||
if meta.can_symlink:
|
||||
group.add_argument(
|
||||
"--symlinks",
|
||||
default=True,
|
||||
action="store_true",
|
||||
dest="symlinks",
|
||||
help="try to use symlinks rather than copies, when symlinks are not the default for the platform",
|
||||
)
|
||||
if meta.can_copy:
|
||||
group.add_argument(
|
||||
"--copies",
|
||||
"--always-copy",
|
||||
default=not meta.can_symlink,
|
||||
action="store_true",
|
||||
dest="copies",
|
||||
help="try to use copies rather than symlinks, even when symlinks are the default for the platform",
|
||||
)
|
||||
|
||||
def create(self):
|
||||
self.install_patch()
|
||||
|
||||
def install_patch(self):
|
||||
text = self.env_patch_text()
|
||||
if text:
|
||||
pth = self.purelib / "_virtualenv.pth"
|
||||
logging.debug("create virtualenv import hook file %s", ensure_text(str(pth)))
|
||||
pth.write_text("import _virtualenv")
|
||||
dest_path = self.purelib / "_virtualenv.py"
|
||||
logging.debug("create %s", ensure_text(str(dest_path)))
|
||||
dest_path.write_text(text)
|
||||
|
||||
def env_patch_text(self):
|
||||
"""Patch the distutils package to not be derailed by its configuration files"""
|
||||
with self.app_data.ensure_extracted(Path(__file__).parent / "_virtualenv.py") as resolved_path:
|
||||
text = resolved_path.read_text()
|
||||
return text.replace('"__SCRIPT_DIR__"', repr(os.path.relpath(str(self.script_dir), str(self.purelib))))
|
||||
|
||||
def _args(self):
|
||||
return super(ViaGlobalRefApi, self)._args() + [("global", self.enable_system_site_package)]
|
||||
|
||||
def set_pyenv_cfg(self):
|
||||
super(ViaGlobalRefApi, self).set_pyenv_cfg()
|
||||
self.pyenv_cfg["include-system-site-packages"] = "true" if self.enable_system_site_package else "false"
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,17 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from abc import ABCMeta
|
||||
|
||||
from six import add_metaclass
|
||||
|
||||
from virtualenv.create.creator import Creator
|
||||
from virtualenv.create.describe import Describe
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class VirtualenvBuiltin(Creator, Describe):
|
||||
"""A creator that does operations itself without delegation, if we can create it we can also describe it"""
|
||||
|
||||
def __init__(self, options, interpreter):
|
||||
Creator.__init__(self, options, interpreter)
|
||||
Describe.__init__(self, self.dest, interpreter)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,65 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from abc import ABCMeta
|
||||
from collections import OrderedDict
|
||||
|
||||
from six import add_metaclass
|
||||
|
||||
from virtualenv.create.describe import PosixSupports, WindowsSupports
|
||||
from virtualenv.create.via_global_ref.builtin.ref import RefMust, RefWhen
|
||||
from virtualenv.util.path import Path
|
||||
|
||||
from ..via_global_self_do import ViaGlobalRefVirtualenvBuiltin
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class CPython(ViaGlobalRefVirtualenvBuiltin):
|
||||
@classmethod
|
||||
def can_describe(cls, interpreter):
|
||||
return interpreter.implementation == "CPython" and super(CPython, cls).can_describe(interpreter)
|
||||
|
||||
@classmethod
|
||||
def exe_stem(cls):
|
||||
return "python"
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class CPythonPosix(CPython, PosixSupports):
|
||||
"""Create a CPython virtual environment on POSIX platforms"""
|
||||
|
||||
@classmethod
|
||||
def _executables(cls, interpreter):
|
||||
host_exe = Path(interpreter.system_executable)
|
||||
major, minor = interpreter.version_info.major, interpreter.version_info.minor
|
||||
targets = OrderedDict(
|
||||
(i, None) for i in ["python", "python{}".format(major), "python{}.{}".format(major, minor), host_exe.name]
|
||||
)
|
||||
must = RefMust.COPY if interpreter.version_info.major == 2 else RefMust.NA
|
||||
yield host_exe, list(targets.keys()), must, RefWhen.ANY
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class CPythonWindows(CPython, WindowsSupports):
|
||||
@classmethod
|
||||
def _executables(cls, interpreter):
|
||||
# symlink of the python executables does not work reliably, copy always instead
|
||||
# - https://bugs.python.org/issue42013
|
||||
# - venv
|
||||
host = cls.host_python(interpreter)
|
||||
for path in (host.parent / n for n in {"python.exe", host.name}):
|
||||
yield host, [path.name], RefMust.COPY, RefWhen.ANY
|
||||
# for more info on pythonw.exe see https://stackoverflow.com/a/30313091
|
||||
python_w = host.parent / "pythonw.exe"
|
||||
yield python_w, [python_w.name], RefMust.COPY, RefWhen.ANY
|
||||
|
||||
@classmethod
|
||||
def host_python(cls, interpreter):
|
||||
return Path(interpreter.system_executable)
|
||||
|
||||
|
||||
def is_mac_os_framework(interpreter):
|
||||
if interpreter.platform == "darwin":
|
||||
framework_var = interpreter.sysconfig_vars.get("PYTHONFRAMEWORK")
|
||||
value = "Python3" if interpreter.version_info.major == 3 else "Python"
|
||||
return framework_var == value
|
||||
return False
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import abc
|
||||
import logging
|
||||
|
||||
from six import add_metaclass
|
||||
|
||||
from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest
|
||||
from virtualenv.util.path import Path
|
||||
|
||||
from ..python2.python2 import Python2
|
||||
from .common import CPython, CPythonPosix, CPythonWindows, is_mac_os_framework
|
||||
|
||||
|
||||
@add_metaclass(abc.ABCMeta)
|
||||
class CPython2(CPython, Python2):
|
||||
"""Create a CPython version 2 virtual environment"""
|
||||
|
||||
@classmethod
|
||||
def sources(cls, interpreter):
|
||||
for src in super(CPython2, cls).sources(interpreter):
|
||||
yield src
|
||||
# include folder needed on Python 2 as we don't have pyenv.cfg
|
||||
host_include_marker = cls.host_include_marker(interpreter)
|
||||
if host_include_marker.exists():
|
||||
yield PathRefToDest(host_include_marker.parent, dest=lambda self, _: self.include)
|
||||
|
||||
@classmethod
|
||||
def needs_stdlib_py_module(cls):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def host_include_marker(cls, interpreter):
|
||||
return Path(interpreter.system_include) / "Python.h"
|
||||
|
||||
@property
|
||||
def include(self):
|
||||
# the pattern include the distribution name too at the end, remove that via the parent call
|
||||
return (self.dest / self.interpreter.install_path("headers")).parent
|
||||
|
||||
@classmethod
|
||||
def modules(cls):
|
||||
return [
|
||||
"os", # landmark to set sys.prefix
|
||||
]
|
||||
|
||||
def ensure_directories(self):
|
||||
dirs = super(CPython2, self).ensure_directories()
|
||||
host_include_marker = self.host_include_marker(self.interpreter)
|
||||
if host_include_marker.exists():
|
||||
dirs.add(self.include.parent)
|
||||
else:
|
||||
logging.debug("no include folders as can't find include marker %s", host_include_marker)
|
||||
return dirs
|
||||
|
||||
|
||||
@add_metaclass(abc.ABCMeta)
|
||||
class CPython2PosixBase(CPython2, CPythonPosix):
|
||||
"""common to macOs framework builds and other posix CPython2"""
|
||||
|
||||
@classmethod
|
||||
def sources(cls, interpreter):
|
||||
for src in super(CPython2PosixBase, cls).sources(interpreter):
|
||||
yield src
|
||||
|
||||
# check if the makefile exists and if so make it available under the virtual environment
|
||||
make_file = Path(interpreter.sysconfig["makefile_filename"])
|
||||
if make_file.exists() and str(make_file).startswith(interpreter.prefix):
|
||||
under_prefix = make_file.relative_to(Path(interpreter.prefix))
|
||||
yield PathRefToDest(make_file, dest=lambda self, s: self.dest / under_prefix)
|
||||
|
||||
|
||||
class CPython2Posix(CPython2PosixBase):
|
||||
"""CPython 2 on POSIX (excluding macOs framework builds)"""
|
||||
|
||||
@classmethod
|
||||
def can_describe(cls, interpreter):
|
||||
return is_mac_os_framework(interpreter) is False and super(CPython2Posix, cls).can_describe(interpreter)
|
||||
|
||||
@classmethod
|
||||
def sources(cls, interpreter):
|
||||
for src in super(CPython2Posix, cls).sources(interpreter):
|
||||
yield src
|
||||
# landmark for exec_prefix
|
||||
exec_marker_file, to_path, _ = cls.from_stdlib(cls.mappings(interpreter), "lib-dynload")
|
||||
yield PathRefToDest(exec_marker_file, dest=to_path)
|
||||
|
||||
|
||||
class CPython2Windows(CPython2, CPythonWindows):
|
||||
"""CPython 2 on Windows"""
|
||||
|
||||
@classmethod
|
||||
def sources(cls, interpreter):
|
||||
for src in super(CPython2Windows, cls).sources(interpreter):
|
||||
yield src
|
||||
py27_dll = Path(interpreter.system_executable).parent / "python27.dll"
|
||||
if py27_dll.exists(): # this might be global in the Windows folder in which case it's alright to be missing
|
||||
yield PathRefToDest(py27_dll, dest=cls.to_bin)
|
||||
|
||||
libs = Path(interpreter.system_prefix) / "libs"
|
||||
if libs.exists():
|
||||
yield PathRefToDest(libs, dest=lambda self, s: self.dest / s.name)
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import abc
|
||||
from textwrap import dedent
|
||||
|
||||
from six import add_metaclass
|
||||
|
||||
from virtualenv.create.describe import Python3Supports
|
||||
from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest
|
||||
from virtualenv.create.via_global_ref.store import is_store_python
|
||||
from virtualenv.util.path import Path
|
||||
|
||||
from .common import CPython, CPythonPosix, CPythonWindows, is_mac_os_framework
|
||||
|
||||
|
||||
@add_metaclass(abc.ABCMeta)
|
||||
class CPython3(CPython, Python3Supports):
|
||||
""" """
|
||||
|
||||
|
||||
class CPython3Posix(CPythonPosix, CPython3):
|
||||
@classmethod
|
||||
def can_describe(cls, interpreter):
|
||||
return is_mac_os_framework(interpreter) is False and super(CPython3Posix, cls).can_describe(interpreter)
|
||||
|
||||
def env_patch_text(self):
|
||||
text = super(CPython3Posix, self).env_patch_text()
|
||||
if self.pyvenv_launch_patch_active(self.interpreter):
|
||||
text += dedent(
|
||||
"""
|
||||
# for https://github.com/python/cpython/pull/9516, see https://github.com/pypa/virtualenv/issues/1704
|
||||
import os
|
||||
if "__PYVENV_LAUNCHER__" in os.environ:
|
||||
del os.environ["__PYVENV_LAUNCHER__"]
|
||||
""",
|
||||
)
|
||||
return text
|
||||
|
||||
@classmethod
|
||||
def pyvenv_launch_patch_active(cls, interpreter):
|
||||
ver = interpreter.version_info
|
||||
return interpreter.platform == "darwin" and ((3, 7, 8) > ver >= (3, 7) or (3, 8, 3) > ver >= (3, 8))
|
||||
|
||||
|
||||
class CPython3Windows(CPythonWindows, CPython3):
|
||||
""" """
|
||||
|
||||
@classmethod
|
||||
def setup_meta(cls, interpreter):
|
||||
if is_store_python(interpreter): # store python is not supported here
|
||||
return None
|
||||
return super(CPython3Windows, cls).setup_meta(interpreter)
|
||||
|
||||
@classmethod
|
||||
def sources(cls, interpreter):
|
||||
for src in super(CPython3Windows, cls).sources(interpreter):
|
||||
yield src
|
||||
if not cls.has_shim(interpreter):
|
||||
for src in cls.include_dll_and_pyd(interpreter):
|
||||
yield src
|
||||
|
||||
@classmethod
|
||||
def has_shim(cls, interpreter):
|
||||
return interpreter.version_info.minor >= 7 and cls.shim(interpreter) is not None
|
||||
|
||||
@classmethod
|
||||
def shim(cls, interpreter):
|
||||
shim = Path(interpreter.system_stdlib) / "venv" / "scripts" / "nt" / "python.exe"
|
||||
if shim.exists():
|
||||
return shim
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def host_python(cls, interpreter):
|
||||
if cls.has_shim(interpreter):
|
||||
# starting with CPython 3.7 Windows ships with a venvlauncher.exe that avoids the need for dll/pyd copies
|
||||
# it also means the wrapper must be copied to avoid bugs such as https://bugs.python.org/issue42013
|
||||
return cls.shim(interpreter)
|
||||
return super(CPython3Windows, cls).host_python(interpreter)
|
||||
|
||||
@classmethod
|
||||
def include_dll_and_pyd(cls, interpreter):
|
||||
dll_folder = Path(interpreter.system_prefix) / "DLLs"
|
||||
host_exe_folder = Path(interpreter.system_executable).parent
|
||||
for folder in [host_exe_folder, dll_folder]:
|
||||
for file in folder.iterdir():
|
||||
if file.suffix in (".pyd", ".dll"):
|
||||
yield PathRefToDest(file, dest=cls.to_dll_and_pyd)
|
||||
|
||||
def to_dll_and_pyd(self, src):
|
||||
return self.bin_dir / src.name
|
||||
|
|
@ -0,0 +1,341 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""The Apple Framework builds require their own customization"""
|
||||
import logging
|
||||
import os
|
||||
import struct
|
||||
import subprocess
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from textwrap import dedent
|
||||
|
||||
from six import add_metaclass, text_type
|
||||
|
||||
from virtualenv.create.via_global_ref.builtin.ref import ExePathRefToDest, PathRefToDest, RefMust
|
||||
from virtualenv.info import IS_MAC_ARM64
|
||||
from virtualenv.util.path import Path
|
||||
from virtualenv.util.six import ensure_text
|
||||
|
||||
from .common import CPython, CPythonPosix, is_mac_os_framework
|
||||
from .cpython2 import CPython2PosixBase
|
||||
from .cpython3 import CPython3
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class CPythonmacOsFramework(CPython):
|
||||
@classmethod
|
||||
def can_describe(cls, interpreter):
|
||||
return is_mac_os_framework(interpreter) and super(CPythonmacOsFramework, cls).can_describe(interpreter)
|
||||
|
||||
@classmethod
|
||||
def sources(cls, interpreter):
|
||||
for src in super(CPythonmacOsFramework, cls).sources(interpreter):
|
||||
yield src
|
||||
# add a symlink to the host python image
|
||||
exe = cls.image_ref(interpreter)
|
||||
ref = PathRefToDest(exe, dest=lambda self, _: self.dest / ".Python", must=RefMust.SYMLINK)
|
||||
yield ref
|
||||
|
||||
def create(self):
|
||||
super(CPythonmacOsFramework, self).create()
|
||||
|
||||
# change the install_name of the copied python executables
|
||||
target = "@executable_path/../.Python"
|
||||
current = self.current_mach_o_image_path()
|
||||
for src in self._sources:
|
||||
if isinstance(src, ExePathRefToDest):
|
||||
if src.must == RefMust.COPY or not self.symlinks:
|
||||
exes = [self.bin_dir / src.base]
|
||||
if not self.symlinks:
|
||||
exes.extend(self.bin_dir / a for a in src.aliases)
|
||||
for exe in exes:
|
||||
fix_mach_o(str(exe), current, target, self.interpreter.max_size)
|
||||
|
||||
@classmethod
|
||||
def _executables(cls, interpreter):
|
||||
for _, targets, must, when in super(CPythonmacOsFramework, cls)._executables(interpreter):
|
||||
# Make sure we use the embedded interpreter inside the framework, even if sys.executable points to the
|
||||
# stub executable in ${sys.prefix}/bin.
|
||||
# See http://groups.google.com/group/python-virtualenv/browse_thread/thread/17cab2f85da75951
|
||||
fixed_host_exe = Path(interpreter.prefix) / "Resources" / "Python.app" / "Contents" / "MacOS" / "Python"
|
||||
yield fixed_host_exe, targets, must, when
|
||||
|
||||
@abstractmethod
|
||||
def current_mach_o_image_path(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def image_ref(cls, interpreter):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class CPython2macOsFramework(CPythonmacOsFramework, CPython2PosixBase):
|
||||
@classmethod
|
||||
def can_create(cls, interpreter):
|
||||
if not IS_MAC_ARM64 and super(CPython2macOsFramework, cls).can_describe(interpreter):
|
||||
return super(CPython2macOsFramework, cls).can_create(interpreter)
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def image_ref(cls, interpreter):
|
||||
return Path(interpreter.prefix) / "Python"
|
||||
|
||||
def current_mach_o_image_path(self):
|
||||
return os.path.join(self.interpreter.prefix, "Python")
|
||||
|
||||
@classmethod
|
||||
def sources(cls, interpreter):
|
||||
for src in super(CPython2macOsFramework, cls).sources(interpreter):
|
||||
yield src
|
||||
# landmark for exec_prefix
|
||||
exec_marker_file, to_path, _ = cls.from_stdlib(cls.mappings(interpreter), "lib-dynload")
|
||||
yield PathRefToDest(exec_marker_file, dest=to_path)
|
||||
|
||||
@property
|
||||
def reload_code(self):
|
||||
result = super(CPython2macOsFramework, self).reload_code
|
||||
result = dedent(
|
||||
"""
|
||||
# the bundled site.py always adds the global site package if we're on python framework build, escape this
|
||||
import sysconfig
|
||||
config = sysconfig.get_config_vars()
|
||||
before = config["PYTHONFRAMEWORK"]
|
||||
try:
|
||||
config["PYTHONFRAMEWORK"] = ""
|
||||
{}
|
||||
finally:
|
||||
config["PYTHONFRAMEWORK"] = before
|
||||
""".format(
|
||||
result,
|
||||
),
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
class CPython2macOsArmFramework(CPython2macOsFramework, CPythonmacOsFramework, CPython2PosixBase):
|
||||
@classmethod
|
||||
def can_create(cls, interpreter):
|
||||
if IS_MAC_ARM64 and super(CPythonmacOsFramework, cls).can_describe(interpreter):
|
||||
return super(CPythonmacOsFramework, cls).can_create(interpreter)
|
||||
return False
|
||||
|
||||
def create(self):
|
||||
super(CPython2macOsFramework, self).create()
|
||||
self.fix_signature()
|
||||
|
||||
def fix_signature(self):
|
||||
"""
|
||||
On Apple M1 machines (arm64 chips), rewriting the python executable invalidates its signature.
|
||||
In python2 this results in a unusable python exe which just dies.
|
||||
As a temporary workaround we can codesign the python exe during the creation process.
|
||||
"""
|
||||
exe = self.exe
|
||||
try:
|
||||
logging.debug("Changing signature of copied python exe %s", exe)
|
||||
bak_dir = exe.parent / "bk"
|
||||
# Reset the signing on Darwin since the exe has been modified.
|
||||
# Note codesign fails on the original exe, it needs to be copied and moved back.
|
||||
bak_dir.mkdir(parents=True, exist_ok=True)
|
||||
subprocess.check_call(["cp", text_type(exe), text_type(bak_dir)])
|
||||
subprocess.check_call(["mv", text_type(bak_dir / exe.name), text_type(exe)])
|
||||
bak_dir.rmdir()
|
||||
metadata = "--preserve-metadata=identifier,entitlements,flags,runtime"
|
||||
cmd = ["codesign", "-s", "-", metadata, "-f", text_type(exe)]
|
||||
logging.debug("Changing Signature: %s", cmd)
|
||||
subprocess.check_call(cmd)
|
||||
except Exception:
|
||||
logging.fatal("Could not change MacOS code signing on copied python exe at %s", exe)
|
||||
raise
|
||||
|
||||
|
||||
class CPython3macOsFramework(CPythonmacOsFramework, CPython3, CPythonPosix):
|
||||
@classmethod
|
||||
def image_ref(cls, interpreter):
|
||||
return Path(interpreter.prefix) / "Python3"
|
||||
|
||||
def current_mach_o_image_path(self):
|
||||
return "@executable_path/../../../../Python3"
|
||||
|
||||
@property
|
||||
def reload_code(self):
|
||||
result = super(CPython3macOsFramework, self).reload_code
|
||||
result = dedent(
|
||||
"""
|
||||
# the bundled site.py always adds the global site package if we're on python framework build, escape this
|
||||
import sys
|
||||
before = sys._framework
|
||||
try:
|
||||
sys._framework = None
|
||||
{}
|
||||
finally:
|
||||
sys._framework = before
|
||||
""".format(
|
||||
result,
|
||||
),
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def fix_mach_o(exe, current, new, max_size):
|
||||
"""
|
||||
https://en.wikipedia.org/wiki/Mach-O
|
||||
|
||||
Mach-O, short for Mach object file format, is a file format for executables, object code, shared libraries,
|
||||
dynamically-loaded code, and core dumps. A replacement for the a.out format, Mach-O offers more extensibility and
|
||||
faster access to information in the symbol table.
|
||||
|
||||
Each Mach-O file is made up of one Mach-O header, followed by a series of load commands, followed by one or more
|
||||
segments, each of which contains between 0 and 255 sections. Mach-O uses the REL relocation format to handle
|
||||
references to symbols. When looking up symbols Mach-O uses a two-level namespace that encodes each symbol into an
|
||||
'object/symbol name' pair that is then linearly searched for by first the object and then the symbol name.
|
||||
|
||||
The basic structure—a list of variable-length "load commands" that reference pages of data elsewhere in the file—was
|
||||
also used in the executable file format for Accent. The Accent file format was in turn, based on an idea from Spice
|
||||
Lisp.
|
||||
|
||||
With the introduction of Mac OS X 10.6 platform the Mach-O file underwent a significant modification that causes
|
||||
binaries compiled on a computer running 10.6 or later to be (by default) executable only on computers running Mac
|
||||
OS X 10.6 or later. The difference stems from load commands that the dynamic linker, in previous Mac OS X versions,
|
||||
does not understand. Another significant change to the Mach-O format is the change in how the Link Edit tables
|
||||
(found in the __LINKEDIT section) function. In 10.6 these new Link Edit tables are compressed by removing unused and
|
||||
unneeded bits of information, however Mac OS X 10.5 and earlier cannot read this new Link Edit table format.
|
||||
"""
|
||||
try:
|
||||
logging.debug("change Mach-O for %s from %s to %s", ensure_text(exe), current, ensure_text(new))
|
||||
_builtin_change_mach_o(max_size)(exe, current, new)
|
||||
except Exception as e:
|
||||
logging.warning("Could not call _builtin_change_mac_o: %s. " "Trying to call install_name_tool instead.", e)
|
||||
try:
|
||||
cmd = ["install_name_tool", "-change", current, new, exe]
|
||||
subprocess.check_call(cmd)
|
||||
except Exception:
|
||||
logging.fatal("Could not call install_name_tool -- you must " "have Apple's development tools installed")
|
||||
raise
|
||||
|
||||
|
||||
def _builtin_change_mach_o(maxint):
|
||||
MH_MAGIC = 0xFEEDFACE
|
||||
MH_CIGAM = 0xCEFAEDFE
|
||||
MH_MAGIC_64 = 0xFEEDFACF
|
||||
MH_CIGAM_64 = 0xCFFAEDFE
|
||||
FAT_MAGIC = 0xCAFEBABE
|
||||
BIG_ENDIAN = ">"
|
||||
LITTLE_ENDIAN = "<"
|
||||
LC_LOAD_DYLIB = 0xC
|
||||
|
||||
class FileView(object):
|
||||
"""A proxy for file-like objects that exposes a given view of a file. Modified from macholib."""
|
||||
|
||||
def __init__(self, file_obj, start=0, size=maxint):
|
||||
if isinstance(file_obj, FileView):
|
||||
self._file_obj = file_obj._file_obj
|
||||
else:
|
||||
self._file_obj = file_obj
|
||||
self._start = start
|
||||
self._end = start + size
|
||||
self._pos = 0
|
||||
|
||||
def __repr__(self):
|
||||
return "<fileview [{:d}, {:d}] {!r}>".format(self._start, self._end, self._file_obj)
|
||||
|
||||
def tell(self):
|
||||
return self._pos
|
||||
|
||||
def _checkwindow(self, seek_to, op):
|
||||
if not (self._start <= seek_to <= self._end):
|
||||
msg = "{} to offset {:d} is outside window [{:d}, {:d}]".format(op, seek_to, self._start, self._end)
|
||||
raise IOError(msg)
|
||||
|
||||
def seek(self, offset, whence=0):
|
||||
seek_to = offset
|
||||
if whence == os.SEEK_SET:
|
||||
seek_to += self._start
|
||||
elif whence == os.SEEK_CUR:
|
||||
seek_to += self._start + self._pos
|
||||
elif whence == os.SEEK_END:
|
||||
seek_to += self._end
|
||||
else:
|
||||
raise IOError("Invalid whence argument to seek: {!r}".format(whence))
|
||||
self._checkwindow(seek_to, "seek")
|
||||
self._file_obj.seek(seek_to)
|
||||
self._pos = seek_to - self._start
|
||||
|
||||
def write(self, content):
|
||||
here = self._start + self._pos
|
||||
self._checkwindow(here, "write")
|
||||
self._checkwindow(here + len(content), "write")
|
||||
self._file_obj.seek(here, os.SEEK_SET)
|
||||
self._file_obj.write(content)
|
||||
self._pos += len(content)
|
||||
|
||||
def read(self, size=maxint):
|
||||
assert size >= 0
|
||||
here = self._start + self._pos
|
||||
self._checkwindow(here, "read")
|
||||
size = min(size, self._end - here)
|
||||
self._file_obj.seek(here, os.SEEK_SET)
|
||||
read_bytes = self._file_obj.read(size)
|
||||
self._pos += len(read_bytes)
|
||||
return read_bytes
|
||||
|
||||
def read_data(file, endian, num=1):
|
||||
"""Read a given number of 32-bits unsigned integers from the given file with the given endianness."""
|
||||
res = struct.unpack(endian + "L" * num, file.read(num * 4))
|
||||
if len(res) == 1:
|
||||
return res[0]
|
||||
return res
|
||||
|
||||
def mach_o_change(at_path, what, value):
|
||||
"""Replace a given name (what) in any LC_LOAD_DYLIB command found in the given binary with a new name (value),
|
||||
provided it's shorter."""
|
||||
|
||||
def do_macho(file, bits, endian):
|
||||
# Read Mach-O header (the magic number is assumed read by the caller)
|
||||
cpu_type, cpu_sub_type, file_type, n_commands, size_of_commands, flags = read_data(file, endian, 6)
|
||||
# 64-bits header has one more field.
|
||||
if bits == 64:
|
||||
read_data(file, endian)
|
||||
# The header is followed by n commands
|
||||
for _ in range(n_commands):
|
||||
where = file.tell()
|
||||
# Read command header
|
||||
cmd, cmd_size = read_data(file, endian, 2)
|
||||
if cmd == LC_LOAD_DYLIB:
|
||||
# The first data field in LC_LOAD_DYLIB commands is the offset of the name, starting from the
|
||||
# beginning of the command.
|
||||
name_offset = read_data(file, endian)
|
||||
file.seek(where + name_offset, os.SEEK_SET)
|
||||
# Read the NUL terminated string
|
||||
load = file.read(cmd_size - name_offset).decode()
|
||||
load = load[: load.index("\0")]
|
||||
# If the string is what is being replaced, overwrite it.
|
||||
if load == what:
|
||||
file.seek(where + name_offset, os.SEEK_SET)
|
||||
file.write(value.encode() + b"\0")
|
||||
# Seek to the next command
|
||||
file.seek(where + cmd_size, os.SEEK_SET)
|
||||
|
||||
def do_file(file, offset=0, size=maxint):
|
||||
file = FileView(file, offset, size)
|
||||
# Read magic number
|
||||
magic = read_data(file, BIG_ENDIAN)
|
||||
if magic == FAT_MAGIC:
|
||||
# Fat binaries contain nfat_arch Mach-O binaries
|
||||
n_fat_arch = read_data(file, BIG_ENDIAN)
|
||||
for _ in range(n_fat_arch):
|
||||
# Read arch header
|
||||
cpu_type, cpu_sub_type, offset, size, align = read_data(file, BIG_ENDIAN, 5)
|
||||
do_file(file, offset, size)
|
||||
elif magic == MH_MAGIC:
|
||||
do_macho(file, 32, BIG_ENDIAN)
|
||||
elif magic == MH_CIGAM:
|
||||
do_macho(file, 32, LITTLE_ENDIAN)
|
||||
elif magic == MH_MAGIC_64:
|
||||
do_macho(file, 64, BIG_ENDIAN)
|
||||
elif magic == MH_CIGAM_64:
|
||||
do_macho(file, 64, LITTLE_ENDIAN)
|
||||
|
||||
assert len(what) >= len(value)
|
||||
|
||||
with open(at_path, "r+b") as f:
|
||||
do_file(f)
|
||||
|
||||
return mach_o_change
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue