(загрузка (обновление)) => Невозможно установить!: *e из необязательного потока

Я установил samestep/boot-refresh 0.1.0. В загрузочный REPL, когда я изменяю исходный файл и набираю:

boot.user=> (boot (refresh))

Я получил:

java.lang.IllegalStateException: Can't set!: *e from non-binding thread

Что я делаю не так?


Вот полная трассировка стека:

boot.user=> *e
#error {
 :cause "Can't set!: *e from non-binding thread"
 :via
 [{:type java.lang.IllegalStateException
   :message "Can't set!: *e from non-binding thread"
   :at [clojure.lang.Var set "Var.java" 218]}]
 :trace
 [[clojure.lang.Var set "Var.java" 218]
  [clojure.tools.namespace.repl$print_and_return invokeStatic "repl.clj" 22]
  [clojure.tools.namespace.repl$print_and_return invoke "repl.clj" 20]
  [clojure.tools.namespace.repl$do_refresh invokeStatic "repl.clj" 96]
  [clojure.tools.namespace.repl$do_refresh invoke "repl.clj" 82]
  [clojure.tools.namespace.repl$refresh invokeStatic "repl.clj" 145]
  [clojure.tools.namespace.repl$refresh doInvoke "repl.clj" 128]
  [clojure.lang.RestFn invoke "RestFn.java" 397]
  [samestep.boot_refresh$eval541$fn__542$fn__547$fn__548$fn__549 invoke "boot_refresh.clj" 14]
  [clojure.lang.AFn applyToHelper "AFn.java" 152]
  [clojure.lang.AFn applyTo "AFn.java" 144]
  [clojure.core$apply invokeStatic "core.clj" 646]
  [clojure.core$with_bindings_STAR_ invokeStatic "core.clj" 1881]
  [clojure.core$with_bindings_STAR_ doInvoke "core.clj" 1881]
  [clojure.lang.RestFn invoke "RestFn.java" 425]
  [samestep.boot_refresh$eval541$fn__542$fn__547$fn__548 invoke "boot_refresh.clj" 13]
  [boot.core$run_tasks invoke "core.clj" 1019]
  [boot.core$boot$fn__918 invoke "core.clj" 1029]
  [clojure.core$binding_conveyor_fn$fn__4676 invoke "core.clj" 1938]
  [clojure.lang.AFn call "AFn.java" 18]
  [java.util.concurrent.FutureTask run "FutureTask.java" 266]
  [java.util.concurrent.ThreadPoolExecutor runWorker "ThreadPoolExecutor.java" 1142]
  [java.util.concurrent.ThreadPoolExecutor$Worker run "ThreadPoolExecutor.java" 617]
  [java.lang.Thread run "Thread.java" 745]]}

Мой собственный код не упоминается в трассировке стека. Я видел раньше работу (boot (refresh)) из REPL, но не смог выяснить, что я делаю по-другому, чтобы вызвать эту ошибку.


Обновление 1

После долгого бинарного поиска со многими операторами печати - поскольку, как объяснено в ответе @amalloy, трассировка стека для реального исключения недоступна - я обнаружил это:

В пространстве имен move-test этот оператор:

(def subset-sum-spec fargish.workspace-test/subset-sum-spec)

вызывал сбой (boot (refresh)). Когда я заменил его на:

(:require … [fargish.workspace-test :refer [subset-sum-spec]] …)

в операторе ns снова работало (boot (refresh)). Это устраняет текущую проблему на данный момент, но я все же хотел бы знать, что происходит.


Обновление 2

Продолжая пытаться заставить (boot (refresh)) работать, стало ясно, что проблема каждый раз разная. ответ @amalloy предполагает, что реальным ответом на этот вопрос будет какой-то способ найти исключение и трассировку стека, которые c.t.n.repl/print- and-return не может сохранить в *e. Я попробовал несколько идей, но соответствующие переменные кажутся частными и их трудно найти.

Как узнать ошибку, которая приводит к сбою (boot (refresh))?


person Ben Kovitz    schedule 28.03.2017    source источник
comment
Обновление - это отдельный вопрос о том, как требовать vars. Но вкратце: вы должны требовать переменные из другого пространства имен, если хотите их использовать.   -  person amalloy    schedule 29.03.2017
comment
@amalloy После дальнейшего изучения выясняется, что проблема каждый раз разная. Выкладываю второе обновление…   -  person Ben Kovitz    schedule 01.04.2017
comment
Я столкнулся с тем же сообщением об ошибке и отправил сообщение об этом с обновлением загрузки, хотя я сомневаюсь, что проблема существует. (Моя проблема заключалась в том, что я преобразовал функцию в другую ns и не смог обновить ссылки). Трюк с (bind) у меня не сработал.   -  person James    schedule 11.08.2017


Ответы (2)


Я не знаю насчет boot или c.t.namespace, но эта трассировка ошибки выглядит так, как будто загрузка выполняется refresh из нового потока, а refresh вызывает какую-то ошибку. Он пытается распространить ошибку за вас, установив *e, но не может установить *e, потому что он не находится в потоке repl. Таким образом, вместо того, чтобы увидеть настоящую ошибку, вы получаете эту бесполезную «ошибку, вызванную неспособностью сообщить об ошибке».

Кажется, ошибка обнаружена здесь, и c.t.namespace пытается избежать установки *e, когда нет запущенного repl (путем проверки того, привязан ли *e), но ошибочно предполагает, что если repl запущен, то поток repl должен быть тем, в котором он находится. вызывается из, тогда как, по-видимому, загрузка вызывает его из другого потока. Вы пробовали просто позвонить (refresh)? Я не знаю, что должна делать обертка (boot ...), но она может вам не понадобиться и, похоже, вызывает проблемы. Это также объясняет, почему вы видели работу (boot (refresh)): оболочка (boot ...) (вероятно) сама ничего не ломает, а вместо этого просто ухудшает отчет об ошибках, когда что-то еще не работает.

Конечно, как только вы устраните эту проблему, обновление не будет работать: вы просто сможете увидеть реальную ошибку, а не мета-ошибку! Надеюсь, этого будет достаточно, чтобы помочь вам добиться прогресса.

person amalloy    schedule 28.03.2017
comment
Спасибо! Это может быть как раз то понимание, которое мне нужно. (refresh) возвращает загрузочную задачу, которая корректно выполняет обновление при загрузке — по крайней мере, так я понимаю на данный момент. Чтобы запустить задачу из REPL, думаю нужно сделать (boot (refresh)), но документация очень легкий. Теперь продолжаем ковыряться… - person Ben Kovitz; 28.03.2017

Repl обычно имеет *e привязку к среде, в которой вы выполняете свой код. Если что-то, что требует *e, выполняется в другом потоке или вне repl, у него не будет *e для привязки.

Я обошел эту проблему, используя with-binding, чтобы гарантировать, что при обновлении fn всегда будет *e для привязки ошибки.

    (with-bindings {#'*e nil} (refresh))

Как только у обновления будет *e для привязки ошибки, оно должно удалить ошибку для вас.

person Karl Mikkelsen    schedule 06.04.2017
comment
Когда я делаю (with-bindings {#'*e nil} (boot (refresh))), я получаю то же сообщение об ошибке. Я полагаю, что ошибка должна быть в boot. (with-bindings {#'*e nil} (refresh)) просто вернул объект, возвращенный (refresh). Вы работаете boot? - person Ben Kovitz; 18.09.2017