Skip to main content

Polarion REST API client, wrappers & helpers

Project description

Polarion REST API Client

Python 3.10+ License: MIT PDM

A Python client for the Polarion ALM REST API, combining an auto-generated low-level client (from the upstream OpenAPI spec) with high-level resource wrappers for common operations.

Both synchronous and asynchronous usage are supported out of the box.


Features

  • Auto-generated client from the Polarion OpenAPI specification via openapi-python-client
  • High-level resource wrappers with full CRUD support for Projects, Work Items, Work Item Attachments, Documents, Document Parts, Document Comments, Document Attachments, and Document Table extraction
  • Sync and Async — every operation works with both PolarionClient (sync) and PolarionAsyncClient (async)
  • Automatic pagination — fetch all pages transparently with page_size=-1 or iterate lazily with .iter()
  • Batch operations — bulk create, update, and delete work items in a single request
  • Typed error hierarchyJSONAPIError subclasses (Unauthorized, Forbidden, NotFound, Conflict, ServerError) for precise exception handling
  • Environment-based configuration — configure via environment variables or .env files (with python-dotenv support)
  • SSL via truststore — uses the OS certificate store by default for enterprise environments

Project Structure

.
├── codegen/                                # OpenAPI specs + generation config
│   ├── polarion-openapi.json               # upstream Polarion REST spec (input)
│   ├── polarion-openapi-clean.json         # cleaned spec (derived)
│   └── client-config.yaml                  # openapi-python-client config
├── scripts/                                # helper scripts
│   ├── clean_rest_spec.py                  # applies Polarion-specific fixes to the spec
│   ├── rest_json_validator.py              # JSON / OpenAPI validation
│   ├── regenerate_polarion_rest_client.sh  # end-to-end: clean → validate → generate → copy
│   └── set_version.py                      # set package version in pyproject.toml
├── src/
│   └── polarion_rest_client/               # installable package
│       ├── __init__.py                     # public API surface
│       ├── client.py                       # PolarionClient & PolarionAsyncClient
│       ├── session.py                      # get_env_vars() environment loader
│       ├── error.py                        # typed exception hierarchy
│       ├── resource.py                     # PolarionResource base class
│       ├── paging.py                       # generic pagination utilities
│       ├── project.py                      # Project resource
│       ├── workitem.py                     # WorkItem resource
│       ├── workitem_attachment.py          # WorkItemAttachment resource
│       ├── document.py                     # Document resource
│       ├── document_part.py                # DocumentPart resource
│       ├── document_comment.py             # DocumentComment resource
│       ├── document_attachment.py          # DocumentAttachment resource
│       ├── document_table_utils.py         # HTML table extraction helpers
│       └── openapi/                        # auto-generated client (committed)
├── tests/                                  # unit + integration tests
├── examples/                               # runnable usage examples
│   ├── common.py                           # shared helpers for examples
│   ├── workitem/                           # work item examples
│   └── document/                           # document examples
├── pyproject.toml                          # PDM / PEP 621 metadata
├── CHANGELOG.md
├── LICENSE
└── README.md

Requirements

  • Python 3.10+
  • PDM (pipx install pdm recommended)

Installation

From PyPI

pip install polarion-rest-client

From Source

git clone https://gitlab.com/elimesika-group/polarion-rest-client.git
cd polarion-rest-client
pdm install --dev

Configuration

The client reads configuration from environment variables. Set them in your shell or place them in a .env file (requires python-dotenv).

Required Variables

Variable Description
POLARION_URL Base URL of the Polarion server (e.g., https://polarion.example.com)

Authentication (one of the following)

Variable Description
POLARION_TOKEN Personal access token (preferred)
POLARION_USERNAME + POLARION_PASSWORD Basic authentication credentials

Optional Variables

Variable Default Description
POLARION_TOKEN_PREFIX Bearer Authorization header prefix for token auth
POLARION_VERIFY_SSL true Set to false to disable SSL verification
POLARION_TIMEOUT 30.0 Request timeout in seconds

Example .env File

POLARION_URL=https://polarion.example.com
POLARION_TOKEN=your-personal-access-token
POLARION_VERIFY_SSL=true
POLARION_TIMEOUT=30.0

Quick Start

Synchronous

import polarion_rest_client as prc
from polarion_rest_client.project import Project

pc = prc.PolarionClient(**prc.get_env_vars())

project_api = Project(pc)
project = project_api.get("my-project-id")
print(project)

Asynchronous

import asyncio
import polarion_rest_client as prc
from polarion_rest_client.project import Project

async def main():
    async with prc.PolarionAsyncClient(**prc.get_env_vars()) as pc:
        project_api = Project(pc)
        project = await project_api.get("my-project-id")
        print(project)

asyncio.run(main())

High-Level Resources

Each resource wrapper accepts either a PolarionClient (sync) or PolarionAsyncClient (async) and provides a consistent API.

Project

from polarion_rest_client.project import Project

api = Project(pc)

api.list()                                     # list all projects
api.get("project-id")                          # get a single project
api.create("new-project", tracker_prefix="NP") # create (async job, waits by default)
api.patch("project-id", name="New Name")       # update attributes
api.delete("project-id")                       # delete (async job)
api.exists("project-id")                       # check existence (returns bool)
api.list_templates()                           # list project templates

WorkItem

from polarion_rest_client.workitem import WorkItem

api = WorkItem(pc)

api.create("proj", wi_type="task", title="My Task")
api.get("proj", "WI-001")
api.update("proj", "WI-001", title="Updated Title")
api.delete("proj", ["WI-001", "WI-002"])

# Pagination
items = api.list("proj", page_size=50)             # single page
all_items = api.list("proj", page_size=-1)          # fetch all pages
for item in api.iter("proj", page_size=100):        # lazy iteration
    print(item["id"])

# Batch operations
api.update_many("proj", [
    {"id": "WI-001", "attributes": {"severity": "critical"}},
    {"id": "WI-002", "attributes": {"severity": "major"}},
])
api.update_many_same_attrs("proj", ["WI-001", "WI-002"],
    attributes={"status": "approved"})

# Search
api.find_by_title("proj", "My Task")

# Global (cross-project) operations
all_items = api.list_all(page_size=50)
for item in api.iter_all(page_size=100):
    print(item["id"])
api.update_all([{"id": "proj/WI-001", "attributes": {"severity": "critical"}}])
api.delete_all([{"id": "proj/WI-001"}])

# Enum options & workflow actions
api.get_available_enum_options("proj", "WI-001", "status")
api.get_available_enum_options_for_type("proj", "status", wi_type="task")
api.get_current_enum_options("proj", "WI-001", "status")
api.get_workflow_actions("proj", "WI-001")

# Relationships (generic JSON:API)
api.get_relationships("proj", "WI-001", "categories")
api.create_relationships("proj", "WI-001", "categories",
    [{"type": "categories", "id": "proj/cat-1"}])
api.update_relationships("proj", "WI-001", "categories",
    [{"type": "categories", "id": "proj/cat-2"}])
api.delete_relationships("proj", "WI-001", "categories",
    [{"type": "categories", "id": "proj/cat-2"}])

# Move to/from document
api.move_to_document("proj", "WI-001", target_document="proj/space/doc-name")
api.move_from_document("proj", "WI-001")

# Test parameter definitions
api.list_test_parameter_definitions("proj", "WI-001")
api.get_test_parameter_definition("proj", "WI-001", "param-id")

WorkItemAttachment

from polarion_rest_client.workitem_attachment import WorkItemAttachment

api = WorkItemAttachment(pc)

api.list("proj", "WI-001")
api.get("proj", "WI-001", "attachment-id")
api.get_content("proj", "WI-001", "attachment-id")          # returns bytes
api.create("proj", "WI-001",
    file_data=b"...", file_name="diagram.png", mime_type="image/png")
api.update("proj", "WI-001", "attachment-id", title="New Title")
api.delete("proj", "WI-001", "attachment-id")
for att in api.iter("proj", "WI-001"):
    print(att["id"])

Document

from polarion_rest_client.document import Document

api = Document(pc)

api.create("proj", "space", module_name="my-doc", title="My Document")
api.get("proj", "space", "my-doc")
api.update("proj", "space", "my-doc", title="Updated Title")
api.branch("proj", "space", "my-doc", target_project_id="other-proj")
api.copy("proj", "space", "my-doc", target_document_name="my-doc-copy")
api.merge_from_master("proj", "space", "my-doc")
api.merge_to_master("proj", "space", "my-doc")

DocumentPart

from polarion_rest_client.document_part import DocumentPart

api = DocumentPart(pc)

api.list("proj", "space", "my-doc", page_size=-1)
api.get("proj", "space", "my-doc", "part-id", fields_parts="@all")
api.create("proj", "space", "my-doc", part_type="workitem", work_item_id="WI-001")
for part in api.iter("proj", "space", "my-doc"):
    print(part["id"])

DocumentComment

from polarion_rest_client.document_comment import DocumentComment

api = DocumentComment(pc)

api.list("proj", "space", "my-doc")
api.get("proj", "space", "my-doc", "comment-id")
api.create("proj", "space", "my-doc", text="Review needed")
api.update("proj", "space", "my-doc", "comment-id", resolved=True)
api.reply("proj", "space", "my-doc", "comment-id", text="Done")
api.resolve("proj", "space", "my-doc", "comment-id")
api.unresolve("proj", "space", "my-doc", "comment-id")

DocumentAttachment

from polarion_rest_client.document_attachment import DocumentAttachment

api = DocumentAttachment(pc)

api.list("proj", "space", "my-doc")
api.get("proj", "space", "my-doc", "attachment-id")
api.create("proj", "space", "my-doc",
    file_data=b"...", file_name="diagram.png", mime_type="image/png")
api.update("proj", "space", "my-doc", "attachment-id", title="Updated Title")
content = api.get_content("proj", "space", "my-doc", "attachment-id")

Document Table Extraction

Extract structured data from HTML tables embedded in Polarion documents into pandas DataFrames.

from polarion_rest_client.document_table_utils import (
    extract_document_tables_by_columns,
)

df = extract_document_tables_by_columns(
    pc, "proj", "space", "my-doc",
    expected_columns=["Name", "Version", "Status"],
)
print(df)

Error Handling

The client provides a typed exception hierarchy rooted in PolarionError:

PolarionError
├── HTTPStatusError          # non-2xx without JSON:API errors payload
└── JSONAPIError             # JSON:API errors[] present
    ├── Unauthorized         # 401
    ├── Forbidden            # 403
    ├── NotFound             # 404
    ├── Conflict             # 409
    └── ServerError          # 5xx
from polarion_rest_client import NotFound, Unauthorized

try:
    project_api.get("nonexistent")
except NotFound as e:
    print(f"Not found: {e.detail}")
except Unauthorized:
    print("Check your credentials")

Examples

The examples/ directory contains runnable scripts demonstrating common workflows. Set the following environment variables before running:

Variable Description
POLARION_TEST_PROJECT_ID Project ID for examples
POLARION_TEST_SPACE_ID Space ID (defaults to _default)
POLARION_TEST_WI_TYPE Work item type (defaults to task)
# Work item CRUD
python examples/workitem/workitem_CRUD.py

# Document CRUD
python examples/document/document_CRUD.py

# Document attachment CRUD
python examples/document/document_attachment_CRUD.py

# Document comment CRUD
python examples/document/document_comment_CRUD.py

# Pagination
python examples/workitem/workitem_paging.py
python examples/document/document_paging.py

# Batch updates
python examples/workitem/workitem_batch_update.py

# Work item attachment CRUD
python examples/workitem/workitem_attachment_CRUD.py

# Advanced work item operations (enum options, workflow, move, global list)
python examples/workitem/workitem_advanced.py

# Table extraction
python examples/document/document_tables.py

Regeneration Workflow

The generated client can be rebuilt from an updated OpenAPI specification.

  1. Place or update the upstream spec:
codegen/polarion-openapi.json
  1. Run the pipeline (clean, validate, generate, copy):
pdm run regenerate-client
  1. Verify the import:
python -c "import polarion_rest_client.openapi as pkg; print('OK:', pkg.__name__)"

The generated package under src/polarion_rest_client/openapi/ is committed so that consumers can use the client without running the generator.


Bumping to a New Polarion Version (Developer Guide)

When a new Polarion server version is released and a new OpenAPI specification becomes available, follow this end-to-end procedure to update the client.

Prerequisites

  • Access to the new Polarion REST API JSON specification (exported from the target server)
  • A working development environment (pdm install --dev)
  • Access to a Polarion test server running the new version (for integration tests)

Step-by-Step Procedure

1. Obtain the new OpenAPI specification

Export the REST API JSON from the new Polarion server (typically available at https://<server>/polarion/rest/v1/openapi.json) and place it at:

cp /path/to/new-spec.json codegen/polarion-openapi.json

2. Clean the specification

The upstream spec contains known quirks that must be patched before code generation. Run the cleaner:

python scripts/clean_rest_spec.py \
    codegen/polarion-openapi.json \
    codegen/polarion-openapi-clean.json

The cleaner applies the following deterministic fixes:

Fix What it does
octet_stream_upload Normalizes application/octet-stream upload schemas to string/binary
wildcard_error_responses Expands vendor 4XX-5XX entries into concrete 4xx/5xx responses
missing_downloads_items Adds missing items for array schemas in jobsSingle*Response
error_source_nullability Marks error source objects as nullable
expand_reference_only_components Wraps bare $ref component schemas in allOf for the generator

Review the output. If the new spec introduces new quirks that break generation, add a fixer function in scripts/clean_rest_spec.py, register it in apply_all_fixes(), and document it in the table above.

3. Validate the cleaned specification

python scripts/rest_json_validator.py codegen/polarion-openapi-clean.json

This checks JSON syntax and validates the spec against the OpenAPI schema. Fix any errors before proceeding.

4. Regenerate the client

pdm run regenerate-client

This runs the full pipeline (clean, validate, generate, copy into src/polarion_rest_client/openapi/). Alternatively, steps 2-4 are equivalent to this single command.

5. Check for breaking changes in the generated code

After regeneration, verify that the high-level wrappers still compile and function correctly:

python -c "import polarion_rest_client.openapi as pkg; print('OK:', pkg.__name__)"

Common issues to watch for:

Symptom Likely cause Resolution
ImportError in a resource module Endpoint or model was renamed/removed in the new spec Update the import paths in the affected wrapper (e.g., project.py, workitem.py)
TypeError on a wrapper method call Generated function signature changed (new/renamed params) Run resolve_page_params() diagnostics; update keyword arguments in the wrapper
New endpoints available Polarion added new REST resources Consider adding new high-level wrappers under src/polarion_rest_client/
Generator warnings about unsupported schemas New spec patterns the cleaner doesn't handle yet Add a new fixer in scripts/clean_rest_spec.py

6. Update the package version

Bump the version to reflect the new Polarion server version. The format is XX.YY.ZZ where XX.YY matches the Polarion version and ZZ starts at 01 for a new server release:

pdm run python scripts/set_version.py XX.YY.01

For example, if upgrading to Polarion 25.12:

pdm run python scripts/set_version.py 25.12.01

7. Run the test suite

# Unit tests (no server required)
pdm run pytest -m unit

# Integration tests (requires POLARION_* env vars pointing to the new server)
pdm run pytest -m integration

Fix any failures caused by API changes before proceeding.

8. Verify the examples

The examples/ directory contains runnable scripts that exercise the high-level wrappers against a real server. Run them all to confirm that the new spec hasn't broken any workflows:

# Ensure env vars point to the new Polarion server
export POLARION_URL=https://polarion-new.example.com
export POLARION_TOKEN=...
export POLARION_TEST_PROJECT_ID=my-test-project

# Run the full example suite
bash examples/run_all.sh

If any example fails, investigate whether the issue is in the generated client (step 5) or the high-level wrapper, and fix accordingly.

9. Update documentation

  • Update CHANGELOG.md with the new version and a summary of changes
  • If new high-level resources were added, update the High-Level Resources section of this README
  • If new environment variables or configuration options were introduced, update the Configuration section

10. Build and verify

pdm run pre_build
pdm build

Sanity-check the wheel:

pip install dist/polarion_rest_client-*.whl
python -c "import polarion_rest_client; print(polarion_rest_client.__version__)"

11. Commit and release

git add -A
git commit -m "chore(release): bump to Polarion XX.YY (vXX.YY.01)"
git tag -a vXX.YY.01 -m "Polarion XX.YY, patch 01"
git push origin HEAD && git push origin vXX.YY.01

Then follow the Releases and Publishing section to publish to PyPI.

Custom Templates (Advanced)

You can override the Jinja templates used by openapi-python-client to customize the generated code (naming conventions, default headers, timeouts, docstrings, etc.).

  1. Place overrides under codegen/custom_templates/ using the same relative paths as the upstream templates.
  2. The regeneration script automatically passes --custom-template-path when this directory exists.

To discover the upstream template paths:

python -c "
import importlib.resources as r, openapi_python_client as opc
print((r.files(opc) / 'templates').as_posix())
"

Copy only the files you want to override; the rest fall back to defaults.


Versioning

Package versions follow the format XX.YY.ZZ:

Segment Meaning
XX.YY Polarion server version (e.g., 25.06)
ZZ Client patch number (e.g., 08)

PEP 440 normalizes 25.06.08 to 25.6.8 on PyPI. Git tags use the human-friendly form (v25.06.08).

Set the version with:

pdm run python scripts/set_version.py 25.06.08

Tests

Tests are parameterized to run in both sync and async modes.

pdm run pytest

Test markers:

  • unit — fast tests, no external services
  • integration — tests that hit a real Polarion server
pdm run pytest -m unit
pdm run pytest -m integration

Contributing

  1. Create a feature branch:
git checkout -b feature/my-feature
  1. Run the full pipeline before committing:
pdm run pre_build
  1. Run tests:
pdm run pytest
  1. Lint:
flake8 . --exclude ./venv --max-line-length=120
  1. Commit following Conventional Commits and open a Merge Request with a clear description, issue references, and test evidence.

Avoid manual edits to files under src/polarion_rest_client/openapi/ — they are regenerated.


Releases and Publishing

Quick Release Checklist

  1. Update CHANGELOG.md
  2. Set version: pdm run python scripts/set_version.py XX.YY.ZZ
  3. Regenerate and build:
pdm run pre_build
pdm build
  1. Dry-run on TestPyPI:
pdm publish -r testpypi --username __token__ --password "$TESTPYPI_TOKEN"
  1. Verify from TestPyPI:
pip install -i https://test.pypi.org/simple/ \
    --extra-index-url https://pypi.org/simple \
    polarion-rest-client==<version>
  1. Publish to PyPI:
pdm publish --username __token__ --password "$PYPI_TOKEN"
  1. Tag and push:
git tag -a vXX.YY.ZZ -m "Polarion XX.YY, patch ZZ"
git push origin vXX.YY.ZZ

Token Setup (One-Time)

Registry Registration Token Scope
TestPyPI Required for dry-run TESTPYPI_TOKEN — Entire account (first upload)
PyPI Required for release PYPI_TOKEN — Entire account, then project-scoped

Never commit tokens. Pass them via environment variables. After your first PyPI release, create a project-scoped token and revoke the wide one.

Troubleshooting

Error Fix
"File already exists" / 400 Bump patch version, rebuild, retry
403 "user isn't allowed to upload" Use an Entire account token for first upload
Name conflict Change [project].name in pyproject.toml
Packaging issues rm -rf dist && pdm run pre_build && pdm build

License

Licensed under the MIT License.

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

polarion_rest_client-25.6.10.tar.gz (718.3 kB view details)

Uploaded Source

Built Distribution

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

polarion_rest_client-25.6.10-py3-none-any.whl (2.7 MB view details)

Uploaded Python 3

File details

Details for the file polarion_rest_client-25.6.10.tar.gz.

File metadata

  • Download URL: polarion_rest_client-25.6.10.tar.gz
  • Upload date:
  • Size: 718.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: pdm/2.26.7 CPython/3.12.13 Linux/5.15.154+

File hashes

Hashes for polarion_rest_client-25.6.10.tar.gz
Algorithm Hash digest
SHA256 76e1d3e0eadcae902a1415a4483946affcdd4ab446b23efbb939fa6c8196a638
MD5 e085abe1010c6042263527e891ab0313
BLAKE2b-256 51b45b227b8d84e38aa741510106e46646b64c3aba8f4d0bd3fa3b49294207a2

See more details on using hashes here.

File details

Details for the file polarion_rest_client-25.6.10-py3-none-any.whl.

File metadata

File hashes

Hashes for polarion_rest_client-25.6.10-py3-none-any.whl
Algorithm Hash digest
SHA256 bb247beb8967192dcbf115597c3a00f048113eda1cb76d63e121d13bc45e4362
MD5 e7be1cafe5b7261f1166e54b46b7cd3e
BLAKE2b-256 e4cbc4c24a588a6d276820c7317db4c94b3bb441aad0b8d4027408baeada53e9

See more details on using hashes here.

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