Skip to content

Commit abf81f8

Browse files
committed
feat: add AlwaysNone
1 parent e7b03d8 commit abf81f8

File tree

3 files changed

+30
-11
lines changed

3 files changed

+30
-11
lines changed

polyfactory/__init__.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
from .exceptions import ConfigurationException
22
from .factories import BaseFactory
3-
from .fields import Fixture, Ignore, PostGenerated, Require, Use, NeverNone
4-
from .persistence import AsyncPersistenceProtocol, SyncPersistenceProtocol
3+
from .fields import AlwaysNone, Fixture, Ignore, NeverNone, PostGenerated, Require, Use
4+
from .persistence import AsyncPersistenceProtocol
55

66
__all__ = (
7+
"AlwaysNone",
78
"AsyncPersistenceProtocol",
89
"BaseFactory",
910
"ConfigurationException",
1011
"Fixture",
1112
"Ignore",
13+
"NeverNone",
1214
"PostGenerated",
1315
"Require",
14-
"NeverNone",
15-
"SyncPersistenceProtocol",
1616
"Use",
1717
)

polyfactory/factories/base.py

+12-5
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
)
5555
from polyfactory.exceptions import ConfigurationException, MissingBuildKwargException, ParameterException
5656
from polyfactory.field_meta import Null
57-
from polyfactory.fields import Fixture, Ignore, NeverNone, PostGenerated, Require, Use
57+
from polyfactory.fields import AlwaysNone, Fixture, Ignore, NeverNone, PostGenerated, Require, Use
5858
from polyfactory.utils.helpers import (
5959
flatten_annotation,
6060
get_collection_type,
@@ -949,6 +949,10 @@ def should_set_none_value(cls, field_meta: FieldMeta) -> bool:
949949
"""
950950
field_value = hasattr(cls, field_meta.name) and getattr(cls, field_meta.name)
951951
never_none = field_value and isinstance(field_value, NeverNone)
952+
always_none = field_value and isinstance(field_value, AlwaysNone)
953+
954+
if always_none:
955+
return True
952956

953957
return (
954958
cls.__allow_none_optionals__
@@ -1026,7 +1030,7 @@ def _check_declared_fields_exist_in_model(cls) -> None:
10261030
f"{field_name} is declared on the factory {cls.__name__}"
10271031
f" but it is not part of the model {cls.__model__.__name__}"
10281032
)
1029-
if isinstance(field_value, (Use, PostGenerated, Ignore, Require, NeverNone)):
1033+
if isinstance(field_value, (Use, PostGenerated, Ignore, Require, NeverNone, AlwaysNone)):
10301034
raise ConfigurationException(error_message)
10311035

10321036
@classmethod
@@ -1047,9 +1051,12 @@ def process_kwargs(cls, **kwargs: Any) -> dict[str, Any]:
10471051
if cls.should_set_field_value(field_meta, **kwargs) and not cls.should_use_default_value(field_meta):
10481052
field_value = getattr(cls, field_meta.name, None)
10491053

1050-
# TODO why do we need the BaseFactory check here, only dunder methods which are ignored would trigger this?
1051-
# NeverNone should be treated as a normally-generated field
1052-
if field_value and not hasattr(BaseFactory, field_meta.name) and not isinstance(field_value, NeverNone):
1054+
# NeverNone & AlwaysNone should be treated as a normally-generated field, since this changes logic
1055+
# within get_field_value.
1056+
excluded_field_value = field_value and isinstance(field_value, (NeverNone, AlwaysNone))
1057+
1058+
# TODO why do we need the BaseFactory check here, only dunder methods which are ignored would trigger this? # noqa: FIX002
1059+
if field_value and not hasattr(BaseFactory, field_meta.name) and not excluded_field_value:
10531060
if isinstance(field_value, Ignore):
10541061
continue
10551062

polyfactory/fields.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,23 @@ class Require:
2323

2424

2525
class NeverNone:
26-
"""A factory field that marks as always generated, even if it's an optional"""
26+
"""A factory field that marks as always generated, even if it's an optional."""
27+
28+
29+
class AlwaysNone:
30+
"""A factory field that marks as never generated, setting the value to None, regardless of if it's an optional field
31+
32+
This is distinct from Ignore() which does not set a value for a field at all. If Ignore() is used and a default value
33+
for a field is not set on the underlying model, then the field will not be set at all.
34+
"""
2735

2836

2937
class Ignore:
30-
"""A factory field that marks an attribute as ignored."""
38+
"""A factory field that marks an attribute as ignored. This prevents the factory generating any value for this field.
39+
40+
If you are using this on a pydantic model this will cause the field to be omitted from the resulting pydantic model
41+
if there is no default value set for the pydantic field.
42+
"""
3143

3244

3345
class Use(Generic[P, T]):

0 commit comments

Comments
 (0)