plural_to_python

10 June 2008
22:51

By cmlenz as Python

1 def plural_to_python(expr):
2 """Gets a C expression as used in PO files for plural forms and returns a
3 Python function that implements an equivalent expression.
4
5 >>> f = plural_to_python('(n != 1)')
6 >>> f(0), f(1), f(2), f(3)
7 (1, 0, 1, 1)
8
9 >>> f = plural_to_python('n==1 ? 0 : n==2 ? 1 : 2')
10 >>> f(0), f(1), f(2), f(3)
11 (2, 0, 1, 2)
12
13 >>> f = plural_to_python('(n==1')
14 Traceback (most recent call last):
15 ...
16 ValueError: Syntax error in plural form expression
17
18 >>> f = plural_to_python('(foo==1)')
19 Traceback (most recent call last):
20 ...
21 ValueError: Unsafe plural form expression
22
23 :param expr: a string containing a C expression as used for plural forms
24 in PO files
25 :return: the Python equivalent of the expression as a callable that expects
26 the number as single positional argument
27 :rtype: ``callable``
28
29 :note: The implementation of this function is based on the ``c2py``
30 function in the standard ``gettext`` module.
31 """
32 # Security check, allow only the "n" identifier
33 tokens = tokenize.generate_tokens(StringIO(expr).readline)
34 try:
35 if [x for x in tokens if x[0] == token.NAME and x[1] != 'n']:
36 raise ValueError('Unsafe plural form expression')
37 except tokenize.TokenError:
38 raise ValueError('Syntax error in plural form expression')
39
40 # Replace some C operators by their Python equivalents
41 expr = expr.replace('&&', ' and ').replace('||', ' or ')
42 expr = re.sub(r'\!([^=])', ' not \\1', expr)
43
44 # Define C-like "cond ? true : false"
45 def _test(condition, true, false):
46 if condition:
47 return true
48 else:
49 return false
50
51 # Regular expression and replacement function used to transform
52 # "a?b:c" to "test(a,b,c)".
53 regex = re.compile(r'(.*?)\?(.*?):(.*)')
54 def _repl(m):
55 return "test(%s, %s, %s)" % (m.group(1), m.group(2),
56 regex.sub(_repl, m.group(3)))
57
58 # Code to transform the plural expression, taking care of parentheses
59 stack = ['']
60 for char in expr:
61 if char == '(':
62 stack.append('')
63 elif char == ')':
64 subexpr = regex.sub(_repl, stack.pop())
65 stack[-1] += '(%s)' % subexpr
66 else:
67 stack[-1] += char
68 expr = regex.sub(_repl, stack.pop())
69
70 try:
71 return eval('lambda n: int(%s)' % expr, {'test': _test}, {})
72 except SyntaxError:
73 raise ValueError('Syntax error in plural form expression')
74

Download Raw Source

Comments

No comments so far.