mirror of
https://github.com/django/django.git
synced 2025-06-24 04:49:11 +00:00
Refs #36383 -- Extended DeconstructibleSerializer to support non-identifier keyword arguments.
In Python, keyword arguments must normally be valid identifiers (i.e., variable names that follow Python's naming rules). However, Python dicts can have keys that aren't valid identifiers, like "foo-bar" or "123foo". This commit ensures that keyword arguments that are nt valid identifiers, are properly handled when deconstructing an object.
This commit is contained in:
parent
0f94ecd49d
commit
4647e2b866
@ -102,10 +102,20 @@ class DeconstructibleSerializer(BaseSerializer):
|
|||||||
arg_string, arg_imports = serializer_factory(arg).serialize()
|
arg_string, arg_imports = serializer_factory(arg).serialize()
|
||||||
strings.append(arg_string)
|
strings.append(arg_string)
|
||||||
imports.update(arg_imports)
|
imports.update(arg_imports)
|
||||||
|
non_ident_kwargs = {}
|
||||||
for kw, arg in sorted(kwargs.items()):
|
for kw, arg in sorted(kwargs.items()):
|
||||||
arg_string, arg_imports = serializer_factory(arg).serialize()
|
if kw.isidentifier():
|
||||||
imports.update(arg_imports)
|
arg_string, arg_imports = serializer_factory(arg).serialize()
|
||||||
strings.append("%s=%s" % (kw, arg_string))
|
imports.update(arg_imports)
|
||||||
|
strings.append("%s=%s" % (kw, arg_string))
|
||||||
|
else:
|
||||||
|
non_ident_kwargs[kw] = arg
|
||||||
|
if non_ident_kwargs:
|
||||||
|
# Serialize non-identifier keyword arguments as a dict.
|
||||||
|
kw_string, kw_imports = serializer_factory(non_ident_kwargs).serialize()
|
||||||
|
strings.append(f"**{kw_string}")
|
||||||
|
imports.update(kw_imports)
|
||||||
|
|
||||||
return "%s(%s)" % (name, ", ".join(strings)), imports
|
return "%s(%s)" % (name, ", ".join(strings)), imports
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -181,6 +181,9 @@ Migrations
|
|||||||
|
|
||||||
* Migrations now support serialization of :class:`zoneinfo.ZoneInfo` instances.
|
* Migrations now support serialization of :class:`zoneinfo.ZoneInfo` instances.
|
||||||
|
|
||||||
|
* Serialization of deconstructible objects now supports keyword arguments with
|
||||||
|
names that are not valid Python identifiers.
|
||||||
|
|
||||||
Models
|
Models
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
|
||||||
|
@ -113,6 +113,7 @@ databrowse
|
|||||||
datafile
|
datafile
|
||||||
datetimes
|
datetimes
|
||||||
declaratively
|
declaratively
|
||||||
|
deconstructible
|
||||||
deduplicates
|
deduplicates
|
||||||
deduplication
|
deduplication
|
||||||
deepcopy
|
deepcopy
|
||||||
|
@ -41,6 +41,13 @@ class DeconstructibleInstances:
|
|||||||
return ("DeconstructibleInstances", [], {})
|
return ("DeconstructibleInstances", [], {})
|
||||||
|
|
||||||
|
|
||||||
|
@deconstructible
|
||||||
|
class DeconstructibleArbitrary:
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.args = args
|
||||||
|
self.kwargs = kwargs
|
||||||
|
|
||||||
|
|
||||||
class Money(decimal.Decimal):
|
class Money(decimal.Decimal):
|
||||||
def deconstruct(self):
|
def deconstruct(self):
|
||||||
return (
|
return (
|
||||||
@ -1143,6 +1150,24 @@ class WriterTests(SimpleTestCase):
|
|||||||
"models.CharField(default=migrations.test_writer.DeconstructibleInstances)",
|
"models.CharField(default=migrations.test_writer.DeconstructibleInstances)",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_serialize_non_identifier_keyword_args(self):
|
||||||
|
instance = DeconstructibleArbitrary(
|
||||||
|
**{"kebab-case": 1, "my_list": [1, 2, 3], "123foo": {"456bar": set()}},
|
||||||
|
regular="kebab-case",
|
||||||
|
**{"simple": 1, "complex": 3.1416},
|
||||||
|
)
|
||||||
|
string, imports = MigrationWriter.serialize(instance)
|
||||||
|
self.assertEqual(
|
||||||
|
string,
|
||||||
|
"migrations.test_writer.DeconstructibleArbitrary(complex=3.1416, "
|
||||||
|
"my_list=[1, 2, 3], regular='kebab-case', simple=1, "
|
||||||
|
"**{'123foo': {'456bar': set()}, 'kebab-case': 1})",
|
||||||
|
)
|
||||||
|
self.assertEqual(imports, {"import migrations.test_writer"})
|
||||||
|
result = self.serialize_round_trip(instance)
|
||||||
|
self.assertEqual(result.args, instance.args)
|
||||||
|
self.assertEqual(result.kwargs, instance.kwargs)
|
||||||
|
|
||||||
def test_register_serializer(self):
|
def test_register_serializer(self):
|
||||||
class ComplexSerializer(BaseSerializer):
|
class ComplexSerializer(BaseSerializer):
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user