Skip to content
Open
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
34 changes: 19 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ from typing import Annotated, Union, Optional, Any
def public_function(
# validate against built-in or custom types
a: str,
# 'a' is positional-only as it appears ahead of the '/'
/,
# support for type unions
b: Union[int, float], # or from Python 3.10 `int | float`
# validate type of container items
Expand Down Expand Up @@ -61,7 +63,8 @@ def public_function(
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}

public_function(
# NB parameters 'a' through 'i' can be passed positionally
# NB 'a' is positional-only (must be passed positionally); 'b' through
# 'i' can also be passed positionally
"zero", # a
1.0, # b
{"two": 2}, # c
Expand Down Expand Up @@ -95,7 +98,7 @@ returns:
And if there are invalid inputs...
```python
public_function(
a=["not a string"], # INVALID
["not a string"], # a, positional-only, passed positionally; INVALID
b="not an int or a float", # INVALID
c={2: "two"}, # INVALID, key not a str and value not an int or float
d=3.2, # valid input
Expand Down Expand Up @@ -129,24 +132,27 @@ h
And if the inputs do not match the signature...
```python
public_function(
"zero",
"invalid input", # invalid (not int or float), included in errors
{"two": 2},
3.2,
"zero", # a, positional-only, passed positionally
"invalid input", # b, invalid (not int or float), included in errors
{"two": 2}, # c, passed positionally...
3.2, # d
# no argument passed for required positional args 'e', 'f', 'g', 'h' and 'i'
a="a again", # passing multiple values for parameter 'a'
# no argument passed for required keyword arg 'j'
a="a again", # 'a' is positional-only: cannot be passed as a keyword arg
c={"three": 3}, # ...and again by keyword: passing multiple values for 'c'
not_a_kwarg="not a kwarg", # including an unexpected kwarg
# no argument passed for required keyword arg 'j'
)
```
raises:
```
InputsError: Inputs to 'public_function' do not conform with the function signature:

Got multiple values for argument: 'a'.
Got multiple values for argument: 'c'.

Got unexpected keyword argument: 'not_a_kwarg'.

Received positional-only argument as keyword argument: 'a'.

Missing 5 positional arguments: 'e', 'f', 'g', 'h' and 'i'.

Missing 1 keyword-only argument: 'j'.
Expand Down Expand Up @@ -232,6 +238,10 @@ In short, if you only want to validate the type of function inputs then Pydantic
* `collections.abc.Mapping`
* packing and optionally coercing, parsing and validating packed objects, i.e. objects
received to, for example, *args and **kwargs.
* positional-only parameters (those defined ahead of a `/` in the signature). As when
calling an undecorated function, a positional-only parameter can only be satisfied
positionally; a keyword input matching its name is absorbed by **kwargs if the
signature provides for **kwargs, otherwise it raises.

`valimp` does NOT support:
- Validation of subscripted types in `collections.abc.Callable`. Any subscriptions to
Expand All @@ -241,12 +251,6 @@ In short, if you only want to validate the type of function inputs then Pydantic
verify that an object passed to a parameter annotated as `Callable` is in fact
callable).

`valimp` does NOT currently support:
- Positional-only arguments. Any '/' in the signature (to define
positional-only arguments) will be ignored. Consequently valimp DOES
allow intended positional-only arguments to be passed as keyword
arguments.

The library has been built with development in mind and PRs are very much welcome!

## License
Expand Down
15 changes: 11 additions & 4 deletions docs/tutorials/tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1708,12 +1708,15 @@
"\n",
"Valimp also validates that inputs conform with a function's signature.\n",
"\n",
"Positional-only parameters (those defined ahead of a `/` in the signature) are supported. As when calling an undecorated function, a positional-only parameter can only be satisfied positionally; a keyword input matching its name is absorbed by `**kwargs` if the signature provides for `**kwargs`, otherwise it raises.\n",
"\n",
"A `valimp.InputsError` will be raised if at least one of the following is true.\n",
"* A required argument is not passed (missing positional argument).\n",
"* A required keyword-only argument is not passed (missing keyword-only argument).\n",
"* A keyword argument is passed that is not represented in the signature (unexpected keyword argument).\n",
"* More arguments are passed positionally than accommodated for by the signature (excess positional arguments).\n",
"* A parameter is passed both positionally and as a keyword argument (got multiple values).\n",
"* A positional-only argument is passed as a keyword argument (received positional-only argument as keyword argument).\n",
"\n",
"All signature errors are advised in the error message, together with any errors relating to invalid types."
]
Expand All @@ -1728,7 +1731,9 @@
"@parse\n",
"def pf(\n",
" a: int,\n",
" /, # 'a' is positional-only\n",
" b: int,\n",
" c: int,\n",
" *,\n",
" kw_a: int,\n",
"):\n",
Expand All @@ -1742,7 +1747,7 @@
"metadata": {},
"outputs": [],
"source": [
"pf(3, \"not an int\", 5, a=3, not_a_kwarg=3)"
"pf(3, \"not an int\", 4, 5, a=3, c=2, not_a_kwarg=3)"
]
},
{
Expand All @@ -1754,17 +1759,19 @@
"---------------------------------------------------------------------------\n",
"InputsError Traceback (most recent call last)\n",
"Cell In[47], line 1\n",
"----> 1 pf(3, \"not an int\", 5, a=3, not_a_kwarg=3)\n",
"----> 1 pf(3, \"not an int\", 4, 5, a=3, c=2, not_a_kwarg=3)\n",
"\n",
"InputsError: Inputs to 'pf' do not conform with the function signature:\n",
"\n",
"Got multiple values for argument: 'a'.\n",
"Got multiple values for argument: 'c'.\n",
"\n",
"Received 1 excess positional argument as:\n",
"\t'5' of type <class 'int'>.\n",
"\n",
"Got unexpected keyword argument: 'not_a_kwarg'.\n",
"\n",
"Received positional-only argument as keyword argument: 'a'.\n",
"\n",
"Missing 1 keyword-only argument: 'kw_a'.\n",
"\n",
"The following inputs to 'pf' do not conform with the corresponding type annotation:\n",
Expand Down Expand Up @@ -1797,7 +1804,7 @@
"\n",
"InputsError: Inputs to 'pf' do not conform with the function signature:\n",
"\n",
"Missing 1 positional argument: 'b'.\n",
"Missing 2 positional arguments: 'b' and 'c'.\n",
"```"
]
},
Expand Down
Loading
Loading