diff --git a/CHANGELOG.md b/CHANGELOG.md index cf3d3d2e..2402e1db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Changed * `import` now returns nil instead of the last module's string representation (#1174) + * `defn` now processes name metadata alongside `attr-map?`, enabling `defasync` to effectively pass meta keys like `:decorators` (part of #1178) ### Fixed * Fix a bug in `defn` where the `attr-map?` and function metdata were merged into a seq instead of a map, causing `macroexpand` to fail in some cases (#1186) diff --git a/src/basilisp/core.lpy b/src/basilisp/core.lpy index 1fd88cb5..8feef87d 100644 --- a/src/basilisp/core.lpy +++ b/src/basilisp/core.lpy @@ -338,8 +338,10 @@ After the name, an optional mapping of meta attributes may be provided. Any metadata values given will be attached to the metadata of the - interned Var. A few special meta keys change how ``defn`` emits the - final Var and function: + interned Var, taking precedence over existing metadata. + + A few special meta keys change how ``defn`` emits the final Var and + function: - ``:decorators`` is an optional vector of functions which will wrap the final function emitted by ``defn``. Like standard Python @@ -391,6 +393,7 @@ body (if fmeta (rest body) body) + fmeta (meta fname) multi? (seq? (first body)) fsigs (if multi? (loop [arities body diff --git a/tests/basilisp/test_core_macros.lpy b/tests/basilisp/test_core_macros.lpy index b1f19688..dcc770f3 100644 --- a/tests/basilisp/test_core_macros.lpy +++ b/tests/basilisp/test_core_macros.lpy @@ -1,5 +1,5 @@ (ns tests.basilisp.test-core-macros - (:import contextlib inspect os socket tempfile) + (:import asyncio contextlib inspect os socket tempfile) (:require [basilisp.string :as str] [basilisp.test :refer [deftest is are testing]])) @@ -96,7 +96,24 @@ (is (= {:abc 10 :lmn 15 :xyz 11} (select-keys vmeta [:abc :lmn :xyz]))))) (testing "macroexpand with attr-map?" - (is (macroexpand '(defn fx {:abc 10} [] :kw))))) + (is (macroexpand '(defn fx {:abc 10} [] :kw)))) + + (testing "decorators" + (testing "single" + (let [add-5% #(fn [] (+ (%) 5)) + fvar (defn f10 {:decorators [add-5%]} [] 7)] + (is (= 12 (f10))))) + + (testing "single with arg" + (let [add-x% (fn [x] #(fn [] (+ (%) x))) + fvar (defn f11 {:decorators [(add-x% 10)]} [] 7)] + (is (= 17 (f11))))) + + (testing "mix" + (let [add-5% #(fn [] (+ (%) 5)) + add-x% (fn [x] #(fn [] (+ (%) x))) + fvar (defn f12 {:decorators [(add-x% 10) add-5%]} [] 9)] + (is (= 24 (f12))))))) (deftest defasync-test (testing "single arity defasync" @@ -189,7 +206,24 @@ (is (:async vmeta)) (is (inspect/iscoroutinefunction af8)) (is (= "0.1" (:added vmeta))) - (is (= "another multi-arity docstring" (:doc vmeta))))))) + (is (= "another multi-arity docstring" (:doc vmeta))))) + + (testing "async decorators" + (testing "single" + (let [add-5% #(fn ^:async _ [] (+ (await (%)) 5)) + fvar (defasync ^{:decorators [add-5%]} af9 [] 7)] + (is (= 12 (asyncio/run (af9)))))) + + (testing "single with arg" + (let [add-x% (fn [x] #(fn ^:async _ [] (+ (await (%)) x))) + fvar (defasync ^{:decorators [(add-x% 10)]} af10 [] 7)] + (is (= 17 (asyncio/run (af10)))))) + + (testing "mix" + (let [add-5% #(fn ^:async _ [] (+ (await (%)) 5)) + add-x% (fn [x] #(fn ^:async _ [] (+ (await (%)) x))) + fvar (defasync af11 {:decorators [(add-x% 10) add-5%]} [] 9)] + (is (= 24 (asyncio/run (af11))))))))) (deftest defn-private-test (testing "single arity defn-"