Typed client for Phrase TMS (Memsource) generated from OpenAPI.
Project description
phrappy
Typed, batteries-included Python client for Phrase TMS (Memsource) generated from the public OpenAPI spec. Comes with both sync and async clients, fully equipped with first-class Pydantic v2 models.
The build process is fully automated and project release is planned to follow the Phrase TMS bi-weekly release cadence.
This project is not an official Phrase/Memsource SDK. Official documentation can be found at developers.phrase.com
Installation
pip install phrappy
Requirements: Python ≥ 3.10
Quickstart
1) Authenticate to get a token
Either use your authentication method of choice directly to get a token.
from phrappy import Phrappy
from phrappy.models import LoginDto
pp = Phrappy()
login_response = pp.authentication.login(LoginDto(
userName="your_name",
password="<password>"
))
token = login_response.token
pp.close()
All typed methods also accept dict inputs that are then validated under the hood. For example:
from phrappy import Phrappy
pp = Phrappy()
login_response = pp.authentication.login({
"userName":"your_name",
"password":"<password>"
})
token = login_response.token
pp.close()
Or use the convenience method for authenticating and getting a Phrappy instance that carries its token.
from phrappy import Phrappy
pp = Phrappy.from_creds(username="name@example.com", password="…")
me = pp.authentication.who_am_i()
print(me.user.uid)
pp.close()
Using a context manager closes the underlying HTTP client automatically:
from phrappy import Phrappy
with Phrappy(token="<YOUR_TOKEN>") as pp:
me = pp.authentication.who_am_i()
print(me.user.userName)
3) Async usage
import asyncio
from phrappy import AsyncPhrappy
async def main():
async with AsyncPhrappy(token="<YOUR_TOKEN>") as app:
me = await app.authentication.who_am_i()
print(me.user.userName)
asyncio.run(main())
Examples
Create a project and upload a job (multipart)
from pathlib import Path
from phrappy import Phrappy, cdh_generator
from phrappy.models import CreateProjectV3Dto, JobCreateRequestDto
with Phrappy(token="<YOUR_TOKEN>") as pp:
proj = pp.project.create_project_v3(CreateProjectV3Dto(name="Demo", sourceLang="en", targetLangs=["sv"]))
p = Path("example.txt"); p.write_text("Hello from phrappy")
jobs = pp.job.create_job(
project_uid=proj.uid,
content_disposition=cdh_generator(p.name),
file_bytes=p.read_bytes(),
memsource=JobCreateRequestDto(targetLangs=proj.targetLangs),
)
print([j.uid for j in jobs.jobs or []])
List your assigned projects
me = pp.authentication.who_am_i()
page = pp.project.list_assigned_projects(me.user.uid, target_lang=["sv"]) # returns a typed page model
for item in page.content or []:
print(item.name, item.status)
API design
- Typed models everywhere! Inputs/outputs are Pydantic v2 models generated from the OpenAPI. You can pass either a model instance or a
dictfor body/header parameters; the client will validate and coerce. - Rich method docstrings based on operation descriptions and typing information.
- Every operation exists in both
PhrappyandAsyncPhrappyunder the same tag-based namespaces. - Built on
httpxwith httpx.Client/httpx.AsyncClient under the hood. - Response models are lenient by default: unexpected fields in a Phrase TMS response are accepted and retained on the model (Pydantic
extra="allow") rather than raisingValidationError. This shields production callers from benign upstream drift when the API response diverges from the documented schema. Request models stay strict so caller typos fail fast. - Set
PHRAPPY_STRICT=1in the environment before importingphrappyto flip response models toextra="forbid"for CI drift detection. The setting is read once at import time; per-call toggling is not supported.
If you find a mismatch between the API behavior and the generated models, please open an issue with the request/response payloads (redacted) and the package version.
Configuration
- Defaults to
https://cloud.memsource.com/web. Override viaPhrappy(base_url=...)orAsyncPhrappy(base_url=...). - Pass
timeout=(seconds) to the client constructor. Per-request timeouts are also supported onmake_requestif you wrap custom calls.
Testing
The test suite has three layers:
- Offline fixture tests — captured Phrase TMS response bodies replayed through the generated Pydantic response models. Runs by default, no network, fast.
- Drift detection — same fixtures re-run under
PHRAPPY_STRICT=1to surface upstream schema divergence (unexpected fields, etc.) that lenient mode would silently tolerate. - Live tests — end-to-end against a real Phrase TMS account. Creates and deletes real resources, costs a handful of words per run.
# 1) offline suite (default — no credentials needed)
pytest -m "not live and not destructive" -q
# 2) drift detection — re-runs the offline suite under PHRAPPY_STRICT=1
scripts/run-strict-tests.sh
# 3) live tests — will create and delete assets in your account!
export PHRAPPY_TOKEN=ApiToken_...
pytest -m live -q
Strict mode is gated by tests/test_phrappy/fixtures/_drift_allowlist.json,
a hand-maintained list of fixture paths with known upstream schema drift
(fields Phrase returns that the OpenAPI spec doesn't declare). The strict
suite asserts both directions:
- Fixtures on the allowlist MUST raise
ValidationErrorin strict mode. If upstream fixes one, the test fails so the allowlist stays honest. - Fixtures not on the allowlist MUST validate cleanly. A new drifter fails loudly instead of being lost in the noise.
Env vars used by the live tests and the fixture-capture helper:
PHRAPPY_TOKENor (PHRAPPY_USERandPHRAPPY_PASSWORD)PHRAPPY_BASE_URL(optional)
Fixtures under tests/test_phrappy/fixtures/ are captured by
scripts/capture_fixtures.py (see its --dry-run output for what it
would capture). New fixtures are redacted of user-identifying fields
before they are committed.
Releasing
Releases are automated by .github/workflows/release.yml. The short version:
- Bump
__version__insrc/phrappy/_meta.py. - Add a
### X.Y.Zsection to the## Release notesbelow. - Commit to
main, runpytest -m "not live and not destructive"locally. git tag -a vX.Y.Z -m "phrappy vX.Y.Z" && git push origin main && git push origin vX.Y.Z.
The tagged push triggers regen + tests + python -m build + PyPI publish (OIDC trusted publishing) + sync to kuhnemann/phrappy + GitHub Release. Full runbook and the one-time credential setup are in tasks/issue-3/RELEASE.md on the builder repo.
Roadmap
- Complete the test suite
- Streaming uploads/downloads
- Convenience functions for AsyncJob interactions
- Toggle for type validation / raw dict input/output
Release notes
0.4.0
- Response models are now lenient by default (
extra="allow"); request models stay strict. SetPHRAPPY_STRICT=1before importing to flip response models toextra="forbid"for CI drift detection. - Models refreshed against Phrase TMS spec as of 2026-04-21 (12 bi-weekly releases of drift caught up). New
language_assetstag module. Manifest grew 401 → 409 operations. ~130 new hoisted enum classes inphrappy.models. - Release pipeline rewritten: tag-and-push releases via GitHub Actions, OIDC trusted publishing to PyPI, no host-local
copy_to_public.py. - Offline fixture-based test harness (295 captured response fixtures) with a drift allowlist that turns
PHRAPPY_STRICT=1into a CI regression gate.
0.3.0
- Models and operations as of Phrase TMS v25.21 per 28/10 2025. Fixed alias handling.
0.2.0
- Improved naming of enums that are hoisted from schema inline anonymous declarations.
0.1.0
- Complete rewrite of build pipeline with fully automated and repeatable builds.
- Support for polymorph input and output schemas.
- Slight change in API surface due to schema naming normalization.
- Added context manager support.
- Minimal test suite implemented.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file phrappy-0.4.0.tar.gz.
File metadata
- Download URL: phrappy-0.4.0.tar.gz
- Upload date:
- Size: 213.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
96defd5388670dbe4a21aa4e3fb660a782568fb05855a614230395d6bf72fb3a
|
|
| MD5 |
9c145460e64983339d77a77335d7502d
|
|
| BLAKE2b-256 |
ca0092a361450aad0244ab8ee9e975debd9782f8152aed74370d5e10b266b1bb
|
Provenance
The following attestation bundles were made for phrappy-0.4.0.tar.gz:
Publisher:
release.yml on kuhnemann/phrappy-builder
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
phrappy-0.4.0.tar.gz -
Subject digest:
96defd5388670dbe4a21aa4e3fb660a782568fb05855a614230395d6bf72fb3a - Sigstore transparency entry: 1354469789
- Sigstore integration time:
-
Permalink:
kuhnemann/phrappy-builder@a095168977447392dd3de6e6d4c977d5f35da1fe -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/kuhnemann
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a095168977447392dd3de6e6d4c977d5f35da1fe -
Trigger Event:
push
-
Statement type:
File details
Details for the file phrappy-0.4.0-py3-none-any.whl.
File metadata
- Download URL: phrappy-0.4.0-py3-none-any.whl
- Upload date:
- Size: 294.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
420be2dd4fc189c4433e73d780ab1faf2e4354104a7b659e9b4f4517916bf815
|
|
| MD5 |
dce3b37fba1675f3ef8dc224f1806eb3
|
|
| BLAKE2b-256 |
16f008650e0e0cdf35f51702cae73f3a56cd2ff13d687e1f168eb8056c49299d
|
Provenance
The following attestation bundles were made for phrappy-0.4.0-py3-none-any.whl:
Publisher:
release.yml on kuhnemann/phrappy-builder
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
phrappy-0.4.0-py3-none-any.whl -
Subject digest:
420be2dd4fc189c4433e73d780ab1faf2e4354104a7b659e9b4f4517916bf815 - Sigstore transparency entry: 1354469979
- Sigstore integration time:
-
Permalink:
kuhnemann/phrappy-builder@a095168977447392dd3de6e6d4c977d5f35da1fe -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/kuhnemann
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a095168977447392dd3de6e6d4c977d5f35da1fe -
Trigger Event:
push
-
Statement type: