Skip to content

Commit

Permalink
Fix ImportString documentation about default value validation (#8386)
Browse files Browse the repository at this point in the history
  • Loading branch information
dolfinus committed Dec 18, 2023
1 parent ed0fd9b commit 3d2ebef
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 7 deletions.
34 changes: 27 additions & 7 deletions pydantic/types.py
Expand Up @@ -887,15 +887,11 @@ class ImportString:
On model instantiation, pointers will be evaluated and imported. There is
some nuance to this behavior, demonstrated in the examples below.
> A known limitation: setting a default value to a string
> won't result in validation (thus evaluation). This is actively
> being worked on.
**Good behavior:**
```py
from math import cos
from pydantic import BaseModel, ImportString, ValidationError
from pydantic import BaseModel, Field, ImportString, ValidationError
class ImportThings(BaseModel):
Expand Down Expand Up @@ -925,7 +921,31 @@ class ImportThings(BaseModel):
# Actual python objects can be assigned as well
my_cos = ImportThings(obj=cos)
my_cos_2 = ImportThings(obj='math.cos')
assert my_cos == my_cos_2
my_cos_3 = ImportThings(obj='math:cos')
assert my_cos == my_cos_2 == my_cos_3
# You can set default field value either as Python object:
class ImportThingsDefaultPyObj(BaseModel):
obj: ImportString = math.cos
# or as a string value (but only if used with `validate_default=True`)
class ImportThingsDefaultString(BaseModel):
obj: ImportString = Field(default='math.cos', validate_default=True)
my_cos_default1 = ImportThingsDefaultPyObj()
my_cos_default2 = ImportThingsDefaultString()
assert my_cos_default1.obj == my_cos_default2.obj == math.cos
# note: this will not work!
class ImportThingsMissingValidateDefault(BaseModel):
obj: ImportString = 'math.cos'
my_cos_default3 = ImportThingsMissingValidateDefault()
assert my_cos_default3.obj == 'math.cos' # just string, not evaluated
```
Serializing an `ImportString` type to json is also possible.
Expand All @@ -939,7 +959,7 @@ class ImportThings(BaseModel):
# Create an instance
m = ImportThings(obj='math:cos')
m = ImportThings(obj='math.cos')
print(m)
#> obj=<built-in function cos>
print(m.model_dump_json())
Expand Down
18 changes: 18 additions & 0 deletions tests/test_types.py
Expand Up @@ -877,6 +877,8 @@ class PyObjectModel(BaseModel):
[
('math:cos', 'math.cos', 'json'),
('math:cos', math.cos, 'python'),
('math.cos', 'math.cos', 'json'),
('math.cos', math.cos, 'python'),
pytest.param(
'os.path', 'posixpath', 'json', marks=pytest.mark.skipif(sys.platform == 'win32', reason='different output')
),
Expand All @@ -903,6 +905,22 @@ class PyObjectModel(BaseModel):
assert PyObjectModel(thing=value).model_dump(mode=mode) == {'thing': expected}


@pytest.mark.parametrize(
('value', 'validate_default', 'expected'),
[
(math.cos, True, math.cos),
('math:cos', True, math.cos),
(math.cos, False, math.cos),
('math:cos', False, 'math:cos'),
],
)
def test_string_import_default_value(value: Any, validate_default: bool, expected: Any):
class PyObjectModel(BaseModel):
thing: ImportString = Field(default=value, validate_default=validate_default)

assert PyObjectModel().thing == expected


@pytest.mark.parametrize('value', ['oss', 'os.os', f'{__name__}.x'])
def test_string_import_any_expected_failure(value: Any):
"""Ensure importString correctly fails to instantiate when it's supposed to"""
Expand Down

0 comments on commit 3d2ebef

Please sign in to comment.