Repository: litestar-org/polyfactory
Pull request #529 (2024-04-28 11:41:58)

feat: copy mutable args
<!--
By submitting this pull request, you agree to:
- follow [Litestar's Code of Conduct](https://github.com/litestar-org/.github/blob/main/CODE_OF_CONDUCT.md)
- follow [Litestar's contribution guidelines](https://github.com/litestar-org/.github/blob/main/CONTRIBUTING.md)
- follow the [PSFs's Code of Conduct](https://www.python.org/psf/conduct/)
-->

## Description

- Copy mutable values to avoid instance being reused between created instances
- Using hashability for as test, as done with dataclasses

<!--
Please add in issue numbers this pull request will close, if applicable
Examples: Fixes #4321 or Closes #1234

Ensure you are using a supported keyword to properly link an issue:
https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword
-->
## Closes


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=starlite-api_polyfactory&pullRequest=529) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=starlite-api_polyfactory&pullRequest=529&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/component_measures?id=starlite-api_polyfactory&pullRequest=529&metric=new_accepted_issues&view=list)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=starlite-api_polyfactory&pullRequest=529&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=starlite-api_polyfactory&pullRequest=529&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=starlite-api_polyfactory&pullRequest=529&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=starlite-api_polyfactory&pullRequest=529)


github-actions[bot]: Documentation preview will be available shortly at https://litestar-org.github.io/polyfactory-docs-preview/529
sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=starlite-api_polyfactory&pullRequest=529) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=starlite-api_polyfactory&pullRequest=529&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/component_measures?id=starlite-api_polyfactory&pullRequest=529&metric=new_accepted_issues&view=list)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=starlite-api_polyfactory&pullRequest=529&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=starlite-api_polyfactory&pullRequest=529&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=starlite-api_polyfactory&pullRequest=529&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=starlite-api_polyfactory&pullRequest=529)


github-actions[bot]: Documentation preview will be available shortly at https://litestar-org.github.io/polyfactory-docs-preview/529
sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=starlite-api_polyfactory&pullRequest=529) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=starlite-api_polyfactory&pullRequest=529&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/component_measures?id=starlite-api_polyfactory&pullRequest=529&metric=new_accepted_issues&view=list)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=starlite-api_polyfactory&pullRequest=529&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=starlite-api_polyfactory&pullRequest=529&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=starlite-api_polyfactory&pullRequest=529&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=starlite-api_polyfactory&pullRequest=529)


github-actions[bot]: Documentation preview will be available shortly at https://litestar-org.github.io/polyfactory-docs-preview/529
sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=starlite-api_polyfactory&pullRequest=529) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=starlite-api_polyfactory&pullRequest=529&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/component_measures?id=starlite-api_polyfactory&pullRequest=529&metric=new_accepted_issues&view=list)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=starlite-api_polyfactory&pullRequest=529&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=starlite-api_polyfactory&pullRequest=529&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=starlite-api_polyfactory&pullRequest=529&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=starlite-api_polyfactory&pullRequest=529)


github-actions[bot]: Documentation preview will be available shortly at https://litestar-org.github.io/polyfactory-docs-preview/529

Patch:
diff --git a/polyfactory/factories/base.py b/polyfactory/factories/base.py
index 60d32bf..e694b05 100644
--- a/polyfactory/factories/base.py
+++ b/polyfactory/factories/base.py
@@ -30,6 +30,7 @@ from typing import (
     ClassVar,
     Collection,
     Generic,
+    Hashable,
     Iterable,
     Mapping,
     Sequence,
@@ -289,7 +290,7 @@ class BaseFactory(ABC, Generic[T]):
         )
 
     @classmethod
-    def _handle_factory_field(
+    def _handle_factory_field(  # noqa: PLR0911
         cls,
         field_value: Any,
         build_context: BuildContext,
@@ -319,7 +320,10 @@ class BaseFactory(ABC, Generic[T]):
         if isinstance(field_value, Fixture):
             return field_value.to_value()
 
-        return field_value() if callable(field_value) else field_value
+        if callable(field_value):
+            return field_value()
+
+        return field_value if isinstance(field_value, Hashable) else copy.deepcopy(field_value)
 
     @classmethod
     def _handle_factory_field_coverage(


Test patch:
diff --git a/tests/test_factory_fields.py b/tests/test_factory_fields.py
index 7bcd680..e07aa9f 100644
--- a/tests/test_factory_fields.py
+++ b/tests/test_factory_fields.py
@@ -1,6 +1,7 @@
 import random
+from dataclasses import dataclass
 from datetime import datetime, timedelta
-from typing import Any, Optional, Union
+from typing import Any, ClassVar, List, Optional, Union
 
 import pytest
 
@@ -8,6 +9,7 @@ from pydantic import BaseModel
 
 from polyfactory.decorators import post_generated
 from polyfactory.exceptions import ConfigurationException, MissingBuildKwargException
+from polyfactory.factories.dataclass_factory import DataclassFactory
 from polyfactory.factories.pydantic_factory import ModelFactory
 from polyfactory.fields import Ignore, PostGenerated, Require, Use
 
@@ -202,3 +204,18 @@ def test_non_existing_model_fields_raises_with__check__model__(
         match="unknown_field is declared on the factory NoFieldModelFactory but it is not part of the model NoFieldModel",
     ):
         ModelFactory.create_factory(NoFieldModel, bases=None, __check_model__=True, unknown_field=factory_field)
+
+
+def test_mutable_defaults() -> None:
+    @dataclass
+    class A:
+        a: List[str]
+
+    class AFactory(DataclassFactory[A]):
+        a: ClassVar[List[str]] = []
+
+    AFactory.build().a.append("a")
+    assert AFactory.build().a == []
+
+    next(iter(AFactory.coverage())).a.append("value")
+    assert next(iter(AFactory.coverage())).a == []