Skip to content

Commit ac22931

Browse files
committed
py/modsys: Add optional mutable attributes sys.ps1/ps2 and use them.
This allows customising the REPL prompt strings. Signed-off-by: Damien George <[email protected]>
1 parent cac939d commit ac22931

File tree

15 files changed

+94
-12
lines changed

15 files changed

+94
-12
lines changed

docs/library/sys.rst

+6
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ Constants
132132
If you need to check whether your program runs on MicroPython (vs other
133133
Python implementation), use `sys.implementation` instead.
134134

135+
.. data:: ps1
136+
ps2
137+
138+
Mutable attributes holding strings, which are used for the REPL prompt. The defaults
139+
give the standard Python prompt of ``>>>`` and ``...``.
140+
135141
.. data:: stderr
136142

137143
Standard error `stream`.

ports/unix/main.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ STATIC int do_repl(void) {
193193

194194
input_restart:
195195
vstr_reset(&line);
196-
int ret = readline(&line, ">>> ");
196+
int ret = readline(&line, mp_repl_get_ps1());
197197
mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT;
198198

199199
if (ret == CHAR_CTRL_C) {
@@ -240,7 +240,7 @@ STATIC int do_repl(void) {
240240
// got a line with non-zero length, see if it needs continuing
241241
while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) {
242242
vstr_add_byte(&line, '\n');
243-
ret = readline(&line, "... ");
243+
ret = readline(&line, mp_repl_get_ps2());
244244
if (ret == CHAR_CTRL_C) {
245245
// cancel everything
246246
printf("\n");
@@ -265,13 +265,13 @@ STATIC int do_repl(void) {
265265
// use simple readline
266266

267267
for (;;) {
268-
char *line = prompt(">>> ");
268+
char *line = prompt((char *)mp_repl_get_ps1());
269269
if (line == NULL) {
270270
// EOF
271271
return 0;
272272
}
273273
while (mp_repl_continue_with_input(line)) {
274-
char *line2 = prompt("... ");
274+
char *line2 = prompt((char *)mp_repl_get_ps2());
275275
if (line2 == NULL) {
276276
break;
277277
}

ports/unix/variants/coverage/mpconfigvariant.h

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#define MICROPY_PY_BUILTINS_HELP (1)
4646
#define MICROPY_PY_BUILTINS_HELP_MODULES (1)
4747
#define MICROPY_PY_SYS_GETSIZEOF (1)
48+
#define MICROPY_PY_SYS_PS1_PS2 (1)
4849
#define MICROPY_PY_SYS_TRACEBACKLIMIT (1)
4950
#define MICROPY_PY_MATH_CONSTANTS (1)
5051
#define MICROPY_PY_MATH_FACTORIAL (1)

py/modsys.c

+4
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_settrace_obj, mp_sys_settrace);
185185

186186
#if MICROPY_PY_SYS_ATTR_DELEGATION
187187
STATIC const uint16_t sys_mutable_keys[] = {
188+
#if MICROPY_PY_SYS_PS1_PS2
189+
MP_QSTR_ps1,
190+
MP_QSTR_ps2,
191+
#endif
188192
#if MICROPY_PY_SYS_TRACEBACKLIMIT
189193
MP_QSTR_tracebacklimit,
190194
#endif

py/mpconfig.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -1356,6 +1356,11 @@ typedef double mp_float_t;
13561356
#define MICROPY_PY_SYS_ATEXIT (0)
13571357
#endif
13581358

1359+
// Whether to provide sys.{ps1,ps2} mutable attributes, to control REPL prompts
1360+
#ifndef MICROPY_PY_SYS_PS1_PS2
1361+
#define MICROPY_PY_SYS_PS1_PS2 (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
1362+
#endif
1363+
13591364
// Whether to provide "sys.settrace" function
13601365
#ifndef MICROPY_PY_SYS_SETTRACE
13611366
#define MICROPY_PY_SYS_SETTRACE (0)
@@ -1385,7 +1390,7 @@ typedef double mp_float_t;
13851390
// Whether the sys module supports attribute delegation
13861391
// This is enabled automatically when needed by other features
13871392
#ifndef MICROPY_PY_SYS_ATTR_DELEGATION
1388-
#define MICROPY_PY_SYS_ATTR_DELEGATION (MICROPY_PY_SYS_TRACEBACKLIMIT)
1393+
#define MICROPY_PY_SYS_ATTR_DELEGATION (MICROPY_PY_SYS_PS1_PS2 || MICROPY_PY_SYS_TRACEBACKLIMIT)
13891394
#endif
13901395

13911396
// Whether to provide "uerrno" module

py/mpstate.h

+4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
// variable, but in the future it is hoped that the state can become local.
4242

4343
enum {
44+
#if MICROPY_PY_SYS_PS1_PS2
45+
MP_SYS_MUTABLE_PS1,
46+
MP_SYS_MUTABLE_PS2,
47+
#endif
4448
#if MICROPY_PY_SYS_TRACEBACKLIMIT
4549
MP_SYS_MUTABLE_TRACEBACKLIMIT,
4650
#endif

py/qstrdefs.h

+4
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ Q()
3939
Q(*)
4040
Q(_)
4141
Q(/)
42+
#if MICROPY_PY_SYS_PS1_PS2
43+
Q(>>> )
44+
Q(... )
45+
#endif
4246
#if MICROPY_PY_BUILTINS_STR_OP_MODULO
4347
Q(%#o)
4448
Q(%#x)

py/repl.c

+10
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@
3333

3434
#if MICROPY_HELPER_REPL
3535

36+
#if MICROPY_PY_SYS_PS1_PS2
37+
const char *mp_repl_get_psx(unsigned int entry) {
38+
if (mp_obj_is_str(MP_STATE_VM(sys_mutable)[entry])) {
39+
return mp_obj_str_get_str(MP_STATE_VM(sys_mutable)[entry]);
40+
} else {
41+
return "";
42+
}
43+
}
44+
#endif
45+
3646
STATIC bool str_startswith_word(const char *str, const char *head) {
3747
size_t i;
3848
for (i = 0; str[i] && head[i]; i++) {

py/repl.h

+26
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,34 @@
3131
#include "py/mpprint.h"
3232

3333
#if MICROPY_HELPER_REPL
34+
35+
#if MICROPY_PY_SYS_PS1_PS2
36+
37+
const char *mp_repl_get_psx(unsigned int entry);
38+
39+
static inline const char *mp_repl_get_ps1(void) {
40+
return mp_repl_get_psx(MP_SYS_MUTABLE_PS1);
41+
}
42+
43+
static inline const char *mp_repl_get_ps2(void) {
44+
return mp_repl_get_psx(MP_SYS_MUTABLE_PS2);
45+
}
46+
47+
#else
48+
49+
static inline const char *mp_repl_get_ps1(void) {
50+
return ">>> ";
51+
}
52+
53+
static inline const char *mp_repl_get_ps2(void) {
54+
return "... ";
55+
}
56+
57+
#endif
58+
3459
bool mp_repl_continue_with_input(const char *input);
3560
size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print, const char **compl_str);
61+
3662
#endif
3763

3864
#endif // MICROPY_INCLUDED_PY_REPL_H

py/runtime.c

+5
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ void mp_init(void) {
135135
MP_STATE_VM(sys_exitfunc) = mp_const_none;
136136
#endif
137137

138+
#if MICROPY_PY_SYS_PS1_PS2
139+
MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS1]) = MP_OBJ_NEW_QSTR(MP_QSTR__gt__gt__gt__space_);
140+
MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS2]) = MP_OBJ_NEW_QSTR(MP_QSTR__dot__dot__dot__space_);
141+
#endif
142+
138143
#if MICROPY_PY_SYS_SETTRACE
139144
MP_STATE_THREAD(prof_trace_callback) = MP_OBJ_NULL;
140145
MP_STATE_THREAD(prof_callback_is_executing) = false;

shared/runtime/pyexec.c

+5-5
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ STATIC int pyexec_friendly_repl_process_char(int c) {
433433

434434
vstr_add_byte(MP_STATE_VM(repl_line), '\n');
435435
repl.cont_line = true;
436-
readline_note_newline("... ");
436+
readline_note_newline(mp_repl_get_ps2());
437437
return 0;
438438

439439
} else {
@@ -454,7 +454,7 @@ STATIC int pyexec_friendly_repl_process_char(int c) {
454454

455455
if (mp_repl_continue_with_input(vstr_null_terminated_str(MP_STATE_VM(repl_line)))) {
456456
vstr_add_byte(MP_STATE_VM(repl_line), '\n');
457-
readline_note_newline("... ");
457+
readline_note_newline(mp_repl_get_ps2());
458458
return 0;
459459
}
460460

@@ -468,7 +468,7 @@ STATIC int pyexec_friendly_repl_process_char(int c) {
468468
vstr_reset(MP_STATE_VM(repl_line));
469469
repl.cont_line = false;
470470
repl.paste_mode = false;
471-
readline_init(MP_STATE_VM(repl_line), ">>> ");
471+
readline_init(MP_STATE_VM(repl_line), mp_repl_get_ps1());
472472
return 0;
473473
}
474474
}
@@ -598,7 +598,7 @@ int pyexec_friendly_repl(void) {
598598
}
599599

600600
vstr_reset(&line);
601-
int ret = readline(&line, ">>> ");
601+
int ret = readline(&line, mp_repl_get_ps1());
602602
mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT;
603603

604604
if (ret == CHAR_CTRL_A) {
@@ -651,7 +651,7 @@ int pyexec_friendly_repl(void) {
651651
// got a line with non-zero length, see if it needs continuing
652652
while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) {
653653
vstr_add_byte(&line, '\n');
654-
ret = readline(&line, "... ");
654+
ret = readline(&line, mp_repl_get_ps2());
655655
if (ret == CHAR_CTRL_C) {
656656
// cancel everything
657657
mp_hal_stdout_tx_str("\r\n");

tests/cmdline/repl_sys_ps1_ps2.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# test changing ps1/ps2
2+
import usys
3+
usys.ps1 = "PS1"
4+
usys.ps2 = "PS2"
5+
(1 +
6+
2)

tests/cmdline/repl_sys_ps1_ps2.py.exp

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
MicroPython \.\+ version
2+
Use \.\+
3+
>>> # test changing ps1/ps2
4+
>>> import usys
5+
>>> usys.ps1 = "PS1"
6+
PS1usys.ps2 = "PS2"
7+
PS1(1 +
8+
PS22)
9+
3
10+
PS1

tests/run-tests.py

+1
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
433433

434434
if not has_coverage:
435435
skip_tests.add("cmdline/cmd_parsetree.py")
436+
skip_tests.add("cmdline/repl_sys_ps1_ps2.py")
436437

437438
# Some tests shouldn't be run on a PC
438439
if args.target == "unix":

tests/unix/extra_coverage.py.exp

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ utime utimeq
4545
argv atexit byteorder exc_info
4646
exit getsizeof implementation maxsize
4747
modules path platform print_exception
48-
stderr stdin stdout tracebacklimit
49-
version version_info
48+
ps1 ps2 stderr stdin
49+
stdout tracebacklimit version version_info
5050
ementation
5151
# attrtuple
5252
(start=1, stop=2, step=3)

0 commit comments

Comments
 (0)