-
-
Notifications
You must be signed in to change notification settings - Fork 44
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
feature: Support __new__
signatures for classses
#320
Comments
Thanks for the request. Could you give a concrete example where this would be needed/useful? |
For instance: https://github.com/pydantic/pydantic-core/pull/1420/files#diff-7e296e0875fa785fb0d0f6904dc84ce9145cdd30e12b96ef6cb37d48752135e1R77. But more generally, people can define The logic to determine the correct signature can get overly complicated. Maybe what could be done for now (and to avoid introducing breaking changes): If no This does not reflect the runtime behavior but is probably the past of least resistance here. Wdyt? Happy to push a PR. |
Thanks 🙂 Sounds like this can quickly become super complicated indeed. How does runtime behave with class inheritance and metaclasses 😵? From the docs you linked ( 🙏 ):
Do I understand correctly that when doing |
What about the following? class A:
def __init__(self, ...): ...
class B(A):
def __new__(cls, ...): ...
class C(B): ... Should we use the parameters from |
>>> class A:
... def __init__(self, a, b): ...
...
>>> class B(A):
... def __new__(cls, c, d): ...
...
>>> b = B()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: B.__new__() missing 2 required positional arguments: 'c' and 'd' |
Looks like >>> class A:
... def __new__(cls, a, b): ...
...
>>> class B(A):
... def __init__(self, c, d): ...
...
>>> b = B()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: A.__new__() missing 2 required positional arguments: 'a' and 'b' Fortunately that seems to make the change easy: simply try to get params from Metaclasses can wait for now 🙈 |
Note that default C implementations of
Looks good, however, I'm a bit afraid that this may break existing setups. It is common for classes to implement a "catch-all"
I don't expect anyone to request support for it anyway. Mypy doesn't even support it and it is very uncommon to implement |
The thing is, our logic happens after visiting AST or inspecting runtime objects. At that point we don't see any of the default C implementations. We only see what was declared in the loaded modules.
I'm not sure to understand. If you define both >>> class A:
... def __new__(cls, *args, **kwargs):
... print("in new")
... print(args, kwargs)
... def __init__(self, *args, **kwargs):
... print("in init")
... print(args, kwargs)
...
>>> a = A()
in new
() {} EDIT: ah, unless we're supposed to explicitly call |
So, yeah, it seems to make sense to use the
Not even sure about the last two points, as maybe we should loop on the first two while following the MRO... All this to support the case where devs don't want to duplicate correct signature from |
What about the following: class A:
def __init__(self, param1: str): ...
class B(A):
def __new__(cls, *args, **kwargs):
# some logic
return super().__new__(cls) Surely users would like the parameters data to be fetched from Sounds like a case where we won't be able to satisfy everyone. I'm wondering if this should live in an extension, but I'm not sure the extension system will support it in its current state. |
Usually you call
We can check how Sphinx does it. I'm not entirely sure about the current logic, but PR can be found here: sphinx-doc/sphinx#7384. It supports metaclass' |
Awesome, thanks for the link! Looks like they give precedence to # Now we check if the 'obj' class has a '__new__' method
new = get_user_defined_function_or_method(self.object, '__new__')
if new is not None:
self.env.app.emit('autodoc-before-process-signature', new, True)
try:
return inspect.signature(new, bound_method=True)
except ValueError:
pass
# Finally, we should have at least __init__ implemented
init = get_user_defined_function_or_method(self.object, '__init__')
if init is not None:
... If neither In Griffe we just need to decide whether we give precedence to I'd be interested to see how common |
Couldn't find anything relevant in a Github search. But maybe this isn't too much of a big deal, seems like Sphinx introduced the functionality as a non breaking change and no one complained about it :) |
Is your feature request related to a problem? Please describe.
The
Class.parameters
property uses__init__
by default:griffe/src/_griffe/models.py
Lines 1605 to 1615 in 58eb9f4
It would be nice if
__new__
could be supported as well. Behavior when both are defined should probably be considered. The typing specification regarding constructors might help in defining a strategy.Describe the solution you'd like
Describe alternatives you've considered
Additional context
Boost priority
The text was updated successfully, but these errors were encountered: