|
953 | 953 |
|
954 | 954 | (atexit/register (.-shutdown *executor-pool*))
|
955 | 955 |
|
| 956 | +;; Declare `bound-fn*` now for use in `future-call`, though it is defined much later |
| 957 | +;; with the rest of the Var binding functions and macros. |
| 958 | +(def ^:redef bound-fn*) |
| 959 | + |
956 | 960 | (defn future-call
|
957 | 961 | "Call the no args function f in another thread. Returns a Future object.
|
958 | 962 | The value returned by f can be fetched using `deref` or `@`, though
|
959 | 963 | doing so may block unless the `deref` with a timeout argument is used."
|
960 | 964 | ([f]
|
961 | 965 | (future-call f *executor-pool*))
|
962 | 966 | ([f pool]
|
963 |
| - (.submit pool f))) |
| 967 | + (.submit pool (bound-fn* f)))) |
964 | 968 |
|
965 | 969 | (defmacro future
|
966 | 970 | "Execute the expressions of body in another thread. Returns a Future object.
|
|
3647 | 3651 | :macro true}
|
3648 | 3652 | with-open with)
|
3649 | 3653 |
|
| 3654 | +(defn get-thread-bindings |
| 3655 | + "Return the current thread-local bindings as a map of Var/value pairs." |
| 3656 | + [] |
| 3657 | + (basilisp.lang.runtime/get-thread-bindings)) |
| 3658 | + |
3650 | 3659 | (defn push-thread-bindings
|
3651 | 3660 | "Takes a map of Var/value pairs and applies the given value to the Var in the
|
3652 | 3661 | current thread.
|
|
3688 | 3697 | (finally
|
3689 | 3698 | (pop-thread-bindings))))))
|
3690 | 3699 |
|
| 3700 | +(defn with-bindings* |
| 3701 | + "Execute the function `f` with the given arguments `args` (as by `apply`) with the |
| 3702 | + thread-local Var bindings specified in the Var/value map `bindings-map` installed. |
| 3703 | + |
| 3704 | + The thread-local Var bindings will be popped after executing `f` in all cases. |
| 3705 | + |
| 3706 | + Returns the return value of `f`." |
| 3707 | + [bindings-map f & args] |
| 3708 | + (push-thread-bindings bindings-map) |
| 3709 | + (try |
| 3710 | + (apply f args) |
| 3711 | + (finally |
| 3712 | + (pop-thread-bindings)))) |
| 3713 | + |
| 3714 | +(defmacro with-bindings |
| 3715 | + "Execute the expressions given in the `body` with the thread-local Var bindings |
| 3716 | + specified in the Var/value map `bindings-map` installed. |
| 3717 | + |
| 3718 | + The thread-local Var bindings will be popped after executing the body in all cases. |
| 3719 | + |
| 3720 | + Returns the value of `body`." |
| 3721 | + [bindings-map & body] |
| 3722 | + `(with-bindings* ~bindings-map (fn [] ~@body))) |
| 3723 | + |
| 3724 | +(defn bound-fn* |
| 3725 | + "Return a function which executes `f` with the same thread-local Var bindings as |
| 3726 | + were in effect when `bound-fn*` was called. |
| 3727 | + |
| 3728 | + `f` will be called with the same arguments as the returned function. |
| 3729 | + |
| 3730 | + Returns the return value of `f`. |
| 3731 | + |
| 3732 | + This is primarily useful for creating functions which might need to run on a different |
| 3733 | + thread, but which should have the same Var bindings in place as when it was defined." |
| 3734 | + [f] |
| 3735 | + (let [current-bindings (get-thread-bindings)] |
| 3736 | + (fn [& args] |
| 3737 | + (apply with-bindings* current-bindings f args)))) |
| 3738 | + |
| 3739 | +(defmacro bound-fn |
| 3740 | + "Return a function with the same function tail (everything after `fn` when defining |
| 3741 | + a function) which will be executed with the same thread-local Var bindings as were |
| 3742 | + in effect when `bound-fn` was called. |
| 3743 | + |
| 3744 | + Returns the return value of the defined function. |
| 3745 | + |
| 3746 | + This is primarily useful for creating functions which might need to run on a different |
| 3747 | + thread, but which should have the same Var bindings in place as when it was defined." |
| 3748 | + [& fn-tail] |
| 3749 | + `(bound-fn* (fn ~@fn-tail))) |
| 3750 | + |
| 3751 | +(defn with-redefs-fn |
| 3752 | + "Temporarily re-bind the given Var roots to the given values while executing |
| 3753 | + the function `f`, binding back to the original value afterwards. |
| 3754 | + |
| 3755 | + Changes to the Var roots will be visible in all threads. |
| 3756 | + |
| 3757 | + Note that Basilisp directly links Var references in compiled code by default |
| 3758 | + for performance reasons. Direct linking can be disabled for all Vars during |
| 3759 | + compilation by setting the `--use-var-indirection` compiler flag at startup. |
| 3760 | + Direct linking can be disabled for individual Vars by setting the `^:redef` |
| 3761 | + meta flag where the Var is `def`'ed. |
| 3762 | + |
| 3763 | + `with-redefs` will throw an Exception if directly linked Vars are given in |
| 3764 | + the bindings." |
| 3765 | + [bindings-map f] |
| 3766 | + (when-not (or (:use-var-indirection *compiler-options*) |
| 3767 | + (every? #(:redef (meta %)) (keys bindings-map))) |
| 3768 | + (throw |
| 3769 | + (ex-info (str "Cannot redef selected Vars; either apply ^:redef meta to Vars or " |
| 3770 | + "restart Basilisp with '--use-var-indirection false' compiler flag") |
| 3771 | + {:unredefable-vars (remove #(:redef (meta %)) (keys bindings-map))}))) |
| 3772 | + (let [redef-vars (fn [m] |
| 3773 | + (doseq [v m |
| 3774 | + :let [vvar (first v) |
| 3775 | + vval (second v)]] |
| 3776 | + (.bind-root vvar vval))) |
| 3777 | + originals (reduce (fn [m elem] |
| 3778 | + (let [vvar (first elem)] |
| 3779 | + (assoc m vvar (.-root vvar)))) |
| 3780 | + {} |
| 3781 | + bindings-map)] |
| 3782 | + (redef-vars bindings-map) |
| 3783 | + (try |
| 3784 | + (f) |
| 3785 | + (finally |
| 3786 | + (redef-vars originals))))) |
| 3787 | + |
| 3788 | +(defmacro with-redefs |
| 3789 | + "Temporarily re-bind the given Var roots to the given values while executing |
| 3790 | + the body, binding back to the original value afterwards. |
| 3791 | + |
| 3792 | + Changes to the Var roots will be visible in all threads. |
| 3793 | + |
| 3794 | + Note that Basilisp directly links Var references in compiled code by default |
| 3795 | + for performance reasons. Direct linking can be disabled for all Vars during |
| 3796 | + compilation by setting the `--use-var-indirection` compiler flag at startup. |
| 3797 | + Direct linking can be disabled for individual Vars by setting the `^:redef` |
| 3798 | + meta flag where the Var is `def`'ed. |
| 3799 | + |
| 3800 | + `with-redefs` will throw an Exception if directly linked Vars are given in |
| 3801 | + the bindings." |
| 3802 | + [bindings & body] |
| 3803 | + (when-not (and (vector? bindings) |
| 3804 | + (even? (count bindings)) |
| 3805 | + (pos? (count bindings))) |
| 3806 | + (throw |
| 3807 | + (ex-info "Expected an even number of bindings" |
| 3808 | + {:bindings bindings}))) |
| 3809 | + (let [var-bindings (reduce (fn [v pair] |
| 3810 | + (let [vvar (first pair) |
| 3811 | + vval (second pair)] |
| 3812 | + (conj v `(var ~vvar) vval))) |
| 3813 | + [] |
| 3814 | + (partition 2 bindings))] |
| 3815 | + `(with-redefs-fn |
| 3816 | + ~(apply hash-map var-bindings) |
| 3817 | + (fn [] |
| 3818 | + ~@body)))) |
| 3819 | + |
3691 | 3820 | (defn ^:private perf-counter
|
3692 | 3821 | []
|
3693 | 3822 | (py-time/perf-counter))
|
|
4089 | 4218 | ctx
|
4090 | 4219 | namespace))))
|
4091 | 4220 |
|
| 4221 | +(defn load-reader |
| 4222 | + "Read and evaluate the set of forms in the `io.TextIOBase` instance `reader`. |
| 4223 | + |
| 4224 | + Most often this is useful if you want to split your namespace across multiple source |
| 4225 | + files. `require` will try to force you into a namespace-per-file paradigm (which is |
| 4226 | + generally preferred, but not right in every scenario). `load-reader` will load the |
| 4227 | + contents of the named file directly into the current namespace. |
| 4228 | + |
| 4229 | + Note that unlike `require`, files loaded by `load-reader` will not be cached and will |
| 4230 | + thus incur compilation time on subsequent loads." |
| 4231 | + [reader] |
| 4232 | + (let [src (some-> (python/getattr reader "name" nil) |
| 4233 | + (pathlib/Path) |
| 4234 | + (.resolve) |
| 4235 | + (python/str)) |
| 4236 | + ctx (basilisp.lang.compiler.CompilerContext. (or src "<Load Input>"))] |
| 4237 | + (doseq [form (seq (basilisp.lang.reader/read reader |
| 4238 | + *resolver* |
| 4239 | + *data-readers*))] |
| 4240 | + (basilisp.lang.compiler/compile-and-exec-form form |
| 4241 | + ctx |
| 4242 | + *ns*)))) |
| 4243 | + |
| 4244 | +(defn load-file |
| 4245 | + "Read and evaluate the set of forms contained in the file located at `path`. |
| 4246 | + |
| 4247 | + Most often this is useful if you want to split your namespace across multiple source |
| 4248 | + files. `require` will try to force you into a namespace-per-file paradigm (which is |
| 4249 | + generally preferred, but not right in every scenario). `load-file` will load the |
| 4250 | + contents of the named file directly into the current namespace. |
| 4251 | + |
| 4252 | + Note that unlike `require`, files loaded by `load-file` will not be cached and will |
| 4253 | + thus incur compilation time on subsequent loads." |
| 4254 | + [path] |
| 4255 | + (with-open [f (python/open path ** :mode "r")] |
| 4256 | + (load-reader f))) |
| 4257 | + |
| 4258 | +(defn load |
| 4259 | + "Read and evaluate the set of forms contained in the files identified by `paths`. |
| 4260 | + |
| 4261 | + The provided paths should not include a file suffix. |
| 4262 | + |
| 4263 | + Most often this is useful if you want to split your namespace across multiple source |
| 4264 | + files. `require` will try to force you into a namespace-per-file paradigm (which is |
| 4265 | + generally preferred, but not right in every scenario). `load` will load the contents |
| 4266 | + of the named file directly into the current namespace. |
| 4267 | + |
| 4268 | + Note that unlike `require`, files loaded by `load` will not be cached and will thus |
| 4269 | + incur compilation time on subsequent loads. |
| 4270 | + |
| 4271 | + This function is provided for compatibility with Clojure. Users should prefer |
| 4272 | + `load-file` (or perhaps `load-reader` or `load-string`) to this function." |
| 4273 | + [& paths] |
| 4274 | + (doseq [path (seq paths)] |
| 4275 | + (load-file (str path ".lpy")))) |
| 4276 | + |
| 4277 | +(defn load-string |
| 4278 | + "Read and evaluate the set of forms contained in the string `s`." |
| 4279 | + [s] |
| 4280 | + (load-reader (io/StringIO s))) |
| 4281 | + |
4092 | 4282 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
4093 | 4283 | ;; Reference (Atom/Namespace/Var) Utilities ;;
|
4094 | 4284 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
4206 | 4396 | (defn all-ns
|
4207 | 4397 | "Return a sequence of all namespaces."
|
4208 | 4398 | []
|
4209 |
| - (vals @basilisp.lang.runtime.Namespace/-NAMESPACES)) |
| 4399 | + (vals (basilisp.lang.runtime.Namespace/ns-cache))) |
4210 | 4400 |
|
4211 | 4401 | (defn find-ns
|
4212 | 4402 | "Return the namespace named by sym if it exists, or nil otherwise."
|
|
4519 | 4709 | (refer-lib current-ns libspec))
|
4520 | 4710 | nil))
|
4521 | 4711 |
|
| 4712 | +(defonce ^:dynamic ^:private *loaded-libs* |
| 4713 | + (atom #{})) |
| 4714 | + |
| 4715 | +(defn loaded-libs |
| 4716 | + "Return a set of all loaded Basilisp namespace names as symbols. |
| 4717 | + |
| 4718 | + Namespace names are only added to this list if they appear in an `ns` macro." |
| 4719 | + [] |
| 4720 | + @*loaded-libs*) |
| 4721 | + |
4522 | 4722 | (defmacro ns
|
4523 | 4723 | "Use this namespace pre-amble at the top of every namespace to declare
|
4524 | 4724 | the namespace name and import necessary Python modules and require
|
|
4566 | 4766 | (:import opts)))]
|
4567 | 4767 | `(do
|
4568 | 4768 | (in-ns (quote ~name))
|
| 4769 | + (swap! *loaded-libs* conj (quote ~name)) |
4569 | 4770 | ~(when doc
|
4570 | 4771 | `(alter-meta! (the-ns (quote ~name)) assoc :doc ~doc))
|
4571 | 4772 | (refer-basilisp ~@refer-filters)
|
4572 | 4773 | ~requires
|
4573 | 4774 | ~uses
|
4574 | 4775 | ~@imports)))
|
4575 | 4776 |
|
| 4777 | +(defn requiring-resolve |
| 4778 | + "Resolve the namespaced symbol `sym` as by `resolve`. If resolution fails, attempts |
| 4779 | + to require `sym`'s namespace (as by `require`) before resolving again." |
| 4780 | + [sym] |
| 4781 | + (if (qualified-symbol? sym) |
| 4782 | + (or (resolve sym) |
| 4783 | + (do (require (symbol (namespace sym))) |
| 4784 | + (resolve sym))) |
| 4785 | + (throw |
| 4786 | + (ex-info "Cannot resolve an unqualified symbol" |
| 4787 | + {:sym sym})))) |
| 4788 | + |
4576 | 4789 | ;;;;;;;;;;;;;;;;;;;;;
|
4577 | 4790 | ;; Regex Functions ;;
|
4578 | 4791 | ;;;;;;;;;;;;;;;;;;;;;
|
|
0 commit comments