Skip to content

Failed to inspect __new__ and __init_subclass__ methods generated by warnings.deprecated #119605

Open
@XuehaiPan

Description

@XuehaiPan

Bug report

Bug description:

PEP 702 – Marking deprecations using the type system introduces a new API warnings.deprecated for deprecation.

While decorating a class object, it will update the __new__ method:

cpython/Lib/warnings.py

Lines 589 to 603 in 2268289

original_new = arg.__new__
@functools.wraps(original_new)
def __new__(cls, *args, **kwargs):
if cls is arg:
warn(msg, category=category, stacklevel=stacklevel + 1)
if original_new is not object.__new__:
return original_new(cls, *args, **kwargs)
# Mirrors a similar check in object.__new__.
elif cls.__init__ is object.__init__ and (args or kwargs):
raise TypeError(f"{cls.__name__}() takes no arguments")
else:
return original_new(cls)
arg.__new__ = staticmethod(__new__)

and the __init_subclass__ method:

cpython/Lib/warnings.py

Lines 605 to 625 in 2268289

original_init_subclass = arg.__init_subclass__
# We need slightly different behavior if __init_subclass__
# is a bound method (likely if it was implemented in Python)
if isinstance(original_init_subclass, MethodType):
original_init_subclass = original_init_subclass.__func__
@functools.wraps(original_init_subclass)
def __init_subclass__(*args, **kwargs):
warn(msg, category=category, stacklevel=stacklevel + 1)
return original_init_subclass(*args, **kwargs)
arg.__init_subclass__ = classmethod(__init_subclass__)
# Or otherwise, which likely means it's a builtin such as
# object's implementation of __init_subclass__.
else:
@functools.wraps(original_init_subclass)
def __init_subclass__(*args, **kwargs):
warn(msg, category=category, stacklevel=stacklevel + 1)
return original_init_subclass(*args, **kwargs)
arg.__init_subclass__ = __init_subclass__

For a class (cls) that does not implement the __new__ and __init_subclass__ methods, the warnings.deprecated decorator will generate these two methods (based on object.__new__ and type.__init_subclass__ ) and assign them to cls.__dict__.

However, if users want to inspect the methods in cls.__dict__, the inspect module fails to get the correct definition of cls.__new__. It is defined in warnings.py rather than object.__new__.

# test.py

from warnings import deprecated  # or from typing_extensions import deprecated


class Foo:
    def __new__(cls):
        return super().__new__(cls)


@deprecated("test")
class Bar:
    pass
In [1]: import inspect

In [2]: from test import Foo, Bar

In [3]: inspect.getsourcelines(Foo.__new__)
Out[3]: (['    def __new__(cls):\n', '        return super().__new__(cls)\n'], 7)

In [4]: inspect.getsourcelines(Bar.__new__) 
TypeError: module, class, method, function, traceback, frame, or code object was expected, got builtin_function_or_method

Expected to have inspect.getsourcelines(Bar.__new__) to be source code in /.../lib/python3.13/warnings.py.

CPython versions tested on:

3.13

Operating systems tested on:

Linux

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.13bugs and security fixes3.14bugs and security fixesstdlibPython modules in the Lib dirtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions