summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Pope <nick.pope@flightdataservices.com>2020-05-01 15:05:20 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-06-24 11:45:34 +0200
commit074844e9479a9ee1420685b36189aa2881289993 (patch)
treef281c25ce69d6b4cf79668183111fd8250537ea5
parent162765d6c3182e36095d29543e21b44b908625fc (diff)
Fixed #31529 -- Added support for serialization of pathlib.Path/PurePath and os.PathLike in migrations.
-rw-r--r--django/db/migrations/serializer.py17
-rw-r--r--docs/releases/3.2.txt3
-rw-r--r--docs/topics/migrations.txt10
-rw-r--r--tests/migrations/test_writer.py41
4 files changed, 71 insertions, 0 deletions
diff --git a/django/db/migrations/serializer.py b/django/db/migrations/serializer.py
index ead81c398a..e19c881cda 100644
--- a/django/db/migrations/serializer.py
+++ b/django/db/migrations/serializer.py
@@ -5,6 +5,8 @@ import decimal
import enum
import functools
import math
+import os
+import pathlib
import re
import types
import uuid
@@ -217,6 +219,19 @@ class OperationSerializer(BaseSerializer):
return string.rstrip(','), imports
+class PathLikeSerializer(BaseSerializer):
+ def serialize(self):
+ return repr(os.fspath(self.value)), {}
+
+
+class PathSerializer(BaseSerializer):
+ def serialize(self):
+ # Convert concrete paths to pure paths to avoid issues with migrations
+ # generated on one platform being used on a different platform.
+ prefix = 'Pure' if isinstance(self.value, pathlib.Path) else ''
+ return 'pathlib.%s%r' % (prefix, self.value), {'import pathlib'}
+
+
class RegexSerializer(BaseSerializer):
def serialize(self):
regex_pattern, pattern_imports = serializer_factory(self.value.pattern).serialize()
@@ -298,6 +313,8 @@ class Serializer:
collections.abc.Iterable: IterableSerializer,
(COMPILED_REGEX_TYPE, RegexObject): RegexSerializer,
uuid.UUID: UUIDSerializer,
+ pathlib.PurePath: PathSerializer,
+ os.PathLike: PathLikeSerializer,
}
@classmethod
diff --git a/docs/releases/3.2.txt b/docs/releases/3.2.txt
index 3cc39ff285..8544df77c3 100644
--- a/docs/releases/3.2.txt
+++ b/docs/releases/3.2.txt
@@ -200,6 +200,9 @@ Migrations
filename fragment that will be used to name a migration containing only that
operation.
+* Migrations now support serialization of pure and concrete path objects from
+ :mod:`pathlib`, and :class:`os.PathLike` instances.
+
Models
~~~~~~
diff --git a/docs/topics/migrations.txt b/docs/topics/migrations.txt
index 2d609f01da..b0d75a4f09 100644
--- a/docs/topics/migrations.txt
+++ b/docs/topics/migrations.txt
@@ -720,6 +720,11 @@ Django can serialize the following:
- ``uuid.UUID`` instances
- :func:`functools.partial` and :class:`functools.partialmethod` instances
which have serializable ``func``, ``args``, and ``keywords`` values.
+- Pure and concrete path objects from :mod:`pathlib`. Concrete paths are
+ converted to their pure path equivalent, e.g. :class:`pathlib.PosixPath` to
+ :class:`pathlib.PurePosixPath`.
+- :class:`os.PathLike` instances, e.g. :class:`os.DirEntry`, which are
+ converted to ``str`` or ``bytes`` using :func:`os.fspath`.
- ``LazyObject`` instances which wrap a serializable value.
- Enumeration types (e.g. ``TextChoices`` or ``IntegerChoices``) instances.
- Any Django field
@@ -728,6 +733,11 @@ Django can serialize the following:
- Any class reference (must be in module's top-level scope)
- Anything with a custom ``deconstruct()`` method (:ref:`see below <custom-deconstruct-method>`)
+.. versionchanged:: 3.2
+
+ Serialization support for pure and concrete path objects from
+ :mod:`pathlib`, and :class:`os.PathLike` instances was added.
+
Django cannot serialize:
- Nested classes
diff --git a/tests/migrations/test_writer.py b/tests/migrations/test_writer.py
index 6a65e87d5a..5635dc62f0 100644
--- a/tests/migrations/test_writer.py
+++ b/tests/migrations/test_writer.py
@@ -4,7 +4,9 @@ import enum
import functools
import math
import os
+import pathlib
import re
+import sys
import uuid
from unittest import mock
@@ -429,6 +431,45 @@ class WriterTests(SimpleTestCase):
"default=uuid.UUID('5c859437-d061-4847-b3f7-e6b78852f8c8'))"
)
+ def test_serialize_pathlib(self):
+ # Pure path objects work in all platforms.
+ self.assertSerializedEqual(pathlib.PurePosixPath())
+ self.assertSerializedEqual(pathlib.PureWindowsPath())
+ path = pathlib.PurePosixPath('/path/file.txt')
+ expected = ("pathlib.PurePosixPath('/path/file.txt')", {'import pathlib'})
+ self.assertSerializedResultEqual(path, expected)
+ path = pathlib.PureWindowsPath('A:\\File.txt')
+ expected = ("pathlib.PureWindowsPath('A:/File.txt')", {'import pathlib'})
+ self.assertSerializedResultEqual(path, expected)
+ # Concrete path objects work on supported platforms.
+ if sys.platform == 'win32':
+ self.assertSerializedEqual(pathlib.WindowsPath.cwd())
+ path = pathlib.WindowsPath('A:\\File.txt')
+ expected = ("pathlib.PureWindowsPath('A:/File.txt')", {'import pathlib'})
+ self.assertSerializedResultEqual(path, expected)
+ else:
+ self.assertSerializedEqual(pathlib.PosixPath.cwd())
+ path = pathlib.PosixPath('/path/file.txt')
+ expected = ("pathlib.PurePosixPath('/path/file.txt')", {'import pathlib'})
+ self.assertSerializedResultEqual(path, expected)
+
+ field = models.FilePathField(path=pathlib.PurePosixPath('/home/user'))
+ string, imports = MigrationWriter.serialize(field)
+ self.assertEqual(
+ string,
+ "models.FilePathField(path=pathlib.PurePosixPath('/home/user'))",
+ )
+ self.assertIn('import pathlib', imports)
+
+ def test_serialize_path_like(self):
+ path_like = list(os.scandir(os.path.dirname(__file__)))[0]
+ expected = (repr(path_like.path), {})
+ self.assertSerializedResultEqual(path_like, expected)
+
+ field = models.FilePathField(path=path_like)
+ string = MigrationWriter.serialize(field)[0]
+ self.assertEqual(string, 'models.FilePathField(path=%r)' % path_like.path)
+
def test_serialize_functions(self):
with self.assertRaisesMessage(ValueError, 'Cannot serialize function: lambda'):
self.assertSerializedEqual(lambda x: 42)