Customizando o prompt do IPython 5+

Há poucos meses atrás foi lançada a versão 5 do IPython. O shell que antes já era super legal, agora ficou mais interessante ainda, com syntax highlighting no código digitado e um esquema de completação inline usando umas listinhas bem convenientes:

screen-shot-2016-09-18-at-8-41-50-am

Porém, a forma de configuração do prompt mudou. Algumas das dicas apresentadas no post Sintonia Fina no IPython não se aplicam mais. Assim, vou mostrar aqui como fazer para que o seu IPython 5+ fique com aparência semelhante à abaixo:

screen-shot-2016-09-18-at-8-40-12-am

O que mudou basicamente é que ao invés de simplesmente definir a string que será usada como prompt em um atributo, agora é preciso implementar uma subclasse de Prompts, com alguns métodos para alterar o comportamento do seu prompt:

from IPython.terminal.prompts import Prompts, Token


class MyCustomPrompt(Prompts):

    def in_prompt_tokens(self, cli=None):
        return [(Token.Prompt, '>>> '), ]

    def out_prompt_tokens(self, cli=None):
        return [(Token.Prompt, ''), ]

    def continuation_prompt_tokens(self, cli=None, width=None):
        return [(Token.Prompt, ''), ]

No exemplo acima, customizei os prompts de entrada (de In [1]: para o tradicional >>>), de saída ( de Out[1]: para nada) e de continuação (de ...: para nada).

O legal dessa nova abordagem é que agora temos mais flexibilidade pra customizar os prompts, colocando valores dinâmicos dentro deles se quisermos.

Como configurar seu prompt

A configuração do IPython se dá através de perfis, então vamos começar criando o perfil default:

$ ipython profile create

Isso irá criar um diretório ~/.ipython com a seguinte estrutura:

.ipython
├── extensions
├── nbextensions
└── profile_default
    ├── ipython_config.py
    ├── ipython_kernel_config.py
    ├── log
    ├── pid
    ├── security
    └── startup
        └── README

Agora salve a classe MyCustomPrompt em um arquivo ~/.ipython/my_custom_prompt.py e depois disso edite o arquivo ~/.ipython/profile_default/ipython_config.py para que tenha o seguinte conteúdo:

from my_custom_prompt import MyCustomPrompt


c = get_config()

c.TerminalInteractiveShell.prompts_class = MyCustomPrompt
c.TerminalIPythonApp.display_banner = False
c.TerminalInteractiveShell.separate_in = ''

Pronto, agora o seu IPython 5+ deverá se parecer com aquele que mostrei no screenshot lá no começo do post.

Se você estiver usando uma versão anterior do IPython, verifique meu post anterior sobre o mesmo assunto: https://pythonhelp.wordpress.com/2013/12/29/sintonia-fina-do-ipython/

Confira as minhas configurações do IPython em: https://github.com/stummjr/dotfiles/tree/master/ipython

Sintonia fina do IPython

Para IPython 5+, veja o post mais recente.

Quem já usou o IPython alguma vez sabe que ele é um tanto quanto “espaçoso”. Cada comando digitado gera uma nova linha, além de ele usar um prompt de entrada e saída com a contagem dos comandos digitados. Se, assim como eu, você não curte o visual do IPython padrão, este texto é para você.

Por padrão, o IPython se apresenta assim:

ipython1

Veja quantos espaços em branco. Ele é bem diferente do shell Python padrão, que é bem menos espaçoso.

Para deixá-lo mais parecido com o shell Python basicão, basta dar uma “tunadinha” nele. Em outro post, já mostrei como configurar alguns aspectos do IPython. Neste post, vou apresentar algumas configurações adicionais que podem ser feitas. Ao final dele, seu IPython vai ficar parecido com:

ipython2

Configurando o IPython

Antes de qualquer coisa é preciso criar um perfil, através do seguinte comando no shell do seu sistema operacional:

$ ipython profile create

Isso irá criar um perfil chamado de default e todas as configurações desse perfil estarão no diretório ~/.config/ipython/profile_default/ (ao menos no Ubuntu).

Feito isso, agora você pode editar o arquivo de configurações do IPython (~/.config/ipython/profile_default/ipython_config.py). Ele está repleto de linhas de código Python comentadas, mas vou me deter aqui apenas àquelas que descomentei e customizei.

Desabilitar o banner de apresentação

Toda vez que é iniciado, o IPython mostra um banner parecido com:

Python 2.7.4 (default, Sep 26 2013, 03:20:26) 
Type "copyright", "credits" or "license" for more information.

IPython 0.13.2 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

Depois de ver duas ou três vezes, já decoramos essas informações e elas não são mais necessárias. Para desabilitar a apresentação dessas informações ao iniciar o IPython, utilizei a seguinte definição:

c.TerminalIPythonApp.display_banner = False

Removendo os \n entre as entradas

Uma das coisas mais irritantes no IPython é a quantidade de espaços em branco que ele coloca na tela. Muitos desses espaços são gerados pela separação entre as entradas através de um caractere de \n. Dá pra ajeitar isso, colocando um caractere vazio como separador:

c.TerminalInteractiveShell.separate_in = ''

Removendo a confirmação de fechamento

O IPython sempre pergunta se você deseja mesmo fechá-lo ao pressionar C-d. É claro que eu quero te fechar, ou tu tá achando que pressionei Ctrl e depois d sem querer? Dá pra evitar essa chateação com:

c.TerminalInteractiveShell.confirm_exit = False

Permitindo ao IPython modificar o título do terminal

Isso pode ser bem útil pra quem costuma ficar com várias abas de terminal abertas. Habilite isso com a seguinte linha:

c.TerminalInteractiveShell.term_title = True

Customizando o prompt de saída

Por padrão, linhas que contenham saída são precedidas por Out [x]:. Isso me incomodava um pouco, então resolvi alterá-lo e deixá-lo mais parecido com o shell padrão. Você também pode fazer isso com a seguinte linha:

c.PromptManager.out_template = ''

Customizando o prompt de entrada

O prompt de entrada no IPython apresenta o texto In [x]:. Para fazer com que o prompt de entrada seja igual ao clássico >>> do shell padrão, faça:

c.PromptManager.in_template = '>>> '

Completação por tab gulosa

Para habilitar a completação em resultados de chamadas de funções, ou em elementos de sequências, basta habilitar a configuração abaixo:

c.IPCompleter.greedy = True

Atenção: comentários no arquivo advertem que pode ser um pouco perigoso habilitar a configuração acima, pois para poder completar baseando-se no resultado de uma chamada de função, o ipython terá que chamá-la.

O ipython_config.py completo

# Configuration file for ipython.
c = get_config()

# Whether to display a banner upon starting IPython.
c.TerminalIPythonApp.display_banner = False

# Remove those annoying newlines between each input
c.TerminalInteractiveShell.separate_in = ''

# Set to confirm when you try to exit IPython with an EOF (Control-D in Unix)
c.TerminalInteractiveShell.confirm_exit = False

# Enable auto setting the terminal title.
c.TerminalInteractiveShell.term_title = True

# Output prompt.
c.PromptManager.out_template = ''

# Bring back the classic Python REPL prompt.
c.PromptManager.in_template = '>>> '

# Activate greedy completion
# This will enable completion on elements of lists, results of function calls,
# etc., but can be unsafe because the code is actually evaluated on TAB.
c.IPCompleter.greedy = True

Sugestões?

Se tiver mais alguma sugestão, envie um comentário.

Dicas de produtividade no IPython

O IPython é o meu shell Python favorito. É cheio de recursos que facilitam o dia-a-dia de quem passa parte de sua vida imerso em um shell Python. Neste post, vou listar alguns recursos que me ajudam bastante diariamente.

Recuperando o resultado da última operação

É coisa bem comum estar em uma sessão no shell Python, testar uma determinada operação e logo depois perceber que queria atribuir o resultado daquela operação para alguma variável. No IPython é barbada, com o _:

In [5]: 10 * 2 + 4 * 4
Out[5]: 36
In [6]: print _
36
In [7]: x = _
In [8]: print x
36

Ou seja, o resultado da execução do último comando é sempre referenciado pelo _ (underscore). Além disso, dois underscores referem-se sempre ao resultado obtido pela execução do penúltimo comando e três underscores ao resultado do antepenúltimo comando. Assim:

  • _: resultado do último comando.
  • __: resultado do penúltimo comando.
  • ___: resultado do antepenúltimo comando.

Isso é particularmente útl quando estamos imersos em uma sessão de descoberta usando o IPython. Torna o processo muito mais ágil. Veja:

In [18]: 1
Out[18]: 1
In [19]: 2
Out[19]: 2
In [20]: 3
Out[20]: 3
In [21]: _ + __ + ___
Out[21]: 6

Além disso, podemos nos referir à execuções específicas, usando a numeração que o IPython usa para diferenciar um par entrada-saída de outro. A sintaxe é bem simples: _ix, onde x é o número da entrada correspondente. Veja:

In [18]: 1
Out[18]: 1
In [19]: 2
Out[19]: 2
In [20]: 3
Out[20]: 3
In [21]: _ + __ + ___
Out[21]: 6
In [22]: print _i19 + 20
Out[22]: 22

Cool, huh?

Chamando o help de um objeto

Já falei sobre isso em um post anterior, mas veja de novo o uso do ponto de interrogação (?) para ver a documentação relacionada a determinado objeto:

In [31]: import math
In [32]: math.sqrt?
Type: builtin_function_or_method
String Form:<built-in function sqrt>
Docstring:
sqrt(x)
Return the square root of x.

Isso por si só já me faz usar o IPython ao invés do shell padrão.

As funções mágicas

O IPython é repleto de funções mágicas (magic functions) que fornecem enormes facilidades pro usuário. Elas são precedidas pelo caractere %. Vamos ver alguns exemplos.

Em um post anterior, falei sobre o módulo timeit que é usado para medir tempo de execução de programas Python. Existe uma função mágica pra quebrar esse galho pra gente. Por exemplo, se eu estiver na dúvida sobre qual trecho de código executaria de forma mais rápida, poderia fazer o seguinte:

In [35]: %%timeit sum = 0
 ...: for i in range(0, 10000):
 ...: sum += i
 ...: 
1000 loops, best of 3: 324 us per loop
In [36]: %%timeit sum = 0
 ...: for i in xrange(0, 10000):
 ...: sum += i
 ...: 
1000 loops, best of 3: 268 us per loop

Se o código a ser testado for de uma linha só, podemos usar %timeit (modo linha) ao invés de %%timeit (modo célula).

Outra função interessante é relacionada ao logging da sessão atual. %logstart faz com que a sessão atual passe a ser gravada em um arquivo .py com os comandos digitados dentro dele. %logon e %logoff servem para pausar e recomeçar o logging em uma sessão.

Executando comandos no sistema

Dentro de uma sessão IPython, podemos invocar comandos no sistema operacional usando o caractere !:

In[1]: !ls
 bin
 Desktop
 music
 pictures
 src
 videos
In[2]: !ps
 PID TTY TIME CMD
 25277 pts/0 00:00:00 bash
 25446 pts/0 00:00:00 ipython
 25458 pts/0 00:00:00 sh
 25459 pts/0 00:00:00 ps

Assim fica fácil interagir com o SO quando necessário for.

É isso. Se você tiver alguma outra dica interessante sobre o IPython, poste nos comentários.

Customizando o IPython

Dias atrás estive tentando descobrir como trocar o prompt-padrão do IPython [1]. Para quem não sabe, o prompt-padrão de comandos do IPython é:

In [x]:

Onde x é um contador de entradas fornecidos pelo usuário. O que eu queria era substituir tal prompt pelo prompt padrão do shell Python:

>>>

Para isso, é preciso antes criar um perfil de configurações de usuário para o IPython, com o comando (em um shell Linux):

$ ipython profile create

Assim, será criado um diretório com as configurações do IPython em seu diretório de usuário, onde está armazenado o arquivo de configurações ipython_config.py (no meu caso está em ~/.config/ipython/profile_default/ipython_config.py).

Para substituir o prompt do IPython, procure pela linha com o seguinte conteúdo dentro do arquivo de configurações e descomente-a:

# c.PromptManager.in_template = 'In [\\#]: '

Após descomentar tal linha, substitua o valor ‘In [\\#]: ‘ por ‘>>> ‘, para que tal linha fique como a linha abaixo:

c.PromptManager.in_template = '>>> '

Uma coisa que acho irritante é o IPython sempre pedindo confirmação quando desejo fechá-lo, após ter pressionado ^-d. Para evitar que ele pergunte se você deseja mesmo fechá-lo, basta descomentar a seguinte linha no mesmo arquivo e trocar o valor True para False:

# c.TerminalInteractiveShell.confirm_exit = True

A linha deverá ficar assim:

c.TerminalInteractiveShell.confirm_exit = False

Além dessas, existem muitas coisas que podem ser personalizadas no IPython. Você pode vê-las comentadas no arquivo supracitado. Para personalizar outros aspectos relacionados ao prompt de comandos, vá até a seção “PromptManager configuration” do arquivo e altere de acordo com o seu gosto.

[1] http://ipython.org

Acesso fácil à documentação com ipython

Para quem não sabe, quando desejamos informações de ajuda sobre algum módulo/método/builtin, podemos invocar o builtin help(). Por exemplo, quero saber detalhes sobre a função len():

>>> help(len)
Help on built-in function len in module __builtin__:
len(...)
 len(object) -> integer
 Return the number of items of a sequence or mapping.
(END)

Isso irá ler o atributo __doc__ do objeto em questão. Mas, em minha opinião de preguiçoso, é um pouco chato sempre chamar a função help(), passando como argumento o objeto do qual desejamos mais informações. Se você concorda comigo, saiba que o ipython [1] fornece um “atalho” bem simples para acessarmos a documentação de determinado objeto. Basta adicionar um ponto de interrogração ao final do nome do objeto, que o ipython mostra o texto de ajuda. Veja a figura abaixo:

Image

iPython mostrando informações sobre método.

Além da interrogação simples (?), podemos utilizar também a interrogação dupla (??) para obter informações adicionais sobre o método/módulo, incluindo o seu código-fonte, quando disponível. Veja abaixo:

Image

iPython mostrando informações extra

É claro que as vantagens do ipython vão muito além desse recurso. Mas, as outras ficam para outro post.

[1] http://ipython.org/

bPython, um shell “tunado”

Em outros posts, comentei sobre o Dreampie e sobre o iPython, ambos programas para interpretação interativa de código Python. Agora chegou a vez de outra alternativa bem legal, o bPython. O bPython é bem diferente das outras opções, começando por seu estilão ncurses de mostrar as informações.

bpython

bPython completando função

Como mostra o screenshot acima, o bPython, além de completar os nomes de funções e módulos, também mostra o texto de ajuda envolto por uma caixinha, que some assim que não for mais necessária. Isso é muito mais prático do que, por exemplo, antes de chamar uma função, executar help(função) para descobrir o que faz a função e quais são seus argumentos. Além desses recursos de ajuda, o bPython oferece diversos outros recursos, que irei sumarizar a seguir:

  • rewind: permite “voltar no tempo”. Quando executado, esse recurso reavalia a última linha.
  • show source: quando F2 é pressionado após o nome de um módulo, o bPython busca e mostra o código-fonte daquele módulo.
  • pastebin: envia o código digitado pelo usuário ao pastebin, um serviço na web para armazenamento de trechos de código.
  • Destaque de sintaxe: assim como os outros programas apresentados, o bPython também colore a sintaxe do código, a medida que vai sendo digitado.
  • Salvar código: permite salvar o código digitado no shell em um arquivo no disco.
  • Auto-indentação: o bPython indenta automaticamente a próxima linha quando, por exemplo, escrevemos um cabeçalho de função, de loop for, etc.
  • Completação de nomes de arquivos: em diversas situações, é preciso que utilizemos o caminho de arquivos do disco no código Python, como por exemplo, quando vamos abrir um arquivo com a função open. O bPython completa e mostra as opções de arquivos do disco, à medida que digitamos o caminho do arquivo.

Comparado ao iPython, o bPython possui menos recursos. Mas, para quem não vai utilizar os recursos avançados que o iPython oferece, o bPython é uma excelente alternativa. Na página oficial do projeto, existe uma página que apresenta as alternativas ao bPython e, inclusive, deixa claro que os “concorrentes” podem ser mais adequados para determinados usuários. Mais um exemplo legal em um projeto de software livre.

Mais screenshots e screencasts, veja: http://bpython-interpreter.org/screenshots

iPython, muito mais que um simples shell

O iPython não é somente mais um shell Python. Apesar de servir muito bem como um simples substituto ao shell Python padrão, existe tanta coisa por trás desse projeto que é, no mínimo, injusto caracterizá-lo apenas como uma alternativa ao shell Python padrão. Além dos esperados recursos como completação de nomes de módulos/funções, o iPython fornece um rico conjunto de funcionalidades que o tornam um ambiente de desenvolvimento e experimentação único. Vou listar algumas das funcionalidades disponíveis:

  • Execução de comandos de sistema:com uma sintaxe mais simples que uma chamada a os.system(), é possível a execução de comandos no sistema. Basta preceder o comando com o caractere de exclamação(!).
    • Exemplo: !ls -l
  • Redirecionamento de saída de comandos:é possível armazenar em uma lista o resultado da execução de um comando do sistema. Para isso, basta uma atribuição simples.
    • Exemplo: arquivos = !ls
  • Funções avançadas de histórico: atalhos específicos para execução de comandos anteriormente executados.
  • Criação de aliases (apelidos) para comandos.
  • Autoindentação: ativada por padrão, é também possível desativá-la.
  • Execução de programas Python:com apenas um comando, é possível executar um arquivo Python dentro do iPython.
    • Exemplo: %run arquivo.py
  • Avaliar tempo de execução de comandos:com um simples comando, é possível “medir” o tempo de execução de trechos de código Python.
    • Ex.: %timeit print “Hello, world!” Muitas das funcionalidades aqui apresentadas não são novidade, pois podem ser obtidas através da utilização de módulos, como o timeit, no caso dessa funcionalidade. O interessante é a praticidade para utilização desses recursos dentro do iPython.
  • Console gráfico (qt): o iPython oferece um console que pode, por exemplo, apresentar imagens inline no texto do console. Assim, é possível plotar gráficos direto no shell.
  • Computação paralela: o iPython é muito utilizado para computação científica, onde é necessária a realização de tarefas que, muitas vezes, se tornam inviáveis em apenas uma máquina isolada. Para isso, a arquitetura do iPython já foi projetada visando dar suporte a execução paralela de comandos. Veja mais em: http://minrk.github.com/scipy-tutorial-2011/parallel_intro.html
  • Interface Web: também é possível acessar um ambiente iPython através de uma interface web.
  • E muito mais: para descobrir, execute o iPython e digite %quickref para ler a referência.
iPython

Tela do iPython

Na minha opinião, esse é um ambiente feito especialmente para quem passa o dia inteiro imerso em um shell Python e precisa de uma solução mais completa para facilitar o seu dia-a-dia. Excelente para a experimentação típica de quem trabalha com pesquisa científica, também pode ser muito bem aproveitado por programadores Python que desejam um shell mais completo. Recomendo!

Alternativas ao shell Python padrão

O shell padrão disponível quando instalamos Python em um sistema é um tanto quanto básico, não oferecendo recursos úteis ao desenvolvedor como completação de palavras, histórico de comandos, dentre outros recursos disponíveis através de bibliotecas como a readline. Porém, existem algumas formas de obtermos tais recursos para nosso shell, as quais serão listadas neste post. Segue uma lista das alternativas:

  1. Configurar o shell Python padrão para que, quando iniciado, carregue algumas funcionalidades da libreadline. Essa alternativa já foi coberta em um post anterior;
  2. Dreampie, um shell gráfico alternativo cheio de funcionalidades úteis, já apresentado em um post anterior;
  3. iPythonhttp://ipython.org
  4. bPythonhttp://bpython-interpreter.org
  5. Reinteracthttp://fishsoup.net/software/reinteract

Em breve, postarei textos descrevendo o iPython, o bPython e o Reinteract. Desde já, sugiro aos interessados na linguagem Python que instalem esses programas, pois possuem vários benefícios ao desenvolvedor.