Skip to main content

whitson PVT SDK

License: Apache-2.0

HTTP client for the whitson PVT external API. Python 3.10+.

Install

# uv (recommended)
uv add whitson-pvt-sdk

# pip
pip install whitson-pvt-sdk

Quick start

from whitson_pvt_sdk import WhitsonPVTClient
from whitson_pvt_sdk.shared.models import ClientCredentials

client = WhitsonPVTClient(
    credentials=ClientCredentials(client_id="...", client_secret="..."),
    base_url="https://internal.pvt.whitson.com",
)

regions = client.regions.list()
well = client.wells.get(well_id=123)
sample = client.samples.get(sample_id=456)

Authentication is handled automatically through the external API token endpoint. If you need the same bearer token for an external integration, use the explicit token helper rather than an auth resource:

token = client.get_access_token()

Retries

The SDK retries transient read failures by default. GET requests are attempted up to 3 times for network timeouts/transport errors and HTTP 408, 429, 500, 502, 503, and 504 responses. Mutating requests (POST, PUT, PATCH, DELETE, and multipart uploads) are not retried by default, except on HTTP 429 (rate limiting). Token exchange follows the same retry timing and attempt policy.

Retry delays honor Retry-After, retry-after-ms, and X-RateLimit-Reset headers when present. X-RateLimit-Limit and X-RateLimit-Remaining are left available on the raw HTTP response internally, but do not affect retry timing.

If retries are exhausted on HTTP 429, the SDK raises RateLimitError with retry_after_seconds when retry timing headers are present. max_attempts includes the first request; use RetryConfig(max_attempts=1) to disable retries. RetryConfig.methods controls non-429 retries only; remove 429 from RetryConfig.statuses to disable all-method rate-limit retries.

Configure retries on the client:

from whitson_pvt_sdk import WhitsonPVTClient
from whitson_pvt_sdk.shared.models import ClientCredentials, RetryConfig

client = WhitsonPVTClient(
    credentials=ClientCredentials(client_id="...", client_secret="..."),
    base_url="https://internal.pvt.whitson.com",
    retry_config=RetryConfig(max_attempts=1),  # disables retries
)

Configure default request timeouts with timeout; downloads and uploads use file_timeout:

client = WhitsonPVTClient(
    credentials=ClientCredentials(client_id="...", client_secret="..."),
    base_url="https://internal.pvt.whitson.com",
    timeout=30.0,
    file_timeout=60.0,
)

Pagination (v2)

v2 list endpoints (regions, projects, fluid models, black oil tables, wells) are cursor-paginated. Use iterate() for lazy traversal or list_all() for an eager list:

for region in client.regions.iterate(limit=50):
    print(region.name)

regions = client.regions.list_all(limit=50)

Each response still includes a pagination field when you need manual cursor control:

page = client.regions.list()
print(page.pagination.next_cursor)

Pass cursor and limit to control pagination:

page = client.regions.list(limit=50)
page = client.regions.list(cursor=page.pagination.next_cursor)

Limit defaults to the API default (usually 20) when omitted. iterate() and list_all() are available on all cursor-paginated v2 resources: regions, wells, projects, fluid_models, and black_oil_tables.

All five resources also accept name= on list(), iterate(), and list_all(). This is a case-insensitive exact match; the API trims surrounding whitespace.

Staged imports and OSDU export (v2)

client.import_sessions exposes archive upload, inspection, per-record resolution, selected-record commit, and deletion. client.import_records exposes the four single-record OSDU import endpoints.

from pathlib import Path
from whitson_pvt_sdk.v2.models import ImportSessionCreateOptionsModel

session = client.import_sessions.create(
    Path("archive.zip").read_bytes(),
    ImportSessionCreateOptionsModel(region_id=123),
)
records = client.import_sessions.list_records(session.id)
# Review records before calling update_resolution() and commit().

Export OSDU archives with client.reports.export(report_id=123, format="osdu"). The native archive format remains the default.

See Updating to the current external API v2 for changed model names, commit-selection semantics, and API deployment requirements.

More runnable examples are available in examples.

Development

Prerequisites

  • uv — Python package & project manager

    curl -LsSf https://astral.sh/uv/install.sh | sh
    

Setup

uv sync                           # installs Python deps + dev tools
uv tool install rust-just         # installs just (command runner) globally
just install-hooks                # installs the commit message hook

Commit Messages

Commit messages use Conventional Commits so release notes can be generated from Git history. The local commit-msg hook validates messages after running just install-hooks.

Use:

type: subject
type(scope): subject
type!: breaking subject

Allowed types are feat, fix, docs, test, refactor, perf, build, ci, chore, and release.

Examples:

feat: add pypi publishing workflow
fix(http): normalize localhost base urls
docs: add examples env setup

Tasks

just lint                        # ruff check
just format                      # ruff format
just ty                          # ty check
just test                        # pytest
just integration                 # opt-in tests against a real API
just build                       # uv build
just generate v1                 # regenerate v1 models and endpoint wrappers
just generate v2                 # regenerate v2 models and endpoint wrappers
just generate-all                # regenerate both v1 and v2
just all                         # generate-all + lint/format + build

Integration tests are skipped by default. They create an isolated region, well, sample, and simple experiment for each run so existing staging data is not modified. The external API does not currently expose delete endpoints for these resources, so test data is left behind with unique sdk-it-* names. Run them against a real API by setting credentials:

export WHITSON_INTEGRATION_BASE_URL=https://internal.pvt.whitson.com
export WHITSON_INTEGRATION_CLIENT_ID=...
export WHITSON_INTEGRATION_CLIENT_SECRET=...

just integration

For local testing without real M2M credentials, the API supports EXTERNAL_API_AUTH_BYPASS=true with its local/debug safeguards. Restart the API using the Flask development CLI, bound to 127.0.0.1 with a local database, then use local-sdk-test as both integration client ID and secret. See pvt-api/docs/local-m2m-testing.md in the API repository. The flag belongs to the API process, not the SDK; do not enable it in deployments.

Optional IDs enable project, fluid-model, calculation, black-oil-table, and report checks that cannot be backed by created fixtures: WHITSON_INTEGRATION_PROJECT_ID, WHITSON_INTEGRATION_FLUID_MODEL_ID, WHITSON_INTEGRATION_BLACK_OIL_TABLE_ID, WHITSON_INTEGRATION_SAMPLE_ID, and WHITSON_INTEGRATION_REPORT_ID. The sample ID must have an adjusted composition in the configured fluid model; conversion is separately tested with a created sample containing a characterized C7+ residue. Calculation errors fail tests rather than skipping the dependent endpoints.

The report ID enables real native and OSDU archive round trips, legacy preflight/ import, and all four single-record OSDU endpoints. Choose a report with an available PDF, wells, samples, and RAFS-supported experiments (CCE/CVD/DLE/MSS). The round-trip tests use only public SDK methods: export → create a unique region → upload → review → commit → read back → delete the staging session → re-export. Native and OSDU high-fidelity tests compare native data and file hashes and check that the source report is unchanged. Structured OSDU tests compare the data actually represented in RAFS, not native-only fields.

The round trip leaves its region and imported entities behind (their IDs are logged); only the staging session can be deleted through the external API. Export failures are test failures, not skips, and occur before the round-trip region is created. Integration requests do not retry, so API errors remain visible. To run just this test:

uv run pytest tests/integration/test_v2_report_roundtrip.py -v -s -m integration -o addopts=''

Use include_whitson_native_payload=True and import_mode="whitson_high_fidelity" for full native fidelity. RAFS-only archives do not preserve all native metadata, composition details, primary-experiment flags, or saturation-pressure classifications. JSON-only File.Generic import intentionally returns an unsupported record without committing file bytes; use archive import for files. Single-record analysis metadata needs a reviewed resolved_payload with experiment data before committing; set its entity_type discriminator explicitly.

The OpenAPI/wiring test requires only WHITSON_INTEGRATION_BASE_URL, not credentials.

Publishing

Publishing uses GitHub Actions and PyPI Trusted Publishing; no PyPI API token is stored in this repository. Configure the whitson-pvt-sdk project on PyPI to trust this GitHub repository and the pypi environment, then publish a GitHub Release to build and upload the package.

Before creating a release, update version in pyproject.toml and run:

just test
just lint
just ty
just publish-check
just release-notes 0.1.1

just release-notes generates deterministic Markdown from Conventional Commits since the previous Git tag. Create the GitHub Release with:

just release 0.1.1

The release recipe writes generated notes to a temporary file and passes them to gh release create. Publishing to PyPI starts when the GitHub Release is published. The GitHub CLI must be authenticated with release permissions; use gh auth login and gh auth status to set up and verify access.

Code generation

Generated code comes from the live API's OpenAPI spec:

This fetches /external/{version}/docs/openapi.json from the configured BASE_URL, runs datamodel-code-generator for Pydantic models, then uses the repo-specific generator in scripts/sdk_generator/ for endpoint modules and resource facades.

Generated outputs live under:

  • whitson_pvt_sdk/_generated/{version}/models.py
  • whitson_pvt_sdk/_generated/{version}/resources.py
  • whitson_pvt_sdk/_generated/shared/reports.py
  • whitson_pvt_sdk/{version}/models/__init__.py re-exports generated models
  • whitson_pvt_sdk/{version}/resources.py re-exports public resource classes

Resource classes call HTTPTransport directly and expose SDK-shaped method names such as list, get, create, update, create_bulk, and update_bulk. Shared endpoint modules in _generated/shared/ centralize implementation that spans versions (report import/export).

Authentication endpoints are intentionally excluded from generated resources; auth is infrastructure owned by HTTPTransport.

Package structure

whitson_pvt_sdk/
├── __init__.py              # WhitsonPVTClient
├── http.py                  # HTTPTransport (httpx, auth, retries)
├── errors.py                # SDKError, NotFoundError, ...
├── shared/models.py         # hand-maintained shared models
├── shared/pagination.py     # Paginator utility
├── _generated/              # generated models, resource facades, shared adapters
├── v1/                      # public v1 client/resources/model re-exports
└── v2/                      # public v2 client/resources/model re-exports

License

Licensed under the Apache License, Version 2.0. See LICENSE.

Metadata

Release files for whitson-pvt-sdk 1.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for whitson-pvt-sdk 1.3.0
File Size Uploaded
whitson_pvt_sdk-1.3.0.tar.gz 37.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for whitson-pvt-sdk 1.3.0
File Interpreter ABI Platform
whitson_pvt_sdk-1.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 83.7 kB

Release files / whitson_pvt_sdk-1.3.0.tar.gz

Download URL whitson_pvt_sdk-1.3.0.tar.gz
Size 37.2 kB
Tags Source
SHA-256 checksum
How to use checksums
fc68b1304670bc4774e9d4ee9382392f015087bb773203b6b52152fccb18a464
BLAKE2b-256 checksum
How to use checksums
2cc337fe062ef44be118a351b604960cfadc58ae518365297795bec85f1cc208
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.

Transparency log

Release files / whitson_pvt_sdk-1.3.0-py3-none-any.whl

Download URL whitson_pvt_sdk-1.3.0-py3-none-any.whl
Size 46.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
26bedda4331ad78e1ac09ceae28cc3185b36bb8538c9662c686fd7b901dc69e5
BLAKE2b-256 checksum
How to use checksums
d6ccd2d39ab0605540f57bc2a2e7e6aa32b43ab5118d9cd1faf340e073a783ed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.3.0 This release

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page