Comments (17)
@carocad @danielcompton Thanks again for reporting this and the detailed information above.
I've thought a lot about this. You can read all the boring details but the summary is this: it's really difficult to support this use case in a general way. Furthermore, AFAICT, using conformers to transform values (as in the examples above) is not an intended use case for spec's conformers, so I'm less inclined to put in the time to make this work.
As a result, I've changed the code to return a (hopefully) more helpful error message and I've added a note to the README.
I realize that many people use conforms to transform values, so it's a bit of a bummer that they won't be able to use Expound out of the box, but I don't see a way to implement this cleanly right now.
If you are so inclined, you can implement your own custom printer that supports conformers. You could even wrap the normal printer and then just handle this particular error in a custom way. To be frank, it's a non-trivial amount of work to replace the current printer, but it's at least a a way to try out a custom solution (and you could share your code via an additional library or gist).
Thanks again for the help!
from expound.
The basic problem is that Expound didn't deal at all with conformers. Whoops.
It's a bit tricky. When some data does not match a spec, Expound tries to highlight the bad portion of the data. This works well until we start conforming the data, which alters the path to the bad data (which is stored in the :in
key).
I need to think a little bit about how to best support this.
from expound.
However I'm not able to make a failing test case for them anymore. I'm not sure what to do to make them fail sorry.
@danielcompton Not a problem. I'm just trying to get a sense of the use cases since I haven't yet used conformers in my specs.
@carocad Thanks, that's very helpful to know! I do think this is a missing feature in Expound, but it's good for me to know what a valid conformer looks like, so I can at least build the Expound feature on this constraint.
Apologies for the delay on this feature: I have been thinking about it, but I was working on some other features and haven't yet had time to sit down and code it. Hopefully this week.
from expound.
@carocad Thank you very much for reporting this!
I'm having trouble reproducing this, but that is probably because I commented out (s/conformer parse-radiuses)
(since I don't have the definition of that function). Would it be possible to post the definition of parse-radiuses
and rads-regex
?
Thanks very much!
from expound.
@bhb sorry my mistake. Here are the definitions:
(defn- parse-radiuses
[text]
(map edn/read-string (str/split text #";")))
;(->radiuses "1200.50;100;500;unlimited;100")
(def rads-regex #"((\d+(\.\d+)?)|unlimited)(;((\d+(\.\d+)?)|unlimited))*")
The full spec is here
Hope it helps
from expound.
@carocad Thank you! I can reproduce the bug now. I'll investigate ASAP.
from expound.
We just hit this too when using conformers (which are very handy in general). Even just an interim bug fix to not throw here and return a best effort error would be very helpful.
from expound.
I monkey patched highlighted-form
so we could keep using expound. Here's what I came up with quickly:
(defn highlighted-form
"Given a form and a path into that form, returns a pretty printed
string that highlights the value at the path."
[form path]
(let [value-at-path (value-in form path)
relevant (str "(" ::relevant "|(" ::kv-relevant "\\s+" ::kv-relevant "))")
regex (re-pattern (str "(.*)" relevant ".*"))
s (binding [*print-namespace-maps* false] (pprint-str (walk/prewalk-replace {::irrelevant '...} (summary-form form path))))
[line prefix & _more] (re-find regex s)
highlighted-line (some-> line
(string/replace (re-pattern relevant) (indent 0 (count prefix) (pprint-str value-at-path)))
(str "\n" (highlight-line prefix (pprint-str value-at-path))))]
;;highlighted-line
(no-trailing-whitespace (if (some? line)
(string/replace s line highlighted-line)
s))))
regex (re-pattern (str "(.*)" relevant ".*"))
s (binding [*print-namespace-maps* false] (pprint-str (walk/prewalk-replace {::irrelevant '...} (summary-form form path))))
[line prefix & _more] (re-find regex s)
- highlighted-line (some-> line
- (string/replace (re-pattern relevant) (indent 0 (count prefix) (pprint-str value-at-path)))
- (str "\n" (highlight-line prefix (pprint-str value-at-path))))]
+ highlighted-line (-> line
+ (string/replace (re-pattern relevant) (indent 0 (count prefix) (pprint-str value-at-path)))
+ (str "\n" (highlight-line prefix (pprint-str value-at-path))))]
;;highlighted-line
- (no-trailing-whitespace (if (some? line)
- (string/replace s line highlighted-line)
- s))))
+ (no-trailing-whitespace (string/replace s line highlighted-line))))
from expound.
@danielcompton Thanks very much for posting the patched version of highlighted-form
!
I'll think more about doing an interim bug fix. My concern is that this might not be appropriate for all users - in some cases, it's more confusing to silently return an inaccurate error rather than loudly fail when conformers are used (and harder to Google for the root cause).
One strategy I've used in our code is to wrap Expound in a try/catch and fall back on explain
if it fails like so https://gist.github.com/bhb/e7a3653479bc19bf2822174b22f32ddb. I don't know if that will help in your particular use case though.
from expound.
@danielcompton Could you possibly add some examples of the conformers you are using? I want to make sure any solution I implement works in your use case. Thanks!
from expound.
(defn conform-by
"Returns a conformer which will associate the top level key into the keen params.
Useful for multi-specs.
(conform-by
"
[tl-key payload-key]
(s/conformer (fn [m]
(let [id (get m tl-key)]
(if (and id (map? (get m payload-key)))
(assoc-in m [payload-key tl-key] id)
:clojure.spec.alpha/invalid)))))
(s/def :myapp.query/id qualified-keyword?)
(defmulti query-params :myapp.query/id)
(s/def :myapp.query/params (s/multi-spec query-params :myapp.query/id))
(s/def :user/id string?)
(defmethod query-params :myapp/lookup-user [_]
(s/keys :req [:user/id]))
(s/def :myapp/query
(s/and
(conform-by :myapp.query/id :myapp.query/params)
(s/keys :req [:myapp.query/id
:myapp.query/params])))
Here's an example of our conformers. However I'm not able to make a failing test case for them anymore. I'm not sure what to do to make them fail sorry.
from expound.
Hmmm I just realized that my conformer doesnt conform to the Clojure definition. "i.e. it should return either a (possibly converted) value or :clojure.spec/invalid". My conformer just throws or returns an empty list if it couldnt succeed.
Maybe that is why @danielcompton conformer doesnt fail anymore but mine still does :/
from expound.
Yeah, I have a feeling my early conformers may have been throwing exceptions. However I still can't reproduce my initial problems.
from expound.
I haven't forgotten about this. It's not clear how to accomplish this with the current implementation of pretty-printing the invalid value, so I'm refactoring that code to see if I can enable this scenario.
from expound.
Thanks for the investigation into this. It's a bit disappointing spec's conformers aren't supported for this use case but I guess it is what it is. Thanks!
from expound.
@danielcompton I don't think you're alone in your disappointment on this issue. I haven't personally used conformers but I know a lot of people are pushing to either have spec support this use case or find a good library solution.
from expound.
@danielcompton I've got a fix for this issue in 0.7.2-SNAPSHOT. If you have time to try it out, please let me know what you think!
from expound.
Related Issues (20)
- Error reports returned as :cause string in an exception when produced by instrumentation within generative testing HOT 11
- allow passing options map to expound/expound-str HOT 1
- Improve grouping of spec errors HOT 2
- Unnecessary dependency on `cider-nrepl` HOT 1
- Regression of #3 HOT 4
- Easier editing of error messages
- `printer` has invalid spec HOT 2
- "Cannot convert path" on instrumentation failures in the wild HOT 4
- (cljs.spec.test.alpha/instrument) breaks expound due to wrong arity HOT 4
- should expound work anywhere s/explain works? - clojurescript error HOT 7
- Error in :ret check when using with Orchestra 2020.07.12-1 HOT 5
- Internal error in `lift-singleton-groups` when having a datomic db value in fn args HOT 13
- Crash bug when printing, if a datomic db is present and the spec fails HOT 19
- Feature inquiry: a convenience function for validating a value against a spec HOT 4
- Wrapped `s/keys` does not properly display unqualified keyword specs HOT 5
- ClassCastException from `expound-str` when running in AWS Lambda HOT 9
- Small *print-length* and/or *print-level* sometimes yield NPE HOT 5
- PersistentList cannot be cast to class Named HOT 3
- proposal : defmsg equivalent for arbitrary predicates HOT 15
- optional include location of the spec error in the message. HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from expound.