Personalizando el prompt de la consola interactiva de Python (y de IPython)

Cuando entramos en la consola interactiva de Python (escribiendo python en una shell (bash, cmd,…)) vemos que cada línea en la que podemos escribir código empieza por >>>. Luego hay líneas de continuación de código, por ejemplo cuando escribimos un for, que empiezan por .... Un ejemplo de este comportamiento sería algo como lo siguiente (lo que comento, el prompt, lo he resaltado en verde):

>>> for i in range(3):
...     print(i)
...
0
1
2
>>>

Este comportamiento se puede modificar. En el módulo sys tenemos unas cadenas que definen este comportamiento y están almacenadas en sys.ps1 para la entrada (>>>) y sys.ps2 para la continuación (...). Vamos a modificarlo, inicia la consola interactiva y escribe:

Después de haber escrito lo anterior, si ahora replico el código del ejemplo anterior con el bucle for se debería de ver así:

Escribe >> for i in range(3):
Continúa ...     print(i)
Continúa ...
0
1
2
Escribe >>

Lo anterior es un poco horrible porque los distintos prompts no mantienen el mismo ancho y el código es más complicado de leer pero, recordad, esto es solo un ejemplo.

Podemos hacerlo un poco más interesante añadiendo el número de la entrada, similar a lo que hace IPython con su In [32]:. Leyendo la documentación nos indica que si queremos que la cadena se actualice podemos usar un objeto y llamará a str() cada vez que lo evalúe. Podemos crear una clase con un método mágico __str__. Para obtener el número de la entrada voy a usar la función get_current_history_length contenida en el módulo readline:

El resultado debería ser algo parecido a lo siguiente (los prompts originales en verde y el nuevo para la entrada en azul):

>>> import sys
>>> import readline
>>>
>>> class PS1:
...     def __str__(self):
...         return f"In [{readline.get_current_history_length()}]: "
...
>>> sys.ps1 = PS1()
In [83]:

El número empieza por 83 porque dependerá lo que tengáis en vuestra historia. Si queréis que empiece por 0 podéis limpiar la historia usando la función clear_history, dentro del módulo readline, antes de cambiar el prompt. Como no quería que se os borrase la historia lo he dejado tal cual.

¿Y en IPython?

En IPython el mecanismo es más complejo pero también más potente. Hemos de usar la clase IPython.terminal.prompts.Prompts que define el comportamiento de los prompts. En este caso, a diferencia de la consola interactiva normal, que muestra dos opciones, entrada (>>>) y continuación (...), tenemos cuatro posibilidades definidas mediante los métodos de la clase Prompts.

A partir de este punto voy a considerar que estás usando una consola de IPython, que es donde tendrán efecto los cambios:

Cuyo resultado nos mostrará:

Podemos ver que hay 4 métodos llamados continuation_prompt_tokens, in_prompt_tokens, out_prompt_tokens, rewrite_prompt_tokens que definen el prompt en IPython:

  • in_prompt_tokens: Este es el prompt tipico que vemos en las entradas de la consola de IPython (In [1]:), similar a sys.ps1.
  • continuation_prompt_tokens: Este es el prompt típico de continuación, (...:), similar a sys.ps2.
  • out_prompt_tokens: Este es el que muestra IPython para el resultado de la salida (Out[1]:).
  • rewrite_prompt_tokens: Esta, después de buscar, no tengo ni idea de para qué sirve. La documentación dice The rewrite prompt is shown to highlight how special syntax has been interpreted (default like —–>). Si alguien me lo aclara se lo agradezco.

Voy a modificar la de la entrada y la de la salida para que haga cosas interesantes. Primero pongo un ejemplo y lo comento después:

Defino ambos métodos, el del prompt de entrada y el de salida en la clase MisPrompts que hereda de Prompts. Dentro de cada método le pido que haga una serie de cosas. En el de entrada le digo que me muestre la ruta en la que me encuentro, antes del número de celda de entrada. Para definir cada cosa que se debe mostrar en el prompt uso tipos que provienen de la biblioteca pygments y que usa IPython internamente. IPython define unos cuantos de esos tokens como Prompt, PromptNum, OutPrompt y OutPromptNum y permiten darle estilo a los valores definidos por cada uno de ellos. Por ejemplo, en el método out_prompt_tokens podrías cambiar Token.OutPrompt por Token.Prompt y verías como cambia el color. Cuando instancio la clase MisPrompts le paso una instancia de la shell de IPython. En el código anterior, sería ip. Internamente se usa, entre otras cosas, para obtener el número de la celda que se está ejecutando, e.g., self.shell.execution_count.

Bien, con todo lo anterior. Si ahora hacemos lo siguiente:

Empezaremos a ver los prompts de entrada y de salida de una forma parecida a lo siguiente:

  • En Windows:
  • En linux:

Estas cosas las podéis meter en los ficheros de arranque que usan la consola interactiva de Python o de IPython para que se comporte siempre de la forma en que lo hayáis configurado.

Espero que os haya resultado útil.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

nine + 1 =