Message ID | 20220618213832.25165-1-ludo@gnu.org |
---|---|
State | Accepted |
Headers | show |
Series | Report location of invalid configuration field values | expand |
Context | Check | Description |
---|---|---|
cbaines/comparison | success | View comparision |
cbaines/git branch | success | View Git branch |
cbaines/applying patch | success | View Laminar job |
cbaines/issue | success | View issue |
Hello, Ludovic Courtès <ludo@gnu.org> writes: > Previously field type errors would be reported in a non-standard way, > and without any source location information. This fixes it. > > * gnu/services/configuration.scm (configuration-field-error): Add a > 'loc' parameter and honor it. Use 'formatted-message' instead of plain > 'format'. > (define-configuration-helper)[field-sanitizer]: New procedure. > Use it. Use STEM as the identifier of the syntactic constructor of the > record type. Add a 'sanitize' property to each field. Remove now > useless STEM macro that would call 'validate-configuration'. > * gnu/services/mail.scm (serialize-listener-configuration): Adjust to > new 'configuration-field-error' prototype. > * tests/services/configuration.scm ("wrong type for a field"): New test. > * po/guix/POTFILES.in: Add gnu/services/configuration.scm. Very nice! I had been meaning to look at what define-configure could be improved w.r.t. the recently added sanitizers; I felt perhaps `define-configure' would perhaps loose its relevance, but I'm happy to see you saw value in upgrading it! The first part LGTM, although I so rarely dabble with syntax-case that it looks a bit alien to my eyes now. [...] > --- a/tests/services/configuration.scm > +++ b/tests/services/configuration.scm > @@ -19,6 +19,7 @@ You forgot to add your copyright notice line. > (define-module (tests services configuration) > #:use-module (gnu services configuration) > + #:use-module (guix diagnostics) > #:use-module (guix gexp) > #:use-module (srfi srfi-34) > #:use-module (srfi srfi-64)) > @@ -43,6 +44,17 @@ (define-configuration port-configuration > 80 > (port-configuration-port (port-configuration))) > > +(test-equal "wrong type for a field" > + '("configuration.scm" 56 11) ;error location > + (guard (c ((configuration-error? c) > + (let ((loc (error-location c))) > + (list (basename (location-file loc)) > + (location-line loc) > + (location-column loc))))) > + (port-configuration > + ;; This is line 55; the test relies on line/column numbers! > + (port "This is not a number!")))) > + It seems fragile to rely on the line/column number, but if we truly need to test that, I don't see a better options. Thanks, Maxim
Hi, Maxim Cournoyer <maxim.cournoyer@gmail.com> skribis: > Very nice! I had been meaning to look at what define-configure could be > improved w.r.t. the recently added sanitizers; I felt perhaps > `define-configure' would perhaps loose its relevance, but I'm happy to > see you saw value in upgrading it! Yeah, the mechanism that ‘define-configuration’ had had become redundant. > The first part LGTM, although I so rarely dabble with syntax-case that > it looks a bit alien to my eyes now. Well, ‘define-configuration’ is a bit hairy. :-) >> +++ b/tests/services/configuration.scm >> @@ -19,6 +19,7 @@ > > You forgot to add your copyright notice line. Fixed. >> +(test-equal "wrong type for a field" >> + '("configuration.scm" 56 11) ;error location >> + (guard (c ((configuration-error? c) >> + (let ((loc (error-location c))) >> + (list (basename (location-file loc)) >> + (location-line loc) >> + (location-column loc))))) >> + (port-configuration >> + ;; This is line 55; the test relies on line/column numbers! >> + (port "This is not a number!")))) >> + > > It seems fragile to rely on the line/column number, but if we truly need > to test that, I don't see a better options. Yeah; there is another option, which is to read code from a string port and to simulate its location, but it’s more lines of code for little IMO. Pushed, thanks! 6505f727e1 services: configuration: Remove 'validate-configuration'. fb7e6ccba7 services: configuration: Report the location of field type errors. Ludo’.
diff --git a/gnu/services/configuration.scm b/gnu/services/configuration.scm index f6b20fb82b..c39ea5a02a 100644 --- a/gnu/services/configuration.scm +++ b/gnu/services/configuration.scm @@ -27,7 +27,8 @@ (define-module (gnu services configuration) #:use-module (guix records) #:use-module (guix gexp) #:use-module ((guix utils) #:select (source-properties->location)) - #:use-module ((guix diagnostics) #:select (formatted-message location-file)) + #:use-module ((guix diagnostics) + #:select (formatted-message location-file &error-location)) #:use-module ((guix modules) #:select (file-name->module-name)) #:use-module (guix i18n) #:autoload (texinfo) (texi-fragment->stexi) @@ -87,9 +88,17 @@ (define-condition-type &configuration-error &error (define (configuration-error message) (raise (condition (&message (message message)) (&configuration-error)))) -(define (configuration-field-error field val) - (configuration-error - (format #f "Invalid value for field ~a: ~s" field val))) +(define (configuration-field-error loc field value) + (raise (apply + make-compound-condition + (formatted-message (G_ "invalid value ~s for field '~a'") + value field) + (condition (&configuration-error)) + (if loc + (list (condition + (&error-location (location loc)))) + '())))) + (define (configuration-missing-field kind field) (configuration-error (format #f "~a configuration missing required field ~a" kind field))) @@ -210,9 +219,33 @@ (define (define-configuration-helper serialize? serializer-prefix syn) (id #'stem #'serialize- type)))))) #'(field-type ...) #'((custom-serializer ...) ...)))) + (define (field-sanitizer name pred) + ;; Define a macro for use as a record field sanitizer, where NAME + ;; is the name of the field and PRED is the predicate that tells + ;; whether a value is valid for this field. + #`(define-syntax #,(id #'stem #'validate- #'stem #'- name) + (lambda (s) + ;; Make sure the given VALUE, for field NAME, passes PRED. + (syntax-case s () + ((_ value) + (with-syntax ((name #'#,name) + (pred #'#,pred) + (loc (datum->syntax #'value + (syntax-source #'value)))) + #'(if (pred value) + value + (configuration-field-error + (and=> 'loc source-properties->location) + 'name value)))))))) + #`(begin + ;; Define field validation macros. + #,@(map field-sanitizer + #'(field ...) + #'(field-predicate ...)) + (define-record-type* #,(id #'stem #'< #'stem #'>) - #,(id #'stem #'% #'stem) + stem #,(id #'stem #'make- #'stem) #,(id #'stem #'stem #'?) (%location #,(id #'stem #'stem #'-location) @@ -220,10 +253,13 @@ (define-record-type* #,(id #'stem #'< #'stem #'>) source-properties->location)) (innate)) #,@(map (lambda (name getter def) - #`(#,name #,getter (default #,def))) + #`(#,name #,getter (default #,def) + (sanitize + #,(id #'stem #'validate- #'stem #'- name)))) #'(field ...) #'(field-getter ...) #'(field-default ...))) + (define #,(id #'stem #'stem #'-fields) (list (configuration-field (name 'field) @@ -240,12 +276,7 @@ (define #,(id #'stem #'stem #'-fields) '#,(id #'stem #'% #'stem) 'field) field-default))) (documentation doc)) - ...)) - (define-syntax-rule (stem arg (... ...)) - (let ((conf (#,(id #'stem #'% #'stem) arg (... ...)))) - (validate-configuration conf - #,(id #'stem #'stem #'-fields)) - conf)))))))) + ...)))))))) (define no-serialization ;syntactic keyword for 'define-configuration' '(no serialization)) diff --git a/gnu/services/mail.scm b/gnu/services/mail.scm index d99743ac31..c2fd4d8670 100644 --- a/gnu/services/mail.scm +++ b/gnu/services/mail.scm @@ -285,7 +285,7 @@ (define (serialize-listener-configuration field-name val) (serialize-fifo-listener-configuration field-name val)) ((inet-listener-configuration? val) (serialize-inet-listener-configuration field-name val)) - (else (configuration-field-error field-name val)))) + (else (configuration-field-error #f field-name val)))) (define (listener-configuration-list? val) (and (list? val) (and-map listener-configuration? val))) (define (serialize-listener-configuration-list field-name val) diff --git a/po/guix/POTFILES.in b/po/guix/POTFILES.in index 201e5dcc87..f50dd00422 100644 --- a/po/guix/POTFILES.in +++ b/po/guix/POTFILES.in @@ -4,6 +4,7 @@ gnu.scm gnu/packages.scm gnu/services.scm gnu/system.scm +gnu/services/configuration.scm gnu/services/shepherd.scm gnu/home/services.scm gnu/home/services/ssh.scm diff --git a/tests/services/configuration.scm b/tests/services/configuration.scm index 334a1e409b..cf3e504233 100644 --- a/tests/services/configuration.scm +++ b/tests/services/configuration.scm @@ -19,6 +19,7 @@ (define-module (tests services configuration) #:use-module (gnu services configuration) + #:use-module (guix diagnostics) #:use-module (guix gexp) #:use-module (srfi srfi-34) #:use-module (srfi srfi-64)) @@ -43,6 +44,17 @@ (define-configuration port-configuration 80 (port-configuration-port (port-configuration))) +(test-equal "wrong type for a field" + '("configuration.scm" 56 11) ;error location + (guard (c ((configuration-error? c) + (let ((loc (error-location c))) + (list (basename (location-file loc)) + (location-line loc) + (location-column loc))))) + (port-configuration + ;; This is line 55; the test relies on line/column numbers! + (port "This is not a number!")))) + (define-configuration port-configuration-cs (port (number 80) "The port number." empty-serializer))