1
0
mirror of https://github.com/django/django.git synced 2024-12-22 17:16:24 +00:00

Fixed #31529 -- Added support for serialization of pathlib.Path/PurePath and os.PathLike in migrations.

This commit is contained in:
Nick Pope 2020-05-01 15:05:20 +01:00 committed by Mariusz Felisiak
parent 162765d6c3
commit 074844e947
4 changed files with 71 additions and 0 deletions

View File

@ -5,6 +5,8 @@ import decimal
import enum import enum
import functools import functools
import math import math
import os
import pathlib
import re import re
import types import types
import uuid import uuid
@ -217,6 +219,19 @@ class OperationSerializer(BaseSerializer):
return string.rstrip(','), imports 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): class RegexSerializer(BaseSerializer):
def serialize(self): def serialize(self):
regex_pattern, pattern_imports = serializer_factory(self.value.pattern).serialize() regex_pattern, pattern_imports = serializer_factory(self.value.pattern).serialize()
@ -298,6 +313,8 @@ class Serializer:
collections.abc.Iterable: IterableSerializer, collections.abc.Iterable: IterableSerializer,
(COMPILED_REGEX_TYPE, RegexObject): RegexSerializer, (COMPILED_REGEX_TYPE, RegexObject): RegexSerializer,
uuid.UUID: UUIDSerializer, uuid.UUID: UUIDSerializer,
pathlib.PurePath: PathSerializer,
os.PathLike: PathLikeSerializer,
} }
@classmethod @classmethod

View File

@ -200,6 +200,9 @@ Migrations
filename fragment that will be used to name a migration containing only that filename fragment that will be used to name a migration containing only that
operation. operation.
* Migrations now support serialization of pure and concrete path objects from
:mod:`pathlib`, and :class:`os.PathLike` instances.
Models Models
~~~~~~ ~~~~~~

View File

@ -720,6 +720,11 @@ Django can serialize the following:
- ``uuid.UUID`` instances - ``uuid.UUID`` instances
- :func:`functools.partial` and :class:`functools.partialmethod` instances - :func:`functools.partial` and :class:`functools.partialmethod` instances
which have serializable ``func``, ``args``, and ``keywords`` values. 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. - ``LazyObject`` instances which wrap a serializable value.
- Enumeration types (e.g. ``TextChoices`` or ``IntegerChoices``) instances. - Enumeration types (e.g. ``TextChoices`` or ``IntegerChoices``) instances.
- Any Django field - Any Django field
@ -728,6 +733,11 @@ Django can serialize the following:
- Any class reference (must be in module's top-level scope) - Any class reference (must be in module's top-level scope)
- Anything with a custom ``deconstruct()`` method (:ref:`see below <custom-deconstruct-method>`) - 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: Django cannot serialize:
- Nested classes - Nested classes

View File

@ -4,7 +4,9 @@ import enum
import functools import functools
import math import math
import os import os
import pathlib
import re import re
import sys
import uuid import uuid
from unittest import mock from unittest import mock
@ -429,6 +431,45 @@ class WriterTests(SimpleTestCase):
"default=uuid.UUID('5c859437-d061-4847-b3f7-e6b78852f8c8'))" "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): def test_serialize_functions(self):
with self.assertRaisesMessage(ValueError, 'Cannot serialize function: lambda'): with self.assertRaisesMessage(ValueError, 'Cannot serialize function: lambda'):
self.assertSerializedEqual(lambda x: 42) self.assertSerializedEqual(lambda x: 42)