In unit-tests, always use StringIO, not io
[zeroinstall.git] / zeroinstall / alias.py
blobeb4152bd6cf9ca3378a8eb4c42b33cb4c6b7cb2d
1 """
2 Support code for 0alias scripts.
3 @since: 0.28
4 """
6 # Copyright (C) 2009, Thomas Leonard
7 # See the README file for details, or visit http://0install.net.
9 from zeroinstall import _, SafeException
10 from zeroinstall import support
12 _old_template = '''#!/bin/sh
13 if [ "$*" = "--versions" ]; then
14 exec 0launch -gd '%s' "$@"
15 else
16 exec 0launch %s '%s' "$@"
18 '''
20 _template = '''#!/bin/sh
21 exec 0launch %s'%s' "$@"
22 '''
24 class NotAnAliasScript(SafeException):
25 pass
27 class ScriptInfo:
28 """@since: 1.3"""
29 uri = None
30 main = None
31 command = None
33 # For backwards compatibility
34 def __iter__(self):
35 return iter([self.uri, self.main])
37 def parse_script(pathname):
38 """Extract the URI and main values from a 0alias script.
39 @param pathname: the script to be examined
40 @return: information about the alias script
41 @rtype: L{ScriptInfo}
42 @raise NotAnAliasScript: if we can't parse the script
43 """
44 with open(pathname, 'rt') as stream:
45 template_header = _template[:_template.index("%s'")]
46 actual_header = stream.read(len(template_header))
47 stream.seek(0)
48 if template_header == actual_header:
49 # If it's a 0alias script, it should be quite short!
50 rest = stream.read()
51 line = rest.split('\n')[1]
52 else:
53 old_template_header = \
54 _old_template[:_old_template.index("-gd '")]
55 actual_header = stream.read(len(old_template_header))
56 if old_template_header != actual_header:
57 raise NotAnAliasScript(_("'%s' does not look like a script created by 0alias") % pathname)
58 rest = stream.read()
59 line = rest.split('\n')[2]
61 info = ScriptInfo()
62 split = line.rfind("' '")
63 if split != -1:
64 # We have a --main or --command
65 info.uri = line[split + 3:].split("'")[0]
66 start, value = line[:split].split("'", 1)
67 option = start.split('--', 1)[1].strip()
68 value = value.replace("'\\''", "'")
69 if option == 'main':
70 info.main = value
71 elif option == 'command':
72 info.command = value
73 else:
74 raise NotAnAliasScript("Unknown option '{option}' in alias script".format(option = option))
75 else:
76 info.uri = line.split("'", 2)[1]
78 return info
80 def write_script(stream, interface_uri, main = None, command = None):
81 """Write a shell script to stream that will launch the given program.
82 @param stream: the stream to write to
83 @param interface_uri: the program to launch
84 @param main: the --main argument to pass to 0launch, if any
85 @param command: the --command argument to pass to 0launch, if any"""
86 assert "'" not in interface_uri
87 assert "\\" not in interface_uri
88 assert main is None or command is None, "Can't set --main and --command together"
90 if main is not None:
91 option = "--main '%s' " % main.replace("'", "'\\''")
92 elif command is not None:
93 option = "--command '%s' " % command.replace("'", "'\\''")
94 else:
95 option = ""
97 stream.write(support.unicode(_template) % (option, interface_uri))