2 Convenience routines for performing common operations.
6 # Copyright (C) 2009, Thomas Leonard
7 # See the README file for details, or visit http://0install.net.
10 from zeroinstall
import support
, SafeException
, logger
11 from zeroinstall
.support
import tasks
15 def get_selections_gui(iface_uri
, gui_args
, test_callback
= None, use_gui
= True):
16 """Run the GUI to choose and download a set of implementations.
17 The user may ask the GUI to submit a bug report about the program. In that case,
18 the GUI may ask us to test it. test_callback is called in that case with the implementations
19 to be tested; the callback will typically call L{zeroinstall.injector.run.test_selections} and return the result of that.
20 @param iface_uri: the required program, or None to show just the preferences dialog
22 @param gui_args: any additional arguments for the GUI itself
24 @param test_callback: function to use to try running the program
25 @type test_callback: L{zeroinstall.injector.selections.Selections} -> str
26 @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)
27 @param use_gui: bool | None
28 @return: the selected implementations
29 @rtype: L{zeroinstall.injector.selections.Selections}
35 if 'DISPLAY' not in os
.environ
:
39 raise SafeException("Can't use GUI because $DISPLAY is not set")
41 from zeroinstall
.injector
import selections
, qdom
42 from io
import BytesIO
44 from os
.path
import join
, dirname
45 gui_exe
= join(dirname(__file__
), '0launch-gui', '0launch-gui')
48 cli
, gui
= socket
.socketpair()
53 # We are the child (GUI)
57 # We used to use pipes to support Python2.3...
58 os
.dup2(gui
.fileno(), 1)
59 os
.dup2(gui
.fileno(), 0)
61 gui_args
= ['-g'] + gui_args
62 if iface_uri
is not None:
63 gui_args
= gui_args
+ ['--', iface_uri
]
64 os
.execvp(sys
.executable
, [sys
.executable
, gui_exe
] + gui_args
)
67 traceback
.print_exc(file = sys
.stderr
)
71 # We are the parent (CLI)
76 logger
.info("Waiting for selections from GUI...")
78 reply
= support
.read_bytes(cli
.fileno(), len('Length:') + 9, null_ok
= True)
80 if not reply
.startswith(b
'Length:'):
81 raise Exception("Expected Length:, but got %s" % repr(reply
))
82 reply
= reply
.decode('ascii')
83 xml
= support
.read_bytes(cli
.fileno(), int(reply
.split(':', 1)[1], 16))
85 dom
= qdom
.parse(BytesIO(xml
))
86 sels
= selections
.Selections(dom
)
88 if dom
.getAttribute('run-test'):
89 logger
.info("Testing program, as requested by GUI...")
90 if test_callback
is None:
91 output
= b
"Can't test: no test_callback was passed to get_selections_gui()\n"
93 output
= test_callback(sels
)
94 logger
.info("Sending results to GUI...")
95 output
= ('Length:%8x\n' % len(output
)).encode('utf-8') + output
96 logger
.debug("Sending: %s", repr(output
))
98 sent
= cli
.send(output
)
99 output
= output
[sent
:]
104 pid
, status
= os
.waitpid(child
, 0)
107 logger
.info("User cancelled the GUI; aborting")
108 return None # Aborted
109 elif status
== 100 << 8:
113 raise SafeException("No GUI available")
115 raise Exception("Error from GUI: code = %d" % status
)
118 for sock
in [cli
, gui
]:
119 if sock
is not None: sock
.close()
123 def ensure_cached(uri
, command
= 'run', config
= None):
124 """Ensure that an implementation of uri is cached.
125 If not, it downloads one. It uses the GUI if a display is
126 available, or the console otherwise.
127 @param uri: the required interface
129 @return: the selected implementations, or None if the user cancelled
130 @rtype: L{zeroinstall.injector.selections.Selections}
132 from zeroinstall
.injector
.driver
import Driver
135 from zeroinstall
.injector
.config
import load_config
136 config
= load_config()
138 from zeroinstall
.injector
.requirements
import Requirements
139 requirements
= Requirements(uri
)
140 requirements
.command
= command
142 d
= Driver(config
, requirements
)
144 if d
.need_download() or not d
.solver
.ready
:
145 sels
= get_selections_gui(uri
, ['--command', command
], use_gui
= None)
146 if sels
!= DontUseGUI
:
148 done
= d
.solve_and_download_impls()
149 tasks
.wait_for_blocker(done
)
151 return d
.solver
.selections