# https://github.com/beanbaginc/python-typelets Project Manual

Generated at: 2026-06-20 05:57:27 UTC

## Table of Contents

- [Overview and Module Architecture](#page-1)
- [General Python Typing Utilities](#page-2)
- [Django Typing Extensions](#page-3)
- [Versioning, Releases, and Contributing](#page-4)

<a id='page-1'></a>

## Overview and Module Architecture

### Related Pages

Related topics: [General Python Typing Utilities](#page-2), [Django Typing Extensions](#page-3)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/beanbaginc/python-typelets/blob/main/README.md)
- [typelets/__init__.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/__init__.py)
- [typelets/_version.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/_version.py)
- [typelets/json.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/json.py)
- [typelets/funcs.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/funcs.py)
- [typelets/symbols.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/symbols.py)
- [typelets/runtime.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/runtime.py)
- [typelets/django/json.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/json.py)
- [typelets/django/urls.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/urls.py)
- [typelets/django/forms.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/forms.py)
- [typelets/django/strings.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/strings.py)
- [typelets/django/models.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/models.py)
- [doc-requirements.txt](https://github.com/beanbaginc/python-typelets/blob/main/doc-requirements.txt)
</details>

# Overview and Module Architecture

## Purpose and Scope

Typelets is a Python typing utility module that augments built-in Python types and types provided by third-party libraries, with a strong emphasis on the Django web framework. The project was originally developed to support the [Review Board](https://www.reviewboard.org) codebase and has been extracted into a reusable package under the [Beanbag](https://www.beanbaginc.com) organization.

According to [README.md](https://github.com/beanbaginc/python-typelets/blob/main/README.md), Typelets is published on PyPI as the `typelets` package and follows [semantic versioning](https://semver.org/). The module is intentionally *not* a runtime library in the traditional sense — most of its public surface consists of `TypeAlias` declarations and lightweight runtime helpers used to express static type information.

## High-Level Architecture

The package is organized into two tiers:

1. **General Python types** under the top-level `typelets` namespace.
2. **Django-specific types** under the `typelets.django` sub-namespace, which depend on Django being importable and build upon the general tier.

The top-level package entry point ([typelets/__init__.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/__init__.py)) re-exports only version-related symbols (`VERSION`, `__version__`, `__version_info__`, `get_package_version`, `get_version_string`, `is_release`) sourced from [typelets/_version.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/_version.py). This means **no type aliases are re-exported from the package root**; consumers must import them from their specific submodule, for example:

```python
from typelets.json import JSONDict
from typelets.django.forms import FormData
```

The following diagram summarizes how the modules relate:

```mermaid
graph TD
    A[typelets/__init__.py] --> B[typelets/_version.py]
    C[typelets/funcs.py] --> C1[ParamSpec, MethodDirective]
    D[typelets/json.py] --> D1[JSONValue, JSONDict, SerializableJSONValue]
    E[typelets/symbols.py] --> E1[UNSET, Unsettable]
    F[typelets/runtime.py] --> F1[raise_invalid_type]
    G[typelets/django/json.py] --> D
    H[typelets/django/urls.py] --> H1[AnyURL]
    I[typelets/django/forms.py] --> I1[FormData, FormFiles]
    J[typelets/django/strings.py] --> J1[StrOrPromise, StrPromise]
    K[typelets/django/models.py] --> K1[ModelAnyPK, ModelIntPK]
    G --> J
```

## Submodule Catalog

The following table summarizes each public submodule, the types it exposes, and the layer (general vs. Django-specific) it belongs to. This information is assembled from each module's leading docstring and `TypeAlias` declarations.

| Submodule | Layer | Key Public Names | Source |
|---|---|---|---|
| `typelets.funcs` | General | `KwargsDict`, `TParams`, `TReturn_co`, `TOwner`, `MethodDirective` | [typelets/funcs.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/funcs.py) |
| `typelets.json` | General | `JSONValue`, `JSONDict`, `JSONDictImmutable`, `JSONList`, `JSONListImmutable`, `BaseSerializableJSONValue`, `BaseSerializableJSONDict`, `BaseSerializableJSONDictImmutable`, `BaseSerializableJSONList`, `BaseSerializableJSONListImmutable` | [typelets/json.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/json.py) |
| `typelets.runtime` | General | `raise_invalid_type` | [typelets/runtime.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/runtime.py) |
| `typelets.symbols` | General | `UNSET`, `UnsetSymbol.UNSET`, `Unsettable` | [typelets/symbols.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/symbols.py) |
| `typelets.django.auth` | Django | User-related types | [README.md](https://github.com/beanbaginc/python-typelets/blob/main/README.md) |
| `typelets.django.forms` | Django | `FormData`, `FormFiles` | [typelets/django/forms.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/forms.py) |
| `typelets.django.json` | Django | `SerializableDjangoJSONValue`, `SerializableDjangoJSONDict`, … | [typelets/django/json.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/json.py) |
| `typelets.django.models` | Django | `ModelAnyPK`, `ModelIntPK` | [typelets/django/models.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/models.py) |
| `typelets.django.strings` | Django | `StrOrPromise`, `StrPromise` | [typelets/django/strings.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/strings.py) |
| `typelets.django.urls` | Django | `AnyURL` | [typelets/django/urls.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/urls.py) |

## Design Conventions and Dependencies

### Type-only declarations

Nearly every file uses `from __future__ import annotations` and the `TYPE_CHECKING` guard pattern. For example, [typelets/funcs.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/funcs.py) imports `Callable`, `Concatenate`, and `TypeAlias` under `if TYPE_CHECKING:` so the `typing_extensions` backports are evaluated only by static analyzers, not at runtime. This keeps Typelets usable on any modern Python version without forcing a hard dependency on `typing_extensions` at import time.

### Generic, consumer-supplied extension points

The JSON layer ([typelets/json.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/json.py)) deliberately exposes *base* types parameterized by a `TypeVar` (`_T`) — `BaseSerializableJSONValue`, `BaseSerializableJSONDict`, etc. Consumers define their own `TypeAlias` against these generics to inject additional serializable types such as `datetime` or `UUID`. The Django JSON submodule ([typelets/django/json.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/json.py)) is itself a concrete instantiation of this pattern: it parameterizes the base types with a union of `Decimal`, `StrPromise`, `UUID`, `date`, `datetime`, `time`, and `timedelta`.

### Optional runtime helper

The only meaningfully runtime-impacting utility is `raise_invalid_type` in [typelets/runtime.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/runtime.py). It accepts `Never` as its first parameter so a type checker treats the call as the terminus of a branch (like `assert_never`), while still allowing the developer to choose the exception type (defaulting to `ValueError`) and a custom message — useful for public APIs where `AssertionError` would be a poor signal.

### Versioning

[typelets/_version.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/_version.py) stores the version as a six-element tuple `(Major, Minor, Micro, tag, release_num, released)`. The current build is `(1, 2, 0, 'alpha', 0, False)`. `get_version_string()` produces a human-readable string such as `1.2 alpha 0 (dev)`, while `get_package_version()` produces a PEP 440 / PyPI-compatible identifier such as `1.2a0`. `is_release()` returns the released flag, which controls whether the display version is suffixed with `(dev)`.

### Documentation build

[doc-requirements.txt](https://github.com/beanbaginc/python-typelets/blob/main/doc-requirements.txt) pins the documentation stack: `beanbag-docutils~=2.4` for the Beanbag Sphinx extensions, `furo` for the theme, `sphinx-copybutton` for clipboard buttons, and `sphinx~=7.3.0`. The hosted output lives at [typelets.readthedocs.io](https://typelets.readthedocs.io/), which is where every submodule's `Version Added` annotation links back to.

## Common Failure Modes

A few patterns that can trip up first-time consumers:

- **Importing from the top-level package fails.** Because [typelets/__init__.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/__init__.py) does not re-export any type aliases, `from typelets import JSONDict` will raise `ImportError`. Always import from the specific submodule.
- **Django submodules require Django installed.** Any import under `typelets.django.*` will fail with `ModuleNotFoundError` if Django is not present in the environment. Importing the general tier is safe even without Django.
- **`raise_invalid_type` is typed as returning `Never`.** Static analyzers may complain if the calling site does not already treat the surrounding branch as unreachable. The function is documented as a replacement for `typing.assert_never` in API code where `AssertionError` is inappropriate.

## See Also

- [JSON and Serialization Types](https://github.com/beanbaginc/python-typelets/blob/main/typelets/json.py)
- [Symbols and Unsettable Values](https://github.com/beanbaginc/python-typelets/blob/main/typelets/symbols.py)
- [Runtime Type Helpers](https://github.com/beanbaginc/python-typelets/blob/main/typelets/runtime.py)
- [Project README](https://github.com/beanbaginc/python-typelets/blob/main/README.md)

---

<a id='page-2'></a>

## General Python Typing Utilities

### Related Pages

Related topics: [Overview and Module Architecture](#page-1), [Django Typing Extensions](#page-3)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [typelets/funcs.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/funcs.py)
- [typelets/json.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/json.py)
- [typelets/symbols.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/symbols.py)
- [typelets/runtime.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/runtime.py)
- [typelets/__init__.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/__init__.py)
- [README.md](https://github.com/beanbaginc/python-typelets/blob/main/README.md)
</details>

# General Python Typing Utilities

## Overview

`python-typelets` is a Python typing utility module designed to augment the standard and third-party types provided by Python and its ecosystem. It originated as supporting infrastructure for [Review Board](https://www.reviewboard.org/) and is published for broader reuse under the MIT license. Source: [README.md](https://github.com/beanbaginc/python-typelets/blob/main/README.md)

The repository is organized into two broad categories: **general Python utilities** (covered on this page) and **Django-specific utilities**. The general utilities are split across four modules:

| Module | Purpose |
|---|---|
| `typelets.funcs` | Typing for callable signatures, methods, and keyword arguments |
| `typelets.json` | Typing for JSON structures and JSON-serializable application data |
| `typelets.symbols` | Sentinels and type aliases for marking unset / unsettable values |
| `typelets.runtime` | Helpers for runtime type-checking with static-typing compatibility |

These are all exposed through the package top-level import: `typelets/__init__.py` re-exports only the version metadata (`VERSION`, `__version__`, `get_version_string`, `is_release`, etc.), while the modules above are intended to be imported by their subpath (for example, `from typelets.funcs import KwargsDict`). Source: [typelets/__init__.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/__init__.py)

```mermaid
flowchart LR
    A[typelets.funcs] -->|Function & method typing| B[Static Type Checkers]
    C[typelets.json] -->|JSON structure & serializable value typing| B
    D[typelets.symbols] -->|Unset sentinel & Unsettable alias| B
    E[typelets.runtime] -->|raise_invalid_type type-guard| B
    A -.uses.-> A
    C -.generic over.-> C
```

## Function Typing (`typelets.funcs`)

The `funcs` module provides primitives for typing functions, methods, and their parameters.

### Core Type Variables

Three reusable `TypeVar`/`ParamSpec` aliases anchor the module:

- `TParams = ParamSpec('TParams')` — a `ParamSpec` representing an arbitrary callable's parameter list. Source: [typelets/funcs.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/funcs.py)
- `TReturn_co = TypeVar('TReturn_co', covariant=True)` — a covariant return type.
- `TOwner = TypeVar('TOwner', bound=object)` — the owner class of a method.

### `KwargsDict`

`KwargsDict: TypeAlias = Dict[str, Any]` is the canonical typing for "a dictionary of keyword arguments". It lets callers and callees share a single, unambiguous type when functions accept arbitrary keyword arguments. Source: [typelets/funcs.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/funcs.py)

### `MethodDirective`

`MethodDirective` is a `Protocol[TOwner, TParams, TReturn_co]` that acts as a typed descriptor-like placeholder for a method on a class. It exposes an `__self__: TOwner` attribute and overloads `__get__` so that accessing the directive returns either `Callable[Concatenate[TOwner, TParams], TReturn_co]` (unbound) or `Callable[TParams, TReturn_co]` (bound). This is useful when returning "a method of class X" from a function while preserving full static typing. Source: [typelets/funcs.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/funcs.py)

### `BaseParamsFromFunc`

`BaseParamsFromFunc(Generic[TParams])` is the base class for objects whose `__call__` signature should be re-typed to match another function or method. It is meant to be paired with the `type_func_params_as` and `type_method_params_as` decorators (not shown in the provided source, but referenced in the docstring). The class is generic over `TParams`, so a subclass can inherit the `ParamSpec` of a target function. Source: [typelets/funcs.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/funcs.py)

The docstring enumerates five rules that subclasses must follow when implementing `__call__`:

1. Include `self` even if the wrapped function does not.
2. Always annotate the return type as `Any` regardless of the decorated function.
3. Preserve positional-only `/` and variadic `*` markers from the wrapped function.
4. Always end the signature with `*args: TParams.args, **kwargs: TParams.kwargs`.
5. Positional-only markers combined with class decoration may require `# type: ignore`.

## JSON Typing (`typelets.json`)

The `json` module provides a hierarchy of type aliases describing JSON-shaped data and an extension point for JSON-serializable application types.

### Native JSON Types

The base types describe values that JSON natively supports:

- `JSONValue: TypeAlias = Union[JSONDict, JSONDictImmutable, JSONList, JSONListImmutable, None, bool, float, int, str]` — any JSON-encodable value. Source: [typelets/json.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/json.py)
- `JSONDict: TypeAlias = Dict[str, JSONValue]` — a mutable string-keyed JSON object.
- `JSONDictImmutable: TypeAlias = Mapping[str, JSONValue]` — an immutable variant, recommended for return types where mutation should be discouraged.

The accompanying `JSONList` / `JSONListImmutable` aliases (referenced in the union) provide the equivalent for list-shaped JSON.

### Extensible Serializable Types

A second tier of aliases is parameterized by an application-defined type variable `_T`:

- `BaseSerializableJSONValue[_T]` — adds `_T` to the `JSONValue` union.
- `BaseSerializableJSONDict[_T]` — `Dict[str, BaseSerializableJSONValue[_T]]`.
- `BaseSerializableJSONDictImmutable[_T]` — the immutable mapping variant.
- `BaseSerializableJSONList[_T]` / `BaseSerializableJSONListImmutable[_T]` — list variants.

Consumers are expected to create their own concrete alias from these bases, for example:

```python
SerializableJSONValue = BaseSerializableJSONValue[Union[datetime, UUID]]
SerializableJSONDict = BaseSerializableJSONDict[Union[datetime, UUID]]
```

This pattern keeps the native JSON contract strict while letting consumers document exactly which extra types their serializers accept. Source: [typelets/json.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/json.py)

## Symbols (`typelets.symbols`)

The `symbols` module centralizes sentinel values that distinguish "no value provided" from falsy or `None` values.

- `UnsetSymbol` is an `Enum` with a single member `UNSET = '<UNSET>'`.
- The exported `UNSET` constant is typed `Final[Literal[UnsetSymbol.UNSET]]`, so static type checkers narrow it precisely to that enum member. Source: [typelets/symbols.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/symbols.py)
- `Unsettable[_T]: TypeAlias = Union[Literal[UnsetSymbol.UNSET], _T]` lets a function declare a parameter that can be either explicitly unset or a real value of type `_T`. The docstring example annotates an `__init__(self, value: Unsettable[str])` parameter.

This is especially useful for `__init__` methods and configuration APIs that must distinguish "argument omitted" from "argument passed as `None`" or `False`.

## Runtime Utilities (`typelets.runtime`)

The `runtime` module exposes a single helper, `raise_invalid_type(value: Never, message: str, exception_type: type[Exception] = ValueError) -> Never`. Source: [typelets/runtime.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/runtime.py)

It is designed to act as a **type guard** in code paths that static checkers consider unreachable. Unlike `typing.assert_never`, which raises `AssertionError`, `raise_invalid_type` lets callers choose the exception class and message, making it more appropriate for public API argument validation. Because its parameter is annotated `Never`, a type checker treats every call site as one that definitely raises, so subsequent code in the same branch is considered unreachable without any runtime check.

## Common Patterns and Failure Modes

- **Decorating classes with `BaseParamsFromFunc`** can trigger type errors when the wrapped function uses positional-only arguments; the module's docstring recommends a targeted `# type: ignore` on the decorator line. Source: [typelets/funcs.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/funcs.py)
- **JSON serializable types** require a parallel alias per container shape — `BaseSerializableJSONValue`, `BaseSerializableJSONDict`, and `BaseSerializableJSONDictImmutable` must each be specialized if you want consistent coverage. Source: [typelets/json.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/json.py)
- **`UNSET` comparison** relies on `UnsetSymbol.UNSET` being a singleton; do not instantiate the enum elsewhere.
- **`raise_invalid_type`** is purely a static-typing convenience — it still raises whatever exception class you pass at runtime, so behavior in tests depends on the supplied `exception_type`. Source: [typelets/runtime.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/runtime.py)

## See Also

- [Typelets documentation on Read the Docs](https://typelets.readthedocs.io/)
- `typelets.django.auth` — `AnyUser` for logged-in or anonymous Django users. Source: [typelets/django/auth.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/auth.py)
- `typelets.django.forms` — `FormData` and `FormFiles` for form handling. Source: [typelets/django/forms.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/forms.py)
- `typelets.django.json` — `SerializableDjangoJSONValue` built on top of `typelets.json`. Source: [typelets/django/json.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/json.py)
- `typelets.django.models` — `ModelAnyPK` / `ModelIntPK` aliases. Source: [typelets/django/models.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/models.py)
- `typelets.django.strings` — `StrOrPromise` / `StrPromise`. Source: [typelets/django/strings.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/strings.py)
- `typelets.django.urls` — `AnyURL` for `URLPattern | URLResolver`. Source: [typelets/django/urls.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/urls.py)

---

<a id='page-3'></a>

## Django Typing Extensions

### Related Pages

Related topics: [Overview and Module Architecture](#page-1), [General Python Typing Utilities](#page-2)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [typelets/django/strings.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/strings.py)
- [typelets/django/json.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/json.py)
- [typelets/django/urls.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/urls.py)
- [typelets/django/forms.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/forms.py)
- [typelets/django/models.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/models.py)
- [typelets/django/auth.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/auth.py)
</details>

# Django Typing Extensions

## Overview

The `typelets.django` subpackage augments Python and `typing_extensions` with Django-aware type aliases. These aliases are used to give static type checkers (and reviewers reading annotations) a more precise picture of the values exchanged with Django's runtime, including localized strings, URL routing objects, form payloads, model primary keys, authenticated users, and JSON-serializable values.

The Django subpackage is one of two top-level groups advertised by the project. According to [README.md:9-25](https://github.com/beanbaginc/python-typelets/blob/main/README.md), Typelets also ships general-purpose Python helpers (`funcs`, `json`, `runtime`, `symbols`); the Django modules complement those by re-binding Django's runtime types into stable, importable aliases.

All six Django modules are short, declarative, and rely on `typing_extensions.TypeAlias` (introduced in PEP 613), so they participate cleanly in `from __future__ import annotations` style code. The package does not implement any runtime behavior beyond re-exporting the alias; each module is a pure typing shim.

## Module Catalog

The six Django modules are small and self-contained. The table below summarizes the public alias each one contributes.

| Module | Public Alias | Underlying Type | Purpose |
| --- | --- | --- | --- |
| `typelets.django.auth` | `AnyUser` | `Union[AbstractBaseUser, AnonymousUser]` | Authenticated or anonymous user |
| `typelets.django.forms` | `FormData` | `Mapping[str, Any]` | Posted form data dict |
| `typelets.django.forms` | `FormFiles` | `MultiValueDict[str, UploadedFile]` | Posted form files |
| `typelets.django.json` | `SerializableDjangoJSONValue` | `BaseSerializableJSONValue[_SerializableJSONValueTypes]` | JSON values supported by `DjbletsJSONEncoder` |
| `typelets.django.json` | `SerializableDjangoJSONDict` | `BaseSerializableJSONDict[_SerializableJSONValueTypes]` | Dict of such values |
| `typelets.django.json` | `SerializableDjangoJSONListImmutable` | `BaseSerializableJSONListImmutable[_SerializableJSONValueTypes]` | Immutable list of such values |
| `typelets.django.models` | `ModelAnyPK` | `Any` | Any Django primary key |
| `typelets.django.models` | `ModelIntPK` | `int` | Integer Django primary key |
| `typelets.django.strings` | `StrOrPromise` | `Union[str, _StrPromise]` | Eager or lazy localized string |
| `typelets.django.strings` | `StrPromise` | `_StrPromise` | Lazy localized string |
| `typelets.django.urls` | `AnyURL` | `Union[URLPattern, URLResolver]` | URL pattern or resolver |

Sources: [typelets/django/auth.py:14-15](), [typelets/django/forms.py:18-26](), [typelets/django/json.py:32-49](), [typelets/django/models.py:24-42](), [typelets/django/strings.py:35-43](), [typelets/django/urls.py:21-26]().

## Localized Strings (`typelets.django.strings`)

Django exposes eager translations through `gettext` and lazy translations through `gettext_lazy`. The lazy form returns a `Promise` whose `str()` form is only realized when needed. Source: [typelets/django/strings.py:9-22]().

This module declares `StrOrPromise` to capture both forms, and `StrPromise` to indicate a lazy, unresolved string. The implementation uses `TYPE_CHECKING` to import Django's private `_StrOrPromise` and `_StrPromise` for static analysis, while falling back to a `NewType('StrPromise', Promise)` shim at runtime:

```python
if TYPE_CHECKING:
    from django.utils.functional import _StrOrPromise, _StrPromise
else:
    from django.utils.functional import Promise
    _StrPromise: TypeAlias = NewType('StrPromise', Promise)
    _StrOrPromise: TypeAlias = Union[str, _StrPromise]
```

The comment in the source explains the reason for the runtime shim: "The main reason we're using NewType here is to avoid Sphinx thinking this is an attribute during doc generation" — Source: [typelets/django/strings.py:14-19](). The result is that annotations stay readable (`s: StrOrPromise = gettext_lazy('...')`) without forcing runtime imports of Django's private names.

## Forms, Models, Authentication, and URLs

`typelets.django.forms` provides `FormData` (`Mapping[str, Any]`) and `FormFiles` (`MultiValueDict[str, UploadedFile]`). They mirror the shape of the `data` and `files` keyword arguments that Django passes to `Form.__init__` and views, letting callers annotate functions that consume raw POST payloads precisely. Source: [typelets/django/forms.py:18-26]().

`typelets.django.models` exposes two primary-key aliases. `ModelAnyPK` is the documented replacement for Django's default `Any` primary-key annotation, while `ModelIntPK` documents codebases that have committed to integer keys. The docstring on each alias is explicit: "Django primary keys default to `typing.Any`, which doesn't particularly help code stay readable" — Source: [typelets/django/models.py:16-22, 36-42]().

`typelets.django.auth` provides the `AnyUser` alias as `Union[AbstractBaseUser, AnonymousUser]`, which mirrors what Django's `request.user` returns at runtime. This avoids forcing callers to spell out the union at every call site. Source: [typelets/django/auth.py:14-15]().

`typelets.django.urls` defines `AnyURL: TypeAlias = Union[URLPattern, URLResolver]`. The module's example uses `path('/', my_view)` and `path('/', include(...))` interchangeably, which is exactly the situation in which `django.urls.path` returns either type depending on whether an `include` is present. Source: [typelets/django/urls.py:21-26]().

## Django-Aware JSON Serialization

`typelets.django.json` extends the generic JSON types from `typelets.json` with Django-aware leaf types. The leaf set is `_SerializableJSONValueTypes = Union[Decimal, StrPromise, UUID, date, datetime, time, timedelta]`, and three public aliases are derived from the parameterized `BaseSerializable*` families. Source: [typelets/django/json.py:23-49]().

The relationship between the generic JSON types and the Django-aware variants can be visualized as follows:

```mermaid
graph LR
  BaseSerializableJSONValue --> SerializableDjangoJSONValue
  BaseSerializableJSONDict --> SerializableDjangoJSONDict
  BaseSerializableJSONListImmutable --> SerializableDjangoJSONListImmutable
  SerializableDjangoJSONValue --> SerializableDjangoJSONDict
  SerializableDjangoJSONValue --> SerializableDjangoJSONListImmutable
  StrPromise --> SerializableDjangoJSONValue
  Decimal --> SerializableDjangoJSONValue
  UUID --> SerializableDjangoJSONValue
  date --> SerializableDjangoJSONValue
  datetime --> SerializableDjangoJSONValue
  time --> SerializableDjangoJSONValue
  timedelta --> SerializableDjangoJSONValue
```

These aliases are designed to match the surface area of `djblets.util.serializers.DjbletsJSONEncoder`, the encoder that ships with the sister project Djblets. Each alias docstring references that encoder explicitly — Source: [typelets/django/json.py:32-49]().

## Usage Patterns and Common Pitfalls

Two patterns repeat across the modules:

1. **Type-narrowing on returns.** `JSONDictImmutable` and `BaseSerializableJSONListImmutable` are immutable variants. The docstrings on `SerializableDjangoJSONDictImmutable` (referenced from the immutable list alias) recommend using these when "returning data from a function that should not be changed" — Source: [typelets/django/json.py:42-49](). Choosing the immutable variant signals intent to reviewers and to type checkers.

2. **Avoiding Django's `Any` defaults.** Both `ModelAnyPK` and `ModelIntPK` exist to escape Django's default `Any`-typed primary-key annotations. `ModelIntPK` is the strict choice when the codebase has committed to integer primary keys. Source: [typelets/django/models.py:14-42]().

A common pitfall is using `StrOrPromise` in places that strictly require a `str`. Because lazy promises are not strings until rendered, downstream code that does `s + other` or `s.startswith(...)` without forcing evaluation will fail at runtime. The intended usage pattern shown in the source is to assign from `gettext` and `gettext_lazy` interchangeably and let Django handle the resolution — Source: [typelets/django/strings.py:39-43]().

Finally, the Django subpackage is deliberately separate from the generic modules so that projects that don't depend on Django can still import the generic aliases without pulling Django in.

## See Also

- [Typelets JSON Typing Extensions](./JSON-Typing-Extensions.md) — generic JSON aliases that the Django JSON module builds on.
- [Typelets Function Typing Extensions](./Function-Typing-Extensions.md) — `ParamSpec`-based helpers in `typelets.funcs`.
- [Typelets Symbols and Runtime Utilities](./Symbols-and-Runtime-Utilities.md) — `UNSET`/`Unsettable` and `raise_invalid_type`.

---

<a id='page-4'></a>

## Versioning, Releases, and Contributing

### Related Pages

Related topics: [Overview and Module Architecture](#page-1)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [typelets/_version.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/_version.py)
- [typelets/__init__.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/__init__.py)
- [README.md](https://github.com/beanbaginc/python-typelets/blob/main/README.md)
- [dev-requirements.txt](https://github.com/beanbaginc/python-typelets/blob/main/dev-requirements.txt)
- [typelets/json.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/json.py)
- [typelets/django/models.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/models.py)
</details>

# Versioning, Releases, and Contributing

This page documents how the Typelets project is versioned, how releases are produced and distributed, and how external contributors can submit changes for review. The information is derived directly from the package's version module, its public re-exports, and its `README`.

## Version Scheme

Typelets follows [Semantic Versioning](https://semver.org/), as stated in `README.md` ("Typelets follows semantic versioning, meaning no surprises when you upgrade"). Source: [README.md](https://github.com/beanbaginc/python-typelets/blob/main/README.md).

The canonical version is declared as a single tuple constant in [typelets/_version.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/_version.py):

```python
VERSION: tuple[int, int, int, str, int, bool] = (1, 2, 0, 'alpha', 0, False)
```

This tuple encodes `(Major, Minor, Micro, tag, release_num, released)`. Each slot has a specific role:

| Slot | Type | Meaning |
|---|---|---|
| 0 — Major | `int` | Breaking-change counter. |
| 1 — Minor | `int` | Backwards-compatible feature counter. |
| 2 — Micro | `int` | Backwards-compatible bug-fix counter. |
| 3 — tag | `str` | Pre-release marker: `'alpha'`, `'beta'`, `'rc'`, or `'final'`. |
| 4 — release_num | `int` | Counter for the active pre-release tag. |
| 5 — released | `bool` | Whether the current build is a published release. |

The `tag` value is the primary branch indicator: pre-release builds use `alpha`, `beta`, or `rc`, while stable builds use `final`. Source: [typelets/_version.py:18-19](https://github.com/beanbaginc/python-typelets/blob/main/typelets/_version.py).

### Release State Diagram

```mermaid
stateDiagram-v2
    [*] --> alpha: development starts
    alpha --> beta: feature freeze
    beta --> rc: stabilization
    rc --> final: publish to PyPI
    final --> final: bug-fix micro bumps
    final --> alpha: next major/minor cycle
```

## Version Helpers and Module Exports

Three helper functions in `typelets/_version.py` translate the internal tuple into strings used by humans, packaging tools, and runtime checks.

- `get_version_string()` builds a display-friendly version. For the current build it returns `"1.2 alpha 0 (dev)"`. The `(dev)` suffix is appended whenever `is_release()` is `False`. Release-candidate builds are rendered with an `RC` suffix (e.g. `1.2 RC1`). Source: [typelets/_version.py:24-46](https://github.com/beanbaginc/python-typelets/blob/main/typelets/_version.py).
- `get_package_version()` returns a PEP 440–compatible identifier for upload to PyPI. `alpha` becomes the suffix `a`, `beta` becomes `b`, and `rc` is preserved. The current package version is therefore `1.2a0`. Source: [typelets/_version.py:49-69](https://github.com/beanbaginc/python-typelets/blob/main/typelets/_version.py).
- `is_release()` simply returns the boolean stored at `VERSION[5]`. Source: [typelets/_version.py:72-80](https://github.com/beanbaginc/python-typelets/blob/main/typelets/_version.py).

Two module-level constants are derived from the tuple for convenience: `__version_info__ = VERSION[:-1]` (everything except the released flag) and `__version__ = get_package_version()`. These, together with the helpers and the raw `VERSION` tuple, are re-exported through [typelets/__init__.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/__init__.py), which lists them in both `__all__` and `__autodoc_excludes__` to keep them out of the generated API reference. Source: [typelets/__init__.py:1-22](https://github.com/beanbaginc/python-typelets/blob/main/typelets/__init__.py).

## Installation and Release Artifacts

Typelets is published to PyPI under the name `typelets`. Installation is performed with the standard Python package manager:

```console
$ pip install typelets
```

Source: [README.md](https://github.com/beanbaginc/python-typelets/blob/main/README.md) ("To install typelets, run: `$ pip install typelets`"). The version that pip resolves comes directly from `get_package_version()` in [typelets/_version.py:49-69](https://github.com/beanbaginc/python-typelets/blob/main/typelets/_version.py), which means the metadata uploaded to PyPI always matches the in-source `VERSION` tuple.

The project is distributed under the MIT license. Source: [README.md](https://github.com/beanbaginc/python-typelets/blob/main/README.md) ("Typelets is available under the MIT license").

## Development Dependencies

The repository ships a `dev-requirements.txt` that pins the toolchain used during development and continuous integration. Django is pinned per Python version (`~=3.2` on Python 3.8 and `~=4.2` on 3.9+), alongside `django-stubs` for type checking and `pytest` for tests. Source: [dev-requirements.txt](https://github.com/beanbaginc/python-typelets/blob/main/dev-requirements.txt).

## Contributing Workflow

Typelets does not accept pull requests on GitHub. Instead, changes are posted to the Beanbag [Review Board](https://www.reviewboard.org/) server at `https://reviews.reviewboard.org/` using [RBTools](https://www.reviewboard.org/docs/rbtools/). Source: [README.md](https://github.com/beanbaginc/python-typelets/blob/main/README.md) ("Contributions to Typelets can be made on our Review Board server at https://reviews.reviewboard.org/").

The documented workflow is:

1. Install RBTools into the development environment:
   ```console
   $ pip install rbtools
   ```
2. Create a branch in the local Git clone and make the desired changes to the relevant module (for example, editing one of the public type aliases in [typelets/django/models.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/django/models.py) or the JSON type hierarchy in [typelets/json.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/json.py)).
3. Post the change for review:
   ```console
   $ rbt post
   ```
4. Update the change after revisions with the `-u` flag:
   ```console
   $ rbt post -u
   ```

Source: [README.md](https://github.com/beanbaginc/python-typelets/blob/main/README.md) ("Download RBTools … Create a branch in your Git clone and make your changes. Post the change for review: `$ rbt post`. To update your change: `$ rbt post -u`").

The `VERSION` tuple in `typelets/_version.py` and the exported `__version__` constant are the two pieces of metadata that reviewers will check when evaluating release-impact changes, so any change that affects the public API should be reflected in the version tag and release notes before posting.

## See Also

- [README.md](https://github.com/beanbaginc/python-typelets/blob/main/README.md) — installation, documentation, and contribution overview.
- [typelets/_version.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/_version.py) — version tuple and helper functions.
- [typelets/__init__.py](https://github.com/beanbaginc/python-typelets/blob/main/typelets/__init__.py) — public exports of version metadata.
- [dev-requirements.txt](https://github.com/beanbaginc/python-typelets/blob/main/dev-requirements.txt) — pinned development dependencies.
- [Review Board documentation](https://www.reviewboard.org/docs/rbtools/) — RBTools usage reference for the contribution workflow.

---

<!-- evidence_pipeline_checked: true -->

---

## Pitfall Log

Project: beanbaginc/python-typelets

Summary: Found 7 structured pitfall item(s), including 0 high/blocking item(s). Top priority: Identity risk - Identity risk requires verification.

## 1. Identity risk - Identity risk requires verification

- Severity: medium
- Evidence strength: runtime_trace
- Finding: Project evidence flags a identity risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Repro command: `pip install typelets`
- Evidence: identity.distribution | https://github.com/beanbaginc/python-typelets

## 2. Capability evidence risk - Capability evidence risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.assumptions | https://github.com/beanbaginc/python-typelets

## 3. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/beanbaginc/python-typelets

## 4. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: downstream_validation.risk_items | https://github.com/beanbaginc/python-typelets

## 5. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: risks.scoring_risks | https://github.com/beanbaginc/python-typelets

## 6. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/beanbaginc/python-typelets

## 7. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/beanbaginc/python-typelets

<!-- canonical_name: beanbaginc/python-typelets; human_manual_source: deepwiki_human_wiki -->
