PoC & initial concerns
- Dominant language
- Jupyter Notebook
- Stars
- 1
- Forks
- 3
- PR merge metrics
- No merged PRs in 30d
Description
Let's explore the ways to show transformers in action and identify some problems that may in arise when we use the proposed feature in practice. Some challenges would be:
- **Are there any use cases in reference-fragile functions?**
Imagine such a scenario:
```py
def list_of_ints(arg: list[object]) -> list[int]:
return list(map(int, arg)) # dont check if the arg is a list and is entirely made of ints to save time
def append1(arg: list_of_ints) -> None:
arg.append(1)
k = []
func(k)
print(k)
```
We'd expect `[1]` on the output, but we instead get `[]`. That's because `arg` would inherently become a shallow copy of `k` due to implicit `arg = list(map(int, k))` in `list_of_ints`, rendering the in-place `append1` function useless.
Question is: should we use transformers on in-place function parameters in the first place? If we use `@validate_call` with `list[int]` annotation, pydantic will provide a copy of the initial list no matter whether the initial list was OK or not (and that consistency is great).
> [!IMPORTANT]
> Idea: **Discourage using transformers on non-generative (in-place, side-effects having) functions.**
- **Approaching subtype relationships** and **discrepancy with the typing world**. Imagine this:
```py
def print_value(obj: float) -> None:
print(obj)
print_value(1)
```
I'd honestly expect `1` on the output (not `1.0`), because [`float` is a supertype of `int` _in the typing context_](https://peps.python.org/pep-0483/#subtype-relationships).
But, unsurprisingly, I get `1.0` instead. **I will argue that this does violate [Liskov's substution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle)**, even though at runtime `issubclass(int, float)` is `False`. MROs **do not** reflect true typing reality. But it's not even a problem of MRO: super-casting is just unnecessary, e.g. from `bool` to `int` (this time MRO reflects reality, `issubclass(bool, int)` is `True`):
```py
def print_value(obj: int) -> None:
print(obj)
print_value(True)
```
I'd expect `True`, not `1`. And you?
Pydantic's `validate_call` here will cast the `obj` to `1` in the lax mode (default) and raise an error in [the strict mode](https://docs.pydantic.dev/latest/concepts/strict_mode):
```py
>>> @validate_call
... def p(k: int):
... print(k)
...
>>> p(True)
1
>>> @validate_call(config=ConfigDict(strict=True))
... def p(k: int):
... print(k)
...
>>> p(True)
Traceback (most recent call last):
File "", line 1, in
File "/tmp/pip-run-ml8hjt2j/pydantic/validate_call_decorator.py", line 59, in wrapper_function
return validate_call_wrapper(*args, **kwargs)
File "/tmp/pip-run-ml8hjt2j/pydantic/_internal/_validate_call.py", line 81, in __call__
res = self.__pydantic_validator__.validate_python(pydantic_core.ArgsKwargs(args, kwargs))
pydantic_core._pydantic_core.ValidationError: 1 validation error for p
0
Input should be a valid integer [type=int_type, input_value=True, input_type=bool]
For further information visit https://errors.pydantic.dev/2.7/v/int_type
```
And while for primitive-type data it's completely fine, it might be much trickier in full-blown custom classes with MROs.
> [!IMPORTANT]
> Idea: **Promote use cases that don't rely on polymorphism, but on primitive/built-in data types.** I used primitive data types to illustrate a problem with allowing polymorphism (by preserving original types before and after transformations) and the fact that it needs to be underlined transformers are **data-oriented** and **not object-oriented** and thus shouldn't be used primarily on arbitrary, hierarchized interfaces.
- How transformers **affect tracebacks** and how easy it is to recover from type coercion errors, compared to the current flow?
And much more to consider.
Feel free to assign me and I'll try to provide a PoC implementation either by using [forbiddenfruit](https://github.com/clarete/forbiddenfruit) to patch `FunctionType.__call__`/other feasible endpoint or extending the CPython implementation.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reviewing the proposed transformer behavior and the reference-fragile and subtype examples in this issue. Investigate the mentioned FunctionType.__call__ patching route with forbiddenfruit and the alternative of extending CPython. Done means producing a scoped PoC that addresses the listed mutation, subtype, and traceback concerns.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100