Welcome! Please see the About page for a little more info on how this works.

0 votes
in Spec by

Right now, {{instrument}} and {{unstrument}} are great for unconditional instrumentation for tests and for development. I also want to run instrument for just a particular piece of code. For example, I want a test with some stubs or some overrides. Right now I need to instrument and unstrument; I'd prefer to have a with-instrument macro that does the obvious try/finally block for me.

3 Answers

0 votes
by

Comment made by: alexmiller

So (like most things), obvious things aren't. :)

There are several ways to call instrument:

  • (instrument)
  • (instrument sym)
  • (instrument (link: syms))
  • (instrument sym opts)
  • (instrument (link: syms) opts)

The number there is variable. Similarly, a "body" is typically also variadic in other with-style macros. Parsing those two variadic things is ambiguous.

You mentioned the opts map, so I'm assuming you'd want that as an option. So you could narrow the args to: (link: sym-or-syms opts & body). Not sure whether you've then introduced things you don't need in common cases and ruined the usefulness of the macro.

(with-instrument `my-fun {my-opts ...} (test-something))

would expand to

`
(do
(instrument user/my-fun {my-opts ...})
(try

(test-something)
(finally
  (unstrument user/my-fun))))

`

There are maybe interesting things to think about with how much you take into account what's already instrumented. Do you unstrument what you instrument, or do you try to return the instrumentation to what it was before (where some stuff may already have been instrumented)?

0 votes
by

Comment made by: dsg

So, here's the implementation I have been using, which isn't necessarily the one to use, but I think it helps with some of the ambiguity with respect to arguments:

`
(defmacro with-instrumentation
[args & body]
`(let [[arg1# arg2#] ~args

     [sym-or-syms# opts#] (cond
                            (nil? arg1#) [(stest/instrumentable-syms) arg2#]
                            (map? arg1#) [(stest/instrumentable-syms) arg1#]
                            :default     [arg1# arg2#])]
 (try
   (stest/instrument sym-or-syms# opts#)
   ~@body
   (finally
     (stest/unstrument sym-or-syms#)))))

`

It's not perfect, but it has served me well enough.

The question of what happens at the end is a very good one. Ideally, with-instrumentation would have stack-like semantics where instrumentation would return to its previous state. Is that something that can be done with spec?

0 votes
by
...