diff --git a/.verchew.ini b/.verchew.ini index 5ed9c9a..9e50349 100644 --- a/.verchew.ini +++ b/.verchew.ini @@ -17,6 +17,6 @@ version = 1 cli = dot cli_version_arg = -V -version = 7 || 8 || 9 || 10 +version = graphviz optional = true message = This is only needed to generate UML diagrams for documentation. diff --git a/CHANGELOG.md b/CHANGELOG.md index 73989a0..00779ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release Notes +## 2.2.3 (2024-05-26) + +- Added caching to default factory calls. + ## 2.2.2 (2024-01-06) - Fixed `Manager.all()` behavior for patterns with default vales. diff --git a/datafiles/tests/files/1.yml b/datafiles/tests/files/1.yml new file mode 100644 index 0000000..6540615 --- /dev/null +++ b/datafiles/tests/files/1.yml @@ -0,0 +1,2 @@ +bar: 2 +nested: diff --git a/datafiles/utils.py b/datafiles/utils.py index d7b310f..b1f8b54 100644 --- a/datafiles/utils.py +++ b/datafiles/utils.py @@ -3,6 +3,7 @@ import dataclasses import time from contextlib import suppress +from dataclasses import Field from functools import lru_cache from pathlib import Path from pprint import pformat @@ -30,7 +31,7 @@ def get_default_field_value(instance, name): return field.default if not isinstance(field.default_factory, Missing): # type: ignore - return field.default_factory() # type: ignore + return _call_default_factory(field) if not field.init and hasattr(instance, "__post_init__"): return getattr(instance, name) @@ -38,6 +39,11 @@ def get_default_field_value(instance, name): return Missing +@cached +def _call_default_factory(field: Field): + return field.default_factory() # type: ignore + + def prettify(value) -> str: """Ensure value is a dictionary pretty-format it.""" return pformat(dictify(value)) diff --git a/poetry.lock b/poetry.lock index 09ef320..de5b2ab 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2259,6 +2259,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, diff --git a/pyproject.toml b/pyproject.toml index 8f60a6b..ce14453 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "datafiles" -version = "2.2.2" +version = "2.2.3" description = "File-based ORM for dataclasses." license = "MIT" diff --git a/tests/test_instantiation.py b/tests/test_instantiation.py index 1a58a75..23999c8 100644 --- a/tests/test_instantiation.py +++ b/tests/test_instantiation.py @@ -10,6 +10,8 @@ from . import xfail_with_pep_563 +counter = 0 + @datafile("../tmp/sample.yml", manual=True) class SampleWithDefaults: @@ -150,6 +152,25 @@ def when_file_exists(expect): expect(sample.b) == 3.4 expect(sample.c) == 9.9 + def it_only_calls_factory_when_needed(expect): + + def default_factory(): + global counter + counter += 1 + logbreak(f"Called default factory: {counter}") + return "a" + + @datafile("../tmp/sample.yml") + class Sample: + value: str = field(default_factory=default_factory) + + sample = Sample() + + sample.value = "b" + sample.value = "c" + + expect(counter) == 2 + def describe_missing_attributes(): @xfail_with_pep_563 diff --git a/tests/test_saving.py b/tests/test_saving.py index 15883e6..becaa98 100644 --- a/tests/test_saving.py +++ b/tests/test_saving.py @@ -46,7 +46,7 @@ def without_initial_values(sample, expect): """ ) - def with_convertable_initial_values(expect): + def with_convertible_initial_values(expect): sample = Sample(1, 2, 3, 4) sample.datafile.save()