Gostaria de escrever uma função de erro personalizada em R que usa uma formatação mais consistente de mensagens de erro. No R base, os erros são formatados de uma forma que coloca mensagens de erro em uma nova linha se ela exceder a largura do console e a recua em dois espaços:
abort <- function(msg) stop(msg)
> abort("This is a very very very very long error message")
# Error in abort("This is a very very very very long error message") :
# This is a very very very very long error message
Se não exceder a largura do console, será colocado diretamente atrás da chamada:
> abort("Short message")
# Error in abort("Short message") : Short message
Na minha função de erro personalizada, pretendo manter a formatação mais consistente sempre iniciando as mensagens de erro em uma nova linha (semelhante ao cli
formato de erro do pacote):
abort <- function(msg) stop("\n", msg)
Entretanto, ao usar esta função com uma mensagem de erro muito longa (ou uma chamada longa), a formatação de erro base do R adiciona uma nova linha e, portanto, espaços em branco indesejados à saída do erro.
> abort("This is a very very very very very very very very long error message")
# Error in abort("This is a very very very very very very very very long error message") :
#
# This is a very very very very very very very very long error message
Existe alguma maneira de controlar esse comportamento, suprimindo-o ou descobrindo quando ele ocorre para contorná-lo manualmente?
É fácil o bastante suspender temporariamente a impressão de mensagens de erro com
options(show.error.messages = FALSE)
, então imprima qualquer mensagem que você quiser no console. Depois disso, você pode lançar um erro usando um simplesstop(msg)
para interromper a execução, e somente sua própria mensagem personalizada aparecerá.O problema fatal com essa abordagem é que depois que o programa para, você não religou a impressão de erro do usuário, o que é um grande problema. Você não pode simplesmente redefinir as opções do usuário dentro
abort
depois de chamarstop()
, já que o programa parou e seu código de redefinição de opção simplesmente não será executado.on.exit()
É aqui que entra o R básico . Esta função útil permite que você forneça um bloco arbitrário de código para ser executado após a saída da sua função, mesmo que ela saia devido a um erro não tratado . Podemos, portanto, dizeron.exit
para redefinir as opções do usuário quando saímos daabort
função, desde que registremos ason.exit
instruções antes de executarstop()
o programa.Esse mecanismo é essencialmente como
rlang
ele consegue produzir mensagens de erro com formatação personalizada, por meio da funçãorlang:::signal_abort
.Também queremos examinar a pilha de chamadas para que possamos informar ao usuário onde
abort
foi encontrado, então uma implementação mínima pode ser algo como:Testando, obtemos:
E se encontrarmos
abort
dentro de uma função, podemos ver que a execução foi interrompida e obtemos algumas informações sobre a chamada que levou aoabort
seu acionamento:A resposta de @AllanCamerons já responde minha pergunta de uma forma muito elegante. No entanto, há um problema que continua me incomodando: a função sinaliza uma mensagem e um erro. Depois de estudar o código-fonte de
rlang::signal_abort()
, descobri que uma mistura designalCondition()
ecat()
reflete principalmente o comportamento derlang::abort()
:O que (eu acho) isso faz:
signalCondition(cnd)
é onde a condição de erro é sinalizada e especifica a mensagem de erro "oficial" (como manipuladores de condições capturadas), mas não aborta a execução.cat(..., file = stderr())
imprime em stderr mas não sinaliza uma mensagem.stop("")
aborta a execução, mas não sinaliza outro erro (pois isso já foi feito anteriormente) nem imprime uma mensagem de erro (pois ela é suprimida).Por que isso é útil: