Why does Signature.from_function() have to check the type of its argument?
Hi, I'm wondering about the purpose of this code in inspect.Signature.from_function(): """ if not isinstance(func, types.FunctionType): raise TypeError('{!r} is not a Python function'.format(func)) """ Is there any reason why this method would have to explicitly check the type of its argument? Why can't it just accept any object that quacks like a function? The reason why I ran into this is that I was trying to implement PEP 362 for Cython functions (which are not of Python function type types.FunctionType), and these two lines currently force me to copy the entire method body over into my code, just to build a Signature object from introspection. I find that a really bad design. Could this check please be removed completely? Or at least be changed to use isfunction() instead, so that it becomes a bit more easy to monkey patch it? (which is hard enough to explain to users already, BTW) Oh, and that's for 3.3.1, please. Stefan
2013/2/8 Stefan Behnel <stefan_ml@behnel.de>:
Hi,
I'm wondering about the purpose of this code in inspect.Signature.from_function():
""" if not isinstance(func, types.FunctionType): raise TypeError('{!r} is not a Python function'.format(func)) """
Is there any reason why this method would have to explicitly check the type of its argument? Why can't it just accept any object that quacks like a function?
The signature() function checks for types.FunctionType in order to call Signature.from_function(). How would you reimplement that? -- Regards, Benjamin
On Sat, Feb 9, 2013 at 1:06 AM, Benjamin Peterson <benjamin@python.org> wrote:
2013/2/8 Stefan Behnel <stefan_ml@behnel.de>:
Hi,
I'm wondering about the purpose of this code in inspect.Signature.from_function():
""" if not isinstance(func, types.FunctionType): raise TypeError('{!r} is not a Python function'.format(func)) """
Is there any reason why this method would have to explicitly check the type of its argument? Why can't it just accept any object that quacks like a function?
The signature() function checks for types.FunctionType in order to call Signature.from_function(). How would you reimplement that?
I assumed Stefan was wanting to use Signature.from_function() to set __signature__ (as soon as you do that, inspect.signature will do the right thing). At the moment, you would have to build the signature by hand (which was the original intent of the design), but we simply didn't think about projects like Cython that might be providing the full types.FunctionType API without being ordinary function instances. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
Nick Coghlan, 08.02.2013 16:20:
On Sat, Feb 9, 2013 at 1:06 AM, Benjamin Peterson wrote:
2013/2/8 Stefan Behnel:
I'm wondering about the purpose of this code in inspect.Signature.from_function():
""" if not isinstance(func, types.FunctionType): raise TypeError('{!r} is not a Python function'.format(func)) """
Is there any reason why this method would have to explicitly check the type of its argument? Why can't it just accept any object that quacks like a function?
The signature() function checks for types.FunctionType in order to call Signature.from_function(). How would you reimplement that?
It should call isfunction() instead of running an explicit type check.
I assumed Stefan was wanting to use Signature.from_function() to set __signature__ (as soon as you do that, inspect.signature will do the right thing).
Absolutely. Stefan
On Fri, Feb 8, 2013 at 10:54 AM, Stefan Behnel <stefan_ml@behnel.de> wrote:
Nick Coghlan, 08.02.2013 16:20:
On Sat, Feb 9, 2013 at 1:06 AM, Benjamin Peterson wrote:
2013/2/8 Stefan Behnel:
I'm wondering about the purpose of this code in inspect.Signature.from_function():
""" if not isinstance(func, types.FunctionType): raise TypeError('{!r} is not a Python function'.format(func)) """
Is there any reason why this method would have to explicitly check the type of its argument? Why can't it just accept any object that quacks like a function?
The signature() function checks for types.FunctionType in order to call Signature.from_function(). How would you reimplement that?
It should call isfunction() instead of running an explicit type check.
Isn't it possible now for an object to implement __instancecheck__ and claim to be an instance of FunctionType, anyway? (For that matter, shouldn't there be some ABCs for this?)
PJ Eby, 08.02.2013 19:46:
On Fri, Feb 8, 2013 at 10:54 AM, Stefan Behnel wrote:
Nick Coghlan, 08.02.2013 16:20:
On Sat, Feb 9, 2013 at 1:06 AM, Benjamin Peterson wrote:
2013/2/8 Stefan Behnel:
I'm wondering about the purpose of this code in inspect.Signature.from_function():
""" if not isinstance(func, types.FunctionType): raise TypeError('{!r} is not a Python function'.format(func)) """
Is there any reason why this method would have to explicitly check the type of its argument? Why can't it just accept any object that quacks like a function?
The signature() function checks for types.FunctionType in order to call Signature.from_function(). How would you reimplement that?
It should call isfunction() instead of running an explicit type check.
Isn't it possible now for an object to implement __instancecheck__ and claim to be an instance of FunctionType, anyway? (For that matter, shouldn't there be some ABCs for this?)
Wow, good call. Providing an __instancecheck__() method that simply says yes when it's asked for PyFunction_Type really works. Thanks! Stefan
Stefan Behnel, 08.02.2013 22:14:
PJ Eby, 08.02.2013 19:46:
On Fri, Feb 8, 2013 at 10:54 AM, Stefan Behnel wrote:
Nick Coghlan, 08.02.2013 16:20:
On Sat, Feb 9, 2013 at 1:06 AM, Benjamin Peterson wrote:
2013/2/8 Stefan Behnel:
I'm wondering about the purpose of this code in inspect.Signature.from_function():
""" if not isinstance(func, types.FunctionType): raise TypeError('{!r} is not a Python function'.format(func)) """
Is there any reason why this method would have to explicitly check the type of its argument? Why can't it just accept any object that quacks like a function?
The signature() function checks for types.FunctionType in order to call Signature.from_function(). How would you reimplement that?
It should call isfunction() instead of running an explicit type check.
Isn't it possible now for an object to implement __instancecheck__ and claim to be an instance of FunctionType, anyway? (For that matter, shouldn't there be some ABCs for this?)
Wow, good call. Providing an __instancecheck__() method that simply says yes when it's asked for PyFunction_Type really works.
Argh - sorry, got it all wrong. "__instancecheck__()" works exactly the other way round. In the type check above, it's the FunctionType type that gets asked for an instance check, which doesn't help at all in this case because it simply doesn't know about the desired subtype relation. It would work if type(func).__instancecheck__() was used, but that doesn't happen. So, no help from that side, sadly. Stefan
On Fri, Feb 8, 2013 at 5:44 PM, Stefan Behnel <stefan_ml@behnel.de> wrote:
Argh - sorry, got it all wrong. "__instancecheck__()" works exactly the other way round. In the type check above, it's the FunctionType type that gets asked for an instance check, which doesn't help at all in this case because it simply doesn't know about the desired subtype relation. It would work if type(func).__instancecheck__() was used, but that doesn't happen.
So, no help from that side, sadly.
How about you return FunctionType as your __class__ attribute? ;-) Your type() will still be different, but isinstance() also considers the __class__ if it's different from the type(). (At least it does in 2.x, I've not tried it in any 3.x versions yet...)
On 02/10/2013 01:46 PM, PJ Eby wrote:
How about you return FunctionType as your __class__ attribute? ;-)
Your type() will still be different, but isinstance() also considers the __class__ if it's different from the type(). (At least it does in 2.x, I've not tried it in any 3.x versions yet...)
Python 3.2.3 (default, Oct 19 2012, 19:53:16) [GCC 4.7.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. --> class Test(): ... pass ... --> t = Test() --> t.__class__ <class '__main__.Test'> --> import types --> class Test(): ... __class__ = types.FunctionType ... --> t = Test() --> t.__class__ <class 'function'> --> isinstance(t, types.FunctionType) True -- ~Ethan~
On Sat, Feb 9, 2013 at 12:09 AM, Stefan Behnel <stefan_ml@behnel.de> wrote:
Hi,
I'm wondering about the purpose of this code in inspect.Signature.from_function():
""" if not isinstance(func, types.FunctionType): raise TypeError('{!r} is not a Python function'.format(func)) """
Is there any reason why this method would have to explicitly check the type of its argument? Why can't it just accept any object that quacks like a function?
Primarily because the expected approach for alternative objects that want to expose signature in 3.3 was for them to provide the __signature__ attribute directly, rather than providing the far messier grab bag of eclectic attributes exposed by native Python functions (there's also a tentative plan kicking around somewhere to have a string version for 3.4, so we can start doing something useful with functions implemented in C). I suspect the specific reason for the lack of ducktyping is merely to provide a nice error message when people call the class method rather than the module level function, so removing the check sounds reasonable to me. The ultimate decision will be Georg's as RM, though. Probably the quickest way to get a decision from him would be a tracker issue set to release blocker, with a patch removing those two lines and a test confirming that non-functions with the right attributes are correctly accepted (e.g. by copying the attributes from a real function to a custom object and checking Signature.from_function gives the same answer). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
Nick Coghlan, 08.02.2013 16:08:
On Sat, Feb 9, 2013 at 12:09 AM, Stefan Behnel wrote:
I'm wondering about the purpose of this code in inspect.Signature.from_function():
""" if not isinstance(func, types.FunctionType): raise TypeError('{!r} is not a Python function'.format(func)) """
Is there any reason why this method would have to explicitly check the type of its argument? Why can't it just accept any object that quacks like a function?
Primarily because the expected approach for alternative objects that want to expose signature in 3.3 was for them to provide the __signature__ attribute directly, rather than providing the far messier grab bag of eclectic attributes exposed by native Python functions (there's also a tentative plan kicking around somewhere to have a string version for 3.4, so we can start doing something useful with functions implemented in C).
I suspect the specific reason for the lack of ducktyping is merely to provide a nice error message when people call the class method rather than the module level function, so removing the check sounds reasonable to me. The ultimate decision will be Georg's as RM, though. Probably the quickest way to get a decision from him would be a tracker issue set to release blocker, with a patch removing those two lines and a test confirming that non-functions with the right attributes are correctly accepted (e.g. by copying the attributes from a real function to a custom object and checking Signature.from_function gives the same answer).
Thanks for the feedback, patch is up. https://bugs.python.org/issue17159 Stefan
participants (5)
-
Benjamin Peterson -
Ethan Furman -
Nick Coghlan -
PJ Eby -
Stefan Behnel