def test_format_language_with_inner_fstr(): template = fstr("{x:{width}}") assert template.format(x=10, width=3) == " 10" assert template.format(x=3, width=4) == " 3" template = fstr("{x:{width}.{precision}}") assert template.format(x=1.2345, width=4, precision=2) == " 1.2"
def test_not_equal(): # There's a special test for this because there's a special # case in the f-string parser to look for != as not ending an # expression. Normally it would, while looking for !s or !r. assert fstr("{3!=4}").format() == "True" assert fstr("{3!=4:}").format() == "True" assert fstr("{3!=4!s}").format() == "True" assert fstr("{3!=4!s:.3}").format() == "Tru"
def test_side_effect_order(): class X: def __init__(self): self.i = 0 def __format__(self, spec): self.i += 1 return str(self.i) fstr("{x} {x}").format(x=X()) == "1 2"
def test_missing_format_spec(): class Obj: def __format__(self, spec): if not spec: return "*" return spec assert fstr("{Obj():x}").format(Obj=Obj) == "x" assert fstr("{Obj()}").format(Obj=Obj) == "*" assert fstr("{Obj():}").format(Obj=Obj) == "*" assert fstr("{3:}").format() == "3" assert fstr("{3!s:}").format() == "3"
def instantiate_fname_from_template(string, **kwargs): """ Turns filename template into actual filename instance. """ for key in kwargs: # to extract optional arguments vars()[key] = kwargs[ key] # equivalent to version = kwargs[key] when key = 'version' if '{' in string: template = fstr(string) return template.evaluate() else: return string
def test_leading_trailing_spaces(): assert fstr("{ 3}").format() == "3" assert fstr("{ 3}").format() == "3" assert fstr("{3 }").format() == "3" assert fstr("{3 }").format() == "3" assert fstr( "expr={ {x: y for x, y in [(1, 2), ]} }").format() == "expr={1: 2}" assert fstr( "expr={ {x: y for x, y in [(1, 2), ]}}").format() == "expr={1: 2}"
def test_many_expressions(): context = {"x": "X", "width": 1} def make_template(n, extra=""): return fstr(("{x} " + extra) * n) for n in range(250, 260): make_template(n).format(**context) # Test around 256. for i in range(250, 260): actual = make_template(i).format(**context) expected = (context["x"] + " ") * i assert actual == expected actual = make_template(250, "{x:{width}} ").format(**context) expected = (context["x"] + " ") * 500 assert actual == expected # Test lots of expressions and constants. assert fstr("{1} {'x'} {'y'} " * 1000).format() == "1 x y " * 1000
def test_even_double_brace_replacement(): template = fstr("{{}}") assert template.format() == "{}"
def test_errors(bad, etype): with pytest.raises(etype): fstr(bad).format()
def test_invalid_expressions(invalid): with pytest.raises(SyntaxError): fstr(invalid).format()
def test_bad_mismatched_braces(template): with pytest.raises(SyntaxError): fstr(template).format()
def test_trailing_and_leading_space(): assert fstr("{ 1 + 2}").format() == "3" assert fstr("{1 + 2 }").format() == "3" assert fstr("{ 1 + 2 }").format() == "3"
def test_hash_in_string(): # These aren't comments, since they're in strings. d = {"#": "hash"} assert fstr("{'#'}").format() == "#" assert fstr("{d['#']}").format(d=d) == "hash"
def test_newlines_in_expressions(): assert fstr("{0}").format() == "0" assert (fstr("""{3+ 4}""").format() == "7" # noqa: W503 )
def test_call(): def foo(x): return "x=" + str(x) assert fstr("{foo(10)}").format(foo=foo) == "x=10"
def test_expressions_with_triple_quoted_strings(template, expected): assert fstr(template).format() == expected
def test_missing_variable(): with pytest.raises(NameError): fstr("v:{value}").format()
def test_dict(): d = {'"': "double-quote", "'": "single-quote", "foo": "bar"} assert fstr("""{d["'"]}""").format(d=d) == "single-quote" assert fstr("""{d['"']}""").format(d=d) == "double-quote" assert fstr('{d["foo"]}').format(d=d) == "bar" assert fstr("{d['foo']}").format(d=d) == "bar"
def test_lambda(template, expected): assert fstr(template, x=5).format() == expected
def test_ok_mistmatched_braces(template, expected): assert fstr(template).format() == expected
def test_odd_double_brace_replacement(): template = fstr("{{{x}}}") assert template.format(x=1) == "{1}"
def test_no_backslashes_in_expression_part(template): with pytest.raises(SyntaxError): fstr(template).format()
def test_basic(): template = fstr("{x} + {y} = {x + y}", x=1) assert template.format(y=2) == "1 + 2 = 3" assert template.format(y=3) == "1 + 3 = 4"
def test_backslashes_in_string_part(template, expected): assert fstr(template).format() == expected
def dict_inside_braces_with_padding(): template = fstr("{ {x: y} }", x="a") assert template.format(y=1) == "{'a': 1}"
def test_conversions(): assert fstr("{3.14:10.10}").format() == " 3.14" assert fstr("{3.14!s:10.10}").format() == "3.14 " assert fstr("{3.14!r:10.10}").format() == "3.14 " if version_info >= (3, 0): assert fstr("{3.14!a:10.10}").format() == "3.14 " assert fstr('{"a"}').format() == "a" assert fstr('{"a"!r}').format() == "'a'" if version_info >= (3, 0): assert fstr('{"a"!a}').format() == "'a'" # Not a conversion. assert fstr('{"a!r"}').format() == "a!r" # Not a conversion, but show that ! is allowed in a format spec. assert fstr("{3.14:!<10.10}").format() == "3.14!!!!!!" bad_conversions = [ "{3!g}" "{3!A}" "{3!3}" "{3!G}" "{3!!}" "{3!:}" "{3! s}" # no space before conversion char "{x!s{y}}", "{3!ss}", "{3!ss:}", "{3!ss:s}", ] for bad in bad_conversions: with pytest.raises(SyntaxError): fstr(bad).format()
def test_unclosed_braces(brace): with pytest.raises(SyntaxError): fstr("{%s}" % brace).format()
def test_format_with_function(): def add(x, y): return x + y template = fstr("{add(x, y)}", add=add) assert template.format(x=1, y=2) == "3"
def make_template(n, extra=""): return fstr(("{x} " + extra) * n)
def test_empty_format_specifier(template, expected): assert fstr(template).format(x="test") == expected