Last active
January 7, 2018 10:18
-
-
Save evrardjp/57d4e4789ab60f8c0b002665bf54adf3 to your computer and use it in GitHub Desktop.
virtualenv_tools.py fork with a print.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
move-virtualenv | |
~~~~~~~~~~~~~~~ | |
A helper script that moves virtualenvs to a new location. | |
It only supports POSIX based virtualenvs and Python 2 at the moment. | |
:copyright: (c) 2012 by Fireteam Ltd. | |
:license: BSD, see LICENSE for more details. | |
""" | |
import os | |
import re | |
import sys | |
import marshal | |
import optparse | |
import subprocess | |
from types import CodeType | |
ACTIVATION_SCRIPTS = [ | |
'activate', | |
'activate.csh', | |
'activate.fish' | |
] | |
_pybin_match = re.compile(r'^python\d+\.\d+$') | |
_activation_path_re = re.compile(r'^(?:set -gx |setenv |)VIRTUAL_ENV[ =]"(.*?)"\s*$') | |
def update_activation_script(script_filename, new_path): | |
"""Updates the paths for the activate shell scripts.""" | |
with open(script_filename) as f: | |
lines = list(f) | |
def _handle_sub(match): | |
text = match.group() | |
start, end = match.span() | |
g_start, g_end = match.span(1) | |
return text[:(g_start - start)] + new_path + text[(g_end - end):] | |
changed = False | |
for idx, line in enumerate(lines): | |
new_line = _activation_path_re.sub(_handle_sub, line) | |
if line != new_line: | |
lines[idx] = new_line | |
changed = True | |
if changed: | |
print 'A %s' % script_filename | |
with open(script_filename, 'w') as f: | |
f.writelines(lines) | |
def update_script(script_filename, new_path): | |
"""Updates shebang lines for actual scripts.""" | |
with open(script_filename) as f: | |
lines = list(f) | |
if not lines: | |
print 'Debug: No lines' | |
return | |
if not lines[0].startswith('#!'): | |
print 'Debug: no shabang' | |
return | |
args = lines[0][2:].strip().split() | |
print 'Debug: args = {}'.format(str(args)) | |
if not args: | |
print 'Debug: Cannot split' | |
return | |
if not (args[0].endswith('/bin/python') or args[0].endswith('/usr/bin/env python') or args[0].endswith('/bin/python2')): | |
print 'Debug: No known python in shabang' | |
return | |
new_bin = os.path.join(new_path, 'bin', 'python') | |
if new_bin == args[0]: | |
print('Debug: new_bin {} same as shabang {}'.format(str(new_bin),str(args[0]) )) | |
return | |
args[0] = new_bin | |
lines[0] = '#!%s\n' % ' '.join(args) | |
print 'S %s' % script_filename | |
with open(script_filename, 'w') as f: | |
f.writelines(lines) | |
def update_scripts(bin_dir, new_path): | |
"""Updates all scripts in the bin folder.""" | |
for fn in os.listdir(bin_dir): | |
print 'F %s' % fn | |
if fn in ACTIVATION_SCRIPTS: | |
update_activation_script(os.path.join(bin_dir, fn), new_path) | |
else: | |
update_script(os.path.join(bin_dir, fn), new_path) | |
def update_pyc(filename, new_path): | |
"""Updates the filenames stored in pyc files.""" | |
with open(filename, 'rb') as f: | |
magic = f.read(8) | |
code = marshal.load(f) | |
def _make_code(code, filename, consts): | |
return CodeType(code.co_argcount, code.co_nlocals, code.co_stacksize, | |
code.co_flags, code.co_code, tuple(consts), | |
code.co_names, code.co_varnames, filename, | |
code.co_name, code.co_firstlineno, code.co_lnotab, | |
code.co_freevars, code.co_cellvars) | |
def _process(code): | |
new_filename = new_path | |
consts = [] | |
for const in code.co_consts: | |
if type(const) is CodeType: | |
const = _process(const) | |
consts.append(const) | |
if new_path != code.co_filename or consts != list(code.co_consts): | |
code = _make_code(code, new_path, consts) | |
return code | |
new_code = _process(code) | |
if new_code is not code: | |
print 'B %s' % filename | |
with open(filename, 'wb') as f: | |
f.write(magic) | |
marshal.dump(new_code, f) | |
def update_pycs(lib_dir, new_path, lib_name): | |
"""Walks over all pyc files and updates their paths.""" | |
files = [] | |
def get_new_path(filename): | |
filename = os.path.normpath(filename) | |
if filename.startswith(lib_dir.rstrip('/') + '/'): | |
return os.path.join(new_path, filename[len(lib_dir) + 1:]) | |
for dirname, dirnames, filenames in os.walk(lib_dir): | |
for filename in filenames: | |
if filename.endswith(('.pyc', '.pyo')): | |
filename = os.path.join(dirname, filename) | |
local_path = get_new_path(filename) | |
if local_path is not None: | |
update_pyc(filename, local_path) | |
def update_local(base, new_path): | |
"""On some systems virtualenv seems to have something like a local | |
directory with symlinks. It appears to happen on debian systems and | |
it causes havok if not updated. So do that. | |
""" | |
local_dir = os.path.join(base, 'local') | |
if not os.path.isdir(local_dir): | |
return | |
for folder in 'bin', 'lib', 'include': | |
filename = os.path.join(local_dir, folder) | |
target = '../%s' % folder | |
if os.path.islink(filename) and os.readlink(filename) != target: | |
os.remove(filename) | |
os.symlink('../%s' % folder, filename) | |
print 'L %s' % filename | |
def update_paths(base, new_path): | |
"""Updates all paths in a virtualenv to a new one.""" | |
if new_path == 'auto': | |
new_path = os.path.abspath(base) | |
if not os.path.isabs(new_path): | |
print 'error: %s is not an absolute path' % new_path | |
return False | |
print 'Auto new path set to %s' % new_path | |
bin_dir = os.path.join(base, 'bin') | |
base_lib_dir = os.path.join(base, 'lib') | |
lib_dir = None | |
lib_name = None | |
if os.path.isdir(base_lib_dir): | |
for folder in os.listdir(base_lib_dir): | |
if _pybin_match.match(folder): | |
lib_name = folder | |
lib_dir = os.path.join(base_lib_dir, folder) | |
break | |
if lib_dir is None or not os.path.isdir(bin_dir) \ | |
or not os.path.isfile(os.path.join(bin_dir, 'python')): | |
print 'error: %s does not refer to a python installation' % base | |
return False | |
print("Update scripts") | |
update_scripts(bin_dir, new_path) | |
print("Update pycs") | |
update_pycs(lib_dir, new_path, lib_name) | |
print("Update local") | |
update_local(base, new_path) | |
return True | |
def reinitialize_virtualenv(path): | |
"""Re-initializes a virtualenv.""" | |
lib_dir = os.path.join(path, 'lib') | |
if not os.path.isdir(lib_dir): | |
print 'error: %s is not a virtualenv bin folder' % path | |
return False | |
py_ver = None | |
for filename in os.listdir(lib_dir): | |
if _pybin_match.match(filename): | |
py_ver = filename | |
break | |
if py_ver is None: | |
print 'error: could not detect python version of virtualenv %s' % path | |
return False | |
sys_py_executable = subprocess.Popen(['which', py_ver], | |
stdout=subprocess.PIPE).communicate()[0].strip() | |
if not sys_py_executable: | |
print 'error: could not find system version for expected python ' \ | |
'version %s' % py_ver | |
return False | |
lib_dir = os.path.join(path, 'lib', py_ver) | |
args = ['virtualenv', '-p', sys_py_executable] | |
if not os.path.isfile(os.path.join(lib_dir, | |
'no-global-site-packages.txt')): | |
args.append('--system-site-packages') | |
for filename in os.listdir(lib_dir): | |
if filename.startswith('distribute-') and \ | |
filename.endswith('.egg'): | |
args.append('--distribute') | |
new_env = {} | |
for key, value in os.environ.items(): | |
if not key.startswith('VIRTUALENV_'): | |
new_env[key] = value | |
args.append(path) | |
subprocess.Popen(args, env=new_env).wait() | |
def main(): | |
print("Welcome!") | |
parser = optparse.OptionParser() | |
parser.add_option('--reinitialize', action='store_true', | |
help='Updates the python installation ' | |
'and reinitializes the virtualenv.') | |
parser.add_option('--update-path', help='Update the path for all ' | |
'required executables and helper files that are ' | |
'supported to the new python prefix. You can also set ' | |
'this to "auto" for autodetection.') | |
options, paths = parser.parse_args() | |
if not paths: | |
paths = ['.'] | |
rv = 0 | |
if options.reinitialize: | |
for path in paths: | |
reinitialize_virtualenv(path) | |
if options.update_path: | |
for path in paths: | |
print("Updating path {}".format(path)) | |
if not update_paths(path, options.update_path): | |
rv = 1 | |
sys.exit(rv) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment