Skip to main content

Client

Overview

The DiracX client is a comprehensive toolset designed to interact with various services. It consists of three main components:

┌────────┐
│ client │
└────┬───┘
┌──▼──┐
│ api │
└──┬──┘
┌──▼──┐
│ cli │
└─────┘
  1. diracx-client: A client library generated from OpenAPI specifications.
  2. diracx-api: A Python API to interact with services using the diracx-client.
  3. diracx-cli: A command-line interface for direct interaction with the services.

diracx-client

The diracx-client is an auto-generated client library that facilitates communication with services defined by OpenAPI specifications.

Generating a Client

The client is generated using AutoRest, a tool that reads OpenAPI configurations provided by FastAPI routers.

  • Breaking Changes: Each time there is a breaking change in a router, the client needs to be regenerated.

Updating the Client

The CI/CD pipeline handles client regeneration upon each push to the main branch. This process helps detect breaking changes in the developer's code, causing the CI/CD to fail if such changes are present.

If a breaking change is acknowledged and approved, one of the repo admin will regenerate the client on behalf of the developer. Developers can still manually regenerate the client but it requires a few additional tools. The best up-to-date documentation lies in the client-generation CI job.

Structure of the Generated Client

The generated client consists of several key components:

  • models: Represent the data structures.
  • operations: Contain the methods to interact with the API endpoints.
  • aio: Asynchronous client.

Further details can be found in the Python Autorest documentation.

Customising the Generated Client

Modifications to the generated client should be made in _patch.py files to ensure maintainability. Detailed guidance can be found in Python Autorest documentation.

Note: any modification in the synchronous client should also be performed in the asynchronous client (aio), and vice-versa.

Example Usage

Operations are accessible via the DiracClient, which manages token refreshment:

from diracx.client.aio import DiracClient

async with DiracClient() as client:
jobs = await client.jobs.submit_bulk_jobs([x.read() for x in jdl])

Configuring the Generated Client

Clients need to be configured to interact with services. This is performed through DiracxPreferences, which is a BaseSettings Pydantic model that load configuration from the environment.

Configuring a client

Required environment variables to interact with the services:

  • DIRACX_URL: the URL pointing to diracx services
  • DIRACX_CA_PATH: CA path used by the diracx services

Optional environment variables:

  • DIRACX_OUTPUT_FORMAT: output format (e.g. JSON). Default value depends whether the output stream is associated to a terminal.
  • DIRACX_LOG_LEVEL: logging level (e.g. ERROR). Defaults to INFO.
  • DIRACX_CREDENTIALS_PATH: path where access and refresh tokens are stored. Defaults to ~/.cache/diracx/credentials.json.

Getting preferences

Developers can get access to the preferences through the following method:

from diracx.core.preferences import get_diracx_preferences

...

credentials_path = get_diracx_preferences().credentials_path

Note: preferences are cached.

diracx-api

The diracx-api provides a Python API for interacting with services, leveraging the diracx-client.

API Methods

API methods are located in diracx-api/src/diracx/api/. To create an API method:

  1. Import DiracClient.
  2. Decorate the method with @with_client to handle client configuration.
  3. Pass the client as a keyword argument.

Example

from diracx.client.aio import DiracClient
from .utils import with_client

@with_client
async def create_sandbox(paths: list[Path], *, client: DiracClient) -> str:
...

In this example, paths are the parameters of the API. The @with_client decorator allows the method to be called without manually managing the client:

# Managed by @with_client
# Useful for basic work requiring a single call to the service
result = await create_sandbox(paths)

# For optimised performance with multiple service interactions
async with DiracClient() as client:
result = await create_sandbox(paths, client)

diracx-cli

The diracx-cli is a command-line interface built on diracx-client and diracx-api for direct interaction with services. It uses Typer for creating CLI commands and Rich for enhanced content display.

CLI Commands

CLI commands are located in diracx-cli/src/diracx/cli/. To create a CLI command:

  1. Import DiracClient and/or the diracx API.
  2. Import utils.AsyncTyper.
  3. Use the @app.async_command decorator to define commands.

Example

from .utils import AsyncTyper
from diracx.client.aio import DiracClient

app = AsyncTyper()

@app.async_command()
async def submit(jdl: list[FileText]):
async with DiracClient() as client:
...

For more details on Typer and Rich options, refer to their Typer documentation and Rich documentation.

Associating Commands and Subcommands

  • Commands without subcommands (e.g., dirac login) should be implemented directly in src/diracx/__init__.py and decorated with app.async_command().

  • Commands with subcommands (e.g., dirac jobs submit) should have their own modules in src/diracx/<command> and use AsyncTyper.

    • To associate the command with dirac, import the module in src/diracx/__init__.py:
    from . import <command>
    ...

    app.add_typer(<command name>.app, name="<command name>")

Users can then call the CLI:

$ dirac <command>
$ dirac <command> <subcommand> [--options]