Tools.nrepl middleware-dependency sorting issues

74 views
Skip to first unread message

Gary Trakhman

unread,
May 4, 2014, 9:47:25 PM5/4/14
to clo...@googlegroups.com
... inspired this workaround, is there a better way?

Essentially, :expects and :requires are breaking the sort order when they shouldn't, causing certain middlewares to just be in totally the wrong place.  I couldn't find the pattern after staring at for a bit.  Since leiningen calls linearize-middleware-stack internally, there's no more sensible way to work around this and compose with whatever other middlewares people might be using.

I might be able to scrap together a minimal test case..  what do you think?

(ns cider.nrepl.middleware.default
  "Default handler for convenience, works around a flawed dependency mechanism for now"
  (:require [clojure.tools.nrepl.middleware :as mw :refer [set-descriptor!]]
            [clojure.tools.nrepl.middleware.pr-values :refer [pr-values]]
            [clojure.tools.nrepl.middleware.session :refer [session]]
            [clojure.tools.nrepl.misc :refer [response-for]]
            [clojure.tools.nrepl.transport :as t]
            [cider.nrepl.middleware
             [info :as info]
             [complete :as complete]
             [stacktrace :as stacktrace]
             [classpath :as classpath]
             [inspect :as inspect]
             [trace :as trace]
             [exceptions :as exceptions]]
            [cider.nrepl.middleware.util.cljs :as cljs]))

(defmacro defcomposed-handler
  "Generates a handler that subsumes all the other handlers, takes a name,
a sequence of middleware symbols, and a descriptor-fn to transform the derived descriptor."
  [name mws descriptor-fn]
  (let [vars (map resolve mws)
        descriptors (map (comp ::mw/descriptor meta) vars)
        handles (into {} (mapcat :handles descriptors))]
    `(do (defn ~name
           [handler#]
           (-> handler# ~@vars))
         (set-descriptor!
          #'wrap-default
          (~descriptor-fn
           {:handles ~handles})))))

(defn conj-set
  [s val]
  (set (conj s val)))

(defcomposed-handler wrap-default
  [info/wrap-info
   complete/wrap-complete
   stacktrace/wrap-stacktrace
   classpath/wrap-classpath
   inspect/wrap-inspect
   trace/wrap-trace
   exceptions/wrap-exceptions]
  (comp
   #(update-in % [:requires] conj-set #'session)
   #(update-in % [:expects] conj-set #'pr-values)
   cljs/maybe-piggieback))




Gary Trakhman

unread,
May 4, 2014, 10:08:47 PM5/4/14
to clo...@googlegroups.com
The conflict is introduced by trying constrain all the cider-middlewares to live between pr-values and session, with wrap-exceptions needing to be the first along the chain.

This mostly looks good, except for wrap-exceptions/session:
(wrap-describe
 interruptible-eval
 pr-values
 wrap-load-file
 add-stdin
 wrap-exceptions
 wrap-stacktrace
 session
 wrap-complete
 wrap-inspect
 wrap-classpath
 wrap-info
 wrap-trace)

If I add the constraints adding info,complete,and pr-values to the :expects of wrap-exceptions, I get something like:
(wrap-describe
 wrap-complete
 wrap-info
 wrap-exceptions
 interruptible-eval
 pr-values
 wrap-load-file
 add-stdin
 wrap-stacktrace
 session
 wrap-inspect
 wrap-classpath
 wrap-trace)

Which means nothing hits pr-values, which means everything breaks :-).


Jony Hudson

unread,
May 5, 2014, 6:36:01 AM5/5/14
to clo...@googlegroups.com
I'm not sure it's relevant, but one thing that caught me out is that the "interruptible-eval" middleware has a hard dependency on the "pr-values" middleware (i.e. it depends on #'clojure.tools.nrepl.middleware.pr-values/pr-values). I found this caused trouble when I was trying to insert my own pr'ing middleware.


Jony
Reply all
Reply to author
Forward
0 new messages