CrossSync provides a simple way to share logic between async and sync code. It is made up of a small library that provides:
- a set of shims that provide a shared sync/async API surface
- annotations that are used to guide generation of a sync version from an async class
Using CrossSync, the async code is treated as the source of truth, and sync code is generated from it.
Many Asyncio components have direct, 1:1 threaded counterparts for use in non-asyncio code. CrossSync provides a compatibility layer that works with both
| CrossSync | Asyncio Version | Sync Version |
|---|---|---|
| CrossSync.Queue | asyncio.Queue | queue.Queue |
| CrossSync.Condition | asyncio.Condition | threading.Condition |
| CrossSync.Future | asyncio.Future | Concurrent.futures.Future |
| CrossSync.Task | asyncio.Task | Concurrent.futures.Future |
| CrossSync.Event | asyncio.Event | threading.Event |
| CrossSync.Semaphore | asyncio.Semaphore | threading.Semaphore |
| CrossSync.Awaitable | typing.Awaitable | typing.Union (no-op type) |
| CrossSync.Iterable | typing.AsyncIterable | typing.Iterable |
| CrossSync.Iterator | typing.AsyncIterator | typing.Iterator |
| CrossSync.Generator | typing.AsyncGenerator | typing.Generator |
| CrossSync.Retry | google.api_core.retry.AsyncRetry | google.api_core.retry.Retry |
| CrossSync.StopIteration | StopAsyncIteration | StopIteration |
| CrossSync.Mock | unittest.mock.AsyncMock | unittest.mock.Mock |
Custom aliases can be added using CrossSync.add_mapping(class, name)
Additionally, CrossSync provides method implementations that work equivalently in async and sync code:
CrossSync.sleep()CrossSync.gather_partials()CrossSync.wait()CrossSync.condition_wait()CrossSync.event_wait()CrossSync.create_task()CrossSync.retry_target()CrossSync.retry_target_stream()
CrossSync provides a set of annotations to mark up async classes, to guide the generation of sync code.
@CrossSync.convert_sync- marks classes for conversion. Unmarked classes will be copied as-is
- if add_mapping is included, the async and sync classes can be accessed using a shared CrossSync.X alias
@CrossSync.convert- marks async functions for conversion. Unmarked methods will be copied as-is
@CrossSync.drop- marks functions or classes that should not be included in sync output
@CrossSync.pytest- marks test functions. Test functions automatically have all async keywords stripped (i.e., rm_aio is unneeded)
CrossSync.add_mapping- manually registers a new CrossSync.X alias, for custom types
CrossSync.rm_aio- Marks regions of the code that include asyncio keywords that should be stripped during generation
Generation can be initiated using nox -s generate_sync
from the root of the project. This will find all classes with the __CROSS_SYNC_OUTPUT__ = "path/to/output"
annotation, and generate a sync version of classes marked with @CrossSync.convert_sync at the output path.
There is a unit test at tests/unit/data/test_sync_up_to_date.py that verifies that the generated code is up to date
CrossSync is made up of two parts:
- the runtime shims and annotations live in
/google/cloud/aio/_cross_sync - the code generation logic lives in
/.cross_sync/in the repo root