Profile + lightweight open_client¶
The Profile Pydantic model + open_client(profile) for PAT, Basic, and session-cookie auth live in dhis2w-client — no dhis2w-core install needed for library users embedding the client in their own Python tooling. OAuth2 still requires dhis2w-core because OAuth2 token refresh needs the concurrent-writer-safe token store; calling dhis2w_client.open_client(oauth2_profile) raises NotImplementedError pointing at dhis2w_core.open_client.
When to reach for this surface¶
- Embedding
dhis2w-clientin a third-party app (FastAPI service, script, notebook) with PAT, Basic, or session-cookie auth — you don't want the full CLI/MCP runtime weight (Typer, FastMCP, SQLAlchemy, bcrypt, questionary). - OAuth2 auth? Use
dhis2w_core.open_client(profile)instead — sameProfilemodel, full token-store-backed refresh. - Multi-profile TOML resolution? That lives in
dhis2w-core. Usedhis2w_core.profile_from_env()for the full precedence chain (TOML + env), or this module'sprofile_from_env_raw()for the env-only fallback that returnsNoneinstead of consulting TOML.
Worked example — pure dhis2w-client use¶
import asyncio
from dhis2w_client import NoProfileError, Profile, open_client, profile_from_env_raw
async def main() -> None:
"""Build a Profile (in-memory or from env) and open a connected client."""
profile = profile_from_env_raw()
if profile is None:
# No DHIS2_URL+credentials env — construct one explicitly:
profile = Profile(
base_url="https://play.im.dhis2.org/dev-2-43",
auth="pat",
token="d2pat_yourtoken",
)
try:
async with open_client(profile) as client:
me = await client.system.me()
info = await client.system.info()
print(f"Connected to DHIS2 {info.version} as {me.username}")
except NoProfileError as exc:
print(f"misconfigured: {exc}")
asyncio.run(main())
See examples/v{41,42,43}/client/profile_pat_pure_client.py for the runnable, version-pinned form.
Profile model¶
Profile
¶
Bases: BaseModel
Resolved DHIS2 connection settings for a single session.
version is a plugin-tree hint, NOT a wire-client pin. When set, CLI
and MCP bootstraps load the matching dhis2w_core.v{N}.plugins.* tree
(so v43 plugin overrides for BUGS #33/#34/#35 are picked up against a
v43 stack). The wire Dhis2Client always auto-detects the server's
version on connect and rebinds accessors via _dispatch.py —
profile.version doesn't override that. When unset, plugin discovery
falls back to DHIS2_VERSION env var (41/42/43), then to v42.
Source code in packages/dhis2w-client/src/dhis2w_client/profile.py
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | |
Attributes¶
cookie = Field(default=None, repr=False)
class-attribute
instance-attribute
¶
Raw Cookie header value for auth="session" (e.g. JSESSIONID=abc123; the name is included).
Kept out of repr() (Field(repr=False)) and masked in model_dump();
pass context={"reveal": True} to model_dump() to reveal it.
xsrf_token = Field(default=None, repr=False)
class-attribute
instance-attribute
¶
Optional double-submit CSRF token for auth="session" (echoed as the X-XSRF-TOKEN header).
DHIS2 issues an XSRF-TOKEN cookie and expects it echoed back as the
X-XSRF-TOKEN header; None means the instance doesn't require CSRF, so
the header is omitted. Kept out of repr() (Field(repr=False)) and masked
in model_dump(); pass context={"reveal": True} to reveal it.
version = None
class-attribute
instance-attribute
¶
Plugin-tree hint (see class docstring). Wire version is auto-detected on connect().
Exceptions¶
NoProfileError
¶
UnknownProfileError
¶
Bases: LookupError
Raised when a named profile is requested but does not exist in any profile file.
InvalidProfileNameError
¶
Constructors + helpers¶
profile_from_env_raw()
¶
Build a Profile from DHIS2_URL + credentials env vars.
Returns None when DHIS2_URL is unset or no credential pair is present.
Recognises DHIS2_PAT (PAT auth, wins over Basic) and the
DHIS2_USERNAME + DHIS2_PASSWORD pair (Basic auth). Reads
DHIS2_VERSION ("41" / "42" / "43" or "v41" / "v42" / "v43")
into Profile.version when set.
Library callers that want full TOML + env precedence resolution (the
chain the d2w CLI uses) should install dhis2w-core and call
dhis2w_core.profile_from_env() instead.
Source code in packages/dhis2w-client/src/dhis2w_client/profile.py
validate_profile_name(name)
¶
Validate and return a profile name.
Rules
- must not be empty
- first character must be an ASCII letter (A-Z, a-z)
- remaining characters must be letters, digits, or underscore
- max length 64 characters
Typical valid names: local, prod, prod_eu, test42, laohis42.
Raises InvalidProfileNameError on violation. The constraint keeps names
safe as env var suffixes, TOML keys, and unquoted shell arguments.
Source code in packages/dhis2w-client/src/dhis2w_client/profile.py
Open a client from a profile¶
build_auth_provider(profile) returns a PatAuth, BasicAuth, or SessionCookieAuth AuthProvider. open_client(profile) is the async context manager that wires that auth provider into a connected Dhis2Client. Both live at the top of dhis2w_client (re-exported from dhis2w_client.v42.client_context) and on each per-version surface (dhis2w_client.v41, dhis2w_client.v42, dhis2w_client.v43).
build_auth_provider(profile)
¶
Return a PatAuth, BasicAuth, or SessionCookieAuth provider for the profile.
Raises NotImplementedError on profile.auth == "oauth2" pointing at
dhis2w_core — OAuth2 needs the token-store machinery that lives there.
Raises ValueError when the matching credential fields are missing.
Source code in packages/dhis2w-client/src/dhis2w_client/v42/client_context.py
open_client(profile, *, allow_version_fallback=False, version=None, allow_version_mismatch=False, retry_policy=None, http_limits=None, system_cache_ttl=300.0, verify=True)
async
¶
Open a connected Dhis2Client for profile — PAT, Basic, or session auth only.
Yields a connected client inside async with. Raises NotImplementedError
on OAuth2 profiles — use dhis2w_core.open_client(profile, scope=..., profile_name=...)
for those.
allow_version_fallback (default False) mirrors the Dhis2Client
constructor: a server major with no generated tree raises
UnsupportedVersionError. Pass True to bind the nearest-lower
generated tree instead (never nearest-higher).
version (default None) pins the generated tree explicitly; connect()
then raises VersionPinMismatchError when the server reports a different
major unless allow_version_mismatch=True. Leave both at their defaults
to auto-detect from /api/system/info.
verify controls TLS certificate verification — pass False for a
self-signed staging box or a path to a custom CA bundle for a private-CA
instance. Default True.
retry_policy, http_limits, and system_cache_ttl mirror the
underlying Dhis2Client constructor — see its docstring for tuning.