Skip to content

Getting started

Requirements

  • Python 3.11–3.13 (3.14 tested, unofficial bugfix tier)
  • Poetry (or pip, for consumers)
  • Campus service URLs reachable from your environment

Installation

Consumers pin the release wheel by URL (pip resolves it; the release must exist first — see Releases):

# pyproject.toml
campus-api-python = {url = "https://github.com/nyjc-computing/campus-api-python/releases/download/vX.Y.Z/campus_api_python-X.Y.Z-py3-none-any.whl"}

Then poetry install / poetry lock as usual. The SDK depends only on requests and the campus-common / campus-model wheels — it imports no campus-suite module and holds no web framework.

Configuration

The environment is the source of truth. Campus ships no baked-in deployment topology: an unconfigured service raises ValueError naming the variables to set.

Variable Required Mode Description
CLIENT_ID Yes Server OAuth client ID from Campus auth
CLIENT_SECRET Yes Server OAuth client secret from Campus auth
CAMPUS_AUTH_URL No All Auth service base URL (highest precedence)
CAMPUS_API_URL No All API service base URL (highest precedence)
CAMPUS_AUDIT_URL No All Audit service base URL (highest precedence)
AUDIT_API_KEY Yes (audit) All Audit API key (audit_v1_...), sent as the audit root's Bearer
CAMPUS_CLIENT_TIMEOUT No All Request timeout in seconds (default 60); invalid values are logged and ignored
CAMPUS_BASE_URL / BASE_URL No All One origin fronting every service (gateway deployments); consulted last

Per-service precedence: CAMPUS_<SERVICE>_URL → <SERVICE>_URL (bare alias) → CAMPUS_BASE_URL → BASE_URL. For local testing set e.g. CAMPUS_AUTH_URL=http://localhost:5000.

First calls

from campus_python import Campus

campus = Campus(timeout=30)   # mode="server" is the default

# Campus API
for assignment in campus.api.assignments.list():
    print(assignment.title)

timetable_id = campus.api.timetable.get_current()

# Campus Auth (requires CLIENT_ID/CLIENT_SECRET; Basic-authenticated)
client = campus.auth.clients["client_myapp"]
print(client.get().name)
from campus_python import Campus

campus = Campus(mode="device")   # no credentials required

# Public, read-only registry — works without a token
for integration in campus.integrations.list():
    print(integration.provider, integration.connectable)

Authenticating a device client (no secret) is the device flow — after it returns a token, wait_for_token()'s result is applied with use_token().

with campus.with_user_session() as client:
    # every call inside acts as the logged-in user; an expired
    # bearer auto-refreshes once (401 hook, on by default)
    campus.api.submissions.new(assignment_id=aid, student_id=sid, course_id=cid)

Requires a login session id — normally supplied by the campus.flask_campus middleware. See Authentication.

Error handling

Every non-2xx response raises an APIError subclass mapped by status code — see the Errors reference.

from campus_python import errors

try:
    campus.api.assignments["assignment_404"].get()
except errors.NotFoundError as err:
    print("no such assignment:", err.error_description)
except errors.ValidationError as err:
    for field_err in err.field_errors:
        print(field_err.field, field_err.code, field_err.message)

Auth-related errors carry the OAuth protocol error where one exists: err.oauth_error (e.g. "invalid_grant") survives even when the server strips envelope details in production.

Releases

The SDK follows the fleet's strict 0.x scheme: patch releases are additive, minor releases are the breaking line. Every release publishes a wheel attached to its GitHub release; cut one by bumping version in pyproject.toml, then pushing an annotated v* tag — the tag message becomes the release body and CI builds the wheel (with a fresh-venv install smoke) automatically.