diff options
| author | Vito Graffagnino <vito@graffagnino.xyz> | 2020-09-08 18:10:49 +0100 |
|---|---|---|
| committer | Vito Graffagnino <vito@graffagnino.xyz> | 2020-09-08 18:10:49 +0100 |
| commit | 3b0142cedcde39e4c2097ecd916a870a3ced5ec6 (patch) | |
| tree | 2116c49a845dfc0945778f2aa3e2118d72be428b /.config/qutebrowser/scripts/link_pyqt.py | |
| parent | 8cc927e930d5b6aafe3e9862a61e81705479a1b4 (diff) | |
Added the relevent parts of the .config directory. Alss add ssh config
Diffstat (limited to '.config/qutebrowser/scripts/link_pyqt.py')
| -rwxr-xr-x | .config/qutebrowser/scripts/link_pyqt.py | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/.config/qutebrowser/scripts/link_pyqt.py b/.config/qutebrowser/scripts/link_pyqt.py new file mode 100755 index 0000000..ae7eaa6 --- /dev/null +++ b/.config/qutebrowser/scripts/link_pyqt.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Symlink PyQt into a given virtualenv.""" + +import os +import os.path +import argparse +import shutil +import sys +import subprocess +import tempfile +import filecmp + + +class Error(Exception): + + """Exception raised when linking fails.""" + + pass + + +def run_py(executable, *code): + """Run the given python code with the given executable.""" + if os.name == 'nt' and len(code) > 1: + # Windows can't do newlines in arguments... + oshandle, filename = tempfile.mkstemp() + with os.fdopen(oshandle, 'w') as f: + f.write('\n'.join(code)) + cmd = [executable, filename] + try: + ret = subprocess.run(cmd, universal_newlines=True, check=True, + stdout=subprocess.PIPE).stdout + finally: + os.remove(filename) + else: + cmd = [executable, '-c', '\n'.join(code)] + ret = subprocess.run(cmd, universal_newlines=True, check=True, + stdout=subprocess.PIPE).stdout + return ret.rstrip() + + +def verbose_copy(src, dst, *, follow_symlinks=True): + """Copy function for shutil.copytree which prints copied files.""" + if '-v' in sys.argv: + print('{} -> {}'.format(src, dst)) + shutil.copy(src, dst, follow_symlinks=follow_symlinks) + + +def get_ignored_files(directory, files): + """Get the files which should be ignored for link_pyqt() on Windows.""" + needed_exts = ('.py', '.dll', '.pyd', '.so') + ignored_dirs = ('examples', 'qml', 'uic', 'doc') + filtered = [] + for f in files: + ext = os.path.splitext(f)[1] + full_path = os.path.join(directory, f) + if os.path.isdir(full_path) and f in ignored_dirs: + filtered.append(f) + elif (ext not in needed_exts) and os.path.isfile(full_path): + filtered.append(f) + return filtered + + +def needs_update(source, dest): + """Check if a file to be linked/copied needs to be updated.""" + if os.path.islink(dest): + # No need to delete a link and relink -> skip this + return False + elif os.path.isdir(dest): + diffs = filecmp.dircmp(source, dest) + ignored = get_ignored_files(source, diffs.left_only) + has_new_files = set(ignored) != set(diffs.left_only) + return (has_new_files or diffs.right_only or diffs.common_funny or + diffs.diff_files or diffs.funny_files) + else: + return not filecmp.cmp(source, dest) + + +def get_lib_path(executable, name, required=True): + """Get the path of a python library. + + Args: + executable: The Python executable to use. + name: The name of the library to get the path for. + required: Whether Error should be raised if the lib was not found. + """ + code = [ + 'try:', + ' import {}'.format(name), + 'except ImportError as e:', + ' print("ImportError: " + str(e))', + 'else:', + ' print("path: " + {}.__file__)'.format(name) + ] + output = run_py(executable, *code) + + try: + prefix, data = output.split(': ') + except ValueError: + raise ValueError("Unexpected output: {!r}".format(output)) + + if prefix == 'path': + return data + elif prefix == 'ImportError': + if required: + raise Error("Could not import {} with {}: {}!".format( + name, executable, data)) + else: + return None + else: + raise ValueError("Unexpected output: {!r}".format(output)) + + +def link_pyqt(executable, venv_path): + """Symlink the systemwide PyQt/sip into the venv. + + Args: + executable: The python executable where the source files are present. + venv_path: The path to the virtualenv site-packages. + """ + try: + get_lib_path(executable, 'PyQt5.sip') + except Error: + # There is no PyQt5.sip, so we need to copy the toplevel sip. + sip_file = get_lib_path(executable, 'sip') + else: + # There is a PyQt5.sip, it'll get copied with the PyQt5 dir. + sip_file = None + + sipconfig_file = get_lib_path(executable, 'sipconfig', required=False) + pyqt_dir = os.path.dirname(get_lib_path(executable, 'PyQt5.QtCore')) + + for path in [sip_file, sipconfig_file, pyqt_dir]: + if path is None: + continue + + fn = os.path.basename(path) + dest = os.path.join(venv_path, fn) + + if os.path.exists(dest): + if needs_update(path, dest): + remove(dest) + else: + continue + + copy_or_link(path, dest) + + +def copy_or_link(source, dest): + """Copy or symlink source to dest.""" + if os.name == 'nt': + if os.path.isdir(source): + print('{} -> {}'.format(source, dest)) + shutil.copytree(source, dest, ignore=get_ignored_files, + copy_function=verbose_copy) + else: + print('{} -> {}'.format(source, dest)) + shutil.copy(source, dest) + else: + print('{} -> {}'.format(source, dest)) + os.symlink(source, dest) + + +def remove(filename): + """Remove a given filename, regardless of whether it's a file or dir.""" + if os.path.isdir(filename): + shutil.rmtree(filename) + else: + os.unlink(filename) + + +def get_venv_lib_path(path): + """Get the library path of a virtualenv.""" + subdir = 'Scripts' if os.name == 'nt' else 'bin' + executable = os.path.join(path, subdir, 'python') + return run_py(executable, + 'from distutils.sysconfig import get_python_lib', + 'print(get_python_lib())') + + +def get_tox_syspython(tox_path): + """Get the system python based on a virtualenv created by tox.""" + path = os.path.join(tox_path, '.tox-config1') + with open(path, encoding='ascii') as f: + line = f.readline() + _md5, sys_python = line.rstrip().split(' ', 1) + return sys_python + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('path', help="Base path to the venv.") + parser.add_argument('--tox', help="Add when called via tox.", + action='store_true') + args = parser.parse_args() + + if args.tox: + # Workaround for the lack of negative factors in tox.ini + if 'LINK_PYQT_SKIP' in os.environ: + print('LINK_PYQT_SKIP set, exiting...') + sys.exit(0) + executable = get_tox_syspython(args.path) + else: + executable = sys.executable + + venv_path = get_venv_lib_path(args.path) + link_pyqt(executable, venv_path) + + +if __name__ == '__main__': + try: + main() + except Error as e: + print(str(e), file=sys.stderr) + sys.exit(1) |
