diff --git a/CHANGELOG.md b/CHANGELOG.md index c8e09f9..23e3567 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] ### Added +- [MINOR] Added `UNDEFINED`, `Undefined` and `UpdateValue[T]` to `core.util.typing_util` for partial updates: `UpdateValue[T]` is `T` or the `UNDEFINED` sentinel, so an update can tell "not provided" apart from an explicit `None`. Use it as a pydantic field default (`title: UpdateValue[str | None] = UNDEFINED`); request input never validates to `UNDEFINED` (a JSON `"UNDEFINED"` stays a string), and `x is not UNDEFINED` narrows `x` to `T` for type checkers - [MINOR] Added `core.store.entity_repository.EntityRepository`, which reads and writes rows of a table as pydantic models: `create`, `update`, `upsert`, `upsert_many` (postgres and sqlite), `delete`, `list_many`, `get_first`, `get`, `get_one` and `get_one_or_none`. It exposes UUID columns as strings and datetimes as aware utc datetimes, dumps pydantic values in JSON columns, generates ids for UUID primary keys and stamps `createdDate`/`updatedDate` when the table has them; subclasses can override `_convert_value_from_db`/`_convert_value_to_db` for other columns - [MINOR] Added `UUIDFieldFilter` (accepts `uuid.UUID` or `str` values) and module-level `apply_orders`, `apply_field_filters` and `get_field_filters_conditions` (conditions usable on selects, updates and deletes) to `core.store.retriever`; `Retriever` now uses them - [PATCH] Updated `DateFieldFilter` to convert values to utc for the column (naive for columns without a timezone, aware otherwise) so aware datetimes in any timezone compare correctly diff --git a/core/util/typing_util.py b/core/util/typing_util.py index 063b7c0..de7092f 100644 --- a/core/util/typing_util.py +++ b/core/util/typing_util.py @@ -1,7 +1,28 @@ +import enum +import typing from collections.abc import Mapping from collections.abc import Sequence +from pydantic import GetCoreSchemaHandler +from pydantic_core import core_schema + JsonBaseType = str | int | float | bool | None type Json = dict[str, 'Json'] | Mapping[str, 'Json'] | list['Json'] | Sequence['Json'] | JsonBaseType type JsonObject = dict[str, 'Json'] type JsonList = list['Json'] + + +class Undefined(enum.Enum): + UNDEFINED = 'UNDEFINED' + + def __repr__(self) -> str: + return 'UNDEFINED' + + @classmethod + def __get_pydantic_core_schema__(cls, sourceType: typing.Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: # type: ignore[explicit-any] + # NOTE(krishan711): only the default can be UNDEFINED, input values (e.g. "UNDEFINED" in json) must never validate to it + return core_schema.is_instance_schema(cls) + + +UNDEFINED: typing.Final = Undefined.UNDEFINED +type UpdateValue[T] = T | Undefined