| 1 | # -*- coding: utf-8 -*- |
| 2 | """ |
| 3 | Lazy strings. |
| 4 | |
| 5 | Based on speaklater: https://github.com/mitsuhiko/speaklater. |
| 6 | |
| 7 | A lazy string is an object that behaves almost exactly like a string |
| 8 | but where the value is not computed until needed. To define a lazy |
| 9 | you specify a function that produces a string together with the |
| 10 | appropriate arguments for that function. Sage uses lazy strings in |
| 11 | :mod:`sage.misc.misc` so that the filenames for SAGE_TMP (which |
| 12 | depends on the pid of the process running Sage) is not computed when |
| 13 | importing the Sage library. This means that when the doctesting code |
| 14 | imports the Sage library and then forks, the variable SAGE_TMP depends |
| 15 | on the new pid rather than the old one. |
| 16 | |
| 17 | EXAMPLES:: |
| 18 | |
| 19 | sage: from sage.misc.lazy_string import lazy_string |
| 20 | sage: L = [] |
| 21 | sage: s = lazy_string(lambda x: str(len(x)), L) |
| 22 | sage: L.append(5) |
| 23 | sage: s |
| 24 | l'1' |
| 25 | |
| 26 | Note that the function is recomputed each time:: |
| 27 | |
| 28 | sage: L.append(6) |
| 29 | sage: s |
| 30 | l'2' |
| 31 | """ |
| 32 | |
| 33 | #Copyright (c) 2009 by Armin Ronacher. |
| 34 | # |
| 35 | #Some rights reserved. |
| 36 | # |
| 37 | #Redistribution and use in source and binary forms, with or without |
| 38 | #modification, are permitted provided that the following conditions are |
| 39 | #met: |
| 40 | # |
| 41 | # * Redistributions of source code must retain the above copyright |
| 42 | # notice, this list of conditions and the following disclaimer. |
| 43 | # |
| 44 | # * Redistributions in binary form must reproduce the above |
| 45 | # copyright notice, this list of conditions and the following |
| 46 | # disclaimer in the documentation and/or other materials provided |
| 47 | # with the distribution. |
| 48 | # |
| 49 | # * The names of the contributors may not be used to endorse or |
| 50 | # promote products derived from this software without specific |
| 51 | # prior written permission. |
| 52 | # |
| 53 | #THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 54 | #"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 55 | #LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 56 | #A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 57 | #OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 58 | #SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 59 | #LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 60 | #DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 61 | #THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 62 | #(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 63 | #OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 64 | |
| 65 | import types |
| 66 | |
| 67 | def is_lazy_string(obj): |
| 68 | """ |
| 69 | Checks if the given object is a lazy string. |
| 70 | |
| 71 | EXAMPLES:: |
| 72 | |
| 73 | sage: from sage.misc.lazy_string import lazy_string, is_lazy_string |
| 74 | sage: f = lambda: "laziness" |
| 75 | sage: s = lazy_string(f) |
| 76 | sage: is_lazy_string(s) |
| 77 | True |
| 78 | """ |
| 79 | return isinstance(obj, _LazyString) |
| 80 | |
| 81 | def lazy_string(func, *args, **kwargs): |
| 82 | """ |
| 83 | Creates a lazy string by invoking func with args. |
| 84 | |
| 85 | EXAMPLES:: |
| 86 | |
| 87 | sage: from sage.misc.lazy_string import lazy_string |
| 88 | sage: f = lambda: "laziness" |
| 89 | sage: s = lazy_string(f); s |
| 90 | l'laziness' |
| 91 | """ |
| 92 | return _LazyString(func, args, kwargs) |
| 93 | |
| 94 | def _make_lazy_string(ftype, fpickle, args, kwargs): |
| 95 | """ |
| 96 | Used for pickling. |
| 97 | |
| 98 | EXAMPLES:: |
| 99 | |
| 100 | sage: from sage.misc.lazy_string import _make_lazy_string |
| 101 | sage: s = _make_lazy_string(None, lambda: "laziness", (), {}) |
| 102 | sage: s |
| 103 | l'laziness' |
| 104 | """ |
| 105 | if ftype == 'func': |
| 106 | from sage.misc.fpickle import unpickle_function |
| 107 | f = unpickle_function(fpickle) |
| 108 | else: |
| 109 | f = fpickle |
| 110 | return _LazyString(f, args, kwargs) |
| 111 | |
| 112 | class _LazyString(object): |
| 113 | """ |
| 114 | Class for strings created by a function call. |
| 115 | |
| 116 | The proxy implementation attempts to be as complete as possible, so that |
| 117 | the lazy objects should mostly work as expected, for example for sorting. |
| 118 | |
| 119 | EXAMPLES:: |
| 120 | |
| 121 | sage: from sage.misc.lazy_string import lazy_string |
| 122 | sage: f = lambda x: "laziness in the " + repr(x) |
| 123 | sage: s = lazy_string(f, ZZ); s |
| 124 | l'laziness in the Integer Ring' |
| 125 | """ |
| 126 | __slots__ = ('_func', '_args', '_kwargs') |
| 127 | |
| 128 | def __init__(self, func, args, kwargs): |
| 129 | """ |
| 130 | EXAMPLES:: |
| 131 | |
| 132 | sage: from sage.misc.lazy_string import lazy_string |
| 133 | sage: f = lambda x: "laziness" + repr(x) |
| 134 | sage: s = lazy_string(f, 5); s |
| 135 | l'laziness5' |
| 136 | """ |
| 137 | self._func = func |
| 138 | self._args = args |
| 139 | self._kwargs = kwargs |
| 140 | |
| 141 | value = property(lambda x: x._func(*x._args, **x._kwargs)) |
| 142 | |
| 143 | def __contains__(self, key): |
| 144 | """ |
| 145 | EXAMPLES:: |
| 146 | |
| 147 | sage: from sage.misc.lazy_string import lazy_string |
| 148 | sage: f = lambda: "laziness" |
| 149 | sage: s = lazy_string(f) |
| 150 | sage: 'zi' in s |
| 151 | True |
| 152 | sage: 'ni' in s |
| 153 | False |
| 154 | """ |
| 155 | return key in self.value |
| 156 | |
| 157 | def __nonzero__(self): |
| 158 | """ |
| 159 | EXAMPLES:: |
| 160 | |
| 161 | sage: from sage.misc.lazy_string import lazy_string |
| 162 | sage: f = lambda: "laziness" |
| 163 | sage: bool(lazy_string(f)) |
| 164 | True |
| 165 | sage: f = lambda: "" |
| 166 | sage: bool(lazy_string(f)) |
| 167 | False |
| 168 | """ |
| 169 | return bool(self.value) |
| 170 | |
| 171 | def __dir__(self): |
| 172 | """ |
| 173 | We assume that the underlying value provides the methods of a |
| 174 | unicode string. |
| 175 | |
| 176 | EXAMPLES:: |
| 177 | |
| 178 | sage: from sage.misc.lazy_string import lazy_string |
| 179 | sage: f = lambda: "laziness" |
| 180 | sage: s = lazy_string(f) |
| 181 | sage: "split" in dir(s) # indirect doctest |
| 182 | True |
| 183 | """ |
| 184 | return dir(unicode) |
| 185 | |
| 186 | def __iter__(self): |
| 187 | """ |
| 188 | EXAMPLES:: |
| 189 | |
| 190 | sage: from sage.misc.lazy_string import lazy_string |
| 191 | sage: f = lambda: "laziness" |
| 192 | sage: s = lazy_string(f) |
| 193 | sage: "".join(list(s)) # indirect doctest |
| 194 | 'laziness' |
| 195 | """ |
| 196 | return iter(self.value) |
| 197 | |
| 198 | def __len__(self): |
| 199 | """ |
| 200 | EXAMPLES:: |
| 201 | |
| 202 | sage: from sage.misc.lazy_string import lazy_string |
| 203 | sage: f = lambda: "laziness" |
| 204 | sage: s = lazy_string(f) |
| 205 | sage: len(s) |
| 206 | 8 |
| 207 | """ |
| 208 | return len(self.value) |
| 209 | |
| 210 | def __str__(self): |
| 211 | """ |
| 212 | EXAMPLES:: |
| 213 | |
| 214 | sage: from sage.misc.lazy_string import lazy_string |
| 215 | sage: f = lambda: "laziness" |
| 216 | sage: s = lazy_string(f) |
| 217 | sage: str(s) # indirect doctest |
| 218 | 'laziness' |
| 219 | """ |
| 220 | return str(self.value) |
| 221 | |
| 222 | def __unicode__(self): |
| 223 | """ |
| 224 | EXAMPLES:: |
| 225 | |
| 226 | sage: from sage.misc.lazy_string import lazy_string |
| 227 | sage: f = lambda: "laziness" |
| 228 | sage: s = lazy_string(f) |
| 229 | sage: unicode(s) # indirect doctest |
| 230 | u'laziness' |
| 231 | """ |
| 232 | return unicode(self.value) |
| 233 | |
| 234 | def __add__(self, other): |
| 235 | """ |
| 236 | EXAMPLES:: |
| 237 | |
| 238 | sage: from sage.misc.lazy_string import lazy_string |
| 239 | sage: f = lambda: "laziness" |
| 240 | sage: s = lazy_string(f) |
| 241 | sage: s + " supreme" |
| 242 | 'laziness supreme' |
| 243 | """ |
| 244 | return self.value + other |
| 245 | |
| 246 | def __radd__(self, other): |
| 247 | """ |
| 248 | EXAMPLES:: |
| 249 | |
| 250 | sage: from sage.misc.lazy_string import lazy_string |
| 251 | sage: f = lambda: "laziness" |
| 252 | sage: s = lazy_string(f) |
| 253 | sage: "no " + s |
| 254 | 'no laziness' |
| 255 | """ |
| 256 | return other + self.value |
| 257 | |
| 258 | def __mod__(self, other): |
| 259 | """ |
| 260 | EXAMPLES:: |
| 261 | |
| 262 | sage: from sage.misc.lazy_string import lazy_string |
| 263 | sage: f = lambda: "laz%sss" |
| 264 | sage: s = lazy_string(f) |
| 265 | sage: s % "ine" |
| 266 | 'laziness' |
| 267 | """ |
| 268 | return self.value % other |
| 269 | |
| 270 | def __rmod__(self, other): |
| 271 | """ |
| 272 | EXAMPLES:: |
| 273 | |
| 274 | sage: from sage.misc.lazy_string import lazy_string |
| 275 | sage: f = lambda: "ine" |
| 276 | sage: s = lazy_string(f) |
| 277 | sage: "laz%sss" % s |
| 278 | 'laziness' |
| 279 | """ |
| 280 | return other % self.value |
| 281 | |
| 282 | def __mul__(self, other): |
| 283 | """ |
| 284 | EXAMPLES:: |
| 285 | |
| 286 | sage: from sage.misc.lazy_string import lazy_string |
| 287 | sage: f = lambda: "laziness" |
| 288 | sage: s = lazy_string(f) |
| 289 | sage: s * 2 |
| 290 | 'lazinesslaziness' |
| 291 | """ |
| 292 | return self.value * other |
| 293 | |
| 294 | def __rmul__(self, other): |
| 295 | """ |
| 296 | EXAMPLES:: |
| 297 | |
| 298 | sage: from sage.misc.lazy_string import lazy_string |
| 299 | sage: f = lambda: "laziness" |
| 300 | sage: s = lazy_string(f) |
| 301 | sage: 2 * s |
| 302 | 'lazinesslaziness' |
| 303 | """ |
| 304 | return other * self.value |
| 305 | |
| 306 | def __lt__(self, other): |
| 307 | """ |
| 308 | EXAMPLES:: |
| 309 | |
| 310 | sage: from sage.misc.lazy_string import lazy_string |
| 311 | sage: f = lambda: "laziness" |
| 312 | sage: s = lazy_string(f) |
| 313 | sage: s < 'laziness' |
| 314 | False |
| 315 | sage: s < 'azi' |
| 316 | False |
| 317 | sage: s < s |
| 318 | False |
| 319 | """ |
| 320 | return self.value < other |
| 321 | |
| 322 | def __le__(self, other): |
| 323 | """ |
| 324 | EXAMPLES:: |
| 325 | |
| 326 | sage: from sage.misc.lazy_string import lazy_string |
| 327 | sage: f = lambda: "laziness" |
| 328 | sage: s = lazy_string(f) |
| 329 | sage: s <= 'laziness' |
| 330 | True |
| 331 | sage: s <= 'azi' |
| 332 | False |
| 333 | sage: s <= s |
| 334 | True |
| 335 | """ |
| 336 | return self.value <= other |
| 337 | |
| 338 | def __eq__(self, other): |
| 339 | """ |
| 340 | EXAMPLES:: |
| 341 | |
| 342 | sage: from sage.misc.lazy_string import lazy_string |
| 343 | sage: f = lambda: "laziness" |
| 344 | sage: s = lazy_string(f) |
| 345 | sage: s == 'laziness' |
| 346 | True |
| 347 | sage: s == 'azi' |
| 348 | False |
| 349 | sage: s == s |
| 350 | True |
| 351 | """ |
| 352 | return self.value == other |
| 353 | |
| 354 | def __ne__(self, other): |
| 355 | """ |
| 356 | EXAMPLES:: |
| 357 | |
| 358 | sage: from sage.misc.lazy_string import lazy_string |
| 359 | sage: f = lambda: "laziness" |
| 360 | sage: s = lazy_string(f) |
| 361 | sage: s != 'laziness' |
| 362 | False |
| 363 | sage: s != 'azi' |
| 364 | True |
| 365 | sage: s != s |
| 366 | False |
| 367 | """ |
| 368 | return self.value != other |
| 369 | |
| 370 | def __gt__(self, other): |
| 371 | """ |
| 372 | EXAMPLES:: |
| 373 | |
| 374 | sage: from sage.misc.lazy_string import lazy_string |
| 375 | sage: f = lambda: "laziness" |
| 376 | sage: s = lazy_string(f) |
| 377 | sage: s > 'laziness' |
| 378 | False |
| 379 | sage: s > 'azi' |
| 380 | True |
| 381 | sage: s > s |
| 382 | False |
| 383 | """ |
| 384 | return self.value > other |
| 385 | |
| 386 | def __ge__(self, other): |
| 387 | """ |
| 388 | EXAMPLES:: |
| 389 | |
| 390 | sage: from sage.misc.lazy_string import lazy_string |
| 391 | sage: f = lambda: "laziness" |
| 392 | sage: s = lazy_string(f) |
| 393 | sage: s >= 'laziness' |
| 394 | True |
| 395 | sage: s >= 'azi' |
| 396 | True |
| 397 | sage: s >= s |
| 398 | True |
| 399 | """ |
| 400 | return self.value >= other |
| 401 | |
| 402 | def __getattr__(self, name): |
| 403 | """ |
| 404 | We pass attribute lookup through to the underlying value. |
| 405 | |
| 406 | EXAMPLES:: |
| 407 | |
| 408 | sage: from sage.misc.lazy_string import lazy_string |
| 409 | sage: f = lambda: "laziness" |
| 410 | sage: s = lazy_string(f) |
| 411 | sage: s.endswith('ess') |
| 412 | True |
| 413 | sage: s.find('i') |
| 414 | 3 |
| 415 | """ |
| 416 | if name == '__members__': |
| 417 | return self.__dir__() |
| 418 | return getattr(self.value, name) |
| 419 | |
| 420 | def __reduce__(self): |
| 421 | """ |
| 422 | Pickling. |
| 423 | |
| 424 | EXAMPLES:: |
| 425 | |
| 426 | sage: from sage.misc.lazy_string import lazy_string |
| 427 | sage: f = lambda: "laziness" |
| 428 | sage: s = lazy_string(f) |
| 429 | sage: TestSuite(s).run() # indirect doctest |
| 430 | """ |
| 431 | import types |
| 432 | if isinstance(self._func, types.FunctionType): |
| 433 | from sage.misc.fpickle import pickle_function |
| 434 | f = pickle_function(self._func) |
| 435 | ftype = 'func' |
| 436 | else: |
| 437 | f = self.func |
| 438 | ftype = None |
| 439 | return _make_lazy_string, (ftype, f, self._args, self._kwargs) |
| 440 | |
| 441 | def __getitem__(self, key): |
| 442 | """ |
| 443 | EXAMPLES:: |
| 444 | |
| 445 | sage: from sage.misc.lazy_string import lazy_string |
| 446 | sage: f = lambda: "laziness" |
| 447 | sage: s = lazy_string(f) |
| 448 | sage: s[4] |
| 449 | 'n' |
| 450 | """ |
| 451 | return self.value[key] |
| 452 | |
| 453 | def __copy__(self): |
| 454 | """ |
| 455 | EXAMPLES:: |
| 456 | |
| 457 | sage: from sage.misc.lazy_string import lazy_string |
| 458 | sage: f = lambda: "laziness" |
| 459 | sage: s = lazy_string(f) |
| 460 | sage: copy(s) is s |
| 461 | True |
| 462 | """ |
| 463 | return self |
| 464 | |
| 465 | def __repr__(self): |
| 466 | """ |
| 467 | EXAMPLES:: |
| 468 | |
| 469 | sage: from sage.misc.lazy_string import lazy_string |
| 470 | sage: f = lambda: "laziness" |
| 471 | sage: s = lazy_string(f) |
| 472 | sage: s # indirect doctest |
| 473 | l'laziness' |
| 474 | """ |
| 475 | try: |
| 476 | return 'l' + repr(self.value) |
| 477 | except Exception: |
| 478 | return '<%s broken>' % self.__class__.__name__ |