phenostore-sdk-python
Python SDK for Phenostore, a FHIR-compatible data store by PhenoML Inc. Requires Python 3.11 or later.
This follows the Go SDK design: a generated OpenAPI client and models, wrapped with OAuth2 authentication, convenience methods, and FHIR error helpers.
Installation
Install a published release from PyPI:
pip install phenostore-sdk-python
Or install from a checkout:
pip install .
Import the SDK as phenostore.
Python runtime support
We support CPython feature versions that still receive upstream bug fixes or security fixes, currently Python 3.11–3.15. CI tests every supported version. New stable versions are added after compatibility checks, and the supported range is reviewed when Python releases a new feature version or an existing version reaches end of life. See the upstream support schedule.
Support for a version may be removed after upstream end of life, with advance notice and an explicit release-note entry. Python 3.11 remains supported until at least its upstream end of life in October 2027. Runtime requirements are declared in requires-python; we do not impose an upper version bound without a known incompatibility.
Usage
from phenostore import Client, OperationOutcomeError, is_not_found
with Client(
"https://api.phenostore.example.com",
"your-client-id",
"your-client-secret",
"my-tenant",
"my-store",
) as client:
try:
patient = client.read_resource("Patient", "pat-123")
print(patient)
except OperationOutcomeError as error:
if is_not_found(error):
print("not found")
else:
raise
created = client.create_resource(
"Observation",
{
"resourceType": "Observation",
"status": "final",
"code": {"text": "Blood pressure"},
"subject": {"reference": "Patient/pat-123"},
},
)
FHIR resources and bundles are plain dictionaries. Unknown fields and extensions are preserved; the wrapper does not validate resources against a FHIR model. Use client.tenant and client.store to access the configured tenant and store.
| Method | Behavior |
|---|---|
read_resource(resource_type, id) |
Read a resource |
create_resource(resource_type, body, *, if_none_exist=None) |
Create, including conditional create (200 or 201) |
update_resource(resource_type, id, body, *, if_match=None) |
Update or upsert (200 or 201) |
delete_resource(resource_type, id) |
Delete; returns None |
search_resources(resource_type, params=None) |
Search with arbitrary FHIR query parameters |
process_bundle(bundle) |
Execute a transaction or batch bundle |
results = client.search_resources(
"Observation",
{
"subject:Patient.name": "Smith",
"date": ["ge2026-01-01", "lt2027-01-01"],
"_include": ["Observation:subject", "Observation:encounter"],
"_count": 50,
},
)
# A sequence of (name, value) pairs also supports repeated parameters.
client.update_resource("Patient", "pat-123", patient, if_match='W/"1"')
client.create_resource("Patient", patient, if_none_exist="identifier=https://example.com|123")
There is no automatic pagination; use the returned Bundle links/cursors in subsequent searches.
Asynchronous usage
AsyncClient has the same six convenience methods, all awaitable:
from phenostore import AsyncClient
async def read_patient():
async with AsyncClient(base_url, client_id, client_secret, tenant, store) as client:
return await client.read_resource("Patient", "pat-123")
Use a synchronous client in a with block (or call close()), and an asynchronous client in an async with block (or await aclose()). Keep an async client within one event loop.
Authentication and HTTP configuration
OAuth2 client credentials are sent in a form body to <base_url>/oauth/token, matching the Go SDK. Tokens are acquired lazily, reused, and refreshed shortly before expiry. Concurrent calls share token refresh within each execution mode. Token failures raise AuthenticationError; requests are not retried automatically.
Both clients accept scopes=[...], timeout=30.0 (seconds or httpx.Timeout), verify=True (or an ssl.SSLContext for custom TLS), and transport=... (an HTTPX sync or async transport). These settings apply to token exchange and API calls. Redirects are disabled. A base URL may contain a path prefix.
Errors
Convenience methods raise OperationOutcomeError for non-2xx API responses, including non-JSON error bodies. It exposes status_code, raw body bytes, parsed outcome when available, and the original HTTPX response with headers. Its message includes the first FHIR issue when present. is_not_found, is_gone, is_conflict (HTTP 412), and is_forbidden classify the first OperationOutcomeError encountered, including through chained exception wrappers. Earlier API failures in its history do not affect that classification.
Invalid JSON/object responses raise InvalidResponseError. HTTPX connection and timeout exceptions propagate unchanged.
Full generated API
Use client.inner with generated endpoint functions for patch, history, bulk, validation, $docref, metadata, and system endpoints. This retains automatic authentication and HTTP configuration. Use sync functions with Client.inner and async functions with AsyncClient.inner.
from phenostore.gen.api.fhir import get_resource_history
from phenostore.gen.models import ResourceType
response = get_resource_history.sync_detailed(
client.tenant,
client.store,
ResourceType.PATIENT,
"pat-123",
client=client.inner,
)
print(response.status_code, response.headers, response.parsed)
Each endpoint provides sync, sync_detailed, asyncio, and asyncio_detailed variants where applicable. Detailed responses expose status, headers, raw bytes, and typed models. Models support from_dict() and to_dict().
Generated functions follow the generator's error semantics: documented error responses are returned as parsed models, undocumented statuses raise phenostore.gen.errors.UnexpectedStatus, and malformed modeled responses may raise parsing errors. The convenience error helpers apply to convenience methods. Health, token, and SMART endpoints can also use a standalone phenostore.gen.Client without automatic authentication.
Development and generation
Install uv and run:
make install
make generate
make test lint check-generated build
api/openapi.yaml was initially imported verbatim from the Go SDK at commit dc11a82650f4e75dbbd3f7e8cdc292193e3056c8 (the upstream API contract). Subsequent updates arrive through the regeneration workflow. Do not edit it here; change the Phenostore API upstream. phenostore/gen/ is generated with openapi-python-client; never edit generated files by hand.
Generator dependencies are pinned separately in tools/pyproject.toml and tools/uv.lock and are not runtime dependencies. The generator temporarily removes duplicate request-body encodings for identical schemas to work around a multi-media-type bug in 0.29.1, retaining the first media type (FHIR JSON). It never changes the imported spec. Formatting is isolated from local settings. make check-generated regenerates in a temporary directory and compares every generated file without changing the checkout.
CI tests Python 3.11–3.15, checks formatting/types and regeneration, and builds a wheel and source distribution. CI and regeneration use the pinned astral-sh/setup-uv action to install a specific uv version and cache dependencies using both lockfiles, with separate caches for each Python version. The regeneration workflow accepts the same spec-updated repository dispatch payload as the Go SDK (spec_b64, spec_sha) and opens a PR after validation. Configure the upstream dispatcher to include this repository to enable automatic spec updates. A manual workflow run regenerates from the committed spec. PRs opened with GITHUB_TOKEN do not trigger normal PR workflows automatically; run CI manually on the PR branch, or configure a GitHub App token for PR creation.
To upgrade the generator, run uv add --project tools openapi-python-client==<version>, then make generate test lint check-generated build. Commit the tooling lockfile and generated changes together. Dependabot's tooling PRs may need this regeneration step before CI can pass.
Regeneration PRs are created and updated with git and the GitHub CLI (gh), using the automation-owned sdk/regenerate branch. If the regenerated client fails compatibility checks, a pinned Claude Code action attempts to adapt the handwritten wrapper, tests, examples, and README. The full checks must then pass before those adaptations are included in the PR. The imported spec and generated files must not be edited by the agent. If regeneration produces no changes relative to main, an existing regeneration PR is closed. Branch updates use an explicit force-with-lease check, so a concurrent push causes the workflow to fail instead of overwriting it.
Automatic wrapper adaptation authenticates to Anthropic with GitHub Actions OIDC through Workload Identity Federation. The organization, federation rule, service account, and workspace IDs are configured directly in .github/workflows/generate.yml. These are public identifiers; authentication uses the workflow's OIDC token, so no Anthropic API key or Actions variables are needed. Ordinary regeneration and deterministic commit/PR titles do not call Claude.
In the Anthropic Console, an organization admin/owner registers the GitHub issuer https://token.actions.githubusercontent.com in discovery mode, creates a service account with access to the chosen workspace, and creates a federation rule for that account. Use audience https://api.anthropic.com, scope workspace:developer, and subject repo:PhenoML/phenostore-sdk-python:ref:refs/heads/main. Limit the rule's claims to this repository and regeneration workflow as described in the GitHub Actions federation guide. The Console's “Connect workload” wizard can create these resources together. Manual workflow runs must use main to match this identity. Git operations and PR creation continue to use the workflow's built-in GITHUB_TOKEN.
Releases and PyPI publishing
Release Please prepares version/changelog PRs after conventional commits reach main. Merging a release PR creates a GitHub release and starts package publication in the same release-please.yml workflow. Ordinary pushes that do not create a release do not publish a package.
The build job checks out the exact released commit, verifies that the package versions match the release, runs the tests/lint/generation checks, and builds the wheel and source distribution. It installs and smoke tests both distributions in isolated environments before handing them to a separate publish job. Only the publish job can request an OIDC token; it runs in the pypi GitHub environment and uploads with uv publish --trusted-publishing always. No stored PyPI API token is required.
The PyPI Trusted Publisher must identify owner PhenoML, repository phenostore-sdk-python, workflow filename release-please.yml, and environment pypi. For the first upload, configure a pending publisher for project phenostore-sdk-python. GitHub creates the referenced environment on first use if it does not already exist.
If an upload fails, re-run the failed jobs in that workflow run. The publisher checks PyPI for files already uploaded, allowing a partially completed upload to be retried. Re-running the entire workflow may not publish because Release Please only signals a newly created release once.
License
Metadata
Release files for phenostore-sdk-python 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| phenostore_sdk_python-0.1.1.tar.gz | 126.1 kB | Without provenance | Details for phenostore_sdk_python-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| phenostore_sdk_python-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for phenostore_sdk_python-0.1.1-py3-none-any.whl |
Total release size: 194.8 kB
Release files / phenostore_sdk_python-0.1.1.tar.gz
| Download URL | phenostore_sdk_python-0.1.1.tar.gz |
|---|---|
| Size | 126.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c0ce5a9f3cc503fb099ee9762f7cfb71211185c0a5f68e01566a586abb780170
|
|
BLAKE2b-256 checksum How to use checksums |
46574ee8ce125abc038f8780405a5f395f20b68445a015cb1e00149a695c7a9c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.7 {"installer":{"name":"uv","version":"0.13.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / phenostore_sdk_python-0.1.1-py3-none-any.whl
| Download URL | phenostore_sdk_python-0.1.1-py3-none-any.whl |
|---|---|
| Size | 68.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fb05144e972c28e56de8c6b465641eb73d8b45ae81ed1ac140d6fd5bf1b7ba7c
|
|
BLAKE2b-256 checksum How to use checksums |
64c75906e4e9ed4f59450f1374e21ce78f3a177c917b6d285738cad44674570c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.7 {"installer":{"name":"uv","version":"0.13.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for phenostore-sdk-python 0.1.1
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.