commit 81aeab168766946ec49ec379fd10d77aae02b92b
parent f637b94a6161205d1728b068a1c51d7f910a7eb5
Author: Matthew Flatt <mflatt@racket-lang.org>
Date: Thu, 10 Dec 2015 08:33:00 -0700
scribble/eval: add `eval:error` and logging of other exceptions
When an expression in `examples` or `interactions` raises an
exception, the error message is rendered as part of the documentation.
Now, however, unless the expression is wrapped with `eval:error`, an
error is also logged.
Logging an error is a compromise between backward compatibility (for
documents that rely on undeclared but expected errors) and making a
document fail completely (which would be nicer when an error is not
expected).
Diffstat:
4 files changed, 53 insertions(+), 20 deletions(-)
diff --git a/scribble-doc/scribblings/scribble/demo-manual.scrbl b/scribble-doc/scribblings/scribble/demo-manual.scrbl
@@ -51,7 +51,7 @@
@item{@css{RktErr} (errors): @racketerror{example} or the error message in
- @interaction[(+ 1 'a)]}
+ @interaction[(eval:error (+ 1 'a))]}
@item{@css{RktCmt} (comments): @racketcommentfont{example} or
diff --git a/scribble-doc/scribblings/scribble/eval.scrbl b/scribble-doc/scribblings/scribble/eval.scrbl
@@ -54,6 +54,13 @@ Certain patterns in @racket[datum] are treated specially:
before evaluation.}
@item{A @racket[datum] of the form
+ @racket[(@#,indexed-racket[eval:error] #,(svar eval-datum))]
+ is treated like @racket[_eval-datum], but @racket[_eval-datum] is expected to
+ raise an exception: no error is logged if an exception is raised,
+ but an exception is raised if @racket[_eval-datum]
+ does @emph{not} raise an exception.}
+
+ @item{A @racket[datum] of the form
@racket[(@#,indexed-racket[eval:alts] #,(svar show-datum) #,(svar eval-datum))]
is treated as @svar[show-datum] for typesetting and @svar[eval-datum] for evaluation.}
@@ -81,6 +88,10 @@ Certain patterns in @racket[datum] are treated specially:
]
+By default, if evaluation raises an exception, an error is shown as
+the evaluation's result, but an error is also logged. Use @racket[eval:error]
+to avoid logging exceptions.
+
As an example,
@codeblock|{
@@ -99,7 +110,12 @@ As an example,
(my-sqr 42)]
}|
-uses an evaluator whose language is @racketmodname[typed/racket/base].}
+uses an evaluator whose language is @racketmodname[typed/racket/base].
+
+@history[#:changed "1.14" @elem{Added @racket[eval:error] and added
+ logging of exceptions from expressions
+ that are not wrapped with
+ @racket[eval:error].}]}
@defform[(interaction0 maybe-eval maybe-escape datum ...)]{
Like @racket[interaction], but without insetting the code via
@@ -112,7 +128,8 @@ Like @racket[interaction], but without insetting the code via
@defform[(interaction-eval maybe-eval maybe-escape datum)]{
Like @racket[interaction], evaluates the @racket[quote]d form of
-@racket[datum], but returns the empty string and does not catch errors.}
+@racket[datum], but returns the empty string and does not catch
+exceptions (so @racket[eval:error] has no effect).}
@defform[(interaction-eval-show maybe-eval maybe-escape datum)]{
diff --git a/scribble-lib/info.rkt b/scribble-lib/info.rkt
@@ -23,4 +23,4 @@
(define pkg-authors '(mflatt eli))
-(define version "1.13")
+(define version "1.14")
diff --git a/scribble-lib/scribble/eval.rkt b/scribble-lib/scribble/eval.rkt
@@ -205,20 +205,23 @@
(eval-results (list content) out err))
(define (extract-to-evaluate s)
- (let loop ([s s] [expect #f])
- (syntax-case s (code:line code:comment code:contract eval:alts eval:check)
+ (let loop ([s s] [expect #f] [error-expected? #f])
+ (syntax-case s (code:line code:comment code:contract eval:alts eval:check eval:error)
[(code:line v (code:comment . rest))
- (loop (extract s cdr car) expect)]
+ (loop (extract s cdr car) expect error-expected?)]
[(code:comment . rest)
- (values (nothing-to-eval) expect)]
+ (values (nothing-to-eval) expect error-expected?)]
[(code:contract . rest)
- (values (nothing-to-eval) expect)]
+ (values (nothing-to-eval) expect error-expected?)]
+ [(eval:error e)
+ (loop (extract s cdr car) expect #t)]
[(eval:alts p e)
- (loop (extract s cdr cdr car) expect)]
+ (loop (extract s cdr cdr car) expect error-expected?)]
[(eval:check e expect)
(loop (extract s cdr car)
- (list (syntax->datum (datum->syntax #f (extract s cdr cdr car)))))]
- [else (values s expect)])))
+ (list (syntax->datum (datum->syntax #f (extract s cdr cdr car))))
+ error-expected?)]
+ [else (values s expect error-expected?)])))
(define (do-eval ev who)
(define (get-outputs)
@@ -259,13 +262,22 @@
(map (current-print) v))
(close-output-port out)
in)))])))
- (define (do-ev/expect s expect)
+ (define (do-ev/expect s expect error-expected?)
(define-values (val render+output)
(with-handlers ([(lambda (x) (not (exn:break? x)))
(lambda (e)
- (cons ((scribble-exn->string) e)
- (get-outputs)))])
+ (unless error-expected?
+ (log-error "interaction without `eval:error` raised an exception: ~s; form: ~.s"
+ (if (exn? e)
+ (exn-message e)
+ e)
+ s))
+ (values e
+ (cons ((scribble-exn->string) e)
+ (get-outputs))))])
(define val (do-plain-eval ev s #t))
+ (when error-expected?
+ (log-error "interaction failed to raise an expected exception: ~.s" s))
(values val (cons (render-value val) (get-outputs)))))
(when expect
(let ([expect (do-plain-eval ev (car expect) #t)])
@@ -277,10 +289,10 @@
(list (map formatted-result (eval-results-contents str))
(eval-results-out str)
(eval-results-err str))
- (let-values ([(s expect) (extract-to-evaluate str)])
+ (let-values ([(s expect error-expected?) (extract-to-evaluate str)])
(if (nothing-to-eval? s)
(list (list (void)) "" "")
- (do-ev/expect s expect))))))
+ (do-ev/expect s expect error-expected?))))))
(module+ test
(require rackunit)
@@ -621,7 +633,7 @@
#'(quote e)])]))
(define (do-interaction-eval ev e)
- (let-values ([(e expect) (extract-to-evaluate e)])
+ (let-values ([(e expect error-expected?/ignored) (extract-to-evaluate e)])
(unless (nothing-to-eval? e)
(parameterize ([current-command-line-arguments #()])
(do-plain-eval (or ev (make-base-eval)) e #f)))
@@ -646,15 +658,19 @@
[(_ e) (do-interaction-eval-show #f (quote-expr e))]))
(define-syntax racketinput*
- (syntax-rules (eval:alts code:comment)
+ (syntax-rules (eval:alts code:comment eval:check eval:error)
[(_ #:escape id (code:comment . rest)) (racketblock0 #:escape id (code:comment . rest))]
[(_ #:escape id (eval:alts a b)) (racketinput* #:escape id a)]
+ [(_ #:escape id (eval:check a b)) (racketinput* #:escape id a)]
+ [(_ #:escape id (eval:error a)) (racketinput* #:escape id a)]
[(_ #:escape id e) (racketinput0 #:escape id e)]))
(define-syntax racketblock*
- (syntax-rules (eval:alts code:comment)
+ (syntax-rules (eval:alts code:comment eval:check eval:error)
[(_ #:escape id (code:comment . rest)) (racketblock0 #:escape id (code:comment . rest))]
[(_ #:escape id (eval:alts a b)) (racketblock #:escape id a)]
+ [(_ #:escape id (eval:check a b)) (racketblock #:escape id a)]
+ [(_ #:escape id (eval:error a)) (racketblock #:escape id a)]
[(_ #:escape id e) (racketblock0 #:escape id e)]))
(define-code racketblock0+line (to-paragraph/prefix "" "" (list " ")))