def test_new_lines(self): x = 0 self.assertEqual(f('''{x +1}'''), "1") d = {0: 'zero'} assert f('''{d[0 ]}''') == 'zero'
def test_method(self): with self.assertRaises(SyntaxError): str(f("{{}")) # Unbalanced Double braces should not raise a SyntaxError try: str(f("{{}}}}")) except SyntaxError: self.fail()
def test_class_attribute_fstring(self): test_object = type('TestObject', (object,), {}) foo = test_object() setattr(foo, 'test', 5) expected_str = "foo.test=5" actual_str = str(f("foo.test={foo.test}")) self.assertEqual(expected_str, actual_str)
def test_multi_fstring_indicator(self): x = 6 y = 7 result = 13 expected_str = "6+7=13" actual_str = str(f("{x}+{y}={result}")) self.assertEqual(expected_str, actual_str)
def _substitute(t, table="_", path=None, parent="", loc={}): _table_ = table _path_ = path + "_" + table if path else table _parent_ = parent # get only strings - tables will be recursive keys = {} _defaults_ = t.get("_defaults_", loc.get("_defaults_", [])) for k in _defaults_: keys[k] = keys.get(k, str(f("{{__{k}}}"))) keys.update( dict( filter( lambda x: isinstance(t[x[0]], str) or isinstance( t[x[0]], check_for) or isinstance(t[x[0]], float) or isinstance(t[x[0]], int), t.items()))) subs = dict(filter(lambda x: isinstance(t[x[0]], dict), t.items())) lists = dict(filter(lambda x: isinstance(t[x[0]], list), t.items())) # preserve the rest as-is ret = t for remove in keys.keys(): ret.pop(remove, None) for remove in subs.keys(): ret.pop(remove, None) for remove in lists.keys(): ret.pop(remove, None) absoute_keys = dict([(_path_ + "_" + k, v) for k, v in keys.items()]) # can't use separate Namespace due to the implementation of fstring locals().update({"_defaults_": _defaults_}) locals().update(loc) locals().update(keys) locals().update(absoute_keys) for k, v in keys.items(): _key_ = k # add relative ret[k] = _recursive_f(v, locals()) for k, v in lists.items(): _key_ = k # add relative ret[k] = [_recursive_f(v_val, locals()) for v_val in v] for k, v in subs.items(): ret[k] = _substitute(v, k, _path_, _table_, locals()) # Don't export the defaults ret.pop("_defaults_", None) return ret
def test_bad_type_conversion(self): with self.assertRaises(SyntaxError): f("{6+6}!") with self.assertRaises(SyntaxError): f("{6+6}!k") with self.assertRaises(SyntaxError): f("{6+6}!ss")
def _recursive_f(t, loc={}): """ Apply f-string expansion until no more patterns are there (until nothing changes anymore) """ if not (isinstance(t, str) or isinstance(t, check_for)): return t locals().update(loc) while True: ret = f(str(t)) if t == ret: break t = ret # workaround for toml as it exports fstings as arrays # with single letters return str(ret)
def test_len(self): self.assertEqual(len(f("44")), 2)
def test_add_two_fstrings_together(self): self.assertEqual(f("a") + f("b"), "ab")
def test_sort(self): arr = [f("a"), f("b"), f('a')] actual = sorted(arr) expected = [f("a"), f('a'), f("b")] self.assertEqual(actual, expected)
def test_get_item(self): self.assertEqual(f("44")[0], "4")
def test_repr_python2(self): self.assertEqual(repr(f("{u'1'}")), "u'1'")
def test_fstrings_in_formatting(self): width = 10 precision = 4 value = decimal.Decimal('12.34567') assert f('{value:{width}.{precision}}') == "12.35"
def test_newline_strings_within_fstring_are_okay(self): assert f('''{"""a b"""}''') == "a\nb"
def test_add_string_and_fstring(self): self.assertEqual("b" + f("{1+1}"), "b2")
def test_math_expr(self): expected_str = "4" actual_str = str(f("{2+2}")) self.assertEqual(expected_str, actual_str)
def test_bool_expr(self): x = 5 y = 5 expected_str = str(True) actual_str = str(f("{x==y}")) self.assertEqual(expected_str, actual_str)
def test_hexadecimal_formatting(self): value = 1234 assert f('input={value:#06x}') == 'input=0x04d2'
def test_dictionary_fstring(self): simple_dict = {'x': '5', 'y': '10'} expected_str = "5!=10" actual_str = str(f("{simple_dict['x']}!={simple_dict['y']}")) self.assertEqual(expected_str, actual_str)
def test_fstring_evaluates_eagerly(self): a = 4 b = f("{a}") assert b == "4" a = 5 assert b == "4"
def test_add_fstring_and_string(self): self.assertEqual(f("a") + "b", "ab")
def test_newline_strings_are_acceptable(self): assert f('''{1+1} {2+2}''') == '2\n4'
def test_basic_string(self): s = f("Hello") self.assertEqual(str(s), "Hello")
def test_in(self): self.assertIn("6", f("{66+1}"))
def test_capitalize(self): self.assertEqual(f("ab").capitalize(), "Ab")
def test_formatting(self): expected = "a = 4" actual = f("a = %d") % 4 self.assertEqual(expected, actual)
def test_equality(self): self.assertEqual(f("{2+2}"), "4")
def test_datetime_formatting(self): assert f('{date} was on a {date:%A}') == '1991-10-12 was on a Saturday'
def _load_compare(func_name): global d inp = ftoml.load(os.path.join(d, f("{func_name}.in.ftoml"))) out = toml.load(os.path.join(d, f("{func_name}.out.toml"))) assert inp == out
def test_iteration(self): expected = ["6", "1"] actual = list(f("{60+1}")) self.assertEqual(actual, expected)