Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ def public_function(
a: str,
/, # support for positional-only arguments
# support for type unions
b: Union[int, float], # or from Python 3.10 `int | float`
b: int | float, # or Python 3.9 `Union[int, float]`
# validate type of container items
c: dict[str, Union[int, float]], # dict[str, int | float]
c: dict[str, int | float], # dict[str, Union[int, float]]
# coerce input to a specific type
d: Annotated[
Union[int, float, str], # int | float | str
int | float | str, # Union[int, float, str]
Coerce(int)
],
# parse input with reference to earlier inputs...
Expand All @@ -34,7 +34,7 @@ def public_function(
],
# coerce and parse input...
f: Annotated[
Union[str, int], # str | int
str | int, # Union[str, int]
Coerce(str),
Parser(lambda name, obj, _: obj + f"_{name}")
],
Expand All @@ -43,21 +43,21 @@ def public_function(
# validate input is subclass of a specific class (or that class itself) ...
h: type[int],
# ... or of specific classes...
i: type[Union[int, str]], # type[int | str]
i: type[int | str], # type[Union[int, str]]
# support for packing extra arguments if required, can be optionally typed...
*args: Annotated[
Union[int, float, str], # int | float | str
int | float | str, # Union[int, float, str]
Coerce(int)
],
# support for optional types
j: Optional[str], # str | None
j: str | None, # Optional[str]
# define default values dynamically with reference to earlier inputs
k: Annotated[
Optional[float], # float | None
float | None, # Optional[float]
Parser(lambda _, obj, params: params["b"] if obj is None else obj)
] = None,
# support for packing excess kwargs if required, can be optionally typed...
# **kwargs: Union[int, float]
# **kwargs: int | float # Union[int, float]
) -> dict[str, Any]:
return {"a":a, "b":b, "c":c, "d":d, "e":e, "f":f, "g":g, "h":h, "i":i, "args":args, "j":j, "k":k}

Expand Down Expand Up @@ -86,9 +86,9 @@ returns:
'd': 3,
'e': 'four_e_zero',
'f': '5_f',
'g': <class 'str'>,
'h': <class 'bool'>,
'i': <class 'int'>,
'g': str,
'h': bool,
'i': int,
'args': (10, 20),
'j': 'keyword_arg_j',
'k': 1.0}
Expand Down Expand Up @@ -171,7 +171,7 @@ class ADataclass:

a: str
b: Annotated[
Union[str, int],
str | int, # Union[str, int]
Coerce(str),
Parser(lambda name, obj, params: obj + f" {name} {params['a']}")
]
Expand Down
275 changes: 275 additions & 0 deletions docs/readme_examples.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "00d19d9e-5259-49a3-ab98-4d09c87d80b3",
"metadata": {},
"source": [
"# README example"
]
},
{
"cell_type": "markdown",
"id": "ded58180-f6eb-4830-a939-3399f7bb1bf6",
"metadata": {},
"source": [
"#### As at **2026/06/03**"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "a4148dbd-b3fe-4c0d-bbcd-b76b38b2d157",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'a': 'zero',\n",
" 'b': 1.0,\n",
" 'c': {'two': 2},\n",
" 'd': 3,\n",
" 'e': 'four_e_zero',\n",
" 'f': '5_f',\n",
" 'g': str,\n",
" 'h': bool,\n",
" 'i': int,\n",
" 'args': (10, 20),\n",
" 'j': 'keyword_arg_j',\n",
" 'k': 1.0}"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from valimp import parse, Parser, Coerce\n",
"from typing import Annotated, Union, Optional, Any\n",
"\n",
"@parse # add the `valimp.parse`` decorator to a public function or method\n",
"def public_function(\n",
" # validate against built-in or custom types\n",
" a: str,\n",
" /, # support for positional-only arguments\n",
" # support for type unions\n",
" b: int | float, # or Python 3.9 `Union[int, float]`\n",
" # validate type of container items\n",
" c: dict[str, int | float], # dict[str, Union[int, float]]\n",
" # coerce input to a specific type\n",
" d: Annotated[\n",
" int | float | str, # Union[int, float, str]\n",
" Coerce(int)\n",
" ],\n",
" # parse input with reference to earlier inputs...\n",
" e: Annotated[\n",
" str,\n",
" Parser(lambda name, obj, params: obj + f\"_{name}_{params['a']}\")\n",
" ],\n",
" # coerce and parse input...\n",
" f: Annotated[\n",
" str | int, # Union[str, int]\n",
" Coerce(str),\n",
" Parser(lambda name, obj, _: obj + f\"_{name}\")\n",
" ],\n",
" # validate input is a class (rather than an instance)\n",
" g: type,\n",
" # validate input is subclass of a specific class (or that class itself) ...\n",
" h: type[int],\n",
" # ... or of specific classes...\n",
" i: type[int | str], # type[Union[int, str]]\n",
" # support for packing extra arguments if required, can be optionally typed...\n",
" *args: Annotated[\n",
" int | float | str, # Union[int, float, str]\n",
" Coerce(int)\n",
" ],\n",
" # support for optional types\n",
" j: str | None, # Optional[str]\n",
" # define default values dynamically with reference to earlier inputs\n",
" k: Annotated[\n",
" float | None, # Optional[float]\n",
" Parser(lambda _, obj, params: params[\"b\"] if obj is None else obj)\n",
" ] = None,\n",
" # support for packing excess kwargs if required, can be optionally typed...\n",
" # **kwargs: int | float # Union[int, float]\n",
") -> dict[str, Any]:\n",
" return {\"a\":a, \"b\":b, \"c\":c, \"d\":d, \"e\":e, \"f\":f, \"g\":g, \"h\":h, \"i\":i, \"args\":args, \"j\":j, \"k\":k}\n",
"\n",
"public_function(\n",
" # NB 'a' must be passed positionally, 'b' through 'i' can be passed positionally\n",
" \"zero\", # a\n",
" 1.0, # b\n",
" {\"two\": 2}, # c\n",
" 3.3, # d, will be coerced from float to int, i.e. to 3\n",
" \"four\", # e, will be parsed to \"four_e_zero\"\n",
" 5, # f, will be coerced to str and then parsed to \"5_f\"\n",
" str, # g\n",
" bool, # h, a subclass of int\n",
" int, # i, one of the subscripted classes\n",
" \"10\", # extra arg, will be coerced to int and packed\n",
" 20, # extra arg, will be packed\n",
" j=\"keyword_arg_j\",\n",
" # k, not passed, will be assigned dynamically as parameter b (i.e. 1.0)\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "adf51369-0f5d-47f4-a476-8ade7ebb951c",
"metadata": {},
"outputs": [],
"source": [
"public_function(\n",
" [\"not a string\"], # INVALID\n",
" b=\"not an int or a float\", # INVALID\n",
" c={2: \"two\"}, # INVALID, key not a str and value not an int or float\n",
" d=3.2, # valid input\n",
" e=\"valid input\",\n",
" f=5.0, # INVALID, not a str or an int\n",
" g=str, # valid input\n",
" h=str, # INVALID, str is not int or a subclass of int\n",
" i=bool, # valid input\n",
" j=\"valid input\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "b58808cf-5a46-46fc-bf0f-8edcf5687d57",
"metadata": {},
"source": [
"```\n",
"InputsError: The following inputs to 'public_function' do not conform with the corresponding type annotation:\n",
"\n",
"a\n",
"\tTakes type <class 'str'> although received '['not a string']' of type <class 'list'>.\n",
"\n",
"b\n",
"\tTakes input that conforms with <(<class 'int'>, <class 'float'>)> although received 'not an int or a float' of type <class 'str'>.\n",
"\n",
"c\n",
"\tTakes type <class 'dict'> with keys that conform to the first argument and values that conform to the second argument of <dict[str, int | float]>, although the received dictionary contains an item with key '2' of type <class 'int'> and value 'two' of type <class 'str'>.\n",
"\n",
"f\n",
"\tTakes input that conforms with <(<class 'str'>, <class 'int'>)> although received '5.0' of type <class 'float'>.\n",
"\n",
"h\n",
"\tTakes a subclass of <class 'int'> although received '<class 'str'>'.\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b848919c-d7f3-46af-ab83-4e8b95e4bc30",
"metadata": {},
"outputs": [],
"source": [
"public_function(\n",
" \"zero\",\n",
" \"invalid input\", # invalid (not int or float), included in errors\n",
" {\"two\": 2},\n",
" 3.2,\n",
" # no argument passed for required positional args 'e', 'f', 'g', 'h' and 'i'\n",
" a=\"a again\", # 'a' is positional-only: cannot be passed as a kwarg unless sig has **kwargs\n",
" c={\"three\": 3}, # passing multiple values for 'c'\n",
" not_a_kwarg=\"not a kwarg\", # including an unexpected kwarg\n",
" # no argument passed for required keyword arg 'j'\n",
")"
]
},
{
"cell_type": "markdown",
"id": "7291a496-2716-43bd-b1e3-9a13e54a7dd2",
"metadata": {},
"source": [
"```\n",
"InputsError: Inputs to 'public_function' do not conform with the function signature:\n",
"\n",
"Got multiple values for argument: 'c'.\n",
"\n",
"Got unexpected keyword argument: 'not_a_kwarg'.\n",
"\n",
"Got positional-only argument as keyword argument (and signature makes no provision for **kwargs that would otherwise receive it): 'a'.\n",
"\n",
"Missing 5 positional arguments: 'e', 'f', 'g', 'h' and 'i'.\n",
"\n",
"Missing 1 keyword-only argument: 'j'.\n",
"\n",
"The following inputs to 'public_function' do not conform with the corresponding type annotation:\n",
"\n",
"b\n",
"\tTakes input that conforms with <(<class 'int'>, <class 'float'>)> although received 'invalid input' of type <class 'str'>.\n",
" ```"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "c21342f6-d9de-4e8f-9058-9833027b45c0",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'a': \"I'm a and will appear at the end of b\",\n",
" 'b': \"33 b I'm a and will appear at the end of b\"}"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from valimp import parse_cls\n",
"import dataclasses\n",
"\n",
"@parse_cls # place valimp decorator above the dataclass decorator\n",
"@dataclasses.dataclass\n",
"class ADataclass:\n",
" \n",
" a: str\n",
" b: Annotated[\n",
" str | int, # Union[str, int]\n",
" Coerce(str),\n",
" Parser(lambda name, obj, params: obj + f\" {name} {params['a']}\")\n",
" ]\n",
"\n",
"rtrn = ADataclass(\"I'm a and will appear at the end of b\", 33)\n",
"dataclasses.asdict(rtrn)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "946003b7-29fe-4ee9-8c2f-3ec2e432ed93",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "inv_working_313",
"language": "python",
"name": "inv_working_313"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Loading