Metadata-Version: 2.4
Name: whitson-pvt-sdk
Version: 1.2.0
Summary: whitson PVT SDK — HTTP client for the whitson PVT external API
Author: Whitson AS
License-Expression: Apache-2.0
Requires-Dist: httpx>=0.28.0
Requires-Dist: pydantic>=2.12.0
Requires-Python: >=3.10
Project-URL: Homepage, https://github.com/WhitsonAS/whitson-pvt-sdk
Project-URL: Repository, https://github.com/WhitsonAS/whitson-pvt-sdk
Project-URL: Examples, https://github.com/WhitsonAS/whitson-pvt-sdk/tree/main/examples
Project-URL: License, https://github.com/WhitsonAS/whitson-pvt-sdk/blob/main/LICENSE
Description-Content-Type: text/markdown

# whitson PVT SDK

[![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://github.com/WhitsonAS/whitson-pvt-sdk/blob/main/LICENSE)

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

## Install

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

# pip
pip install whitson-pvt-sdk
```

## Quick start

```python
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:

```python
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`, 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:

```python
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`:

```python
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:

```python
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:

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

Pass `cursor` and `limit` to control pagination:

```python
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`.

More runnable examples are available in [examples](https://github.com/WhitsonAS/whitson-pvt-sdk/tree/main/examples).

## Development

### Prerequisites

- **[uv](https://docs.astral.sh/uv/)** — Python package & project manager

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

### Setup

```bash
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:

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

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

Examples:

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

### Tasks

```bash
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:

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

just integration
```

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`,
and `WHITSON_INTEGRATION_REPORT_ID`.

### 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:

```bash
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:

```bash
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](https://github.com/WhitsonAS/whitson-pvt-sdk/blob/main/LICENSE).
