{"id":37069994,"url":"https://github.com/antct/qps-limit","last_synced_at":"2026-01-14T08:03:39.785Z","repository":{"id":175498402,"uuid":"653987945","full_name":"antct/qps-limit","owner":"antct","description":"Run asynchronous functions with a rate limit","archived":false,"fork":false,"pushed_at":"2025-08-07T13:17:56.000Z","size":58,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-01T03:59:45.018Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/antct.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2023-06-15T06:57:05.000Z","updated_at":"2025-08-07T13:18:00.000Z","dependencies_parsed_at":"2025-08-07T15:06:43.079Z","dependency_job_id":"30618c31-72a0-4d81-b65f-1c03d721163d","html_url":"https://github.com/antct/qps-limit","commit_stats":null,"previous_names":["antct/rate_limit","antct/rate-limit","antct/qps-limit"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/antct/qps-limit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antct%2Fqps-limit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antct%2Fqps-limit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antct%2Fqps-limit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antct%2Fqps-limit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/antct","download_url":"https://codeload.github.com/antct/qps-limit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antct%2Fqps-limit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28413527,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T05:26:33.345Z","status":"ssl_error","status_checked_at":"2026-01-14T05:21:57.251Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2026-01-14T08:03:39.331Z","updated_at":"2026-01-14T08:03:39.772Z","avatar_url":"https://github.com/antct.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"## QPS Limit\n\nRun asynchronous functions with a rate limit using `multiprocessing` + `asyncio`\n\nAvailable on Unix (i.e. Linux, MacOS) only, as the default multiprocessing start method is `fork`\n\n### Installation\n\n```bash\npip install qps-limit\n```\n\nor install manually via git\n\n```bash\ngit clone git://github.com/antct/qps-limit.git qps-limit\ncd qps-limit\npython setup.py install\n```\n\n### Usage\n\n```python\nfrom qps_limit import Limiter\n\nLimiter(\n    func=\"an asynchronous function\",\n    params=\"a generator function yields args and kwargs\",\n    callback=\"a callback function that handles the return values of func\",\n    num_workers=\"number of processes, recommended \u003c= number of CPUs\",\n    num_coroutines=\"number of coroutines per worker, adjust according to the situation\",\n    max_qps=\"maximum qps, None means unlimited\",\n    ordered=\"return ordered results or not, the default option is False\"\n)\n```\n\nBTW: The wrapped function returns tuples `(idx, res)` consisting of the data index and the function return value. If `ordered=False` is set, the order of the returned values may be randomized for better performance.\n\n### Quick Start\n\n\u003e 10 workers, each with 128 coroutines, printing \"hello world\" at a maximum qps of 100\n\n```python\nfrom qps_limit import Limiter\n\n\nasync def func(n: int):\n    return \"hello world {}\".format(n)\n\n\ndef params():\n    for n in range(1000):\n        yield (), {\"n\": n}\n\n\nf = Limiter(\n    func=func,\n    params=params,\n    num_workers=10,\n    num_coroutines=128,\n    max_qps=100,\n    ordered=False\n)\n\nfor idx, res in f():\n    print(idx, res)\n```\n\n```\nreceiver: 1000it [00:00, 1057032.26it/s]\nproducer: 100%|██████████████████████████████| 1000/1000 [00:11\u003c00:00, 101.96it/s]\nconsumer: 100%|██████████████████████████████| 1000/1000 [00:11\u003c00:00, 101.95it/s]\n```\n\n\u003e Call the echo interface of Postman with a maximum of 2 qps\n\n```python\nimport aiohttp\n\nfrom qps_limit import Limiter\n\n\nasync def func(n):\n    url = 'https://postman-echo.com/get?n={}'.format(n)\n    async with aiohttp.ClientSession() as session:\n        async with session.get(url) as resp:\n            data = await resp.json()\n            return data['args']['n']\n\n\ndef params():\n    for n in range(100):\n        yield (), {\"n\": n}\n\n\nf = Limiter(\n    func=func,\n    params=params,\n    num_workers=1,\n    num_coroutines=128,\n    max_qps=2,\n    ordered=False\n)\n\nfor idx, res in f():\n    print(idx, res)\n```\n\n\n### Best Practice\n\n\u003e Initialize resources that can not be pickled\n\n```python\nresource = None\n\nasync def func(n):\n    global resource\n    # with process lock\n    if resource is None:\n        resource = {}\n```\n\n\u003e Debugging code using only partial data\n\n```python\nLimiter(\n    ...,\n    max_steps=100\n)\n```\n\n\u003e Early termination if specific conditions are met\n\n```python\ni, max_i = 0, 100\nfor _, res in f():\n    if # condition:\n        if i \u003e max_i:\n            f.stop()\n            break\n        i += 1\n```\n\n\u003e Safely write files with multiple processes\n\n```python\nimport fcntl\n\nwriter = open('...', 'w+')\n\ndef callback(line):\n    global writer\n    fcntl.flock(writer, fcntl.LOCK_EX)\n    writer.write('{}\\n'.format(line))\n    writer.flush()\n    fcntl.flock(writer, fcntl.LOCK_UN)\n\nf = Limiter(\n    ...\n    callback=callback\n)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantct%2Fqps-limit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fantct%2Fqps-limit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantct%2Fqps-limit/lists"}