Utilizando cores para escrever no terminal (Python)

Utilizando cores para escrever no terminal (Python)

Overview

Você já se perdeu em um mar de texto enquanto usava o terminal? Neste post leve e informativo, vamos mergulhar em como Python pode tornar suas mensagens de terminal não apenas informativas, mas também visualmente impactantes, graças ao uso de cores. Desde a abordagem simples com caracteres ANSI, passando pela utilização de pacotes como termcolor e sty, vamos explorar formas práticas e fáceis de melhorar a interação com os usuários dos seus scripts. Prepare-se para adicionar um toque de cor ao seu terminal!

Quem conhece Python sabe que uma das coisas que ele faz muito bem é automatizar tarefas. Durante estas automatizações, escrevemos mensagens no terminal, para que o usuário consiga acompanhar o que está acontecendo.

O problema começa quando escrevermos muitas coisas no terminal… fica aquele tanto de texto amontoado e só quem fez aquele script sabe identificar o que é importante ou não. Uma das formas de resolver isso é utilizar cores para destacar as mensagens importantes.Existem algumas formas de fazer isso. Vou começar pela que considero mais simples e que não depende de outros pacotes, depois vou mostrar como utilizar alguns pacotes mais famosos.

1. Utilizando caracteres ANSI

A primeira forma (e mais simples, na minha opinião) é utilizando caracteres ANSI. Esta forma consiste em colocar um conjunto de carecteres logo antes do texto que você quer colorir.

Veja a relação de códigos ANSI e suas respectivas cores:

RED   = "\033[1;31m"  
BLUE  = "\033[1;34m"
CYAN  = "\033[1;36m"
GREEN = "\033[0;32m"
RESET = "\033[0;0m"
BOLD    = "\033[;1m"
REVERSE = "\033[;7m"

Agora um exemplo de como aplicar essas cores:

>>> print(RED + "ERROR!" + RESET + "Something went wrong...")
ERROR! Something went wrong...

Por que foi necessário concatenar o código RESET? Sem o reset (das cores), o resto da frase ficaria em vermelho.

Agora você pode se divertir a vontade e misturar as cores e estilos da forma que quiser, por exemplo:

>>> print(BOLD + RED + "ERROR!" + RESET + "Something went wrong...")
<strong>ERROR!</strong> Something went wrong...

Como pode ser observado, os caracteres referentes ao RESET removem todas as formatações.

OK. Então vimos que é só concatenar os valores… mas fica feio e pouco prático. Seria uma boa ideia encapsular esta lógica, certo? Podemos criar uma classe que resolve isso e (inclusive) implementa o que falamos no último post (Inicializando dicionário com valores padrões)…

A primeira coisa é criar a classe com um dicionário com um dicionário contendo a relação de cores:

class printer():
    _colors_ = {
        **dict.fromkeys(("RED", "ERROR", "NO"), "\033[1;31m"),
        **dict.fromkeys(("GREEN", "OK", "YES"), "\033[0;32m"),
        **dict.fromkeys(("YELLOW", "WARN", "MAYBE"), "\033[0;93m"),
        "BLUE": "\033[1;34m",
        "CYAN": "\033[1;36m",
        "RESET": "\033[0;0m",
        "BOLD": "\033[;1m",
        "REVERSE": "\033[;7m"
    }

O próximo passo é criar uma função que recebe o nome da cor e retorna o código ANSI correspondente:

    def _get_color_(self, key):
        """Gets the corresponding color ANSI code... """
        try:
            return self._colors_[key]
        except:
            return self._colors_["RESET"]

A função acima tenta retornar o código correspondente ao argumento informado. Se não for possível, retorna o caractere que faz um reset nos formatos.

Agora é só criar a função que faz o print da mensagem:

    def print(self, msg , color="RESET"):
        """Main print function..."""

        # Get ANSI color code.
        color = self._get_color_(key=color)

        # Printing...
        print("{}{}{}".format(color, msg, self._colors_["RESET"]))

A função acima recebe como argumento a mensagem e a cor. Se o nome da cor estiver errado, vai mostrar a mensagem sem a formatação.

Para utilizar é simples:

p = printer()

p.print(msg="SUCCESS Test...", color="GREEN")
p.print(msg="WARN Test...", color="YELLOW")
p.print(msg="ERRPR Test...", color="RED")

O fonte acima vai produzir o seguinte resultado:

SUCCESS Test...
WARN Test...
ERROR Test...

Para facilitar, criei esta classe e adicionei três métodos: success (verde), error (vermelho) e warning (amarelo). O fonte dela está no meu Github.

2. Utilizando o pacote termcolor

Para utilizar este pacote, a primeira coisa é baixa-lo. Para isso, utilize o PIP:

pip install termcolor

Este pacote é bem flexível e possui uma série de opções, mas vou utilizar um exemplo básico aqui:

from termcolor import colored

print(colored('Error Test!!!', 'red'))
print(colored('Warning Test!!!', 'yellow'))
print(colored('Success Test!!!', 'green'))

Disponibilizei este exemplo no meu Github. Existem mais exemplos na pagina deste pacote: https://pypi.python.org/pypi/termcolor

3. Utilizando o pacote sty

Assim como o termcolor, precisamos instalar este pacote. Sugiro utilizar o pip:

pip install sty

Este pacote tem mais possibilidades. Veja alguns exemplos de utilização abaixo:

from sty import fg, bg, ef, rs

print(fg.red + 'ERROR Test!' + fg.rs)
print(fg.li_yellow + 'WARNING Test!' + fg.rs)
print(fg.green + 'SUCCESS Test!' + fg.rs)

O resultado final é o mesmo dos exemplos anteriores. Inclui no meu Github um exemplo mais completo da utilização deste pacote. Link para o pacote: https://pypi.python.org/pypi/sty/1.0.0b4

Espero ter ajudado.