keleshev / schema

Schema validation just got Pythonic
MIT License
2.86k stars 214 forks source link

`Optional` fails when `default` is some builtins (e.g. dict, set, list) #275

Closed gschaffner closed 2 years ago

gschaffner commented 2 years ago

The documentation provides the example

https://github.com/keleshev/schema/blob/09c00eda9599e53f7e6b84d7c91ecd3b42f71772/README.rst#L271-L277

This does not work, however:

>>> from schema import Schema, Optional 
>>> Schema({Optional('data', default=dict): {}}).validate({})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/tmp/tmp.27EtQAmlOm/.venv/lib/python3.10/site-packages/schema.py", line 431, in validate
    new[default.key] = _invoke_with_optional_kwargs(default.default, **kwargs) if callable(default.default) else default.default
  File "/tmp/tmp.27EtQAmlOm/.venv/lib/python3.10/site-packages/schema.py", line 277, in _invoke_with_optional_kwargs
    s = inspect.signature(f)
  File "/usr/lib/python3.10/inspect.py", line 3247, in signature
    return Signature.from_callable(obj, follow_wrapped=follow_wrapped,
  File "/usr/lib/python3.10/inspect.py", line 2995, in from_callable
    return _signature_from_callable(obj, sigcls=cls,
  File "/usr/lib/python3.10/inspect.py", line 2529, in _signature_from_callable
    raise ValueError(
ValueError: no signature found for builtin type <class 'dict'>

I've tested and found the above error to occur with schema v0.7.5 on CPython 3.10.2, CPython 3.7.12, and PyPy3.8-7.3.7.

It looks like this regression was introduced in 958e1ad657273537e73c431fae356ae15eff45c9. Arguably this is due to a bug in Python, though.

Curiously: inspect.signature(list) works fine in CPython 3.6.15, but raises in 3.7.0.

For users wanting to work around this, some working options include e.g. Optional(..., default=lambda: dict()) and Optional(..., default=dict.__call__).

gschaffner commented 2 years ago

Duplicate of #272.