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:
parent
162765d6c3
commit
074844e947
@ -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
|
||||||
|
@ -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
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user