Skip to main content

stapel-vocabularies

CI coverage pypi downloads python license llms.txt

Reference vocabularies too large to inline into a category's feature schema — 14 962 phone models, 107 049 car modifications — stored as levels, terms and parent/child edges rather than as paths, so one 'black' is shared by every model that comes in black and can be translated once and faceted on. Ships the public typeahead and cascade reads a listing composer needs (ETag'd on a revision, anonymous, no cookie), the two VocabularyResolver implementations stapel-attributes' ref_select / ref_hierarchical_select types validate values through (in-process and over comm), a batched transactional loader for reviewed fixtures that spends one revision and one event per file, and Django-free converters that turn a vendor's nested XML or CSV catalogue into such a fixture without loading the document into memory.

Part of the Stapel framework — composable Django apps that deploy as a monolith or as microservices without changing module code.

Install

pip install stapel-vocabularies

At a glance

Fact Value
Version 0.1.2
Python >=3.11 (3.11, 3.12, 3.13, 3.14)
HTTP operations 4
Config axes 1
Usage surface 13
Extension points 5
Error codes 45
Fleet dependencies stapel-attributes · stapel-categories (optional) · stapel-core

Documentation

OpenAPI · capabilities.json · llms.txt (for agents)

What this is

Some option lists are too big to be options. A phone catalogue has 14 962 models; a car catalogue has 107 049 modifications. Inlining those into a category's feature schema is not a tuning question — the schema is fetched on every form render, and it would be megabytes.

stapel-vocabularies is where they live instead.

  • Levels, terms and edges — not paths. Vendor → Model → MemorySize → Color is 56 921 distinct paths in a real phone catalogue and only 15 844 distinct terms. Storing the terms and the parent/child edges between them means one Color=chernyy shared by every model that comes in black: 17 colours to translate instead of 56 921 path nodes, and a facet on a colour code that is answerable at all.
  • A read surface built for a typeahead. terms/?level=Model&parent=apple&q=pro answers a page with total and has_children, prefix matches ranked first. Anonymous, ETag'd on the vocabulary's revision, Cache-Control: public, and no Set-Cookie — so the shared cache in front of it works and a crawler does not start a session per request.
  • Two resolvers, one protocol. ref_select / ref_hierarchical_select in stapel-attributes validate values through a VocabularyResolver. OrmResolver answers from these tables and is registered at startup; CommResolver answers the same questions over the bus, for a service that validates listings but holds no catalogues. Both cache describe by revision, so a re-imported catalogue stops validating against the levels it used to have the moment the import commits.
  • Loading is data plumbing, not an admin screen. manage.py load_vocabulary phones.json is one transaction, one revision increment and one vocabulary.changed event for the whole file, whatever its size. The real phone catalogue — 15 844 terms, 39 749 edges — loads in ~1.2 s.
  • Converters that do not read the file into memory. A vendor's nested XML or a one-path-per-row CSV becomes a reviewable fixture, streamed through iterparse, with codes assigned deterministically (Cyrillic transliterated, collisions numbered in label sort order) so re-converting an unchanged catalogue produces an unchanged diff.

Alpha. See MODULE.md for the agent-facing map of seams.

Quick start

pip install stapel-vocabularies
# settings.py
INSTALLED_APPS = [..., "stapel_vocabularies"]

# urls.py
path("vocabularies/", include("stapel_vocabularies.urls"))   # -> /vocabularies/api/v1/...
python manage.py convert_vocabulary phone_catalog.xml \
    --slug phone-models --name "Phone models" --out fixtures/phone-models.json
python manage.py load_vocabulary fixtures/phone-models.json --replace

A feature then points at it instead of carrying options:

{"type": "ref_select", "optionsRef": {"vocabulary": "phone-models",
                                      "level": "Model",
                                      "parentFeature": "vendor"}}

API

Method Path What
GET /vocabularies/api/v1/vocabularies/ every vocabulary: {slug, name, levels, term_count, revision}
GET /vocabularies/api/v1/vocabularies/{slug}/ one of them
GET /vocabularies/api/v1/vocabularies/{slug}/terms/ ?level= (required), ?parent=, ?q=, ?limit= (≤200, default 50), ?offset={results: [{code, label, level, has_children}], total}
GET /vocabularies/api/v1/vocabularies/{slug}/terms/resolve/ ?level=&codes=a,b,c (≤200) → {code: label}, unknown codes omitted

Accept-Language selects a translated label where the term carries one; the response Varys on it and the ETag covers it.

The fixture format

One file per vocabulary, byte-stable, reviewed as code (schema):

{ "slug": "phone-models", "name": "Phone models", "source": "https://…/phone_catalog.xml",
  "levels": [{"name": "Vendor"}, {"name": "Model", "parent": "Vendor"}],
  "terms": [["Vendor", "apple", "Apple", null], ["Model", "iphone-10", "iPhone 10", null]],
  "edges": [["Vendor", "apple", "Model", "iphone-10"]] }

A level's parent must be declared before it. That single rule is the whole acyclicity argument: a level can only point backwards, so no chain of parents can return to where it started.

License

MIT — see LICENSE.


This page is assembled by stapel-readme from docs/readme.md plus the contract artifacts in docs/. Edit the prose in docs/readme.md; the badges, facts and links above and below it are generated — do not hand-edit README.md.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stapel_vocabularies-0.1.2.tar.gz (70.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stapel_vocabularies-0.1.2-py3-none-any.whl (64.4 kB view details)

Uploaded Python 3

File details

Details for the file stapel_vocabularies-0.1.2.tar.gz.

File metadata

  • Download URL: stapel_vocabularies-0.1.2.tar.gz
  • Upload date:
  • Size: 70.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for stapel_vocabularies-0.1.2.tar.gz
Algorithm Hash digest
SHA256 9e9fe4e1a67a6b973f6595216f03da1b5efe0831b3accb3fb361ee30c819d6ae
MD5 d00d48da523016a14bdb1fc2d02af403
BLAKE2b-256 01fcf6600e2809deacee6a1da51e3a2212d592d7a8242662f588e88f5c859819

See more details on using hashes here.

Provenance

The following attestation bundles were made for stapel_vocabularies-0.1.2.tar.gz:

Publisher: publish.yml on usestapel/stapel-vocabularies

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file stapel_vocabularies-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for stapel_vocabularies-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 3ebb6c154bef0a1c61c1fc3a9837d84a0b7aa1642dd06526be435934b847f39a
MD5 7fc0839143535f979f8384765b76458c
BLAKE2b-256 9d8efb10d1353289776d753324ea1464a11ec36f941676ef9133c15ac0f2fb6a

See more details on using hashes here.

Provenance

The following attestation bundles were made for stapel_vocabularies-0.1.2-py3-none-any.whl:

Publisher: publish.yml on usestapel/stapel-vocabularies

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.1

2 files

0.2.0

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

This release

0.1.2 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page