Skip to content
Snippets Groups Projects
Unverified Commit 6d7ac621 authored by Bryan Maass's avatar Bryan Maass Committed by GitHub
Browse files

mu/defn auto docstrings (#28042)

mu/defn now prepends docstrings onto functions, the way prismatic schema does.


* prepend docstring with m.schema annotation
* refinement of alignment
* fix typo in docstring
parent a63f2f33
No related branches found
No related tags found
No related merge requests found
......@@ -2,6 +2,7 @@
(:refer-clojure :exclude [defn])
(:require
[clojure.core :as core]
[clojure.string :as str]
[malli.core :as mc]
[malli.destructure]
[malli.error :as me]
......@@ -32,12 +33,16 @@
(<= 2000 (count url)) nil
:else url))))
(core/defn- humanize-include-value
"Pass into mu/humanize to include the value received in the error message."
[{:keys [value message]}] (str message ", received: " (pr-str value)))
(core/defn- explain-fn-fail!
"Used as reporting function to minst/instrument!"
[type data]
(let [{:keys [input args output value]} data
humanized (cond input (me/humanize (mc/explain input args))
output (me/humanize (mc/explain output value)))]
humanized (cond input (me/humanize (mc/explain input args) {:wrap humanize-include-value})
output (me/humanize (mc/explain output value) {:wrap humanize-include-value}))]
(throw (ex-info
(pr-str humanized)
(merge {:type type :data data}
......@@ -61,10 +66,18 @@ explain-fn-fail!
(->> arities val :arities (map parse)))
raw-arglists (map :raw-arglist parglists)
schema (as-> (map ->schema parglists) $ (if single (first $) (into [:function] $)))
annotated-doc (str/trim
(str "Inputs: " (if single
(pr-str (first (mapv :raw-arglist parglists)))
(str "(" (str/join "\n " (map (comp pr-str :raw-arglist) parglists)) ")"))
"\n Return: " (str/replace (u/pprint-to-str (:schema return))
"\n"
(str "\n "))
(when (not-empty doc) (str "\n\n " doc))))
id (str (gensym "id"))]
`(let [defn# (core/defn
~name
~@(some-> doc vector)
~@(some-> annotated-doc vector)
~(assoc meta
:raw-arglists (list 'quote raw-arglists)
:schema schema
......
(ns ^:mb/once metabase.util.malli-test
(:require
[clojure.string :as str]
[clojure.test :refer [deftest is testing]]
[malli.core :as mc]
[malli.error :as me]
......@@ -9,8 +10,8 @@
(deftest mu-defn-test
(testing "invalid input"
(mu/defn bar [x :- [:map [:x int?] [:y int?]]] (str x))
(is (= [{:x ["missing required key"]
:y ["missing required key"]}]
(is (= [{:x ["missing required key, received: nil"]
:y ["missing required key, received: nil"]}]
(:humanized
(try (bar {})
(catch Exception e (ex-data e)))))
......@@ -19,15 +20,50 @@
(testing "invalid output"
(mu/defn baz :- [:map [:x int?] [:y int?]] [] {:x "3"})
(is (= {:x ["should be an int"]
:y ["missing required key"]}
(is (= {:x ["should be an int, received: \"3\""]
:y ["missing required key, received: nil"]}
(:humanized
(try (baz)
(catch Exception e (ex-data e)))))
"when baz returns an invalid form um/defn throws")
(is (= "Inputs: []\n Return: [:map [:x int?] [:y int?]]" (:doc (meta #'baz))))
(ns-unmap *ns* 'baz)))
(deftest mu-defn-docstrings
(testing "docstrings are preserved"
(mu/defn ^:private boo :- :int "something very important to remember goes here" [x])
(is (str/ends-with? (:doc (meta #'boo)) "something very important to remember goes here"))
(ns-unmap *ns* 'boo))
(testing "multi-arity, and varargs doc strings should work"
(mu/defn ^:private foo :- [:multi {:dispatch :type}
[:sized [:map [:type [:= :sized]]
[:size int?]]]
[:human [:map
[:type [:= :human]]
[:name string?]
[:address [:map [:street string?]]]]]]
([] {:type :sized :size 3})
([a :- :int] {:type :sized :size a})
([a :- :int b :- :int] {:type :sized :size (+ a b)})
([a b & c :- [:* :int]] {:type :human
:name "Jim"
:address {:street (str (+ a b (apply + c)) " ln")}}))
(is (= (str/join "\n"
[ "Inputs: ([]"
" [a :- :int]"
" [a :- :int b :- :int]"
" [a b & c :- [:* :int]])"
" Return: [:multi"
" {:dispatch :type}"
" [:sized [:map [:type [:= :sized]] [:size int?]]]"
" [:human [:map [:type [:= :human]] [:name string?] [:address [:map [:street string?]]]]]]"])
(:doc (meta #'foo))))
(is (true? (:private (meta #'foo)))))
(ns-unmap *ns* 'foo))
(deftest with-api-error-message
(let [less-than-four-fxn (fn [x] (< x 4))]
(testing "outer schema"
(let [special-lt-4-schema (mu/with-api-error-message
......@@ -41,6 +77,9 @@
(is (= ["Special Number that has to be less than four"]
(me/humanize (mc/explain special-lt-4-schema 8))))
(is (= ["Special Number that has to be less than four, received: 8"]
(me/humanize (mc/explain special-lt-4-schema 8) {:wrap #'mu/humanize-include-value})))
(is (= "Special Number that has to be less than four"
(umd/describe special-lt-4-schema)))))
(testing "inner schema"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment