Show HN: Wat – inspeção profunda de objetos Python
(github.com/igrek51)- WAT é um inspector para identificar rapidamente o que é um objeto desconhecido no runtime do Python, permitindo ver de uma vez tipo, valor, atributos, métodos, tipos pai, assinatura, documentação e até código-fonte
- O uso básico é
wat / object, funcionando da mesma forma quewat(object), e ele também suporta várias sintaxes comowat.short / 'foo','foo' | wat.short,wat('foo', short=True) - É possível encadear modifiers como
.short,.dunder,.long,.code,.caller,.public,.all,.ret,.strpara ajustar o escopo da saída, a forma de retorno, a saída colorida e a exibição do local da chamada - A instalação pode ser feita com
pip install watseguido deimport wat, e para depuração rápida também é possível colar o snippet Insta-Load na sessão Python para usar no mesmo ambiente sem instalar - Exemplos com Django
User,re.match,pathlib,colorsys.hsv_to_rgb,typing.List[str],str | Nonemostram que o WAT pode ser usado para debugging, exploração em REPL e aprendizado dos internals do Python
O que o WAT faz
- WAT é uma ferramenta para explorar e inspecionar objetos Python em runtime
- Quando fica difícil entender o que é um objeto desconhecido, é possível investigar sua natureza no console Python com o inspector
wat - Ao executar
wat / objectsobre umobjectqualquer, é possível ver as seguintes informações- type do objeto
- valor formatado
- variáveis e métodos
- tipos pai
- assinatura
- documentação
- código-fonte
- A mesma inspeção profunda também pode ser usada com a sintaxe
wat(object) Waté apresentado como uma variação da palavra inglesawhat, usada para expressar confusão ou incômodo
Uso básico e sintaxe
- Para digitação rápida, ele usa o operador de divisão
wat / fooé igual awat(foo)
- É possível usar várias sintaxes para a mesma inspeção
wat.short / 'foo': sintaxe para digitação rápidawat.short('foo')wat('foo', short=True): sintaxe natural de Python'foo' | wat.short: sintaxe no estilo pipe do Unix
- É possível ajustar o comportamento da inspeção com a forma
wat.modifier / foo - Os modifiers podem ser encadeados, por exemplo
wat.short.str.gray / 'foo' - Como em Python objetos incluem não só estruturas de dados, mas também funções, classes, módulos e tipos embutidos,
watpode explorar qualquer objeto - Ao digitar
watno interpretador, é possível ver a ajuda do próprio objetowat
Ajustando o escopo da inspeção com modifiers
.shortou.soculta atributos internos como variáveis e métodos do objeto, mostrando apenas valor, tipo, tipos pai, assinatura e documentação.dundermostra atributos dunder que começam com__.longmostra o valor e a docstring sem abreviações.codemostra o código-fonte de funções, métodos e classes.nodocsoculta a documentação de funções e classes.callermostra como e onde a inspeção foi chamada, funcionando fora do REPL em arquivos.publicoculta atributos privados e mostra apenas atributos públicos.allinclui todas as informações possíveis.retretorna o objeto novamente após a inspeção.strretorna a string de resultado em vez de imprimir.graydesativa a saída colorida no console.colorforça a saída colorida no consolewat.localsinspeciona variáveis locais, ewat.globalsinspeciona variáveis globais
Instalação e Insta-Load
- O fluxo de instalação com pip é o seguinte
pip install wat- no Python,
import wat
- O pacote
watnão tem dependências externas - Para debugging rápido, ele oferece o modo Insta-Load, que permite usar na mesma sessão Python sem instalar
- O Insta-Load funciona colando no interpretador um snippet Python que importa
base64ezlib, restaura uma string de código comprimida e codificada, e a executa comexec(..., globals()) - Após executar o snippet do Insta-Load, o objeto
watpode ser usado - Antes de executar o snippet, recomenda-se validar o conteúdo que será executado
- É possível verificar previamente o código extraído com
print(zlib.decompress(base64.b64decode(code)).decode()) - Também é possível colar no interpretador o conteúdo de
inspection.pypara obter o mesmo efeito - Outra opção mencionada é instalar o pacote com pip e revisar o código
- É possível verificar previamente o código extraído com
- O WAT pode ser carregado a partir de um único glyph Unicode
- O loader baseado em string Unicode transforma uma longa sequência de emoji e caracteres combinados em bytes com
ord(c) & 255, depois executa comzlib.decompress(...)eexec(...)
Entendendo o tipo do objeto e como usá-lo
- Em Python, uma linguagem de tipagem dinâmica, às vezes é difícil descobrir o tipo de um objeto, e o WAT Inspector mostra o nome do tipo e o módulo de onde ele veio
- Os exemplos de verificação de tipo mostram valor, tipo e comprimento juntos
wat.short / (1,)imprime o valor(1,), o tipotuplee o comprimento1wat.short / {None}imprime o valor{None}, o tiposete o comprimento1
- No exemplo com o objeto Django
User,wat.short / usermostrastr: admin,repr: <User: admin>, o tipodjango.contrib.auth.models.Usere a lista de tipos pai - Depois de confirmar o tipo real, é possível adicionar type annotations no código para reduzir confusões futuras
- Ao tentar descobrir como usar um objeto desconhecido, é possível imprimir a lista de métodos, a assinatura e a docstring
- O exemplo dado é
wat / ['foo'] - Para ver a docstring completa, use
wat.long
- O exemplo dado é
- Para entender como usar uma função, é possível ver sua docstring e assinatura
- O exemplo dado é
wat / str.split
- O exemplo dado é
Explorando atributos, módulos e código-fonte
- Para inspecionar o interior do objeto-alvo, é possível listar os atributos e o tipo de cada atributo
- O exemplo dado é
wat / re.match('(\\d)_(.*)', '1_title')
- O exemplo dado é
- Também pode ser usado para explorar módulos, listando funções, classes e submódulos do módulo escolhido
- Há um exemplo com
import pathlibseguido dewat / pathlib - Depois é possível explorar mais a fundo com
wat / pathlib.fnmatch
- Há um exemplo com
- Por padrão, o WAT Inspector oculta atributos que começam com
__- É possível ver atributos dunder com
wat.dunder / {}
- É possível ver atributos dunder com
- Para verificar como uma função realmente funciona, é possível ver o código-fonte
- Há um exemplo com
import colorsysseguido dewat.code / colorsys.hsv_to_rgb
- Há um exemplo com
dictelistaninhados são formatados com indentação legível
Sessões de debugging e inspeção de variáveis
- É possível iniciar o depurador interativo do Python com
breakpoint()e inspecionar objetos naquele ponto - No exemplo com Pdb, depois de
import watou de colar o snippet do Insta-Load, usa-sewat / foopara inspecionar variáveis locais ecpara continuar a execução - Variáveis locais e globais podem ser vistas com
wat.localsewat.globals, respectivamente - Ao chamar
wat()sem argumentos, ele imprime as variáveis locais da pilha chamadora sob o títuloLocal variables
Exemplos para aprender os internals do Python
- O texto inclui exemplos de uso voltados ao aprendizado do funcionamento interno do Python
reversed([]) == reversed([])éFalse, ewat.s / reversed([])mostra que o valor é um objetolist_reverseiteratore o tipo também élist_reverseiteratorwat / type('ObjectCreator', (), {})mostra o valor de uma classe criada dinamicamente, o tipotypeesignature: class ObjectCreator()wat / typemostra o valor do própriotype, o tipotype, a assinaturaclass type(…), a documentaçãotype(object) -> the object's type,type(name, bases, dict, **kwds) -> a new type, e atributos públicos comomrowat.s / List[str]mostra o valortyping.List[str], o tipotyping._GenericAlias, os tipos paityping._BaseGenericAlias,typing._Final, e a assinaturadef List(*args, **kwargs)wat(str | None)mostra o valorstr | Nonee o tipotypes.UnionType- Como exemplos de exploração de objetos embutidos do Python, são citados
wat / __builtins__ewat / ... - Também é possível inspecionar o próprio WAT
- Os exemplos dados são
wat.dunder / watewat.code / wat.__truediv__
- Os exemplos dados são
Resumo do funcionamento interno
inspect_format(obj, *, short=False, dunder=False, nodocs=False, long=False, code=False, caller=False, public=False, all=False)monta em string o resultado da inspeção do objeto- Quando
all=True,dunder,long,codeecallersão ativados juntos - Quando
public=True, a saída de membros privados é desativada - Se
sys.stdout.isatty()for verdadeiro, ele obtém a largura do terminal e adiciona separadores─acima e abaixo da saída
- Quando
- A saída da inspeção é gerada na ordem: valor do objeto, representação em string, tipo, tipos pai, comprimento, assinatura, documentação, código-fonte e seção de atributos
- A inspeção de atributos percorre
dir(obj)em ordem alfabética dos nomes- Atributos dunder são excluídos se a opção
dunderestiver desativada - Atributos privados que começam com
_são excluídos se a configuração privada estiver desativada - Se
getattr(obj, key)levantarBaseException, o valor usado passa a ser o próprio objeto de exceção
- Atributos dunder são excluídos se a opção
- Objetos callable têm a assinatura formatada com base em
inspect.signature(obj)- Se falhar, é retornada uma assinatura alternativa no formato
(...) - Classes recebem o prefixo
class, coroutine functions recebemasync def, e funções, métodos, builtins e objetos com__name__recebem o prefixodef
- Se falhar, é retornada uma assinatura alternativa no formato
- Quando
code=Truee o objeto é uma classe ou callable, o código-fonte é impresso cominspect.getsource(obj)- Em caso de
OSError,TypeErrorouIndentationError, é retornada uma mensagem de falha
- Em caso de
- Os formatadores de dict e list retornam
ERROR: too deeply nestedquando a profundidade de indentação ultrapassa 30
Saída colorida e temas
- É possível controlar a saída colorida com variáveis de ambiente
WAT_COLOR="false"desativa a saída colorida no consoleWAT_COLOR="true"força cores mesmo em ambientes non-tty
- A variável de ambiente
WAT_COLORSpermite customizar o tema de cores - O tema padrão é um mapeamento de códigos ANSI no formato
BAR=0;34,TRAIT=1;34,HEAD=1;37,STR=0;32,NUMBER=0;31,NONE=0;35,TRUE=1;32,FALSE=1;31,DOCS=2;37,KEYWORD=0;34,CALLABLE=1;32,VARIABLE=1;33,CODE=0;33 _strip_color(text)remove sequências de escape ANSI usando expressão regular
Inspiração
- O WAT foi inspirado no Rich Inspect
1 comentários
Opiniões no Hacker News
Uau, muito bom. Eu usava o python-ls[0] para um propósito parecido no passado, mas algo quebrou por algum motivo de que não me lembro, e ele também não é mais mantido
Pretendo adicioná-lo à minha caixa de ferramentas de depuração, composta principalmente por snoop[1] e pdbpp. O que eu gostaria no wat é algo como um widget ipy que facilite explorar objetos no Jupyter
Também gostei do hack de exec com base64. Uso Python há muito tempo e, ainda assim, nunca tinha pensado nisso nem visto algo assim até agora, então com certeza vou experimentar para alguns usos
[0] https://github.com/gabrielcnr/python-ls
[1] https://pypi.org/project/snoop/
Parece interessante. Uso dir o tempo todo em Python e, quando a documentação é fraca, às vezes ele é mais útil do que a documentação oficial
O shell interativo é um dos verdadeiros pontos fortes do Python, então é surpreendente que não haja mais ferramentas novas ou inovações desse tipo ao redor dele
Parece uma versão mais vistosa do antigo icecream
https://github.com/gruns/icecream
Se você não conhece, também vale ver mais abaixo a lista de implementações para outras linguagens
https://github.com/gruns/icecream#icecream-in-other-language...
Ferramentas desse tipo são úteis
Há 20 anos, eu criei um introspector de objetos para Zope
Hoje em dia uso devtools diariamente, e icecream e q de vez em quando. Também vou experimentar o wat
from wat import watCom um projeto de caráter tão legal, surpreende que ele não ofereça simplesmente
import watcom a mesma sintaxe de uso. Assim, usuários curiosos poderiam acabar descobrindo o truque ao tentar wat/watimport wat, mas no Python existe a limitação de não poder tornar um módulo chamável. Por isso acabou ficando o mais longofrom wat import watNão tenho certeza, mas
import wat; wat.wat / objecttalvez fosse mais convenienteParece muito útil, mas fico me perguntando se sou o único incomodado com a tendência recente de sobrecarregar operadores totalmente não relacionados, neste caso o operador
/, em nome da legibilidade/é uma escolha estranha. Ainda assim, é uma pena que não seja possível sobrecarregaris. Na prática,wat(foo)provavelmente já teria sido suficientePara evitar imports trabalhosos, você também pode adicionar isto ao arquivo
$PYTHONSTARTUPtry:from wat import watexcept ImportError:passAcabei imprimindo a saída dele e colocando-a em um diretório apontado por
PYTHONPATH, para poder usar sempreVamos ver se vou continuar usando
Uau, se eu tivesse tido uma ferramenta dessas quando estava aprendendo Python, acho que teria mudado o jogo. Ao aprender uma linguagem, enxergar o que acontece por dentro é o caminho essencial, e a depuração padrão do Python é decepcionante, para dizer o mínimo
Em vez disso, instalei o pry e virei um fã entusiasmado de Ruby, mas uma ferramenta dessas talvez me faça tentar Python de novo
O autor usa internamente o módulo inspect do Python da biblioteca padrão para fornecer a funcionalidade. Claro, acrescentou bastante valor em cima disso
Veja
inspection.pyno módulo watNa segunda linha está assim:
import inspect as std_inspect“Se você quiser depurar algo rapidamente, pode usar este inspetor na mesma sessão sem instalar nada”
“Cole este snippet no interpretador Python para carregá-lo na hora”
A ideia de colocar no README do projeto uma cópia inteira do projeto como dados compactados codificados em base64 é bem engenhosa
Combina especialmente bem com esse tipo de projeto, que talvez você não tenha pensado em preparar com antecedência justamente nos ambientes em que acabará precisando dele