Lazy format strings

class sage.misc.lazy_format.LazyFormat[source]

Bases: str

Lazy format strings.

Note

We recommend to use sage.misc.lazy_string.lazy_string() instead, which is both faster and more flexible.

An instance of LazyFormat behaves like a usual format string, except that the evaluation of the __repr__ method of the formatted arguments it postponed until actual printing.

EXAMPLES:

Under normal circumstances, Lazyformat strings behave as usual:

sage: from sage.misc.lazy_format import LazyFormat
sage: LazyFormat("Got `%s`; expected a list")%3
Got `3`; expected a list
sage: LazyFormat("Got `%s`; expected %s")%(3, 2/3)
Got `3`; expected 2/3
>>> from sage.all import *
>>> from sage.misc.lazy_format import LazyFormat
>>> LazyFormat("Got `%s`; expected a list")%Integer(3)
Got `3`; expected a list
>>> LazyFormat("Got `%s`; expected %s")%(Integer(3), Integer(2)/Integer(3))
Got `3`; expected 2/3

To demonstrate the laziness, let us build an object with a broken __repr__ method:

sage: class IDontLikeBeingPrinted():
....:     def __repr__(self):
....:         raise ValueError("do not ever try to print me")
>>> from sage.all import *
>>> class IDontLikeBeingPrinted():
...     def __repr__(self):
...         raise ValueError("do not ever try to print me")

There is no error when binding a lazy format with the broken object:

sage: lf = LazyFormat("<%s>")%IDontLikeBeingPrinted()
>>> from sage.all import *
>>> lf = LazyFormat("<%s>")%IDontLikeBeingPrinted()

The error only occurs upon printing:

sage: lf
<repr(<sage.misc.lazy_format.LazyFormat at 0x...>) failed: ValueError: do not ever try to print me>
>>> from sage.all import *
>>> lf
<repr(<sage.misc.lazy_format.LazyFormat at 0x...>) failed: ValueError: do not ever try to print me>

Common use case:

Most of the time, __repr__ methods are only called during user interaction, and therefore need not be fast; and indeed there are objects x in Sage such x.__repr__() is time consuming.

There are however some uses cases where many format strings are constructed but not actually printed. This includes error handling messages in unittest or TestSuite executions:

sage: QQ._tester().assertIn(0, QQ,
....:                "%s doesn't contain 0"%QQ)
>>> from sage.all import *
>>> QQ._tester().assertIn(Integer(0), QQ,
...                "%s doesn't contain 0"%QQ)

In the above QQ.__repr__() has been called, and the result immediately discarded. To demonstrate this we replace QQ in the format string argument with our broken object:

sage: QQ._tester().assertTrue(True,
....:                "%s doesn't contain 0"%IDontLikeBeingPrinted())
Traceback (most recent call last):
...
ValueError: do not ever try to print me
>>> from sage.all import *
>>> QQ._tester().assertTrue(True,
...                "%s doesn't contain 0"%IDontLikeBeingPrinted())
Traceback (most recent call last):
...
ValueError: do not ever try to print me

This behavior can induce major performance penalties when testing. Note that this issue does not impact the usual assert:

sage: assert True, "%s is wrong"%IDontLikeBeingPrinted()
>>> from sage.all import *
>>> assert True, "%s is wrong"%IDontLikeBeingPrinted()

We now check that LazyFormat indeed solves the assertion problem:

sage: QQ._tester().assertTrue(True,
....:               LazyFormat("%s is wrong")%IDontLikeBeingPrinted())
sage: QQ._tester().assertTrue(False,
....:               LazyFormat("%s is wrong")%IDontLikeBeingPrinted())
Traceback (most recent call last):
...
AssertionError: ...
>>> from sage.all import *
>>> QQ._tester().assertTrue(True,
...               LazyFormat("%s is wrong")%IDontLikeBeingPrinted())
>>> QQ._tester().assertTrue(False,
...               LazyFormat("%s is wrong")%IDontLikeBeingPrinted())
Traceback (most recent call last):
...
AssertionError: ...