Async support

farmOS.py offers support for an async client if you need it. This is made possible by the HTTPX HTTP Python client that farmOS.py uses.

The async client may be more efficient when making many requests to the farmOS server and may be useful when working in an async Python framework.

Making Async requests

Below are simple examples of using the async client. For more information see the HTTPX Async documentation.

To make asynchronous requests, you'll need an AsyncFarmClient.

from farmOS import AsyncFarmClient

hostname, auth = farm_auth
async with AsyncFarmClient(hostname, auth=auth) as farm:
    # Get one page of animal assets.
    response = await farm.asset.get("animal")

Making requests

All of the standard client resource methods are async, so you should use response = await farm_client.resource.get() style for all of the following:

  • resource.get(entity_type, bundle, params)
  • resource.get_id(entity_type, bundle, id)
  • resource.iterate(entity_type, bundle, params)
  • resource.send(entity_type, bundle, payload)
  • resource.delete(entity_type, bundle, id)

Opening and closing clients

Use async with AsyncFarmClient() as client: if you want a context-managed client:

async with AsyncFarmClient() as client:
    ...

Alternatively, use await farm_client.aclose() if you want to close a client explicitly:

farm_client = AsyncFarmClient()
...
await farm_client.aclose()

Examples

See the following examples from the async Asset test (test_asset.py):

from farmOS import AsyncFarmClient

# Create a test asset
test_asset = {
    "type": "equipment",
    "payload": {
        "attributes": {
            "name": "Tractor",
            "manufacturer": "Allis-Chalmers",
            "model": "G",
            "serial_number": "1234567890",
        }
    },
}

async def test_asset_crud(farm_auth):
    hostname, auth = farm_auth
    async with AsyncFarmClient(hostname, auth=auth) as farm:
        post_response = await farm.asset.send(test_asset["type"], test_asset["payload"])
        assert "id" in post_response["data"]

        # Once created, add 'id' to test_asset
        test_asset["id"] = post_response["data"]["id"]

        # Get the asset by ID.
        get_response = await farm.asset.get_id(test_asset["type"], test_asset["id"])

        # Assert that both responses have the correct values.
        for response in [post_response, get_response]:
            for key, value in test_asset["payload"]["attributes"].items():
                assert response["data"]["attributes"][key] == value

        test_asset_changes = {
            "id": test_asset["id"],
            "attributes": {
                "name": "Old tractor",
                "status": "archived",
            },
        }

        # Update the asset.
        patch_response = await farm.asset.send(test_asset["type"], test_asset_changes)
        # Get the asset by ID.
        get_response = await farm.asset.get_id(test_asset["type"], test_asset["id"])

        # Assert that both responses have the correct values.
        for response in [patch_response, get_response]:
            for key, value in test_asset_changes["attributes"].items():
                assert response["data"]["attributes"][key] == value

        # Delete the asset.
        deleted_response = await farm.asset.delete(test_asset["type"], test_asset["id"])
        assert deleted_response.status_code == 204


async def test_asset_get(farm_auth, test_assets):
    hostname, auth = farm_auth
    async with AsyncFarmClient(hostname, auth=auth) as farm:
        # Get one page of assets.
        response = await farm.asset.get(test_asset["type"])
        assert "data" in response
        assert "links" in response
        assert len(response["data"]) == 50

        # Get all assets.
        all_assets = [asset async for asset in farm.asset.iterate(test_asset["type"])]
        assert len(all_assets) > len(response["data"])