1
'''macros.py: Generate macro values from configuration values and provide
2
substitution functions.
4
The following macros are available:
6
LCODE CCODE PCCODE PKGNAME PKGCODE LANG COUNTRY PCOUNTRY DISTRO RELEASE UPLOADER
7
DATE TIMESTAMP CLASS CLASSNAME CLASSNAMESPACE CLASSDEPENDS
14
from decimal import Decimal
17
def _file_map(file, key, sep=None):
18
'''Look up key in given file ("key value" lines).
20
Throw KeyError if key was not found.
26
(k, v) = l.split(sep, 1)
30
if k.find('#') >= 0 or v.find('#') >= 0:
35
raise KeyError('Key %s not found in %s' % (key, file))
40
def __init__(self, distro, locale, cls, release, version):
41
'''Initialize values of macros.
43
This uses information from maps/, config/, some hardcoded aggregate
44
strings (such as package names), and some external input:
46
- locale: Standard locale representation (e. g. pt_BR.UTF-8)
47
- cls: empty, or valid name in maps/classnames
48
- release: distrorelease name (in maps/releaseversions-$distro)
49
- version: export date of language pack tarball, will be used in
53
# chop .* and @* suffixes to get encoding-agnostic locale
55
self['LOCALE'] = (locale.split('.')[0]).split('@')[0]
57
self['LOCALE'] = locale
62
self['CLASSNAME'] = _file_map('maps/classnames', cls)
63
self['CLASSNAMESPACE'] = self['CLASSNAME'] + ' '
65
# language and country
67
(self['LCODE'], self['CCODE']) = self['LOCALE'].split('_')
68
self['PCCODE'] = '(%s)' % self['CCODE']
70
self['COUNTRY'] = _file_map('maps/countries', self['CCODE'], ':')
71
self['PCOUNTRY'] = '(%s)' % self['COUNTRY']
76
self['LCODE'] = self['LOCALE']
79
raise Exception('Internal error: LCODE is empty')
82
self['DATE'] = time.strftime('%a, %d %b %Y %H:%M:%S +0000')
83
self['DISTRO'] = distro
84
self['RELEASE'] = release
85
self['RELEASEVERSION'] = _file_map('maps/releaseversions-' + distro, release.split('-')[0])
86
self['TIMESTAMP'] = version
89
if Decimal(self['RELEASEVERSION']) >= Decimal('9.10'):
91
self['PKGCODE'] = _file_map('maps/locale2pkgname', '%s-%s' % (self['LCODE'], self['CCODE'].lower()), ':')
92
self['LANG'] = _file_map('maps/variants', '%s' % self['PKGCODE'], ':')
94
self['PKGNAME'] = '%s-%s' % (self['CLASS'], self['PKGCODE'])
96
self['PKGNAME'] = self['PKGCODE']
98
self['PKGCODE'] = self['LCODE']
100
self['LANG'] = _file_map('maps/variants', '%s' % self['PKGCODE'], ':')
102
self['LANG'] = _file_map('maps/languages', self['LCODE'], ':')
104
self['PKGNAME'] = '%s-%s' % (self['CLASS'], self['LCODE'])
106
self['PKGNAME'] = self['LCODE']
108
self['PKGCODE'] = self['LCODE']
109
self['LANG'] = _file_map('maps/languages', self['LCODE'], ':')
111
self['PKGNAME'] = '%s-%s' % (self['CLASS'], self['LCODE'])
113
self['PKGNAME'] = self['LCODE']
115
# %CLASS% packages should depend on corresponding generic one
117
self['CLASSDEPENDS'] = ', language-pack-%s' % self['PKGCODE']
119
self['CLASSDEPENDS'] = ''
121
# configuration files
122
f = open('config/UPLOADER')
123
self['UPLOADER'] = f.read().strip()
126
# conflicts / replaces for zh langpack-split
127
if self['PKGCODE'] == 'zh-hans' or self['PKGCODE'] == 'zh-hant':
129
self['OBSOLETESZHBASE'] = ', language-pack-%s-zh-base (<< 1:9.10)' % self['CLASS']
130
self['OBSOLETESZH'] = ', language-pack-%s-zh (<< 1:9.10)' % self['CLASS']
131
self['CONFLICTSZH'] = '\nConflicts: language-pack-%s-zh (<< 1:9.10)' % self['CLASS']
133
self['OBSOLETESZHBASE'] = ', language-pack-zh-base (<< 1:9.10)'
134
self['OBSOLETESZH'] = ', language-pack-zh (<< 1:9.10)'
135
self['CONFLICTSZH'] = '\nConflicts: language-pack-zh (<< 1:9.10)'
137
self['OBSOLETESZHBASE'] = ''
138
self['OBSOLETESZH'] = ''
139
self['CONFLICTSZH'] = ''
141
def __getitem__(self, item):
142
# return empty string as default
143
return self.macros.get(item, '')
145
def __setitem__(self, item, value):
146
self.macros[item] = value
148
def __contains__(self, item):
149
return self.macros.__contains__(item)
151
def subst_string(self, s):
152
'''Substitute all macros in given string.'''
154
re_macro = re.compile('%([A-Z]+)%')
156
m = re_macro.search(s)
158
s = s[:m.start(1) - 1] + self[m.group(1)] + s[m.end(1) + 1:]
164
def subst_file(self, file):
165
'''Substitute all macros in given file.'''
171
f.write(self.subst_string(s))
174
def subst_tree(self, root):
175
'''Substitute all macros in given directory tree.'''
177
for path, dirs, files in os.walk(root):
179
self.subst_file(os.path.join(root, path, f))
182
if __name__ == '__main__':
186
class _T(unittest.TestCase):
188
'''language, no country, no class'''
190
l = LangpackMacros('ubuntu', 'de', '', 'precise', '3.14')
191
self.assertEqual(l.subst_string('%LCODE% "%CCODE%" language-pack-%PKGNAME%'),
192
'de "" language-pack-de')
193
self.assertEqual(l.subst_string('-%PCCODE%-%PKGCODE%-%LANG%-%RELEASE%'),
194
'--de-German-precise')
195
self.assertEqual(l.subst_string('-%COUNTRY%-%PCOUNTRY%-'), '---')
196
self.assertNotEqual(l.subst_string('%UPLOADER%'), '')
197
self.assertTrue(len(l.subst_string('%DATE%')) > 10)
198
self.assertEqual(l.subst_string('-%CLASS%-%CLASSNAME%-%CLASSNAMESPACE%-'), '----')
199
self.assertEqual(l.subst_string('%CLASSDEPENDS%'), '')
200
self.assertEqual(l.subst_string('%DISTRO%'), 'ubuntu')
202
def test_lang_country(self):
203
'''language, country, no class'''
205
for locale in ('de_CH', 'de_CH.utf8'):
206
l = LangpackMacros('ubuntu', locale, '', 'precise', '3.14')
207
self.assertEqual(l.subst_string('%LCODE% "%CCODE%" language-pack-%PKGNAME%'),
208
'de "CH" language-pack-de')
209
self.assertEqual(l.subst_string('-%PCCODE%-%PKGCODE%-%LANG%-%RELEASE%'),
210
'-(CH)-de-German-precise')
211
self.assertEqual(l.subst_string('-%COUNTRY%-%PCOUNTRY%-'), '-Switzerland-(Switzerland)-')
212
self.assertNotEqual(l.subst_string('%UPLOADER%'), '')
213
self.assertTrue(len(l.subst_string('%DATE%')) > 10)
214
self.assertEqual(l.subst_string('-%CLASS%-%CLASSNAME%-%CLASSNAMESPACE%-'), '----')
215
self.assertEqual(l.subst_string('%CLASSDEPENDS%'), '')
217
def test_lang_country_class(self):
218
'''language, country, class'''
220
for locale in ('de_CH', 'de_CH.utf8'):
221
l = LangpackMacros('ubuntu', locale, 'gnome', 'trusty', '3.14')
222
self.assertEqual(l.subst_string('%LCODE% "%CCODE%" language-pack-%PKGNAME%'),
223
'de "CH" language-pack-gnome-de')
224
self.assertEqual(l.subst_string('-%PCCODE%-%PKGCODE%-%LANG%-%RELEASE%'),
225
'-(CH)-de-German-trusty')
226
self.assertEqual(l.subst_string('-%COUNTRY%-%PCOUNTRY%-'), '-Switzerland-(Switzerland)-')
227
self.assertNotEqual(l.subst_string('%UPLOADER%'), '')
228
self.assertTrue(len(l.subst_string('%DATE%')) > 10)
229
self.assertEqual(l.subst_string('-%CLASS%-%CLASSNAME%-%CLASSNAMESPACE%-'),
230
'-gnome-GNOME-GNOME -')
231
self.assertEqual(l.subst_string('%CLASSDEPENDS%'), ', language-pack-de')
232
self.assertEqual(l.subst_string('%BASERECOMMENDS%'), '')
234
def test_zh_split(self):
235
'''Chinese split (PKGCODE)'''
237
l = LangpackMacros('ubuntu', 'zh_CN', 'kde', 'trusty', '3.14')
238
self.assertEqual(l.subst_string('%LCODE% "%CCODE%" language-pack-%PKGNAME%'),
239
'zh "CN" language-pack-kde-zh-hans')
240
self.assertEqual(l.subst_string('-%PCCODE%-%PKGCODE%-%LANG%-%RELEASE%'),
241
'-(CN)-zh-hans-Simplified Chinese-trusty')
242
self.assertEqual(l.subst_string('-%COUNTRY%-%PCOUNTRY%-'), '-China-(China)-')
243
self.assertNotEqual(l.subst_string('%UPLOADER%'), '')
244
self.assertTrue(len(l.subst_string('%DATE%')) > 10)
245
self.assertEqual(l.subst_string('-%CLASS%-%CLASSNAME%-%CLASSNAMESPACE%-'),
247
self.assertEqual(l.subst_string('%CLASSDEPENDS%'), ', language-pack-zh-hans')
249
def test_subst_file(self):
252
l = LangpackMacros('ubuntu', 'de', '', 'precise', '3.14')
253
(fd, fname) = tempfile.mkstemp()
257
f.write('%LCODE% "%CCODE%" language-pack-%PKGNAME%')
260
with open(fname) as f:
261
self.assertEqual(f.read(), 'de "" language-pack-de')
265
def test_unknown_broken(self):
266
'''unknown macros and corner cases'''
268
l = LangpackMacros('ubuntu', 'de', '', 'precise', '3.14')
269
self.assertEqual(l.subst_string('%LCODE%% %%LANG% %%LCODE%% %LCODE LCODE%'),
270
'de% %German %de% %LCODE LCODE%')
271
self.assertEqual(l.subst_string('%LCODE% %MOO% %LCODE%'),