This release is a pre-release and may not be stable for production use.
coho-codegen
Generates a typed content library from your Coho CMS content types: a Pydantic model per content type, and a client whose reads return them. And creates that library, from the template.
It belongs in your content library, the package your sites and services install, as a development dependency beside coho-delivery-sdk. coho-codegen create sets that library up for you:
uvx coho-codegen create acme-content
cd acme-content
uv sync
uv run coho-codegen init --url https://app.coho.example --ref v1.4.0
Commands
coho-codegen create <dir> # a new content library from the template, named after <dir>
coho-codegen init # point the library at a project: asks Coho whose token it is
coho-codegen pull # fetch the type model for the configured ref into coho-ir.json
coho-codegen generate # write the library's coho/ package from coho-ir.json
coho-codegen check # fail if coho/ differs from what generate would write
Commit coho-ir.json and the generated package. generate removes the modules of types no longer on the ref, and leaves anything it did not write. A schema change then shows up as a diff in review, and check in CI catches generated code that is out of date. generate and check never call the server.
init and pull need a project token with read access, coho token create --role viewer. init takes it with --token, from COHO_TOKEN, or by asking, checks it with Coho, writes the account and project it is for into the config, and the token into .env. pull reads COHO_TOKEN from the environment, or for local runs from a .env beside the config, and never from the config itself. Keep it in CI secrets and out of every app that installs the library. pull --ref v1.4.0 pulls another tag than the config names. An unchanged type model costs a 304.
Configuration
coho.config.json, beside the library's pyproject.toml. Paths are relative to it.
{
"url": "https://app.coho.example",
"account": "0192…",
"project": "0192…",
"ref": "v1.4.0",
"typeModel": "coho-ir.json",
"outDir": "src/acme_content/coho"
}
Generate from a tag for a release. A branch's types can change, and generate warns when the model came from one.
What is generated
import os
from coho_delivery_sdk import config_from_env
from acme_content import Coho, resolved
coho = Coho(config_from_env(os.environ))
post = coho.blog_post.by_slug("hello-world", depth=2)
latest = coho.blog_post.list(order="-publishedAt", limit=3)
- One model per content type, in its own module, named from its slug:
blogPostbecomesBlogPost, inblog_post.py. The package's__init__.pyexports them all, withCohoandAsyncCoho, the same reads awaited. Each model derives fromEntryBase, withid,type,slugandlocalebeside its fields. - Fields have Python names, aliased to Coho's:
publishedAtispublished_at. A field whose name would be a keyword, a system value or one of Pydantic's gets_after it,class_, and keeps its id on the wire. - A field is optional when delivery can leave it out, which includes every localized field. A list is never
None: it is empty. A choice is aLiteralof its values, or any otherstr, so a value added after generation never fails to validate. - References are typed by their targets:
Linked[TeamMember], which is the entry, or a stub saying why it is not there. - An asset field is an
Asset | None, or alist[Asset]when it holds several, and anImageAssetwhen it accepts only raster images, which always have dimensions and resize. A single asset is optional even when required, because an asset deleted on the ref is delivered asNone; in a list it is left out. - A repeater holds typed items. Each component type is a model of its own,
Citationincitation.py, deriving fromItemBase, and a repeater is a list of them:list[Citation], orlist[Callout | Quote]when it holds several. An item of a component added after the code was generated is left out of the list, and reported. A repeater whose components are not on the ref holdsRepeaterItems. - A type that extends another derives from its model.
class ServicePage(Page)declares only what it adds or tightens. A type that others extend is read and linked as its family,AnyPage = Page | ServicePage | …, and each entry comes back as its own model, soisinstancetells them apart. - A trait is a
Protocolof the fields it declares,Seoinseo.py, which every type with it satisfies, tomypyand toisinstance. So a function that takes aSeotakes an entry of any type with the trait. orderaccepts only fields the type can be sorted by:BlogPostOrder, aLiteralof each sortable field and its-descending form, so an unsortable order is a type error rather than a refused request.AnyEntryis the union of every type an entry can be;content_typeslists their slugs;sourcenames the ref and schema the code was generated from.- The library reads no environment. The app passes its config to
Coho.
If a type's name would collide with one the generated code defines (Coho, AsyncCoho, AnyEntry, Linked, Unresolved, RepeaterItem), its model gets Entry after it. A type read as one of Coho's own members (by_slug, by_id, by_ids, content, close, aclose) is read as coho.<name>_type. A family whose Any<Type> name a type already has is named <Type>Family. generate says when any of these happens.
License
Apache-2.0
Metadata
Release files for coho-codegen 2026.1.0a1
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 | |
|---|---|---|---|---|
| coho_codegen-2026.1.0a1.tar.gz | 51.2 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| coho_codegen-2026.1.0a1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 99.8 kB
Release files / coho_codegen-2026.1.0a1.tar.gz
| Download URL | coho_codegen-2026.1.0a1.tar.gz |
|---|---|
| Size | 51.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e4b3c98ae1a1ba06b86f95a06e9399ec5678abe96bc791a29d0524a0b7f5ec88
|
|
BLAKE2b-256 checksum How to use checksums |
d7765560b53757cff4abb6e6abbed67e660475e8bed9af3be8cc5a16c0a9a011
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logRelease files / coho_codegen-2026.1.0a1-py3-none-any.whl
| Download URL | coho_codegen-2026.1.0a1-py3-none-any.whl |
|---|---|
| Size | 48.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d6ac789f51d9ad9335535bfe3931e38dcf2d12a03a101fcba543edf207e7c821
|
|
BLAKE2b-256 checksum How to use checksums |
a8439d55dbcd6bc1c171c9d1d40a3115f93e808a8a6c025464ea26f50fdbb92c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logSecurity for coho-codegen 2026.1.0a1
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for coho_codegen-2026.1.0a1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logA 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.