Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

functools.update_wrapper and functools.wraps cannot be used in mypycified files on py312 #16077

Closed
AlexWaygood opened this issue Sep 9, 2023 · 3 comments · Fixed by #16084
Closed
Labels
bug mypy got something wrong topic-mypyc mypyc bugs

Comments

@AlexWaygood
Copy link
Member

Bug Report

Mypycifying a file that uses functools.update_wrapper or functools.wraps breaks if you're using Python 3.12.

To Reproduce

  1. Ensure you're using Python 3.12

  2. Save this code into a file repro.py:

    from functools import update_wrapper
    from typing import Callable
    
    def dont_increase_indentation(split_func: Callable[[], str]) -> Callable[[], str]:
        def split_wrapper() -> str:
            return "foo"
    
        update_wrapper(split_wrapper, split_func)
        return split_wrapper
    
    
    @dont_increase_indentation
    def delimiter_split() -> str:
        return "bar"
  3. Run mypyc repro.py

  4. Run python -c "import repro"

Expected Behavior

The mypycified repro module should import without errors. This is the behaviour you get if you mypycify the module using Python 3.11.

Actual Behavior

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "repro3.py", line 12, in <module>
    @dont_increase_indentation
  File "repro3.py", line 8, in dont_increase_indentation
    update_wrapper(split_wrapper, split_func)
  File "C:\Users\alexw\.pyenv\pyenv-win\versions\3.12.0rc2\Lib\functools.py", line 58, in update_wrapper
    getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
    ^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'split_wrapper_dont_increase_indentation_obj' object has no attribute '__dict__'. Did you mean: '__dir__'?

Notes on the bug

What is causing the bug?

The bug appears to be due to mypyc not creating __dict__ attributes for function objects. This appears to be despite this comment in the source code, which looks like it's intended to create __dict__ attributes for functions precisely due to how functools.update_wrapper works:

# The functools @wraps decorator attempts to call setattr on
# nested functions, so we create a dict for these nested
# functions.
# https://github.com/python/cpython/blob/3.7/Lib/functools.py#L58
if builder.fn_info.is_nested:
callable_class_ir.has_dict = True

Why is the bug only occurring on Python 3.12?

The bug with functools.update_wrapper only occurs with Python 3.12. The interesting thing is that you can reproduce a similar bug with this snippet, but unlike the first snippet, this will also repro the bug on Python 3.11:

from typing import Callable

def dont_increase_indentation(split_func: Callable[[], str]) -> Callable[[], str]:
    def split_wrapper() -> str:
        return "foo"

    split_wrapper.__dict__.update(split_func.__dict__)
    return split_wrapper


@dont_increase_indentation
def delimiter_split() -> str:
    return "bar"

Error when running python -c "import repro" after mypycifying this snippet:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "repro2.py", line 12, in <module>
    @dont_increase_indentation
  File "repro2.py", line 8, in dont_increase_indentation
    split_wrapper.__dict__.update(split_func.__dict__)
AttributeError: 'split_wrapper_dont_increase_indentation_obj' object has no attribute '__dict__'. Did you mean: '__dir__'?

It's unclear to me why functools.update_wrapper works on Python 3.11 and breaks on Python 3.12 (snippet 1), when the "inlined" version (snippet 2) breaks on both Python 3.11 and Python 3.12.

Did functools.update_wrapper change in Python 3.12?

There was a minor change to update_wrapper in Python 3.12 as part of the PEP-695 implementation: python/cpython#104601. Other than this change, however, the source code for update_wrapper and wraps has remained unchanged for the last 10 years.

Your Environment

  • Mypy version used: 1.5.1
  • Python version used: 3.12.0rc2
  • Platform: Windows
@AlexWaygood AlexWaygood added bug mypy got something wrong topic-mypyc mypyc bugs labels Sep 9, 2023
@AlexWaygood AlexWaygood changed the title functools.update_wrapper cannot be used in mypycified files on py312 functools.update_wrapper and functools.wraps cannot be used in mypycified files on py312 Sep 9, 2023
@AlexWaygood
Copy link
Member Author

AlexWaygood commented Sep 9, 2023

(This bug is affecting black's ability to build wheels for Python 3.12: psf/black#3867 (comment).)

@AlexWaygood
Copy link
Member Author

  • Platform: Windows

(@JelleZijlstra is also running into this error locally using macOS)

@hauntsaninja
Copy link
Collaborator

@JukkaL do we need to add a {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict}, getset descriptor or something?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-mypyc mypyc bugs
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants