Skip to content

Commit c114496

Browse files
committed
objstr: Implement kwargs support for str.format().
1 parent ae58795 commit c114496

4 files changed

Lines changed: 35 additions & 18 deletions

File tree

py/modbuiltins.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_any_obj, mp_builtin_any);
141141

142142
STATIC mp_obj_t mp_builtin_bin(mp_obj_t o_in) {
143143
mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR__brace_open__colon__hash_b_brace_close_), o_in };
144-
return mp_obj_str_format(MP_ARRAY_SIZE(args), args);
144+
return mp_obj_str_format(MP_ARRAY_SIZE(args), args, MP_OBJ_NULL);
145145
}
146146
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_bin_obj, mp_builtin_bin);
147147

py/objstr.c

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,7 @@ STATIC NORETURN void terse_str_format_value_error(void) {
832832
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "bad format string"));
833833
}
834834

835-
mp_obj_t mp_obj_str_format(mp_uint_t n_args, const mp_obj_t *args) {
835+
mp_obj_t mp_obj_str_format(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
836836
assert(MP_OBJ_IS_STR_OR_BYTES(args[0]));
837837

838838
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) {
932932
mp_obj_t arg = mp_const_none;
933933

934934
if (field_name) {
935-
if (arg_i > 0) {
936-
if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
937-
terse_str_format_value_error();
938-
} else {
939-
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError,
940-
"can't switch from automatic field numbering to manual field specification"));
941-
}
942-
}
943935
int index = 0;
944-
if (str_to_int(vstr_str(field_name), &index) != vstr_len(field_name) - 1) {
945-
nlr_raise(mp_obj_new_exception_msg(&mp_type_KeyError, "attributes not supported yet"));
936+
const char *field = vstr_str(field_name);
937+
const char *lookup = NULL;
938+
if (MP_LIKELY(unichar_isdigit(*field))) {
939+
if (arg_i > 0) {
940+
if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
941+
terse_str_format_value_error();
942+
} else {
943+
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError,
944+
"can't switch from automatic field numbering to manual field specification"));
945+
}
946+
}
947+
lookup = str_to_int(vstr_str(field_name), &index) + field;
948+
if (index >= n_args - 1) {
949+
nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "tuple index out of range"));
950+
}
951+
arg = args[index + 1];
952+
arg_i = -1;
953+
} else {
954+
for (lookup = field; *lookup && *lookup != '.' && *lookup != '['; lookup++);
955+
mp_obj_t field_q = mp_obj_new_str(field, lookup - field, true/*?*/);
956+
mp_map_elem_t *key_elem = mp_map_lookup(kwargs, field_q, MP_MAP_LOOKUP);
957+
if (key_elem == NULL) {
958+
nlr_raise(mp_obj_new_exception_arg1(&mp_type_KeyError, field_q));
959+
}
960+
arg = key_elem->value;
946961
}
947-
if (index >= n_args - 1) {
948-
nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "tuple index out of range"));
962+
if (*lookup) {
963+
nlr_raise(mp_obj_new_exception_msg(&mp_type_NotImplementedError, "attributes not supported yet"));
949964
}
950-
arg = args[index + 1];
951-
arg_i = -1;
952965
vstr_free(field_name);
953966
field_name = NULL;
954967
} else {
@@ -1775,7 +1788,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_endswith_obj, 2, 3, str_endswith);
17751788
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_strip_obj, 1, 2, str_strip);
17761789
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_lstrip_obj, 1, 2, str_lstrip);
17771790
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_rstrip_obj, 1, 2, str_rstrip);
1778-
MP_DEFINE_CONST_FUN_OBJ_VAR(str_format_obj, 1, mp_obj_str_format);
1791+
MP_DEFINE_CONST_FUN_OBJ_KW(str_format_obj, 1, mp_obj_str_format);
17791792
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_replace_obj, 3, 4, str_replace);
17801793
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_count_obj, 2, 4, str_count);
17811794
MP_DEFINE_CONST_FUN_OBJ_2(str_partition_obj, str_partition);

py/objstr.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ typedef struct _mp_obj_str_t {
5555
else { str_len = ((mp_obj_str_t*)str_obj_in)->len; str_data = ((mp_obj_str_t*)str_obj_in)->data; }
5656

5757
void mp_str_print_json(void (*print)(void *env, const char *fmt, ...), void *env, const byte *str_data, mp_uint_t str_len);
58-
mp_obj_t mp_obj_str_format(mp_uint_t n_args, const mp_obj_t *args);
58+
mp_obj_t mp_obj_str_format(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kwargs);
5959
mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte* data, mp_uint_t len);
6060

6161
mp_obj_t mp_obj_str_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_in);

tests/basics/string_format.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ def test(fmt, *args):
6060
test("{:@=6d}", -123)
6161
test("{:06d}", -123)
6262

63+
print("{foo}/foo".format(foo="bar"))
64+
print("{}".format(123, foo="bar"))
65+
print("{}-{foo}".format(123, foo="bar"))
66+
6367
def test_fmt(conv, fill, alignment, sign, prefix, width, precision, type, arg):
6468
fmt = '{'
6569
if conv:

0 commit comments

Comments
 (0)