ada package

Subpackages

Submodules

ada.api module

Low-level HTTP client for the dCache REST API.

Wraps httpx and provides: - URL encoding of dCache paths - JSON response parsing - HTTP status code to exception mapping - Debug logging of all requests - SSE streaming support

class ada.api.DcacheAPI(base_url: str, auth: AuthProvider, debug: bool = False)

Bases: object

Low-level HTTP client for the dCache REST API.

All service modules use this client to make API calls. It handles authentication, error mapping, and optional debug logging.

close() None

Close the underlying HTTP client.

delete(endpoint: str) Any

Perform a DELETE request.

static encode_path(path: str) str

URL-encode a dCache namespace path.

get(endpoint: str, params: dict[str, str] | None = None, accept: str = 'application/json') Any

Perform a GET request.

patch(endpoint: str, json: dict[str, Any] | None = None) Any

Perform a PATCH request.

post(endpoint: str, json: dict[str, Any] | None = None, data: str | None = None, content_type: str = 'application/json', accept: str = 'application/json') Any

Perform a POST request.

post_raw(endpoint: str, json: dict[str, Any] | None = None) Response

POST that returns the raw httpx.Response (for bulk requests needing headers).

stream_sse(endpoint: str, last_event_id: str | None = None, timeout: int = 3600) Iterator[dict[str, str]]

Open an SSE connection and yield parsed events.

Each yielded dict has keys: event, data, id (all optional).

ada.auth module

Authentication providers for ADA.

Implements the strategy pattern with an abstract AuthProvider base and concrete implementations for token, netrc, and proxy authentication.

Precedence (matching Bash):
  1. Explicit arguments (highest)

  2. Environment variables

  3. Config file values (lowest)

class ada.auth.AuthProvider

Bases: ABC

Abstract base for all authentication methods.

get_httpx_auth() Any

Return an httpx auth object, if applicable (e.g., BasicAuth).

get_ssl_context() Any

Return an SSL context for client certificate auth, if applicable.

abstractmethod headers() dict[str, str]

Return HTTP headers for authentication.

abstractmethod method_name() str

Human-readable name like ‘token’, ‘netrc’, ‘proxy’.

validate(command: str | None = None) None

Validate credentials before use. Override in subclasses.

view_token() dict[str, Any]

Decode and return token properties for display.

class ada.auth.NetrcAuth(netrcfile: str | None = None, hostname: str | None = None)

Bases: AuthProvider

Netrc-based username/password (Basic) authentication.

get_httpx_auth() Any

Parse netrc and return httpx BasicAuth for the API host.

headers() dict[str, str]

Return HTTP headers for authentication.

method_name() str

Human-readable name like ‘token’, ‘netrc’, ‘proxy’.

class ada.auth.ProxyAuth(proxyfile: str | None = None, certdir: str | None = None, igtf: bool = True)

Bases: AuthProvider

X.509 proxy certificate authentication.

get_ssl_context() Any

Return an SSL context configured with the proxy certificate.

headers() dict[str, str]

Return HTTP headers for authentication.

method_name() str

Human-readable name like ‘token’, ‘netrc’, ‘proxy’.

class ada.auth.TokenAuth(token: str, source: str = 'direct')

Bases: AuthProvider

Bearer token authentication (JWT/OIDC or Macaroon).

headers() dict[str, str]

Return HTTP headers for authentication.

method_name() str

Human-readable name like ‘token’, ‘netrc’, ‘proxy’.

validate(command: str | None = None) None

Validate credentials before use. Override in subclasses.

view_token() dict[str, Any]

Decode and return token properties for display.

class ada.auth.TokenFileAuth(tokenfile: str)

Bases: TokenAuth

Reads token from a file (rclone config format or plain bearer token).

ada.auth.decode_jwt(token: str) dict[str, Any]

Decode a JWT and return payload with human-readable timestamps.

Converts exp, nbf, and iat fields from Unix timestamps to ISO 8601 strings (matching the Bash jq .exp |= todate behavior).

ada.auth.decode_jwt_payload(token: str) dict[str, Any]

Decode the payload section of a JWT token.

Does NOT verify the signature — this is for inspection only, matching the Bash version’s behavior.

Returns:

Decoded payload as a dict.

Raises:

AdaAuthError: If the token cannot be decoded.

ada.auth.decode_macaroon(token: str) dict[str, str]

Decode a Macaroon and return its properties as a dict.

Parses the decoded text for known fields like before:, activity:, path:, id:, etc.

ada.auth.decode_macaroon_raw(token: str) str

Decode a Macaroon token to its raw text representation.

Replicates the Bash decoding logic:

base64 -d | awk ‘{print substr($0, 5)}’ | grep -v ‘signature’ | tr -d ‘0’

Returns:

Decoded macaroon text (caveats, etc.).

Raises:

AdaAuthError: If the token cannot be decoded.

ada.auth.extract_macaroon_expiry(decoded_text: str) int | None

Extract the expiration timestamp from decoded Macaroon text.

Looks for before: caveat with ISO 8601 timestamp.

Returns:

Unix timestamp as int, or None if not found.

ada.auth.get_jwt_expiry(token: str) int | None

Extract the expiration timestamp (exp) from a JWT.

Returns:

Unix timestamp as int, or None if not present.

ada.auth.get_jwt_scope(token: str) str

Extract the scope claim from a JWT.

Returns:

The scope string, or empty string if not present.

ada.auth.is_jwt(token: str) bool

Check if a token looks like a JWT (three dot-separated base64url parts).

ada.auth.is_macaroon(token: str) bool

Check if a token looks like a Macaroon (not a JWT).

ada.auth.resolve_auth(token: str | None = None, tokenfile: str | None = None, netrc: str | None = None, proxy: str | None = None, config: AdaConfig | None = None) AuthProvider

Resolve authentication method from args, env vars, and config.

Precedence (from high to low):
  1. Explicit arguments (token, tokenfile, netrc, proxy)

  2. Environment variables ($BEARER_TOKEN, $ada_tokenfile, $ada_netrcfile)

  3. Config file values (tokenfile, netrcfile)

Raises:

AdaAuthError: If no authentication method can be resolved.

ada.auth.validate_token(token: str, source: str = '', command: str | None = None) None

Validate a token (JWT or Macaroon) for expiry and permissions.

Args:

token: The raw token string. source: Description of where the token came from (for error messages). command: The command being executed (e.g., “stage”), used to check

command-specific permissions.

Raises:

AdaTokenExpiredError: If the token has expired or is about to expire. AdaTokenPermissionError: If the token lacks required permissions. AdaAuthError: If the token cannot be decoded.

ada.client module

AdaClient: main entry point for using ADA as a library.

Composes all services and provides a high-level interface for interacting with dCache. Fully independent of the CLI layer.

Usage:

from ada import AdaClient

with AdaClient(api="https://...", tokenfile="/path/to/token") as client:
    files = client.list("/pnfs/data/mydir")
    client.stage("/pnfs/data/mydir/file.dat", lifetime="7D")
    info = client.whoami()
class ada.client.AdaClient(api: str | None = None, tokenfile: str | None = None, token: str | None = None, netrc: str | None = None, proxy: str | None = None, igtf: bool = True, config_paths: list[str] | None = None, debug: bool = False)

Bases: object

High-level client for the dCache REST API.

Can be used as a context manager for automatic cleanup:

with AdaClient(...) as client:
    client.list("/data")
checksum(paths: str | list[str] | None = None, recursive: bool = False, from_file: str | None = None) list[Checksum]

Get checksums for file(s).

close() None

Close the underlying HTTP client.

delete(path: str, recursive: bool = False, force: bool = False) None

Delete a file or directory.

delete_request(request_id: str) None

Delete a bulk request.

find_label(path: str, regex: str, recursive: bool = False) list[tuple[str, list[str]]]

Find files with labels matching a regex.

find_xattr(path: str, key: str, regex: str, recursive: bool = False, all_keys: bool = False) list[tuple[str, dict[str, str]]]

Find files with extended attributes matching a regex.

list(path: str) list[str]

List directory contents.

list_labels(path: str, label: str | None = None) list[str]

List labels attached to a file.

list_xattr(path: str, key: str | None = None) dict[str, str]

List extended attributes of a file.

longlist(paths: str | list[str] | None = None, from_file: str | None = None) list[FileInfo]

Get detailed file listing.

mkdir(path: str, recursive: bool = False) str

Create a directory.

mv(source: str, destination: str) str

Move or rename a file/directory.

quota() list[QuotaInfo]

Get storage quota info.

remove_label(path: str, label: str = '', all_labels: bool = False) str

Remove label(s) from a file.

remove_xattr(path: str, key: str = '', all_keys: bool = False) str

Remove extended attribute(s) from a file.

set_label(path: str, label: str) str

Attach a label to a file.

set_xattr(path: str, attributes: dict[str, str] | str) str

Set extended attributes on a file.

space(poolgroup: str | None = None) SpaceInfo | list[str]

Get storage space info.

stage(paths: str | list[str] | None = None, recursive: bool = False, lifetime: str = '7D', from_file: str | None = None) BulkRequest

Stage files from tape to disk.

stat(path: str) FileInfo

Get complete file/directory metadata.

stat_request(request_id: str) BulkRequestStatus

Get bulk request status.

unstage(paths: str | list[str] | None = None, recursive: bool = False, request_id: str | None = None, from_file: str | None = None) BulkRequest

Unstage files — release disk pins.

view_token() dict

Decode and display the current token.

whoami() UserInfo

Get authenticated user identity.

ada.config module

Configuration loading for ADA.

Supports the same key=value file format as the Bash version so that existing ~/.ada/ada.conf files continue to work without changes.

Precedence (lowest to highest):
  1. Bundled default config (<package>/etc/ada.conf)

  2. System-wide config (/etc/ada.conf)

  3. User config (~/.ada/ada.conf)

  4. Environment variables (ada_api, ada_debug, etc.)

  5. Constructor arguments (handled by caller)

class ada.config.AdaConfig(api: str = '', debug: bool = False, igtf: bool = True, channel_timeout: int = 3600, tokenfile: str | None = None, netrcfile: str | None = None, curl_options: list[str] = <factory>)

Bases: object

Configuration for ADA, assembled from files + env + CLI args.

api: str = ''
channel_timeout: int = 3600
curl_options: list[str]
debug: bool = False
igtf: bool = True
netrcfile: str | None = None
tokenfile: str | None = None
validate() None

Validate configuration values.

Raises:

AdaConfigError: If the API URL is set but has an invalid format.

ada.config.load_config(paths: list[str] | None = None) AdaConfig

Load configuration from files and environment variables.

Args:
paths: Optional list of config file paths to search.

If not provided, uses default search paths.

Returns:

Populated AdaConfig instance.

ada.exceptions module

Exception hierarchy for ADA.

All exceptions inherit from AdaError, allowing callers to catch broad or specific error categories as needed.

exception ada.exceptions.AdaAPIError(message: str, status_code: int = 0, response_body: str = '')

Bases: AdaError

dCache API returned an error.

exception ada.exceptions.AdaAuthError

Bases: AdaError

Authentication setup error (missing file, invalid format).

exception ada.exceptions.AdaAuthenticationError

Bases: AdaError

Server rejected credentials (HTTP 401).

exception ada.exceptions.AdaConfigError

Bases: AdaError

Configuration is missing or invalid.

exception ada.exceptions.AdaError

Bases: Exception

Base exception for all ADA errors.

exception ada.exceptions.AdaForbiddenError(message: str, response_body: str = '')

Bases: AdaAPIError

Access forbidden (HTTP 403).

exception ada.exceptions.AdaNotFoundError(message: str)

Bases: AdaAPIError

Resource not found (HTTP 404).

exception ada.exceptions.AdaPathError

Bases: AdaError

Invalid path or path type mismatch.

exception ada.exceptions.AdaSecurityError

Bases: AdaError

Security violation (e.g., world-readable credentials).

exception ada.exceptions.AdaTokenExpiredError(message: str, seconds_ago: int = 0)

Bases: AdaAuthError

Token has expired or is about to expire.

exception ada.exceptions.AdaTokenPermissionError

Bases: AdaAuthError

Token lacks required permissions (e.g., storage.stage).

exception ada.exceptions.AdaValidationError

Bases: AdaError

Input validation error.

ada.models module

Data models for ADA.

All models are frozen dataclasses representing dCache API entities.

class ada.models.BulkRequest(request_id: str, request_url: str, activity: str, targets: tuple[str, ...] = ())

Bases: object

Result of a bulk (stage/unstage) operation.

activity: str
request_id: str
request_url: str
targets: tuple[str, ...] = ()
class ada.models.BulkRequestStatus(uid: str, status: str, targets: tuple[dict, ...]=(), raw: dict = <factory>)

Bases: object

Status of a bulk request.

raw: dict
status: str
targets: tuple[dict, ...] = ()
uid: str
class ada.models.Channel(channel_id: str, channel_url: str, name: str | None = None, timeout: int | None = None)

Bases: object

SSE event channel.

channel_id: str
channel_url: str
name: str | None = None
timeout: int | None = None
class ada.models.Checksum(path: str, checksum_type: str, value: str)

Bases: object

File checksum.

checksum_type: str
path: str
value: str
class ada.models.FileInfo(path: str, file_type: FileType, size: int | None = None, mtime: datetime | None = None, pnfs_id: str | None = None, current_qos: str | None = None, target_qos: str | None = None, locality: Locality | None = None, labels: tuple[str, ...]=(), extended_attributes: dict[str, str]=<factory>, checksums: tuple[~ada.models.Checksum, ...]=())

Bases: object

Represents a file or directory in dCache.

checksums: tuple[Checksum, ...] = ()
current_qos: str | None = None
extended_attributes: dict[str, str]
file_type: FileType
labels: tuple[str, ...] = ()
locality: Locality | None = None
mtime: datetime | None = None
path: str
pnfs_id: str | None = None
size: int | None = None
target_qos: str | None = None
class ada.models.FileType(*values)

Bases: str, Enum

Filetype in dCache.

DIR = 'DIR'
REGULAR = 'REGULAR'
class ada.models.Locality(*values)

Bases: str, Enum

Locality of file in dCache.

NEARLINE = 'NEARLINE'
ONLINE = 'ONLINE'
ONLINE_AND_NEARLINE = 'ONLINE_AND_NEARLINE'
UNAVAILABLE = 'UNAVAILABLE'
class ada.models.QuotaInfo(quota_type: str, id: int, custodial: int, custodial_limit: int | None, replica: int, replica_limit: int | None)

Bases: object

Storage quota information.

custodial: int
custodial_limit: int | None
id: int
quota_type: str
replica: int
replica_limit: int | None
class ada.models.SSEEvent(event_type: str, event_id: str | None = None, path: str | None = None, object_name: str | None = None, mask: str | None = None, cookie: str | None = None, raw_data: dict | None = None)

Bases: object

A parsed Server-Sent Event.

cookie: str | None = None
event_id: str | None = None
event_type: str
mask: str | None = None
object_name: str | None = None
path: str | None = None
raw_data: dict | None = None
class ada.models.SpaceInfo(total: int, free: int, precious: int, removable: int)

Bases: object

Pool group space information.

free: int
precious: int
removable: int
total: int
class ada.models.Subscription(subscription_id: str, event_type: str, path: str)

Bases: object

SSE event subscription within a channel.

event_type: str
path: str
subscription_id: str
class ada.models.UserInfo(status: str, uid: int | None = None, gids: tuple[int, ...]=(), username: str | None = None, home: str | None = None, root: str | None = None, raw: dict = <factory>)

Bases: object

User identity from whoami.

gids: tuple[int, ...] = ()
home: str | None = None
raw: dict
root: str | None = None
status: str
uid: int | None = None
username: str | None = None

ada.utils module

Utility functions for ADA.

Provides URL encoding, file permission checks, JSON conversion helpers, and human-readable size formatting.

ada.utils.check_config_permissions(filepath: str) None

Verify a config file is not world-writable.

Config files are allowed to be world-readable but not world-writable.

ada.utils.check_file_permissions(filepath: str, *, check_readable: bool = True) None

Verify a file is not world-readable or world-writable.

Replicates the Bash security checks on token files, netrc files, and config files.

Args:

filepath: Path to check. check_readable: If True, also checks world-readable bit.

Raises:

AdaSecurityError: If the file has insecure permissions. FileNotFoundError: If the file does not exist.

ada.utils.encode_path(path: str) str

URL-encode a dCache namespace path.

Equivalent to the Bash urlencode function (jq -sRr @uri). Encodes all characters including / so the entire path becomes a single URL path segment (e.g., /pnfs/data%2Fpnfs%2Fdata).

ada.utils.human_readable_size(size_bytes: int) str

Convert bytes to a human-readable string (e.g., ‘1.5 GiB’).

ada.utils.normalize_path(path: str) str

Normalize a dCache path by removing trailing slashes and double slashes.

ada.utils.parse_lifetime(lifetime_str: str) tuple[int, str]

Parse a lifetime string like ‘7D’, ‘24H’, ‘30M’ into (value, unit).

Returns:

Tuple of (numeric_value, unit_character).

Raises:

AdaValidationError: If the format is invalid.

ada.utils.read_file_list(filepath: str) list[str]

Read a file containing one path per line.

Strips whitespace and ignores empty lines and comments (lines starting with #).

ada.utils.resolve_paths(paths: str | list[str] | None = None, from_file: str | None = None) list[str]

Resolve paths from arguments or file list.

ada.utils.to_json(input_str: str) dict[str, str]

Convert various metadata formats to a JSON dict.

Supports: - JSON format: {"key": "value"} - key=value pairs (one per line, or comma-separated) - Tab-separated key/value pairs

Replicates the Bash to_json function.

Module contents

ADA - Advanced dCache API tool

class ada.AdaClient(api: str | None = None, tokenfile: str | None = None, token: str | None = None, netrc: str | None = None, proxy: str | None = None, igtf: bool = True, config_paths: list[str] | None = None, debug: bool = False)

Bases: object

High-level client for the dCache REST API.

Can be used as a context manager for automatic cleanup:

with AdaClient(...) as client:
    client.list("/data")
checksum(paths: str | list[str] | None = None, recursive: bool = False, from_file: str | None = None) list[Checksum]

Get checksums for file(s).

close() None

Close the underlying HTTP client.

delete(path: str, recursive: bool = False, force: bool = False) None

Delete a file or directory.

delete_request(request_id: str) None

Delete a bulk request.

find_label(path: str, regex: str, recursive: bool = False) list[tuple[str, list[str]]]

Find files with labels matching a regex.

find_xattr(path: str, key: str, regex: str, recursive: bool = False, all_keys: bool = False) list[tuple[str, dict[str, str]]]

Find files with extended attributes matching a regex.

list(path: str) list[str]

List directory contents.

list_labels(path: str, label: str | None = None) list[str]

List labels attached to a file.

list_xattr(path: str, key: str | None = None) dict[str, str]

List extended attributes of a file.

longlist(paths: str | list[str] | None = None, from_file: str | None = None) list[FileInfo]

Get detailed file listing.

mkdir(path: str, recursive: bool = False) str

Create a directory.

mv(source: str, destination: str) str

Move or rename a file/directory.

quota() list[QuotaInfo]

Get storage quota info.

remove_label(path: str, label: str = '', all_labels: bool = False) str

Remove label(s) from a file.

remove_xattr(path: str, key: str = '', all_keys: bool = False) str

Remove extended attribute(s) from a file.

set_label(path: str, label: str) str

Attach a label to a file.

set_xattr(path: str, attributes: dict[str, str] | str) str

Set extended attributes on a file.

space(poolgroup: str | None = None) SpaceInfo | list[str]

Get storage space info.

stage(paths: str | list[str] | None = None, recursive: bool = False, lifetime: str = '7D', from_file: str | None = None) BulkRequest

Stage files from tape to disk.

stat(path: str) FileInfo

Get complete file/directory metadata.

stat_request(request_id: str) BulkRequestStatus

Get bulk request status.

unstage(paths: str | list[str] | None = None, recursive: bool = False, request_id: str | None = None, from_file: str | None = None) BulkRequest

Unstage files — release disk pins.

view_token() dict

Decode and display the current token.

whoami() UserInfo

Get authenticated user identity.