{"id":6970,"library":"aiometer","title":"aiometer: Concurrency Scheduler","description":"aiometer is a Python concurrency scheduling library, compatible with asyncio and trio. It makes it easier to execute many tasks concurrently while controlling concurrency limits and collecting results predictably. It is currently at version 1.0.0 and has a consistent release cadence with a focus on supporting recent Python and `anyio` versions.","status":"active","version":"1.0.0","language":"en","source_language":"en","source_url":"https://github.com/florimondmanca/aiometer","tags":["asyncio","trio","concurrency","scheduling","async-await","structured-concurrency"],"install":[{"cmd":"pip install aiometer","lang":"bash","label":"Install latest version"}],"dependencies":[{"reason":"Provides the underlying asynchronous backend (asyncio or trio) for concurrency primitives.","package":"anyio","optional":false}],"imports":[{"symbol":"run_all","correct":"from aiometer import run_all"},{"symbol":"amap","correct":"from aiometer import amap"},{"symbol":"run_on_each","correct":"from aiometer import run_on_each"},{"symbol":"run_any","correct":"from aiometer import run_any"}],"quickstart":{"code":"import asyncio\nimport functools\nimport aiometer\n\nasync def get_greeting(name: str) -> str:\n    \"\"\"Simulates an async operation, returning a greeting.\"\"\"\n    await asyncio.sleep(0.05) # Simulate I/O\n    return f\"Hello, {name}!\"\n\nasync def main():\n    names = [\"Alice\", \"Bob\", \"Charlie\", \"David\", \"Eve\"]\n\n    print(\"Running tasks with aiometer.run_all (ordered results):\")\n    # Use functools.partial to pass multiple arguments or wrap complex logic\n    greetings_ordered = await aiometer.run_all(\n        [functools.partial(get_greeting, name) for name in names],\n        max_at_once=2, # Limit to 2 concurrent tasks\n        max_per_second=5 # Limit task spawning rate\n    )\n    for greeting in greetings_ordered:\n        print(greeting)\n\n    print(\"\\nRunning tasks with aiometer.amap (unordered results as they become available):\")\n    async with aiometer.amap(get_greeting, names, max_at_once=2) as greetings_unordered:\n        async for greeting in greetings_unordered:\n            print(greeting)\n\nif __name__ == \"__main__\":\n    asyncio.run(main())","lang":"python","description":"This quickstart demonstrates how to use `aiometer.run_all` for ordered results and `aiometer.amap` for results as they become available. It also showcases applying concurrency limits (`max_at_once`, `max_per_second`) and how to adapt functions requiring multiple arguments using `functools.partial` for `aiometer`'s single-argument functions."},"warnings":[{"fix":"Upgrade your Python environment to 3.8 or a later supported version. aiometer 1.0.0 supports Python 3.8 to 3.13.","message":"aiometer dropped support for Python 3.7 starting from version 0.5.0, as it reached its End-of-Life. Ensure your environment uses Python 3.8 or newer.","severity":"breaking","affected_versions":">=0.5.0"},{"fix":"Ensure all parts of your codebase and its dependencies are compatible with `anyio` v3 (for aiometer 0.3.0+) or `anyio` v4 (for aiometer 0.5.0+). The latest `aiometer` (1.0.0) supports `anyio>=3.0,<5`.","message":"Upgrading aiometer to version 0.3.0 or higher requires `anyio` v3. Dependency mismatches may occur if your project or other dependencies are still on `anyio` v1 or v2.","severity":"breaking","affected_versions":">=0.3.0, <0.5.0 (for v3 requirement); >=0.5.0 (for v4 support)"},{"fix":"Use `functools.partial` to pre-fill arguments of your function or wrap your function with a proxy container type (like a `NamedTuple`) that bundles arguments into a single object.","message":"Functions passed to `aiometer.run_on_each` and `aiometer.amap` must accept only a single positional argument. If your asynchronous function requires multiple parameters, you need to refactor it.","severity":"gotcha","affected_versions":"*"},{"fix":"Review exception handling logic if you are directly type-checking `ExceptionGroup` instances, considering the change from `anyio`'s internal type to Python's native `ExceptionGroup` in modern Python/anyio versions.","message":"With `anyio` 4 (supported by `aiometer` 0.5.0+), native `ExceptionGroup` is used. If your code explicitly catches `anyio.ExceptionGroup` types from `anyio` 3.2+, you might need to adjust for the standard library's `ExceptionGroup`.","severity":"gotcha","affected_versions":">=0.5.0"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Pass the coroutine function itself or a `functools.partial` that can be called, not the result of calling it. For instance, `aiometer.run_all([my_async_func])` or `aiometer.run_all([functools.partial(my_async_func, arg)])` instead of `aiometer.run_all([my_async_func()])`.","cause":"Attempting to pass an already-called coroutine object (e.g., `my_async_func()`) instead of the callable coroutine function (`my_async_func`) to `aiometer` functions like `run_all` or `amap`.","error":"TypeError: object <async_function> is not callable"},{"fix":"Refactor your async function to accept a single argument (e.g., a data object) or use `functools.partial` to bind additional arguments before passing the partially applied function to `aiometer`.","cause":"The async function passed to `aiometer.run_on_each` or `aiometer.amap` expects more than one positional argument, but these `aiometer` functions only provide a single argument from the iterable.","error":"TypeError: <function_name> takes N positional arguments but M were given"},{"fix":"Ensure all errors raised within your asynchronous tasks inherit from `Exception` (or at least `BaseException`). Inspect the traceback to identify the source of the non-compliant 'exception' object.","cause":"An asynchronous task within `aiometer` encountered an exception that does not inherit from Python's `BaseException` hierarchy, or a non-exception object was raised. This can sometimes occur with poorly-behaved libraries or custom error types.","error":"RuntimeError: Task <Task ...> got an exception that doesn't inherit from BaseException."}]}