From c1144966417de643d86286dae3189e8c0e09d9c2 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 4 Jan 2015 00:14:13 +0200 Subject: [PATCH] objstr: Implement kwargs support for str.format(). --- py/modbuiltins.c | 2 +- py/objstr.c | 45 ++++++++++++++++++++++------------- py/objstr.h | 2 +- tests/basics/string_format.py | 4 ++++ 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/py/modbuiltins.c b/py/modbuiltins.c index 4a1446d233..3355da259d 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -141,7 +141,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_any_obj, mp_builtin_any); STATIC mp_obj_t mp_builtin_bin(mp_obj_t o_in) { mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR__brace_open__colon__hash_b_brace_close_), o_in }; - return mp_obj_str_format(MP_ARRAY_SIZE(args), args); + return mp_obj_str_format(MP_ARRAY_SIZE(args), args, MP_OBJ_NULL); } MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_bin_obj, mp_builtin_bin); diff --git a/py/objstr.c b/py/objstr.c index 5976e36e71..8d05ae8c9f 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -832,7 +832,7 @@ STATIC NORETURN void terse_str_format_value_error(void) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "bad format string")); } -mp_obj_t mp_obj_str_format(mp_uint_t n_args, const mp_obj_t *args) { +mp_obj_t mp_obj_str_format(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { assert(MP_OBJ_IS_STR_OR_BYTES(args[0])); GET_STR_DATA_LEN(args[0], str, len); @@ -932,23 +932,36 @@ mp_obj_t mp_obj_str_format(mp_uint_t n_args, const mp_obj_t *args) { mp_obj_t arg = mp_const_none; if (field_name) { - if (arg_i > 0) { - if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { - terse_str_format_value_error(); - } else { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, - "can't switch from automatic field numbering to manual field specification")); - } - } int index = 0; - if (str_to_int(vstr_str(field_name), &index) != vstr_len(field_name) - 1) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_KeyError, "attributes not supported yet")); + const char *field = vstr_str(field_name); + const char *lookup = NULL; + if (MP_LIKELY(unichar_isdigit(*field))) { + if (arg_i > 0) { + if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { + terse_str_format_value_error(); + } else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "can't switch from automatic field numbering to manual field specification")); + } + } + lookup = str_to_int(vstr_str(field_name), &index) + field; + if (index >= n_args - 1) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "tuple index out of range")); + } + arg = args[index + 1]; + arg_i = -1; + } else { + for (lookup = field; *lookup && *lookup != '.' && *lookup != '['; lookup++); + mp_obj_t field_q = mp_obj_new_str(field, lookup - field, true/*?*/); + mp_map_elem_t *key_elem = mp_map_lookup(kwargs, field_q, MP_MAP_LOOKUP); + if (key_elem == NULL) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_KeyError, field_q)); + } + arg = key_elem->value; } - if (index >= n_args - 1) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "tuple index out of range")); + if (*lookup) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_NotImplementedError, "attributes not supported yet")); } - arg = args[index + 1]; - arg_i = -1; vstr_free(field_name); field_name = NULL; } else { @@ -1775,7 +1788,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_endswith_obj, 2, 3, str_endswith); MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_strip_obj, 1, 2, str_strip); MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_lstrip_obj, 1, 2, str_lstrip); MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_rstrip_obj, 1, 2, str_rstrip); -MP_DEFINE_CONST_FUN_OBJ_VAR(str_format_obj, 1, mp_obj_str_format); +MP_DEFINE_CONST_FUN_OBJ_KW(str_format_obj, 1, mp_obj_str_format); MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_replace_obj, 3, 4, str_replace); MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_count_obj, 2, 4, str_count); MP_DEFINE_CONST_FUN_OBJ_2(str_partition_obj, str_partition); diff --git a/py/objstr.h b/py/objstr.h index 8894c267e3..5128a005f3 100644 --- a/py/objstr.h +++ b/py/objstr.h @@ -55,7 +55,7 @@ typedef struct _mp_obj_str_t { else { str_len = ((mp_obj_str_t*)str_obj_in)->len; str_data = ((mp_obj_str_t*)str_obj_in)->data; } void mp_str_print_json(void (*print)(void *env, const char *fmt, ...), void *env, const byte *str_data, mp_uint_t str_len); -mp_obj_t mp_obj_str_format(mp_uint_t n_args, const mp_obj_t *args); +mp_obj_t mp_obj_str_format(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kwargs); mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte* data, mp_uint_t len); mp_obj_t mp_obj_str_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_in); diff --git a/tests/basics/string_format.py b/tests/basics/string_format.py index 84ea054758..07071be5ab 100644 --- a/tests/basics/string_format.py +++ b/tests/basics/string_format.py @@ -60,6 +60,10 @@ test("{:@<6d}", -123) test("{:@=6d}", -123) test("{:06d}", -123) +print("{foo}/foo".format(foo="bar")) +print("{}".format(123, foo="bar")) +print("{}-{foo}".format(123, foo="bar")) + def test_fmt(conv, fill, alignment, sign, prefix, width, precision, type, arg): fmt = '{' if conv: