-
-
Notifications
You must be signed in to change notification settings - Fork 19
Description
Hi,
Currently, there doesn't seem to be a way to decorate a function defined with defasync.
The defn macro supports a :decorations attr-map? key, but while defasync is built on top of defn, it only considers the :decorations key in the attr-map? and not in metadata.
As a result, passing a metadata :decorator key, such as in (defasync ^{:decorators [decorator]} something [] ...), has no effect.
Would it be advisable to also support a ^{:decorators [...]} meta key for defasync?
Furthermore, when adapting examples from Python to Basilisp, developers often encounter decorated functions without necessarily understanding how they are internally defined.
For anonymous functions, decorating them is not immediately intuitive. A developer with basic knowledge of decorators might deduce that the anonymous function can be passed as the first argument to the decorator, e.g., (decorator (fn [] ...)). However, if the decorator accepts arguments, it must first be instantiated with those arguments before the anonymous function is passed, e.g., ((decorator arg1 arg2) (fn [] ...)).
Given that this requirement assumes Basilisp developers would need to understand some internal details about decorator (something that is not necessarily expected when using Basilisp, especially when coming from a Clojure background) would it be better to extend support for the :decorators meta key to anonymous functions as well? This enhancement could be particularly valuable when transpiling Python code to Basilisp, making it more intuitive and easier to visually pair code between the two languages. Alternatively, we can just extend the documentation to indicate how to manually decorate anonymous fns.
With that in mind, my suggestions would look something like this:
(defasync ^{:decorators [decorator1 (decorator2 arg1 ..) ...] fn-nane [] ...)
(fn ^{:decorators [decorator1 (decorator2 arg1 ...) ...] _fn-name [] ...)I have conducted a brief survey below to show the current status. I've decided to create the decorator in python to ensure I don't overlook anything:
asyncdec.py
import asyncio
import random
import time
def atimer(message):
def decorator(func):
async def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = await func(*args, **kwargs)
elapsed_time = time.perf_counter() - start_time
print(f"[{message}] function '{func.__name__}' executed in {elapsed_time:.2f} seconds")
return result
return wrapper
return decorator
@atimer("hi")
async def acount():
await asyncio.sleep(random.random()) # Simulate a delay
return [1, 2, 3]
async def main():
result = await acount()
print(result)
if __name__ == "__main__":
asyncio.run(main()) run:
> python .\asyncdec.py
[hi] function 'acount' executed in 0.56 seconds
[1, 2, 3]And then I imported the python atimer decorator for the survey
issue.lpy
(import asyncio
asyncdec)
;; `defasync`
;;
;; No way to decorate it with an async decorator?
(defasync
count1 []
(await (asyncio/sleep (rand)))
[1 2 3])
(defasync main1
[]
(println :result (await (count1))))
(asyncio/run (main1))
;; async `defn`
;;
;; Use of the `:decorators` attr-map? option
(defn count2
{:decorators [(asyncdec/atimer "hey2")]
:async true}
[]
(await (asyncio/sleep (rand)))
[1 2 3])
(defasync main2
[]
(println :result (await (count2))))
(asyncio/run (main2))
;; Async anonymous function
;;
;; An anonymous function can be passed as the first parameter to an instantiated[1] decorator.
;;
;; [1] If a decorator accepts arguments, it needs to be instantiated first with those arguments
;; before passing the anonymous function. In the example below, the decorator accepts a single argument,
;; so it is instantiated first, then the anonymous function is passed to it.
(def count3
((asyncdec/atimer "hey3")
(fn ^{:async true} _count3 []
(await (asyncio/sleep (rand)))
[1 2 3])))
(defasync main3
[]
(println :result (await (count3))))
(asyncio/run (main3))run:
> basilisp run issue.lpy
:result [1 2 3]
[hey2] function '__count2_113' executed in 0.11 seconds
:result [1 2 3]
[hey3] function '___count3_119' executed in 0.29 seconds
:result [1 2 3]