Local Development¶
This guide covers everything you need to develop against axltoolkit
locally: a .env-backed credentials workflow for samples and integration
tests, running the unit and live-UCM test suites, and the coverage tools
that keep the WSDL ↔ Python wrapper surface in sync.
Editable Install¶
Clone the repository and install the package in editable mode with the development extras:
The dev extra adds:
pytest/pytest-cov— unit & integration test runnerruff— linter and formatterpython-dotenv— loads the local.envfilepip-audit— CVE scanner for the dependency treemypy— static type checking
To also build the documentation locally, install the docs extra:
Credentials via .env¶
The samples (samples/*.py) and the integration suite
(tests/integration/) both read UCM credentials from environment
variables. To keep them out of your shell history (and out of Git), put
them in a .env file at the repository root.
Step 1 — Copy the template¶
The repository's .gitignore includes .env, so accidentally
committing it isn't possible without removing the rule.
Step 2 — Fill in the values¶
# UCM publisher hostname or IP
UCM_ADDRESS=ucm-pub.example.com
# AXL application-user credentials
UCM_USERNAME=axladmin
UCM_PASSWORD=replace-me
# Platform / OS admin credentials (PAWS, LogCollection)
UCM_PLATFORM_USERNAME=administrator
UCM_PLATFORM_PASSWORD=replace-me
# AXL schema version (10.0–15.0)
UCM_AXL_VERSION=15.0
# TLS verification:
# true — verify against system CA store (recommended)
# /path/to/ca.pem — verify against a specific CA bundle
# false — disable (LAB ONLY)
UCM_TLS_VERIFY=true
# Request timeouts in seconds
UCM_TIMEOUT=30
UCM_LOG_TIMEOUT=120
[!WARNING] TLS verify defaults —
.env.exampledefaultsUCM_TLS_VERIFY=true(recommended for any real environment). The integration-testconftest.pydefaults tofalseif the variable is unset, because most labs use the self-signed Tomcat certificate that ships with UCM. Set it explicitly totrue(or a CA bundle path) in your.envwhenever you have a trusted chain available.
Step 3 — Sample scripts read it automatically¶
samples/_env.py is a small helper that calls
dotenv.load_dotenv() and exports the variables as Python constants.
Every sample script imports from it:
from _env import (
UCM_ADDRESS, UCM_USERNAME, UCM_PASSWORD,
UCM_AXL_VERSION, UCM_TLS_VERIFY, UCM_TIMEOUT,
)
from axltoolkit import AXLClient
client = AXLClient(
username=UCM_USERNAME,
password=UCM_PASSWORD,
server_ip=UCM_ADDRESS,
version=UCM_AXL_VERSION,
tls_verify=UCM_TLS_VERIFY,
timeout=UCM_TIMEOUT,
)
Run any sample with no extra arguments:
Running Tests¶
Unit tests¶
The fast, offline unit suite under tests/ mocks every SOAP call
and runs in seconds:
Integration tests (live UCM)¶
tests/integration/ connects to the cluster specified in your .env
and exercises real AXL/SXML operations. The suite is destructive on
itself — it creates, modifies, and removes objects prefixed with
AXTK_T_. It does not touch anything else, but you should still
run it against a lab cluster.
# Verbose run with short tracebacks
pytest tests/integration/ -v --tb=short
# Just the smoke subset (connectivity + version)
pytest tests/integration/test_axl_integration.py::test_axl_smoke -v
# Skip the suite if env vars are missing (default behavior)
pytest tests/integration/
If UCM_ADDRESS, UCM_USERNAME, or UCM_PASSWORD is missing, the
fixture raises pytest.skip and the suite turns into a no-op — handy
in CI environments where there's no live UCM.
[!NOTE] Session recovery — the integration suite is long-running. The
RISPortClientautomatically re-establishes its session if UCM returnsError Code = 7(session stale) mid-run, so RISPort tests no longer fail intermittently after several minutes of AXL traffic.
Coverage Scripts¶
Two scripts keep the project honest about API coverage.
scripts/check_coverage.py — WSDL ↔ client parity¶
Compares every SOAP operation declared by the bundled WSDLs against the
self._service.<operation>(...) call sites in each client module.
Used as a CI gate (coverage-check job in .github/workflows/ci.yml):
# Default — AXL 15.0 only
python scripts/check_coverage.py
# All AXL schema versions
python scripts/check_coverage.py --all-versions
# Include the SXML/PAWS/Webdialer WSDL snapshots
python scripts/check_coverage.py --all-versions --include-sxml
# Strict mode also fails on "extra" wrappers
python scripts/check_coverage.py --strict
Exit codes:
0— every WSDL operation has a Python wrapper1— coverage gap detected2— usage error (missing schema/client file)
scripts/check_test_coverage.py — test ↔ client parity¶
The complement of the previous check: enumerates every public method on
each client class and asks "how many of these are exercised by the
integration suite?". It understands parametrized indirect dispatch
(getattr(axl, f"add_{type_key}")(...)) so the numbers reflect the
real coverage, not just the literal text in the test files.
# Human-readable per-verb breakdown
python scripts/check_test_coverage.py
# Just the AXL client, with the full uncovered list
python scripts/check_test_coverage.py --client axl --verbose
# Machine-readable output for a dashboard
python scripts/check_test_coverage.py --client all --json > coverage.json
# CI gate — fail if AXL coverage drops below 60%
python scripts/check_test_coverage.py --strict --min 60
This script is a reporting tool, not a default CI gate — coverage of update/apply/reset/restart methods is intentionally partial because those operations have side effects on the live cluster.
scripts/snapshot_wsdls.py — refresh SXML WSDLs¶
The SXML/PAWS/Webdialer WSDLs are not redistributable with this
package, so the project snapshots them under
tests/fixtures/wsdls/ for coverage checks. Refresh them whenever your
UCM is upgraded:
The script reads UCM_ADDRESS / UCM_USERNAME / UCM_PASSWORD from
your .env, so no command-line arguments are required.
scripts/generate_models.py — regenerate TypedDicts and Enums¶
The _generated_models.py and _generated_enums.py modules are built
from the AXL XSD via this script. If you update the bundled schemas (or
change the generator), regenerate both files in one shot:
The generator emits a _AxlStrEnum base class for every string enum so
that SOAP serialization continues to work on Python 3.11+ (where the
default str(EnumMember) representation changed).
Code Quality¶
# Lint
ruff check axltoolkit/ tests/
# Format check
ruff format --check axltoolkit/ tests/
# Apply formatting in-place
ruff format axltoolkit/ tests/
# Static type checking
mypy axltoolkit/
Dependency Vulnerability Scan¶
The CI workflow runs pip-audit --desc --skip-editable on every push.
To run the same scan locally before pushing:
--skip-editable excludes axltoolkit itself (installed via
pip install -e .) so the audit only inspects the third-party
dependency tree. pip-audit exits non-zero whenever it finds a known
CVE, which is what fails the CI job.
A non-zero exit means at least one dependency has a known CVE — bump
the constraint in pyproject.toml and re-run.
Continuous Integration¶
.github/workflows/ci.yml defines four jobs:
| Job | What it does |
|---|---|
test |
Runs pytest tests/ on Python 3.10–3.13 with coverage |
lint |
ruff check + ruff format --check on Python 3.12 |
coverage-check |
Runs scripts/check_coverage.py --all-versions --include-sxml |
dependency-audit |
Runs pip-audit --desc --skip-editable against installed deps |
All four must pass before a PR is merged.