[Python-ideas] Allow creation of polymorph function (async function executable syncronously)

pylang pylang3 at gmail.com
Wed Mar 6 19:37:29 EST 2019


Jorropo states:

Polymorph function work exacly like async function BUT they assure of the
> ability to execute syncronously. - sic
>

Async functions can call sync functions, but not vice versa.  Consider a
third party solution - trio <https://github.com/python-trio/trio>, that
allows sync functions to call async functions.

>From the docs
<https://trio.readthedocs.io/en/latest/tutorial.html#async-functions>:

async def async_double(x):
    return 2 * x


trio.run(async_double, 3)    # returns 6

---

If you want a stdlib solution, let's revisit Nathaniel Smith's example:


> def maybe_async(fn):
>     @functools.wraps(fn)
>     def wrapper(*args, **kwargs):
>         coro = fn(*args, **kwargs)
>         if asyncio.get_running_loop() is not None:
>             return coro
>         else:
>             return await coro
>

I was unable to run his example as-is (in Python 3.6 at least) since the
`await` keyword is only permitted inside an `async def` function.  However,
the idea is intriguing and can be adapted.  See the example below.

Code:

def maybe_async(fn):
    async def _process(fn, *args, **kwargs):
        coro_fn = fn(*args, **kwargs)
        if asyncio.iscoroutinefunction(fn):
            return await coro_fn
        else:
            return coro_fn

    @functools.wraps(fn)
    def wrapper(*args, **kwarg):
        loop = asyncio.get_event_loop()
        res = loop.run_until_complete(_process(fn, *args, **kwarg))
        return res

    return wrapper

Demo:

@maybe_async
async def agreet(delay):
    print("hello")
    await asyncio.sleep(delay)
    print("world")


@maybe_async
def greet(delay):
    print("hello")
    time.sleep(delay)
    print("world")


agreet(2)    # prints hello world after 2 seconds
greet(1)    # print hello world after 1 second

Now you can call either sync or async functions like regular functions.
Hope this helps.

---


On Wed, Mar 6, 2019 at 12:54 AM Nathaniel Smith <njs at pobox.com> wrote:

> Defining a single polymorphic function is easy at the library level.
> For example, with asyncio:
>
> ----
>
> def maybe_async(fn):
>     @functools.wraps(fn)
>     def wrapper(*args, **kwargs):
>         coro = fn(*args, **kwargs)
>         if asyncio.get_running_loop() is not None:
>             return coro
>         else:
>             return await coro
>
> @maybe_async
> async def my_func(...):
>     ... use asyncio freely in here ...
>
> ----
>
> You can't do it at the language level though (e.g. with your proposed
> 'polymorph' keyword), because the language doesn't know whether an
> event loop is running or not.
>
> Extending this from a single function to a whole library API is
> substantially more complex, because you have to wrap every function
> and method, deal with __iter__ versus __aiter__, etc.
>
> -n
>
> On Tue, Mar 5, 2019 at 8:02 PM Jorropo . <jorropo.pgm at gmail.com> wrote:
> >
> > I was doing some async networking and I wondered, why I have to use 2
> different api for making the same things in async or sync regime.
> > Even if we make 2 perfectly identical api (except function will be sync
> and async), it will still 2 different code).
> >
> > So I first thinked to allow await in syncronous function but that create
> some problems (ex: an async function calling async.create_task) so if we
> allow that we have to asume to allways be in async regime (like js).
> >
> > Or we can differentiate async function wich can be awaited in syncronous
> regime, maybe with a new keyword (here I will use polymorph due to a lack
> of imagination but I find that one too long) ?
> >
> > So a polymorph function can be awaited in a syncronous function, and a
> polymorph function can only await polymorph functions.
> >
> > Polymorph function work exacly like async function BUT they assure of
> the ability to execute syncronously.
> > And in a syncronous regime if an await need to wait (like async.sleep or
> network operation), just wait (like the equivalent of this function in
> syncronous way).
> >
> > So why made that ?
> > To provide the same api for async and sync regime when its possible,
> example http api.
> > This allow to code less librairy.
> > Syncronous users can just use the librairy like any other sync lib (with
> the keyword await for executing but, personally, I think that is worth).
> > And asyncronous users can run multiples tasks using the same lib.
> > Moving from a regime to an other is simpler, source code size is reduced
> (doesn't need to create 2 api for the same lib), gain of time for the same
> reason.
> >
> > Also why it need to await in syncronous function, why not just execute
> polymorph function like any sync function while called in a sync function ?
> > Because we need to create runnable objects for async.run, ...
> >
> > So I would have your though about that, what can be improved, a better
> name for polymorph ?
> > _______________________________________________
> > Python-ideas mailing list
> > Python-ideas at python.org
> > https://mail.python.org/mailman/listinfo/python-ideas
> > Code of Conduct: http://python.org/psf/codeofconduct/
>
>
>
> --
> Nathaniel J. Smith -- https://vorpus.org
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20190306/40d64483/attachment-0001.html>


More information about the Python-ideas mailing list