ohapi
A typed Python client for the ohapi secrets API. It has no dependencies. It uses only the standard library. It needs Python 3.10 or later.
Install
pip install ohapi
The package is not on PyPI yet.
Example
import os
from ohapi import Ohapi, OhapiError, Pending
ohapi = Ohapi(os.environ["OHAPI_TOKEN"])
result = ohapi.secrets.set(project_id, "production", "API_URL", "https://api.example.com", confirm_production=True)
if isinstance(result, Pending):
# Dual control holds the change. Nothing is written yet.
try:
ohapi.change_requests.wait(result.change_request_id)
except OhapiError as error:
if error.code != "rejected":
raise
print("A reviewer refused the change:", error.change_request.status)
else:
print("Wrote version", result.version)
Methods
| Method | Endpoint | Audit row |
|---|---|---|
me() |
GET /api/v1/me |
No |
projects.list(), projects.get(id) |
GET /api/v1/projects, …/{id} |
No |
environments.list(), .iterate(), .get() |
GET …/environments |
No |
secrets.list(), .iterate(), .get() |
GET …/secrets |
No. Metadata only. |
secrets.value() |
GET …/secrets/{key}/value |
Yes. One READ row for each call. |
secrets.pull() |
GET /api/v1/secrets |
Yes. One READ row for the environment. Expired keys are omitted. |
secrets.set(), .create(), .patch(), .delete() |
PUT, POST, PATCH, DELETE |
Yes, when the write lands. A Pending result has no row yet. The row is written when an admin approves the change. |
change_requests.get(), .wait() |
GET /api/v1/change-requests/{id} |
No |
list() takes page and page_size. iterate() takes page_size. The server accepts page numbers up to 10000 and page sizes up to 500.
The client is synchronous. There is no asyncio client.
Pending writes
Dual control can hold a write to production. In that case a write returns Pending with a change_request_id. The value is not written. Check isinstance(result, Pending) before you read version.
A write returns one of these types:
| Call | Result |
|---|---|
set(), create() |
Written(key, version, created, id) |
patch() |
Patched(key) |
delete() |
Deleted(key) |
| Any of them, when dual control holds the change | Pending(change_request_id, key) |
wait(change_request_id, interval_s=5.0, timeout_s=600.0, cancel=None) returns only when the request is APPLIED. It raises OhapiError with code rejected when a reviewer rejects or cancels the request. The terminal request is in error.change_request. To stop a wait from another thread, pass a threading.Event as cancel and set it. A request that is already in flight ends at its own deadline, which is never later than the end of timeout_s.
Omitted and None
set() and create() take format_regex, rotation_due_at, expires_at, value_type and note as optional arguments. value_type is STRING, JSON, PASSWORD or CERTIFICATE. patch() takes the same arguments except value_type. An argument you leave out does not change the stored value. An argument you pass as None clears the stored value. Dates are ISO 8601 strings or datetime objects.
Errors
Every failure of a request, and every input that the SDK refuses, is an OhapiError with a code, a status (0 when there was no response), and a message. A wrong call, for example a missing argument, is still a normal Python TypeError.
| Code | Cause |
|---|---|
invalid |
Bad input. The SDK refused it before any request, or the server answered 400. |
unauthenticated |
401. The key is missing, wrong, or expired. |
forbidden |
403. The key has a read-only role, the route is for admins only, or the key scope does not cover the target. |
not_found |
404. The thing does not exist, or the key cannot reach it. |
conflict |
409. create() found the key already there. |
unprocessable |
422. The server refused the value. |
rate_limited |
429. retry_after_seconds holds the wait. |
server |
Any other HTTP failure. A redirect is also server, and so is a 2xx answer that is not JSON. The SDK never follows a redirect and never reads its Location header. |
network |
No connection. |
timeout |
The whole request, headers and body included, did not finish within timeout_s (30 seconds by default), or wait() ended. |
rejected |
wait() found a rejected or cancelled change request. |
The SDK does not retry. You decide the retry policy. The SDK does not log.
The key
The client keeps the key in a name-mangled attribute. repr() and str() of the client do not print it. A traceback that shows local variables does not contain it either. This protects logs. It does not hide the key from other code in the same process. The SDK refuses a key that contains a carriage return, a line feed or a null character. It also refuses a custom user_agent that is not printable ASCII.
Not included
The SDK does not create, rename, or delete projects and environments. It does not wrap the organization endpoints. Use the dashboard or the HTTP API for those.
Tests
sdk-python/scripts/test.sh
The script runs the unit tests in Docker. The tests need no server.
Metadata
Release files for ohapi 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ohapi-0.1.0.tar.gz | 24.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ohapi-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 39.7 kB
Release files / ohapi-0.1.0.tar.gz
| Download URL | ohapi-0.1.0.tar.gz |
|---|---|
| Size | 24.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9ee28ff5f952c8c8b5e23c30c9ac73fe3743eac4481e886adc8b0639553c8d3a
|
|
BLAKE2b-256 checksum How to use checksums |
0db50c4788c8813fbb307a43ccd366350ccc0b4d34c60ce19ce63e6454dfa7c2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.11
|
Release files / ohapi-0.1.0-py3-none-any.whl
| Download URL | ohapi-0.1.0-py3-none-any.whl |
|---|---|
| Size | 15.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
36eeb08297c70199103f9602bc12aba3a3b21d889770ca5d3e8d4ad82cb35c78
|
|
BLAKE2b-256 checksum How to use checksums |
6c4e756dddd09a14e7974abb7a126a441846d441688e081b4d49b500d75e5ad2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.11
|