{"id":21064025,"url":"https://github.com/luochen1990/easy-sync","last_synced_at":"2025-06-22T18:35:12.349Z","repository":{"id":252190448,"uuid":"839674832","full_name":"luochen1990/easy-sync","owner":"luochen1990","description":"Easily provide synchronous compatibility for your Python asynchronous functions","archived":false,"fork":false,"pushed_at":"2024-10-10T14:44:33.000Z","size":146,"stargazers_count":20,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-06T06:16:33.302Z","etag":null,"topics":["async","async-await","asynchronous","asyncio","dont-repeat-yourself","python","python3"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/luochen1990.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-08-08T05:23:42.000Z","updated_at":"2025-05-03T16:19:52.000Z","dependencies_parsed_at":"2025-05-16T02:34:55.166Z","dependency_job_id":"d4869944-5c41-4729-b121-502d54d761d2","html_url":"https://github.com/luochen1990/easy-sync","commit_stats":null,"previous_names":["luochen1990/easy-async","luochen1990/easy-sync"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/luochen1990/easy-sync","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luochen1990%2Feasy-sync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luochen1990%2Feasy-sync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luochen1990%2Feasy-sync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luochen1990%2Feasy-sync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/luochen1990","download_url":"https://codeload.github.com/luochen1990/easy-sync/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luochen1990%2Feasy-sync/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261344475,"owners_count":23144872,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","async-await","asynchronous","asyncio","dont-repeat-yourself","python","python3"],"created_at":"2024-11-19T17:48:02.759Z","updated_at":"2025-06-22T18:35:07.325Z","avatar_url":"https://github.com/luochen1990.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Easy Sync\n=========\n\n[![codecov](https://codecov.io/github/luochen1990/easy-sync/graph/badge.svg?token=OBG1BWIKC2)](https://codecov.io/github/luochen1990/easy-sync)\n\nEasily provide synchronous compatibility for your Python asynchronous functions\n\nMotivation\n----------\n\nEverytime I provide a Python package to my user, I'm expected to provide both sync version and async version API at the same time.\n\nBut I don't want to always duplicate implementations for both synchronous and asynchronous versions of the same thing, and think out two name for each of them, as it is a waste of time, and far from DRY.\n\nI hope there is some magic which can turn my asynchronous function implementations into synchronous versions, so I can write once and get both of them.\n\nI call this magic `@sync_compatible` here, your decorated function `f` can be called via both `await f(x)` (in a asynchronous context) or `f(x).wait()` (in a synchronous context).\n\n\nFeatures\n--------\n\n1. Expose a single function name for both async and sync version\n2. Automatic provide a sync version from your async function definition (via Metaprogramming)\n3. Lightweight, pure python, and no dependencies\n4. Complex cases such as list comprehensions, nested function definitions are also supported, feel free to write your pythonic code.\n5. Strict type annotation are contained, and validated by pylance the strict mode. all type information is kept here\n6. Unit tests contained, and test coverage ratio is monitored\n\nInstall\n-------\n\n```sh\npip install easy-sync\n```\nor\n\n```sh\npoetry add easy-sync\n```\n\nfrom ([pypi](https://pypi.org/project/easy-sync/))\n\nUsage\n-----\n\n\n### The Automatic Usage\n\n```python\nfrom easy_sync import sync_compatible\n\n@sync_compatible\nasync def async_add(a: int, b: int) -\u003e int:\n    await asyncio.sleep(1)\n    return a + b\n\nprint(async_add(1, 2).wait())\n```\n\nThis will generate a sync version code of your async function, the logic is:\n\n1. Replaces all `await f(...)` statements into `f(...).wait()`\n2. Replaces all `await asyncio.sleep(...)` statements into `time.sleep(...)`.\n\nFor other cases, you might need to define a wrapper for yourself, via [**The Manual Usage**](#the-manual-usage) of `@sync_compatible`\n\n**Tips**\n\n1. Extra decorators is ignored in the generated sync function, since they are written for async functions and probably not works on sync functions, keep them might cause unexpected error. If you really need them, please use [**The Manual Usage**](#the-manual-usage) and add decorators manually.\n2. If you got `.wait() method not found` issues when use the `@sync_compatible` decorator with extra decorators, try lift this outer\n\n\n### The Manual Usage\n\nInstead of automatic generate the sync version, you are allowed to provide the sync function manually, and expose a single name to users.\n\nThis is useful to define your own wrapper, or cover some special cases the automatic usage cannot handle.\n\n```python\nfrom easy_sync import sync_compatible\n\n@sync_compatible(sync_fn = _sync_fetch_url)\nasync def fetch_url(url: str) -\u003e str:\n    async with aiohttp.ClientSession() as session:\n        async with session.get(url) as response:\n            return await response.text()\n\ndef _sync_fetch_url(url: str) -\u003e str:\n    return requests.get(url).text\n\n@sync_compatible\nasync def get_data() -\u003e str:\n    return await fetch_url(\"https://site.name/path\")\n\nprint(get_data().wait())\n```\n\n\nRun tests and Contribute\n------------------------\n\nYou can use `nix develop .` or `poetry shell` under the project root to enter the develop environment.\n\nRun unit tests via `pytest`, or run `pytest --cov=src` for coverage report.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluochen1990%2Feasy-sync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluochen1990%2Feasy-sync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluochen1990%2Feasy-sync/lists"}