Skip to content

Behaviour change in partialmethod in Python3.11 #99152

Description

@garyvdm

Bug report

The behavior of partialmethod changed in python3.11. This caused a library that I use testsuite to fail.

Here is a minimal use case that demonstrates this:

from functools import partialmethod


class Cell:
    def __init__(self):
        self.alive = False

    def set_state(self, state):
        self.alive = bool(state)


class Wrapper:
    def set_state_wrap(self, cell: Cell, state):
        cell.set_state(state)


wrapper = Wrapper()

Cell.set_alive = partialmethod(wrapper.set_state_wrap, True)

cell = Cell()
cell.set_alive()
print(cell.alive)

In python3.10, this prints True. In python3.11 it errors with:

Traceback (most recent call last):
  File "/home/gary/partialmethod.py", line 23, in <module>
    c.set_alive()
TypeError: Wrapper.set_state_wrap() missing 1 required positional argument: 'state'

The arguments passed to Wrapper.set_state_wrap are:
python3.10: wrapper, cell, True
python3.11: cell, True

So partialmethod is forgetting that set_state_wrap is a method of wrapper, and it needs to pass that in as the first arg.

I know that the way that they are using partialmethod here is not how it is intended to be used, and feels wrong. However this change is behavior if intended should be documented.

Your environment

  • CPython versions tested on: Python 3.11.0 (broken) Python 3.10.7 (working)
  • Operating system and architecture: Fedora Linux 36

Activity

  1. added
    type-bugAn unexpected behavior, bug, or error
    on Nov 6, 2022
  2. sweeneyde commented on Nov 6, 2022

    @sweeneyde
    Member

    I bisected to here:

    a918589 is the first bad commit
    commit a918589
    Author: Michael J. Sullivan sully@msully.net
    Date: Wed May 4 21:00:21 2022 -0700

    bpo-46764: Fix wrapping bound method with @classmethod (#31367)
    
  3. sweeneyde commented on Nov 6, 2022

    @sweeneyde
    Member
  4. self-assigned this
    on Nov 7, 2022
  5. pgcd commented on Dec 18, 2022

    @pgcd

    Having stumbled across this in a Django app where I was using Field.contribute_to_class to add some extra functionality to models that I couldn't change directly, I experimented with a few different workarounds with very little success.
    Eventually I decided the easiest solution was an explicit decorator, and this is what I'm using now (in the very specific cases where I need to ensure the old behavior is maintained:

    class partialmethod_with_self(partialmethod):
        def __get__(self, obj, cls=None):
            return self._make_unbound_method().__get__(obj, cls)
    
    

    This works in my case, and it should be backwards-compatible as well (I tested against 3.11 and 3.9.2).

  6. added
    stdlibStandard Library Python modules in the Lib/ directory
    on Nov 26, 2023
  7. goffioul commented on Sep 25, 2024

    @goffioul

    Is this behavior change documented anywhere? Will it ever be fixed? Is it considered as a bug at all by the python devs?

    I am also being hit by this problem in the context of django (and the contribute_to_class pattern). This basically boils down to this snippet (works fine until 3.10, errors out in 3.11 and 3.12):

    from functools import partialmethod
    
    class A:
        @classmethod
        def f1(cls, self, value):
            self.value = value
    
    class B:
        def __init__(self):
            self.value = None
    
    setattr(B, 'set_value_true', partialmethod(A.f1, True))
    
    b = B()
    b.set_value_true()
    print(b.value)
    
  8. duaneg commented on May 22, 2025

    @duaneg
    Contributor

    This was fixed in d058eae for #113157 (which is effectively a dup of this, I think) and delivered in v.3.13.0.

    I believe this issue can be closed.

  9. added a commit that references this issue on Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

stdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions