Browse Source

Fixed #34822 -- Added support for serializing functions decorated with functools.lru_cache in migrations.

`@functools.cache` and `@functools.lru_cache` return an object of type
`functools._lru_cache_wrapper` which prevented the migrations serializer from
working. Simply using the existing `FunctionTypeSerializer` for this additional
type works as expected.
Nick Pope 1 year ago
parent
commit
f92e68c30a

+ 1 - 0
django/db/migrations/serializer.py

@@ -350,6 +350,7 @@ class Serializer:
             types.FunctionType,
             types.BuiltinFunctionType,
             types.MethodType,
+            functools._lru_cache_wrapper,
         ): FunctionTypeSerializer,
         collections.abc.Iterable: IterableSerializer,
         (COMPILED_REGEX_TYPE, RegexObject): RegexSerializer,

+ 3 - 1
docs/releases/5.0.txt

@@ -401,7 +401,9 @@ Management Commands
 Migrations
 ~~~~~~~~~~
 
-* ...
+* Serialization of functions decorated with :func:`functools.cache` or
+  :func:`functools.lru_cache` is now supported without the need to write a
+  custom serializer.
 
 Models
 ~~~~~~

+ 7 - 0
docs/topics/migrations.txt

@@ -788,6 +788,8 @@ Django can serialize the following:
 
   - Functions may be decorated if wrapped properly, i.e. using
     :func:`functools.wraps`
+  - The :func:`functools.cache` and :func:`functools.lru_cache` decorators are
+    explicitly supported
 
 - Unbound methods used from within the class body
 - Any class reference (must be in module's top-level scope)
@@ -797,6 +799,11 @@ Django can serialize the following:
 
     Serialization support for ``enum.Flag`` was added.
 
+.. versionchanged:: 5.0
+
+    Serialization support for functions decorated with :func:`functools.cache`
+    or :func:`functools.lru_cache` was added.
+
 Django cannot serialize:
 
 - Nested classes

+ 12 - 0
tests/migrations/test_writer.py

@@ -90,6 +90,16 @@ def function_with_decorator():
     pass
 
 
+@functools.cache
+def function_with_cache():
+    pass
+
+
+@functools.lru_cache(maxsize=10)
+def function_with_lru_cache():
+    pass
+
+
 class OperationWriterTests(SimpleTestCase):
     def test_empty_signature(self):
         operation = custom_migration_operations.operations.TestOperation()
@@ -581,6 +591,8 @@ class WriterTests(SimpleTestCase):
 
     def test_serialize_decorated_functions(self):
         self.assertSerializedEqual(function_with_decorator)
+        self.assertSerializedEqual(function_with_cache)
+        self.assertSerializedEqual(function_with_lru_cache)
 
     def test_serialize_datetime(self):
         self.assertSerializedEqual(datetime.datetime.now())