Moved Config into its own module
[zeroinstall/zeroinstall-limyreth.git] / zeroinstall / helpers.py
blobde7b9e7b19cd3e6c470ec8fa0ba00c7254179ea5
1 """
2 Convenience routines for performing common operations.
3 @since: 0.28
4 """
6 # Copyright (C) 2009, Thomas Leonard
7 # See the README file for details, or visit http://0install.net.
9 import os, sys, logging
10 from zeroinstall import support
11 from zeroinstall import _
12 from zeroinstall.support import tasks
14 def get_selections_gui(iface_uri, gui_args, test_callback = None):
15 """Run the GUI to choose and download a set of implementations.
16 The user may ask the GUI to submit a bug report about the program. In that case,
17 the GUI may ask us to test it. test_callback is called in that case with the implementations
18 to be tested; the callback will typically call L{zeroinstall.injector.run.test_selections} and return the result of that.
19 @param iface_uri: the required program, or None to show just the preferences dialog
20 @type iface_uri: str
21 @param gui_args: any additional arguments for the GUI itself
22 @type gui_args: [str]
23 @param test_callback: function to use to try running the program
24 @type test_callback: L{zeroinstall.injector.selections.Selections} -> str
25 @return: the selected implementations
26 @rtype: L{zeroinstall.injector.selections.Selections}
27 @since: 0.28
28 """
29 from zeroinstall.injector import selections, qdom
30 from StringIO import StringIO
32 from os.path import join, dirname
33 gui_exe = join(dirname(__file__), '0launch-gui', '0launch-gui')
35 import socket
36 cli, gui = socket.socketpair()
38 try:
39 child = os.fork()
40 if child == 0:
41 # We are the child (GUI)
42 try:
43 try:
44 cli.close()
45 # We used to use pipes to support Python2.3...
46 os.dup2(gui.fileno(), 1)
47 os.dup2(gui.fileno(), 0)
48 if iface_uri is not None:
49 gui_args = gui_args + ['--', iface_uri]
50 os.execvp(gui_exe, [gui_exe] + gui_args)
51 except:
52 import traceback
53 traceback.print_exc(file = sys.stderr)
54 finally:
55 sys.stderr.flush()
56 os._exit(1)
57 # We are the parent (CLI)
58 gui.close()
59 gui = None
61 while True:
62 logging.info("Waiting for selections from GUI...")
64 reply = support.read_bytes(cli.fileno(), len('Length:') + 9, null_ok = True)
65 if reply:
66 if not reply.startswith('Length:'):
67 raise Exception("Expected Length:, but got %s" % repr(reply))
68 xml = support.read_bytes(cli.fileno(), int(reply.split(':', 1)[1], 16))
70 dom = qdom.parse(StringIO(xml))
71 sels = selections.Selections(dom)
73 if dom.getAttribute('run-test'):
74 logging.info("Testing program, as requested by GUI...")
75 if test_callback is None:
76 output = "Can't test: no test_callback was passed to get_selections_gui()\n"
77 else:
78 output = test_callback(sels)
79 logging.info("Sending results to GUI...")
80 output = ('Length:%8x\n' % len(output)) + output
81 logging.debug("Sending: %s", repr(output))
82 while output:
83 sent = cli.send(output)
84 output = output[sent:]
85 continue
86 else:
87 sels = None
89 pid, status = os.waitpid(child, 0)
90 assert pid == child
91 if status == 1 << 8:
92 logging.info("User cancelled the GUI; aborting")
93 return None # Aborted
94 if status != 0:
95 raise Exception("Error from GUI: code = %d" % status)
96 break
97 finally:
98 for sock in [cli, gui]:
99 if sock is not None: sock.close()
101 return sels
103 def ensure_cached(uri, command = 'run'):
104 """Ensure that an implementation of uri is cached.
105 If not, it downloads one. It uses the GUI if a display is
106 available, or the console otherwise.
107 @param uri: the required interface
108 @type uri: str
109 @return: the selected implementations, or None if the user cancelled
110 @rtype: L{zeroinstall.injector.selections.Selections}
112 from zeroinstall.injector import policy, selections
113 from zeroinstall.injector.config import load_config
115 config = load_config()
116 p = policy.Policy(uri, command = command, config = config)
117 p.freshness = 0 # Don't check for updates
119 if p.need_download() or not p.ready:
120 if os.environ.get('DISPLAY', None):
121 return get_selections_gui(uri, ['--command', command])
122 else:
123 done = p.solve_and_download_impls()
124 tasks.wait_for_blocker(done)
126 return selections.Selections(p)