/usr/share/pyshared/zeroinstall/helpers.py is in zeroinstall-injector 2.3.3-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | """
Convenience routines for performing common operations.
@since: 0.28
"""
# Copyright (C) 2009, Thomas Leonard
# See the README file for details, or visit http://0install.net.
from __future__ import print_function
import os, sys
from zeroinstall import support, SafeException, logger
from zeroinstall.support import tasks
DontUseGUI = object()
def get_selections_gui(iface_uri, gui_args, test_callback = None, use_gui = True):
"""Run the GUI to choose and download a set of implementations.
The user may ask the GUI to submit a bug report about the program. In that case,
the GUI may ask us to test it. test_callback is called in that case with the implementations
to be tested; the callback will typically call L{zeroinstall.injector.run.test_selections} and return the result of that.
@param iface_uri: the required program, or None to show just the preferences dialog
@type iface_uri: str
@param gui_args: any additional arguments for the GUI itself
@type gui_args: [str]
@param test_callback: function to use to try running the program
@type test_callback: L{zeroinstall.injector.selections.Selections} -> str
@param use_gui: if True, raise a SafeException if the GUI is not available. If None, returns DontUseGUI if the GUI cannot be started. If False, returns DontUseGUI always. (since 1.11)
@type use_gui: bool | None
@return: the selected implementations
@rtype: L{zeroinstall.injector.selections.Selections}
@since: 0.28"""
if use_gui is False:
return DontUseGUI
if not os.environ.get('DISPLAY', None):
if use_gui is None:
return DontUseGUI
else:
raise SafeException("Can't use GUI because $DISPLAY is not set")
from zeroinstall.injector import selections, qdom
from io import BytesIO
from os.path import join, dirname
gui_exe = join(dirname(__file__), '0launch-gui', '0launch-gui')
import socket
cli, gui = socket.socketpair()
try:
child = os.fork()
if child == 0:
# We are the child (GUI)
try:
try:
cli.close()
# We used to use pipes to support Python2.3...
os.dup2(gui.fileno(), 1)
os.dup2(gui.fileno(), 0)
if use_gui is True:
gui_args = ['-g'] + gui_args
if iface_uri is not None:
gui_args = gui_args + ['--', iface_uri]
os.execvp(sys.executable, [sys.executable, gui_exe] + gui_args)
except:
import traceback
traceback.print_exc(file = sys.stderr)
finally:
sys.stderr.flush()
os._exit(1)
# We are the parent (CLI)
gui.close()
gui = None
while True:
logger.info("Waiting for selections from GUI...")
reply = support.read_bytes(cli.fileno(), len('Length:') + 9, null_ok = True)
if reply:
if not reply.startswith(b'Length:'):
raise Exception("Expected Length:, but got %s" % repr(reply))
reply = reply.decode('ascii')
xml = support.read_bytes(cli.fileno(), int(reply.split(':', 1)[1], 16))
dom = qdom.parse(BytesIO(xml))
sels = selections.Selections(dom)
if dom.getAttribute('run-test'):
logger.info("Testing program, as requested by GUI...")
if test_callback is None:
output = b"Can't test: no test_callback was passed to get_selections_gui()\n"
else:
output = test_callback(sels)
logger.info("Sending results to GUI...")
output = ('Length:%8x\n' % len(output)).encode('utf-8') + output
logger.debug("Sending: %s", repr(output))
while output:
sent = cli.send(output)
output = output[sent:]
continue
else:
sels = None
pid, status = os.waitpid(child, 0)
assert pid == child
if status == 1 << 8:
logger.info("User cancelled the GUI; aborting")
return None # Aborted
elif status == 100 << 8:
if use_gui is None:
return DontUseGUI
else:
raise SafeException("No GUI available")
if status != 0:
raise Exception("Error from GUI: code = %d" % status)
break
finally:
for sock in [cli, gui]:
if sock is not None: sock.close()
return sels
def ensure_cached(uri, command = 'run', config = None):
"""Ensure that an implementation of uri is cached.
If not, it downloads one. It uses the GUI if a display is
available, or the console otherwise.
@param uri: the required interface
@type uri: str
@type command: str
@return: the selected implementations, or None if the user cancelled
@rtype: L{zeroinstall.injector.selections.Selections}"""
from zeroinstall.injector.driver import Driver
if config is None:
from zeroinstall.injector.config import load_config
config = load_config()
from zeroinstall.injector.requirements import Requirements
requirements = Requirements(uri)
requirements.command = command
d = Driver(config, requirements)
if d.need_download() or not d.solver.ready:
sels = get_selections_gui(uri, ['--command', command], use_gui = None)
if sels != DontUseGUI:
return sels
done = d.solve_and_download_impls()
tasks.wait_for_blocker(done)
return d.solver.selections
def exec_man(stores, sels, main = None, fallback_name = None):
"""Exec the man command to show the man-page for this interface.
Never returns.
@type stores: L{zeroinstall.zerostore.Stores}
@type sels: L{zeroinstall.injector.selections.Selections}
@type main: str | None
@type fallback_name: str | None
@since: 1.12"""
interface_uri = sels.interface
selected_impl = sels.selections[interface_uri]
if selected_impl.id.startswith('package'):
impl_path = None
else:
impl_path = selected_impl.get_path(stores)
if main is None:
if sels.commands:
selected_command = sels.commands[0]
else:
print("No <command> in selections!", file=sys.stderr)
sys.exit(1)
main = selected_command.path
if main is None:
print("No main program for interface '%s'" % interface_uri, file=sys.stderr)
sys.exit(1)
prog_name = os.path.basename(main)
if impl_path is None:
# Package implementation
logger.debug("Searching for man-page native command %s (from %s)" % (prog_name, fallback_name))
os.execlp('man', 'man', prog_name)
assert impl_path
logger.debug("Searching for man-page for %s or %s in %s" % (prog_name, fallback_name, impl_path))
# TODO: the feed should say where the man-pages are, but for now we'll accept
# a directory called man in some common locations...
for mandir in ['man', 'share/man', 'usr/man', 'usr/share/man']:
manpath = os.path.join(impl_path, mandir)
if os.path.isdir(manpath):
# Note: unlike "man -M", this also copes with LANG settings...
os.environ['MANPATH'] = manpath
os.execlp('man', 'man', prog_name)
sys.exit(1)
# No man directory given or found, so try searching for man files
manpages = []
for root, dirs, files in os.walk(impl_path):
for f in files:
if f.endswith('.gz'):
manpage_file = f[:-3]
else:
manpage_file = f
if manpage_file.endswith('.1') or \
manpage_file.endswith('.6') or \
manpage_file.endswith('.8'):
manpage_prog = manpage_file[:-2]
if manpage_prog == prog_name or manpage_prog == fallback_name:
os.execlp('man', 'man', os.path.join(root, f))
sys.exit(1)
else:
manpages.append((root, f))
for d in list(dirs):
if d.startswith('.'):
dirs.remove(d)
print("No matching manpage was found for '%s' (%s)" % (fallback_name, interface_uri))
if manpages:
print("These non-matching man-pages were found, however:")
for root, file in manpages:
print(os.path.join(root, file))
sys.exit(1)
|