Opened 8 days ago

Closed 8 days ago

#28894 closed Bug (wontfix)

Invalid migrations caused by incorrect serialization of functools.partial()

Reported by: Nick Pope Owned by: Nick Pope
Component: Migrations Version: 1.9
Severity: Normal Keywords: functools partial migrations serialization
Cc: Triage Stage: Unreviewed
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

So this one was interesting... I ended up with a migration that contained something like this (reduced to a trivial example):

models.CharField(default=functools.partial(int, *('123', ), **None), ...)

The expected output should have been:

models.CharField(default=functools.partial(int, *('123', ), **{}), ...)

It boils down to the following issue:

# Python 2.7.6: Empty args working, empty keywords broken.
>>> import functools
>>> print(functools.partial(int, base=10).args)
()
>>> print(functools.partial(int, '123').keywords)
None
# Python 2.7.14: Empty args working, empty keywords working.
>>> import functools
>>> print(functools.partial(int, base=10).args)
()
>>> print(functools.partial(int, '123').keywords)
{}

Scouring the release notes for Python, I found the following:

"The keywords attribute of functools.partial is now always a dictionary."

This was resolved in 2.7.10, 3.4.4 & 3.5.0.

As Django 2.1 will only support Python 3.5 or later, this only needs to be backported to 1.11 and 2.0 and need not be applied to master.

Pull request incoming...

Change History (5)

comment:1 Changed 8 days ago by Tim Graham

I'm not sure if this merits fixing considering functools.partial support was added in Django 1.9 so this isn't a new or common (apparently) regression. Also, regarding Python support, Django's policy is "We highly recommend and only officially support the latest release of each series." Can you workaround it in your project?

comment:2 Changed 8 days ago by Nick Pope

Indeed - I know of the policies, it's just an awkward problem. I'm not sure... It might be possible to force everything through as keyword arguments because args is unaffected.

I've updated the PR anyway so that it has a proper test...

comment:3 Changed 8 days ago by Tim Graham

How about adding **{} to your code?

>>> print(functools.partial(int, '123', **{}).keywords)
{}

comment:4 Changed 8 days ago by Nick Pope

Cool. Nice hack!

I've just tried changing things to always have at least one keyword argument anyway.

comment:5 Changed 8 days ago by Tim Graham

Resolution: wontfix
Status: assignedclosed

All right, I don't think it's worth adding a workaround to Django then.

Note: See TracTickets for help on using tickets.
Back to Top