Output debug messages in a nicer form for writing test cases
[zeroinstall.git] / zeroinstall / helpers.py
blobc96ccc3d81aa65d80600515f836260d2f6ef6b75
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 _
13 def get_selections_gui(iface_uri, gui_args, test_callback = None):
14 """Run the GUI to choose and download a set of implementations.
15 The user may ask the GUI to submit a bug report about the program. In that case,
16 the GUI may ask us to test it. test_callback is called in that case with the implementations
17 to be tested; the callback will typically call L{zeroinstall.injector.run.test_selections} and return the result of that.
18 @param iface_uri: the required program, or None to show just the preferences dialog
19 @type iface_uri: str
20 @param gui_args: any additional arguments for the GUI itself
21 @type gui_args: [str]
22 @param test_callback: function to use to try running the program
23 @type test_callback: L{zeroinstall.injector.selections.Selections} -> str
24 @return: the selected implementations
25 @rtype: L{zeroinstall.injector.selections.Selections}
26 @since: 0.28
27 """
28 from zeroinstall.injector import selections, qdom
29 from StringIO import StringIO
31 from os.path import join, dirname
32 gui_exe = join(dirname(__file__), '0launch-gui', '0launch-gui')
34 import socket
35 cli, gui = socket.socketpair()
37 try:
38 child = os.fork()
39 if child == 0:
40 # We are the child (GUI)
41 try:
42 try:
43 cli.close()
44 # We used to use pipes to support Python2.3...
45 os.dup2(gui.fileno(), 1)
46 os.dup2(gui.fileno(), 0)
47 if iface_uri is not None:
48 gui_args = gui_args + ['--', iface_uri]
49 os.execvp(gui_exe, [gui_exe] + gui_args)
50 except:
51 import traceback
52 traceback.print_exc(file = sys.stderr)
53 finally:
54 sys.stderr.flush()
55 os._exit(1)
56 # We are the parent (CLI)
57 gui.close()
58 gui = None
60 while True:
61 logging.info("Waiting for selections from GUI...")
63 reply = support.read_bytes(cli.fileno(), len('Length:') + 9, null_ok = True)
64 if reply:
65 if not reply.startswith('Length:'):
66 raise Exception("Expected Length:, but got %s" % repr(reply))
67 xml = support.read_bytes(cli.fileno(), int(reply.split(':', 1)[1], 16))
69 dom = qdom.parse(StringIO(xml))
70 sels = selections.Selections(dom)
72 if dom.getAttribute('run-test'):
73 logging.info("Testing program, as requested by GUI...")
74 if test_callback is None:
75 output = "Can't test: no test_callback was passed to get_selections_gui()\n"
76 else:
77 output = test_callback(sels)
78 logging.info("Sending results to GUI...")
79 output = ('Length:%8x\n' % len(output)) + output
80 logging.debug("Sending: %s", repr(output))
81 while output:
82 sent = cli.send(output)
83 output = output[sent:]
84 continue
85 else:
86 sels = None
88 pid, status = os.waitpid(child, 0)
89 assert pid == child
90 if status == 1 << 8:
91 logging.info("User cancelled the GUI; aborting")
92 return None # Aborted
93 if status != 0:
94 raise Exception("Error from GUI: code = %d" % status)
95 break
96 finally:
97 for sock in [cli, gui]:
98 if sock is not None: sock.close()
100 return sels
102 def ensure_cached(uri):
103 """Ensure that an implementation of uri is cached.
104 If not, it downloads one. It uses the GUI if a display is
105 available, or the console otherwise.
106 @param uri: the required interface
107 @type uri: str
108 @return: a new policy for this program, or None if the user cancelled
109 @rtype: L{zeroinstall.injector.selections.Selections}
111 from zeroinstall.injector import autopolicy, selections
113 p = autopolicy.AutoPolicy(uri, download_only = True)
114 p.freshness = 0 # Don't check for updates
116 if p.need_download() or not p.ready:
117 if os.environ.get('DISPLAY', None):
118 return get_selections_gui(uri, [])
119 else:
120 p.recalculate_with_dl()
121 p.start_downloading_impls()
122 p.handler.wait_for_downloads()
124 return selections.Selections(p)