A TM1py-inspired Python wrapper for the IBM Planning Analytics Workspace (PAW) REST API.
Project description
PAWpy — Planning Analytics Workspace REST API Wrapper
A TM1py-inspired Python wrapper for the PAW REST API.
Install
uv sync --extra dev # installs PAWpy + pytest
uv run python -m pytest # 21 offline tests (no live server needed)
Requires Python ≥3.11 and requests. The URL-builder calls (paw.ui.*,
*.get_embed_url) make no network request and work without a live PAW server.
Releasing
Publishing is automated by .github/workflows/publish.yml via PyPI
Trusted Publishing (OIDC — no API
token stored). To cut a release:
- Bump
versioninpyproject.tomland add aCHANGELOG.mdentry. - One-time on PyPI: add a Trusted Publisher for project
PAWpy→bluestarleo/PAWpy, workflowpublish.yml, environmentpypi. - Tag and push:
git tag v0.1.0 && git push origin v0.1.0
The workflow runs the tests (3.11–3.13), builds, checks the tag matches the package version, and publishes the sdist + wheel to PyPI.
Architecture
PAWService ← top-level entry point (mirrors TM1py's TM1Service)
├── RestService ← session + auth + GET/POST/PATCH/PUT/DELETE core
├── ContentService ← /pacontent/v1/Assets (legacy OData folders / books / assets)
├── ContentV1Service ← /api/v1/content (OAuth-era assets, permissions,
│ bulk ops, asset types — PAW 2.1.21+/3.1.8+)
├── UserGroupService ← /api/v1/content/users|groups (PAW 2.1.21+/3.1.8+)
├── BookService ← books (type=book/dashboard) over ContentService
├── ViewService ← views over ContentService
├── AdminService ← /api/v1/admin (servers, users, groups)
├── UIService ← URL builder for /ui?type=… embed endpoints
└── TM1ProxyService ← /api/v0/tm1/<db>/api/v1/… (TM1 REST via PAW auth;
pass tm1_proxy_base="/api/v1/tm1" on PAW 2.1.21+/3.1.8+)
All base paths (content_base, admin_base) are constructor-overridable, since
they vary across PAW builds (/pacontent/v1 vs /api/v1/content).
Auth Modes
| Mode | How it works |
|---|---|
oauth |
Client-credentials grant against an IdP token_url → Authorization: Bearer (see caveat below) |
cam |
CAM namespace login via POST /login → x-csrf-token — the proven headless mode for on-prem PAW |
native |
TM1 native username/password login via POST /login → x-csrf-token |
passport |
Cognos CAM passport (camid) via POST /login |
session |
Inject an existing csrf_token / session_cookie (dev/test) |
On-prem OAuth caveat (per IBM Docs, "Configuring authorized applications (OAuth)"): PAW's built-in OAuth (Administration → Integrations tile, PAW 2.1.21+/3.1.8+) supports only the interactive authorization-code flow (scope
v0userContext) — "client credentials (not interactive) flows are not supported" against PAW's own/oauth2/token. Useoauthmode only where an external IdP issues bearer tokens your PAW deployment accepts. For headless/scripted access to on-prem PAW, usecammode with directory credentials (PAW shares the CAM directory with TM1, so TM1 service credentials typically work). Authorization-code + refresh-token support is on the roadmap.
Usage
OAuth (IdP-issued tokens — see caveat above)
from PAWpy import PAWService
with PAWService(
host="paw.mycompany.com",
auth_mode="oauth",
client_id="my-client-id",
client_secret="my-client-secret",
token_url="https://idp.mycompany.com/oauth2/token", # required for oauth
scope="paw", # optional
database="Planning Sample", # optional default TM1 database
) as paw:
# List books in a folder (returns Asset objects)
books = paw.books.get_all("/shared/Finance")
# Get embed URL for an iframe (no HTTP call)
url = paw.books.get_embed_url("/shared/Finance/Monthly Report")
# List registered TM1 servers
servers = paw.admin.get_tm1_servers()
# OAuth-era content API (PAW 2.1.21+/3.1.8+): permissions, bulk ops, users
assets = paw.content_v1.list_children("shared")
perms = paw.content_v1.get_effective_permissions(assets[0].id)
users = paw.user_groups.get_users()
# TM1 proxy call (MDX via PAW auth) — returns the raw cellset JSON
tm1 = paw.tm1("Planning Sample")
data = tm1.execute_mdx("SELECT {[Account].[Revenue]} ON 0 FROM [Revenue Cube]")
# Embed URL generation (no HTTP call)
embed = paw.ui.cube_viewer_url("Planning Sample", "plan_BudgetPlan", view="Budget Input")
CAM (headless on-prem — recommended for scripts)
with PAWService(
host="paw.mycompany.com",
auth_mode="cam",
namespace="LDAP",
username="my-username",
password="secret",
) as paw:
...
Multi-tenant (PAW Cloud)
with PAWService(
host="planning-analytics.cloud.ibm.com",
tenant_id="my-tenant-id",
auth_mode="oauth",
client_id="...",
client_secret="...",
) as paw:
...
Mapping to TM1py
| TM1py | PAWpy |
|---|---|
TM1Service |
PAWService |
CubeService |
TM1ProxyService (via PAW) |
DimensionService |
TM1ProxyService (via PAW) |
ProcessService |
TM1ProxyService (via PAW) |
| (no equivalent) | BookService |
| (no equivalent) | ContentService |
| (no equivalent) | AdminService |
| (no equivalent) | UIService |
Versioning against PAW builds
The PAW REST API is still incomplete and grows with each IBM release, so PAWpy is
versioned against two axes: its own semver (PAWpy.__version__) and the
minimum PAW build each API group requires. Each service declares its
API_GROUP; the per-group minimums live in PAWpy/version_requirements.py
(MIN_PAW_VERSION).
paw = PAWService(host="paw.acme.com", auth_mode="oauth", ..., paw_version="2.1.21")
paw.requires("content") # -> "2.1.21" (min PAW build for Content Services)
paw.supports("content") # -> True / False against the known paw_version
paw.assert_supported("content") # raises PAWVersionError if the build is too old
paw.detect_paw_version() # best-effort probe (overridable path/field)
When paw_version is unknown, gating is a no-op — PAWpy never blocks a call
solely because it couldn't determine the version; the server still rejects
genuinely-unsupported requests. The version pins are reconciled on each PAW
release as part of a maintainer-local workflow.
Coverage & release tracking
PAWpy's endpoint coverage is tracked in a maintainer-local coverage matrix (not
part of this repo). Because the PAW REST API is still growing, it is reconciled
on each PAW release: IBM's endpoint inventory (the IBM-linked Postman collection
or the published API references) is re-pulled and diffed against the matrix to
flag endpoints PAW now exposes that PAWpy doesn't yet wrap, so the wrapper
tracks IBM's cadence instead of drifting. New coverage lands here as Added
entries in CHANGELOG.md and Roadmap items below.
Roadmap (aligned with IBM's "future releases" promise)
- Content API v1 (
/api/v1/content, PAW 2.1.21+/3.1.8+) —ContentV1Service: assets incl. content retrieval, permissions (get/set/effective), bulk copy/move/delete/permissions, asset types -
UserGroupService— PAW users/groups reads (/api/v1/content/users|groups); write endpoints not yet documented by IBM -
ViewService— PAW view CRUD -
EmbedTokenService— generate scoped embed tokens -
MCPService— PAW MCP endpoint integration - OAuth authorization-code + refresh-token flow — the only OAuth on-prem PAW supports (client-credentials is rejected per IBM Docs)
- Async support (
aiohttp) - Pydantic models for Books, Assets, Servers
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pawpy-0.4.1.tar.gz.
File metadata
- Download URL: pawpy-0.4.1.tar.gz
- Upload date:
- Size: 29.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d2b36708cdaa616d6fa11193e6133f085352de603ac32ff125a302a36669ac0f
|
|
| MD5 |
843a80d45b6a2bf273799342a8fc4e9a
|
|
| BLAKE2b-256 |
632e265da74aa2c28c72e20f29eace194e2b42ad372fcfe7bc4fc51b2acf795b
|
Provenance
The following attestation bundles were made for pawpy-0.4.1.tar.gz:
Publisher:
publish.yml on bluestarleo/PAWpy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pawpy-0.4.1.tar.gz -
Subject digest:
d2b36708cdaa616d6fa11193e6133f085352de603ac32ff125a302a36669ac0f - Sigstore transparency entry: 2208613652
- Sigstore integration time:
-
Permalink:
bluestarleo/PAWpy@82d43f5f06177b6472c02de356b29a349cb3de2a -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/bluestarleo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@82d43f5f06177b6472c02de356b29a349cb3de2a -
Trigger Event:
push
-
Statement type:
File details
Details for the file pawpy-0.4.1-py3-none-any.whl.
File metadata
- Download URL: pawpy-0.4.1-py3-none-any.whl
- Upload date:
- Size: 32.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
19931598899407144d602665e59d5391b2cdd93864873fa3827c64be268a05ae
|
|
| MD5 |
fcbe13590dcd0322a3151f778fcd9056
|
|
| BLAKE2b-256 |
e64358a32913f720cd47c5edc632c7ec98d732f587eea03e3d1f2755193a2a24
|
Provenance
The following attestation bundles were made for pawpy-0.4.1-py3-none-any.whl:
Publisher:
publish.yml on bluestarleo/PAWpy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pawpy-0.4.1-py3-none-any.whl -
Subject digest:
19931598899407144d602665e59d5391b2cdd93864873fa3827c64be268a05ae - Sigstore transparency entry: 2208613899
- Sigstore integration time:
-
Permalink:
bluestarleo/PAWpy@82d43f5f06177b6472c02de356b29a349cb3de2a -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/bluestarleo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@82d43f5f06177b6472c02de356b29a349cb3de2a -
Trigger Event:
push
-
Statement type: