concerns regarding callable() method
Hi, I have seen in PEP 3100 that callable() function is planned to be removed in Python 3000 with this replacement: "just call the object and catch the exception???". For one, the object (if it is callable) can raise exception itself, so you need to somehow to differentiate between exception raised inside its __call__ and exception raised if object is not callable to begin with. Additionally consider something like something.set_callback (x) Assume that set_callback() wants to check if `x' is callable at all, to raise exception early and make error tracking easier. Currently, you can assert callable (x) But if callable() is removed, there is no apparent replacement. Of course, you cannot call `x' since it might have side-effects or be slow etc. Please reconsider removal of callable() or provide an adequate replacement. Paul
I have seen in PEP 3100 that callable() function is planned to be removed in Python 3000 with this replacement: "just call the object and catch the exception???". For one, the object (if it is callable) can raise exception itself, so you need to somehow to differentiate between exception raised inside its __call__ and exception raised if object is not callable to begin with.
I seem to recall bringing up the same issue a while ago; at the time, the answer was that if you need it, you can write your own: def callable(x): return hasattr(x, '__call__') My own preference would be for such queries to be defined abstractly as a built-in part of the language, but apparently my preference is out of sync with the community in this particular respect.
On 4/8/07, Paul Pogonyshev
I have seen in PEP 3100 that callable() function is planned to be removed in Python 3000 with this replacement: "just call the object and catch the exception???". For one, the object (if it is callable) can raise exception itself, so you need to somehow to differentiate between exception raised inside its __call__ and exception raised if object is not callable to begin with.
Why?
Additionally consider something like
something.set_callback (x)
Assume that set_callback() wants to check if `x' is callable at all, to raise exception early and make error tracking easier. Currently, you can
assert callable (x)
But if callable() is removed, there is no apparent replacement. Of course, you cannot call `x' since it might have side-effects or be slow etc.
assert hasattr(x, '__call__') I note that callable() was introduced before all callable objects had a __call__ attribute. This is no longer the case, so it's not needed.
Please reconsider removal of callable() or provide an adequate replacement.
What if someone passes a callable that doesn't have the expected signature? -- --Guido van Rossum (home page: http://www.python.org/~guido/)
Guido van Rossum wrote:
On 4/8/07, Paul Pogonyshev
wrote: Additionally consider something like
something.set_callback (x)
Assume that set_callback() wants to check if `x' is callable at all, to raise exception early and make error tracking easier. Currently, you can
assert callable (x)
But if callable() is removed, there is no apparent replacement. Of course, you cannot call `x' since it might have side-effects or be slow etc.
assert hasattr(x, '__call__')
I note that callable() was introduced before all callable objects had a __call__ attribute. This is no longer the case, so it's not needed.
I just didn't think about that possibility. If that works the same way, callable() is just a sugar and not something unimplementable in other ways. Therefore, my objection is discarded. (But PEP 3100 should probably be update to mention this, otherwise you may get this complaint again ;)
Please reconsider removal of callable() or provide an adequate replacement.
What if someone passes a callable that doesn't have the expected signature?
Well, I don't know a way to catch such situations now, so removing callable() will not make it worse (even if you don't know about hasattr trick above.) Paul
On 4/8/07, Paul Pogonyshev
Guido van Rossum wrote:
What if someone passes a callable that doesn't have the expected signature?
Well, I don't know a way to catch such situations now, so removing callable() will not make it worse (even if you don't know about hasattr trick above.)
My point is that it's futile to use callable() -- even if it passes, you have no assurance that you actually have a valid callback. So why bother with it at all? It's counter to the spirit of Python. If someone passes you a bad callback, they will see a traceback when you call it. Then they fix their program. That's how it's supposed to work. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
On 4/8/07, Guido van Rossum
On 4/8/07, Paul Pogonyshev
wrote: What if someone passes a callable that doesn't have the expected signature? Well, I don't know a way to catch such situations now, so removing callable() will not make it worse (even if you don't know about hasattr
Guido van Rossum wrote: trick above.) My point is that it's futile to use callable() -- even if it passes, you have no assurance that you actually have a valid callback. So why bother with it at all? It's counter to the spirit of Python. If someone passes you a bad callback, they will see a traceback when you call it. Then they fix their program. That's how it's supposed to work.
But what if you need to do different things based on argument is callable or not? Take for example "Dependency Injection" recipe: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/413268 It uses callable to differentiate whether it needs to use object as singleton or to instantiate it on each request. I'm sure there might be other uses for callable when it's really useful.
Guido van Rossum wrote:
On 4/8/07, Paul Pogonyshev
wrote: Guido van Rossum wrote:
What if someone passes a callable that doesn't have the expected signature?
Well, I don't know a way to catch such situations now, so removing callable() will not make it worse (even if you don't know about hasattr trick above.)
My point is that it's futile to use callable() -- even if it passes, you have no assurance that you actually have a valid callback. So why bother with it at all? It's counter to the spirit of Python. If someone passes you a bad callback, they will see a traceback when you call it. Then they fix their program. That's how it's supposed to work.
I have no problems with Python being untyped. But I want that error stack traces provide some useful information as possible with reasonable effort and that errors happen as early as possible. In particular, stack trace should mention that error occured when you passed something wrong to set_callback() call and not in some obscure place 200 lines later, because otherwise it will only obfuscate error reason. Yes, assert will not catch all errors, but at least it will some. I consider it perfectly acceptable that you cannot test signature, because (since Python is untyped) you could only test number of arguments and even that would probably involve dumb syntax. So, I understand such assert will not catch all errors. But I don't want to remove it, since I find catching at least some errors (e.g. like passing None) an improvement over catching no errors. Paul
On 4/8/07, Paul Pogonyshev
I have no problems with Python being untyped. But I want that error stack traces provide some useful information as possible with reasonable effort and that errors happen as early as possible. In particular, stack trace should mention that error occured when you passed something wrong to set_callback() call and not in some obscure place 200 lines later, because otherwise it will only obfuscate error reason.
Using the duck typing philosophy; "if it quacks like a duck and walks like a duck, then it probably is a duck." But how can you be so sure it is NOT a duck if you have never seen it walk or heard it quack? What if you are passing in an object that is not callable but later on becomes callable? Is it really an error? I think the plan is that in py3k, you will be able to do type-checking using function annotations (bleach). Like this: def set_callback(self, callback : CallableType): self.callback = callback You probably also need to add some more gunk to make it work. I believe it should be able to replace most uses of callable(). -- mvh Björn
"Guido van Rossum"
On 4/8/07, Paul Pogonyshev
wrote: Guido van Rossum wrote:
What if someone passes a callable that doesn't have the expected signature?
Well, I don't know a way to catch such situations now, so removing callable() will not make it worse (even if you don't know about hasattr trick above.)
My point is that it's futile to use callable() -- even if it passes, you have no assurance that you actually have a valid callback. So why bother with it at all? It's counter to the spirit of Python. If someone passes you a bad callback, they will see a traceback when you call it. Then they fix their program. That's how it's supposed to work.
The point of using callable(x), or it's equivalent now of hasattr(x, '__call__') is to reduce reduce the time/lines of code between when an error occurs and when it is reported. Errors should never pass silently. While we currently cannot verify that some callable takes the proper arguments, number of arguments, etc., we *can* verify that it is callable. I think this is a good thing, as allowing the assignment of a non-callable to a name that is supposed to be callable is the silent passing of an error. With relatively minimal effort in Python 3, one could use a function signature object (PEPs 3107 and 362) to verify that a callable takes the proper number of arguments, expected keyword arguments, etc., which while still not allowing one to verify that the implementation of a callback is correct (technically impossible), it does get us closer to being able to know whether a callable "is" or "may not be" crap when it is assigned. If you still think that these two operations are undesireable (testing the callability, and that a callable takes certain arguments), that's fine (I disagree completely). But unless we mangle callables to not support these operations, people are probably going to do them anyways; especially those who are using annotations, function signature objects, etc., in their various frameworks. But maybe I'm misreading or reading to much into your statement of "If someone passes you a bad callback, they will see a traceback when you call it. Then they fix their program. That's how it's supposed to work." - Josiah
On 4/8/07, Josiah Carlson
"Guido van Rossum"
wrote: On 4/8/07, Paul Pogonyshev
wrote: Guido van Rossum wrote:
What if someone passes a callable that doesn't have the expected signature?
Well, I don't know a way to catch such situations now, so removing callable() will not make it worse (even if you don't know about hasattr trick above.)
My point is that it's futile to use callable() -- even if it passes, you have no assurance that you actually have a valid callback. So why bother with it at all? It's counter to the spirit of Python. If someone passes you a bad callback, they will see a traceback when you call it. Then they fix their program. That's how it's supposed to work.
The point of using callable(x), or it's equivalent now of hasattr(x, '__call__') is to reduce reduce the time/lines of code between when an error occurs and when it is reported. Errors should never pass silently.
I'm not sure if that argument weighs much (taken a bit farther it would require static typing :-). Two arguments made earlier are stronger IMO: - The traceback can be much clearer if it comes from setcallback(x) rather than from the actual call, much later. - The pragmatic ability (which often occurs in "DWIM"-ish behavior in template systems) to do different things depending on whether something is callable or not. (While in theory I agree with Phillip's objection that this should be dealt with in a more systematic way, in practice, at least for Python 2.x, I think it's an "okay" thing to do.) But the same thing can be said for properties like iterable, or hashable, and other primitive operations. FWIW, I haven't given up on doing something with abstract base classes here. I think they (or interfaces or generic functions, for that matter :-) provide a more systematic approach than either callable() or hasattr(x, "__call__"). TBC, --Guido
While we currently cannot verify that some callable takes the proper arguments, number of arguments, etc., we *can* verify that it is callable. I think this is a good thing, as allowing the assignment of a non-callable to a name that is supposed to be callable is the silent passing of an error.
With relatively minimal effort in Python 3, one could use a function signature object (PEPs 3107 and 362) to verify that a callable takes the proper number of arguments, expected keyword arguments, etc., which while still not allowing one to verify that the implementation of a callback is correct (technically impossible), it does get us closer to being able to know whether a callable "is" or "may not be" crap when it is assigned.
If you still think that these two operations are undesireable (testing the callability, and that a callable takes certain arguments), that's fine (I disagree completely). But unless we mangle callables to not support these operations, people are probably going to do them anyways; especially those who are using annotations, function signature objects, etc., in their various frameworks.
But maybe I'm misreading or reading to much into your statement of "If someone passes you a bad callback, they will see a traceback when you call it. Then they fix their program. That's how it's supposed to work."
- Josiah
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
Guido> My point is that it's futile to use callable() -- even if it Guido> passes, you have no assurance that you actually have a valid Guido> callback. So why bother with it at all? It's counter to the Guido> spirit of Python. If someone passes you a bad callback, they will Guido> see a traceback when you call it. Then they fix their Guido> program. That's how it's supposed to work. There's one place where I find the traceback somewhat unhelpful. Consider calling a method of a class with incorrect arguments: >>> class C: ... def __init__(self): ... pass ... >>> C(1) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __init__() takes exactly 1 argument (2 given) While in this example it's clear what method wasn't called correctly, a callback called with the wrong number of arguments yields a fairly useless stack trace. I'm thinking in particular of callbacks called from C code (e.g. Gtk signal handlers). I think it would be helpful to check to see if the function being called had an "im_class" attribute. If so, then resolve the class name and include it in the TypeError message: TypeError: C.__init__() takes exactly 1 argument (2 given) Skip
On 4/8/07, Paul Pogonyshev
assert hasattr(x, '__call__')
I note that callable() was introduced before all callable objects had a __call__ attribute. This is no longer the case, so it's not needed. I just didn't think about that possibility. If that works the same way, callable() is just a sugar and not something unimplementable in other ways. Therefore, my objection is discarded. (But PEP 3100 should probably be update to mention this, otherwise you may get this complaint again ;)
I whole-heartedly agree here, because people who start learning python (like me some time ago) usually learn that it's bad to test for __call__ (can't remember where I read about that though), and that we should always use callable() to be on a safe side. When I first heard that callable() was going to be removed I kinda panicked myself, it's good to know that at least there's still a way to be sure that object is callable or not...
participants (7)
-
Alexey Borzenkov
-
Andrew Koenig
-
BJörn Lindqvist
-
Guido van Rossum
-
Josiah Carlson
-
Paul Pogonyshev
-
skip@pobox.com