Skip to content

A set of tools to create hybrid sync/async interfaces.

License

Notifications You must be signed in to change notification settings

parafoxia/Xsync

Xsync

PyPi version PyPI - Status Downloads GitHub last commit License

CI Maintainability Test Coverage

A set of tools to create hybrid sync/async interfaces.

CPython versions 3.7 through 3.11-dev and PyPy versions 3.7 through 3.9 are officially supported.

Windows, MacOS, and Linux are all supported.

What does Xsync do?

Xsync allows developers to create dynamic interfaces which can be run in sync or async contexts.

In simple terms, it makes this possible:

result = my_function()
result = await my_function()

How neat is that?!

Usage

The above behaviour can be achieved with the as_hybrid decorator:

@xsync.as_hybrid()
def my_function():
    ...

This sets my_function up as a "hybrid callable", which is capable of being run both synchronously and asynchronously. However, we also need to set an async implementation for my_function for it to work. We can do this using the set_async_impl decorator:

@xsync.set_async_impl(my_function)
async def my_async_function():
    ...

Doing this tells Xsync to run this function instead of my_function when awaiting:

my_function()        # runs as normal
await my_function()  # calls `my_async_function` instead

Don't worry, my_async_function can still be run directly, as you'd expect.

It also works on methods, class methods, and static methods:

class MyClass:
    @xsync.as_hybrid()
    def my_method(self):
        ...

    @xsync.set_async_impl(my_method)
    async def my_async_method(self):
        ...

    @classmethod
    @xsync.as_hybrid()
    def my_class_method(cls):
        ...

    @classmethod
    @xsync.set_async_impl(my_class_method)
    async def my_async_class_method(cls):
        ...

    @staticmethod
    @xsync.as_hybrid()
    def my_static_method(cls):
        ...

    @staticmethod
    @xsync.set_async_impl(my_static_method)
    async def my_async_static_method(cls):
        ...

The above is the newer (and better) of two available implementations.

View the old implementation

The above behaviour can be achieved with the maybe_async decorator:

@xsync.maybe_async()
def my_function():
    ...

This sets my_function up as a "hybrid callable", which is capable of being run both synchronously and asynchronously. However, we also need to set an async implementation for my_function for it to work. We can do this by creating another function of the same name, but with an _async_ prefix:

async def _async_my_function():
    ...

Xsync searches for a function with the name of the original function prefixed by _async_ at runtime, and runs this instead when awaiting:

my_function()        # runs as normal
await my_function()  # calls `_async_my_function` instead

It also works on methods and class methods:

class MyClass:
    @xsync.maybe_async()
    def my_method(self):
        ...

    async def _async_my_method(self):
        ...

    @classmethod
    @xsync.maybe_async()
    def my_class_method(cls):
        ...

    @classmethod
    async def _async_my_class_method(cls):
        ...

This implementation does not work with static methods.

Contributing

Contributions are very much welcome! To get started:

License

The Xsync module for Python is licensed under the BSD 3-Clause License.