Skip to main content

Python client for Táillí License Manager - offline seat-based licensing

Project description

Táillí License Client for Python

A friendly Python client for the Táillí License Manager. Manage seats and API keys for offline/air-gapped deployments with minimal code.

Installation

pip install tailli-license

Or install from source:

cd license-manager/clients/python
pip install -e .

Quick Start

from tailli_license import LicenseClient

# Connect to your License Manager
client = LicenseClient(
    base_url="http://localhost:8787",
    product_id="my-product"
)

# Use a seat with automatic cleanup
with client.seat() as seat:
    print(f"Allocated seat: {seat.seat_id}")
    # Do your licensed work here
    run_my_application()
# Seat automatically released when done

Features

  • Simple context manager - Seats are automatically released, even on exceptions
  • Auto-refresh - Tokens are refreshed in the background, no manual work needed
  • Zero dependencies - Uses only Python standard library
  • Type hints - Full type annotations for IDE support
  • Friendly errors - Clear exceptions for common issues (seat limit, expired license)

Usage Examples

Check License Status

from tailli_license import LicenseClient

client = LicenseClient("http://localhost:8787", product_id="video-processor")

status = client.status()
print(f"Product: {status['productId']}")
print(f"Seats: {status['seatsInUse']}/{status['seats']} in use")
print(f"Expires: {status['expiry']}")
print(f"Features: {', '.join(status['features'])}")

Use a Seat (Recommended)

The context manager is the cleanest way to use seats:

from tailli_license import LicenseClient, SeatLimitError, LicenseExpiredError

client = LicenseClient("http://localhost:8787", product_id="video-processor")

try:
    with client.seat() as seat:
        print(f"Got seat {seat.seat_id}")

        # Your licensed code here
        process_videos()

except SeatLimitError:
    print("All seats are in use. Please try again later.")
except LicenseExpiredError:
    print("Your license has expired. Please contact support.")

Add Context for Auditing

Track who's using seats for compliance and debugging:

with client.seat(context={"user": "alice", "workstation": "lab-pc-42"}) as seat:
    run_analysis()

Manual Seat Management

For more control, manage seats directly:

# Allocate a seat
seat = client.allocate(context={"user": "bob"})

try:
    # Do work...
    for batch in data_batches:
        process(batch)
finally:
    # Always release when done
    seat.release()

Disable Auto-Refresh

For short operations, you might not need background refresh:

with client.seat(auto_refresh=False) as seat:
    quick_operation()  # Done in < 1 minute

Check Feature Entitlements

See what features your license enables:

entitlements = client.get_entitlements()

if "ai-enhancement" in entitlements["features"]:
    enable_ai_features()

if "batch-processing" in entitlements["features"]:
    enable_batch_mode()

print(f"API keys: {entitlements['keysIssued']}/{entitlements['maxKeys']} used")

Verify User API Keys

Validate JWTs issued by the License Manager:

try:
    payload = client.verify_key(user_provided_token)
    print(f"Valid token for product: {payload['productId']}")
    print(f"Features: {payload['features']}")
except LicenseError:
    print("Invalid or expired token")

Error Handling

The client provides specific exceptions for common scenarios:

from tailli_license import (
    LicenseClient,
    LicenseError,      # Base class for all errors
    SeatLimitError,    # All seats are in use
    LicenseExpiredError,  # License has expired
    ConnectionError,   # Can't reach License Manager
)

client = LicenseClient("http://localhost:8787", product_id="my-product")

try:
    with client.seat() as seat:
        do_work()
except SeatLimitError:
    # All seats taken - maybe queue the request or notify user
    queue_for_later()
except LicenseExpiredError:
    # License expired - notify admin
    notify_admin("License expired!")
except ConnectionError:
    # Can't reach LM - maybe it's down or network issue
    use_cached_state_or_fail_gracefully()
except LicenseError as e:
    # Other license-related errors
    log_error(f"License error: {e}")

Configuration

Client Options

client = LicenseClient(
    base_url="http://localhost:8787",  # License Manager URL
    product_id="my-product",            # Your product ID
    timeout=30.0,                       # Request timeout in seconds
)

Seat Options

with client.seat(
    context={"user": "alice"},  # Metadata for auditing
    auto_refresh=True,          # Keep token fresh (default: True)
) as seat:
    pass

Logging

Enable debug logging to see what's happening:

import logging

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("tailli_license")
logger.setLevel(logging.DEBUG)

Thread Safety

The LicenseClient is thread-safe. You can share one client across threads:

from concurrent.futures import ThreadPoolExecutor

client = LicenseClient("http://localhost:8787", product_id="my-product")

def worker(task_id):
    with client.seat(context={"task": task_id}) as seat:
        process_task(task_id)

with ThreadPoolExecutor(max_workers=4) as executor:
    executor.map(worker, range(10))

Each seat() call gets its own seat allocation.

API Reference

LicenseClient

Method Description
status() Get license status (seats, expiry, features)
allocate(context, auto_refresh) Allocate a seat, returns Seat object
seat(context, auto_refresh) Context manager for seat allocation
verify_key(token) Verify a user API key (JWT)
get_entitlements() Get feature entitlements

Seat

Method/Attribute Description
seat_id Unique identifier for this seat
token Current authentication token
expires_at Token expiration time
release() Release the seat back to the pool
refresh() Manually refresh the token

License

MIT License - see the main Táillí repository for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tailli_license-0.1.0.tar.gz (11.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tailli_license-0.1.0-py3-none-any.whl (8.4 kB view details)

Uploaded Python 3

File details

Details for the file tailli_license-0.1.0.tar.gz.

File metadata

  • Download URL: tailli_license-0.1.0.tar.gz
  • Upload date:
  • Size: 11.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for tailli_license-0.1.0.tar.gz
Algorithm Hash digest
SHA256 19d57bad04f23048ce801d5d5e1b25f7aed9332938d6f68b2d708822c89380bd
MD5 ebd4ebebffdae1e2ab167c13922976df
BLAKE2b-256 71e740bf46509a1722089a606a6cfaaac56d7b3b569202a9270eaed5bf6f0133

See more details on using hashes here.

Provenance

The following attestation bundles were made for tailli_license-0.1.0.tar.gz:

Publisher: publish-python-client.yml on RigrAI/tailli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tailli_license-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: tailli_license-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for tailli_license-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 da60a37702b26a972a3819dada6494cbb4c107beb5581ae7acedd52de1393022
MD5 792427118a2a397be855ab7c72eeda9d
BLAKE2b-256 c6c668b4b58739bb10831eddb0dbbd4a346048294ecf990b9ee357c0aef33ca8

See more details on using hashes here.

Provenance

The following attestation bundles were made for tailli_license-0.1.0-py3-none-any.whl:

Publisher: publish-python-client.yml on RigrAI/tailli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page