1
2 r"""
3 speaklater
4 ~~~~~~~~~~
5
6 A module that provides lazy strings for translations. Basically you
7 get an object that appears to be a string but changes the value every
8 time the value is evaluated based on a callable you provide.
9
10 For example you can have a global `lazy_gettext` function that returns
11 a lazy string with the value of the current set language.
12
13 Example:
14
15 >>> from speaklater import make_lazy_string, text_type
16 >>> sval = u'Hello World'
17 >>> string = make_lazy_string(lambda: sval)
18
19 This lazy string will evaluate to the value of the `sval` variable.
20
21 >>> string
22 l'Hello World'
23 >>> text_type(string) == u'Hello World'
24 True
25 >>> string.upper() == u'HELLO WORLD'
26 True
27
28 If you change the value, the lazy string will change as well:
29
30 >>> sval = u'Hallo Welt'
31 >>> string.upper() == u'HALLO WELT'
32 True
33
34 This is especially handy when combined with a thread local and gettext
35 translations or dicts of translatable strings:
36
37 >>> from speaklater import make_lazy_gettext
38 >>> from threading import local
39 >>> l = local()
40 >>> l.translations = {u'Yes': 'Ja'}
41 >>> lazy_gettext = make_lazy_gettext(lambda: l.translations.get)
42 >>> yes = lazy_gettext(u'Yes')
43 >>> print(yes)
44 Ja
45 >>> l.translations[u'Yes'] = u'Si'
46 >>> print(yes)
47 Si
48
49 Lazy strings are no real strings so if you pass this sort of string to
50 a function that performs an instance check, it will fail. In that case
51 you have to explicitly convert it with `unicode` and/or `string` depending
52 on what string type the lazy string encapsulates.
53
54 To check if a string is lazy, you can use the `is_lazy_string` function:
55
56 >>> from speaklater import is_lazy_string
57 >>> is_lazy_string(u'yes')
58 False
59 >>> is_lazy_string(yes)
60 True
61
62 New in version 1.4: python >= 3.3 (and also 2.6 and 2.7) support,
63 repr(lazystring) is l"foo" on py2 and py3 - no "u" on py2!
64
65 New in version 1.2: It's now also possible to pass keyword arguments to
66 the callback used with `make_lazy_string`.
67
68 :copyright: (c) 2010 by Armin Ronacher.
69 :license: BSD, see LICENSE for more details.
70 """
71
72 import sys
73
74 PY2 = sys.version_info[0] == 2
75 _identity = lambda x: x
76
77 if not PY2:
78 text_type = str
79 implements_to_string = _identity
80 implements_bool = _identity
81 else:
82 text_type = unicode
88
93
96 """Checks if the given object is a lazy string."""
97 return isinstance(obj, _LazyString)
98
101 """Creates a lazy string by invoking func with args."""
102 return _LazyString(__func, args, kwargs)
103
104
105 -def make_lazy_gettext(lookup_func):
106 """Creates a lazy gettext function dispatches to a gettext
107 function as returned by `lookup_func`.
108
109 Example:
110
111 >>> translations = {u'Yes': u'Ja'}
112 >>> lazy_gettext = make_lazy_gettext(lambda: translations.get)
113 >>> x = lazy_gettext(u'Yes')
114 >>> x
115 l'Ja'
116 >>> translations[u'Yes'] = u'Si'
117 >>> x
118 l'Si'
119 """
120 def lazy_gettext(string):
121 if is_lazy_string(string):
122 return string
123 return make_lazy_string(lookup_func(), string)
124 return lazy_gettext
125
126
127 @implements_bool
128 @implements_to_string
129 -class _LazyString(object):
130 """Class for strings created by a function call.
131
132 The proxy implementation attempts to be as complete as possible, so that
133 the lazy objects should mostly work as expected, for example for sorting.
134 """
135 __slots__ = ('_func', '_args', '_kwargs')
136
137 - def __init__(self, func, args, kwargs):
141
142 value = property(lambda x: x._func(*x._args, **x._kwargs))
143
145 return key in self.value
146
148 return bool(self.value)
149
151 return dir(text_type)
152
154 return iter(self.value)
155
157 return len(self.value)
158
160 return text_type(self.value)
161
163 return self.value + other
164
166 return other + self.value
167
169 return self.value % other
170
172 return other % self.value
173
175 return self.value * other
176
178 return other * self.value
179
181 return self.value < other
182
184 return self.value <= other
185
187 return self.value == other
188
190 return self.value != other
191
193 return self.value > other
194
196 return self.value >= other
197
199 if name == '__members__':
200 return self.__dir__()
201 return getattr(self.value, name)
202
205
208
210 return self.value[key]
211
214
216 try:
217 r = repr(self.value)
218 if PY2 and r.startswith('u'):
219 r = r[1:]
220 return 'l' + r
221 except Exception:
222 return '<%s broken>' % self.__class__.__name__
223
224
225 if __name__ == '__main__':
226 import doctest
227 doctest.testmod()
228