codekingpro/portable-devtools
114k
1# -*- coding: utf-8 -*-2r"""3 speaklater4 ~~~~~~~~~~5 6 A module that provides lazy strings for translations. Basically you7 get an object that appears to be a string but changes the value every8 time the value is evaluated based on a callable you provide.9 10 For example you can have a global `lazy_gettext` function that returns11 a lazy string with the value of the current set language.12 13 Example:14 15 >>> from speaklater import make_lazy_string, text_type16 >>> 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 >>> string22 l'Hello World'23 >>> text_type(string) == u'Hello World'24 True25 >>> string.upper() == u'HELLO WORLD'26 True27 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 True33 34 This is especially handy when combined with a thread local and gettext35 translations or dicts of translatable strings:36 37 >>> from speaklater import make_lazy_gettext38 >>> from threading import local39 >>> 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 Ja45 >>> l.translations[u'Yes'] = u'Si'46 >>> print(yes)47 Si48 49 Lazy strings are no real strings so if you pass this sort of string to50 a function that performs an instance check, it will fail. In that case51 you have to explicitly convert it with `unicode` and/or `string` depending52 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_string57 >>> is_lazy_string(u'yes')58 False59 >>> is_lazy_string(yes)60 True61 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 to66 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 72import sys73 74PY2 = sys.version_info[0] == 275_identity = lambda x: x76 77if not PY2:78 text_type = str79 implements_to_string = _identity80 implements_bool = _identity81else:82 text_type = unicode83 84 def implements_to_string(cls):85 cls.__unicode__ = cls.__str__86 cls.__str__ = lambda x: x.__unicode__().encode('utf-8')87 return cls88 89 def implements_bool(cls):90 cls.__nonzero__ = cls.__bool__91 del cls.__bool__92 return cls93 94 95def is_lazy_string(obj):96 """Checks if the given object is a lazy string."""97 return isinstance(obj, _LazyString)98 99 100def make_lazy_string(__func, *args, **kwargs):101 """Creates a lazy string by invoking func with args."""102 return _LazyString(__func, args, kwargs)103 104 105def make_lazy_gettext(lookup_func):106 """Creates a lazy gettext function dispatches to a gettext107 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 >>> x115 l'Ja'116 >>> translations[u'Yes'] = u'Si'117 >>> x118 l'Si'119 """120 def lazy_gettext(string):121 if is_lazy_string(string):122 return string123 return make_lazy_string(lookup_func(), string)124 return lazy_gettext125 126 127@implements_bool128@implements_to_string129class _LazyString(object):130 """Class for strings created by a function call.131 132 The proxy implementation attempts to be as complete as possible, so that133 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):138 self._func = func139 self._args = args140 self._kwargs = kwargs141 142 value = property(lambda x: x._func(*x._args, **x._kwargs))143 144 def __contains__(self, key):145 return key in self.value146 147 def __bool__(self):148 return bool(self.value)149 150 def __dir__(self):151 return dir(text_type)152 153 def __iter__(self):154 return iter(self.value)155 156 def __len__(self):157 return len(self.value)158 159 def __str__(self):160 return text_type(self.value)161 162 def __add__(self, other):163 return self.value + other164 165 def __radd__(self, other):166 return other + self.value167 168 def __mod__(self, other):169 return self.value % other170 171 def __rmod__(self, other):172 return other % self.value173 174 def __mul__(self, other):175 return self.value * other176 177 def __rmul__(self, other):178 return other * self.value179 180 def __lt__(self, other):181 return self.value < other182 183 def __le__(self, other):184 return self.value <= other185 186 def __eq__(self, other):187 return self.value == other188 189 def __ne__(self, other):190 return self.value != other191 192 def __gt__(self, other):193 return self.value > other194 195 def __ge__(self, other):196 return self.value >= other197 198 def __getattr__(self, name):199 if name == '__members__':200 return self.__dir__()201 return getattr(self.value, name)202 203 def __getstate__(self):204 return self._func, self._args, self._kwargs205 206 def __setstate__(self, tup):207 self._func, self._args, self._kwargs = tup208 209 def __getitem__(self, key):210 return self.value[key]211 212 def __copy__(self):213 return self214 215 def __repr__(self):216 try:217 r = repr(self.value)218 if PY2 and r.startswith('u'):219 r = r[1:] # make it look same as on py3220 return 'l' + r221 except Exception:222 return '<%s broken>' % self.__class__.__name__223 224 225if __name__ == '__main__':226 import doctest227 doctest.testmod()228 