feature request: inspect.isgenerator
Is there still time for new feature requests in Python 2.5? I am missing a isgenerator function in the inspect module. Right now I am using def isgenerator(func): return func.func_code.co_flags == 99 but it is rather ugly (horrible indeed). Michele Simionato
"Michele Simionato"
Is there still time for new feature requests in Python 2.5? I am missing a isgenerator function in the inspect module. Right now I am using
def isgenerator(func): return func.func_code.co_flags == 99
but it is rather ugly (horrible indeed).
To me, part of the beauty of Python is that is so easy to write such things so compactly, so that we do not need megaAPIs with hundreds of functions and methods. tjr
Terry Reedy wrote:
def isgenerator(func): return func.func_code.co_flags == 99
but it is rather ugly (horrible indeed).
To me, part of the beauty of Python is that is so easy to write such things so compactly, so that we do not need megaAPIs with hundreds of functions and methods.
so what's so "easy" about the magic constant 99 ? is there some natural and obvious connection between generators and that number that I'm missing, or is that constant perhaps best hidden inside some introspection support module? (I'm still not sure about the use case, though. shouldn't a program treat a generator just like any other callable that returns an iterator?) </F>
Fredrik Lundh wrote:
Terry Reedy wrote:
def isgenerator(func): return func.func_code.co_flags == 99
but it is rather ugly (horrible indeed).
To me, part of the beauty of Python is that is so easy to write such things so compactly, so that we do not need megaAPIs with hundreds of functions and methods.
so what's so "easy" about the magic constant 99 ?
is there some natural and obvious connection between generators and that number that I'm missing, or is that constant perhaps best hidden inside some introspection support module?
It seems to be a combination of CO_NOFREE, CO_GENERATOR, CO_OPTIMIZED and CO_NEWLOCALS. The first four CO_ constants are already in inspect.py, the newer ones (like CO_GENERATOR) aren't. I wonder whether a check shouldn't just return (co_flags & 0x20), which is CO_GENERATOR. Georg
On 5/29/06, Georg Brandl
Fredrik Lundh wrote:
is there some natural and obvious connection between generators and that number that I'm missing, or is that constant perhaps best hidden inside some introspection support module?
It seems to be a combination of CO_NOFREE, CO_GENERATOR, CO_OPTIMIZED and CO_NEWLOCALS.
The first four CO_ constants are already in inspect.py, the newer ones (like CO_GENERATOR) aren't.
All these constants are declared in compiler.consts. Some are also defined in __future__. It would be better to have them all in a single place.
I wonder whether a check shouldn't just return (co_flags & 0x20), which is CO_GENERATOR.
Makes more sense. n
Neal Norwitz
I wonder whether a check shouldn't just return (co_flags & 0x20), which is CO_GENERATOR.
Makes more sense.
Okay, but my point is that the final user should not be expected to know about those implementation details. The one obvious way to me is to have an inspect.isgenerator, analogous to inspect.isfunction, inspect.ismethod, etc. The typical use case is in writing a documentation/debugging tool. Now I was writing a decorator that needed to discriminate in the case it was decorating a regular function versus a generator. Michele Simionato
Michele Simionato wrote:
Neal Norwitz
writes: I wonder whether a check shouldn't just return (co_flags & 0x20), which is CO_GENERATOR.
Makes more sense.
Okay, but my point is that the final user should not be expected to know about those implementation details. The one obvious way to me is to have an inspect.isgenerator, analogous to inspect.isfunction, inspect.ismethod, etc. The typical use case is in writing a documentation/debugging tool. Now I was writing a decorator that needed to discriminate in the case it was decorating a regular function versus a generator.
I'd say, go ahead and write a patch including docs, and I think there's no problem with accepting it (as long as it comes before beta1). Georg
Georg Brandl
I'd say, go ahead and write a patch including docs, and I think there's no problem with accepting it (as long as it comes before beta1).
I was having a look at http://docs.python.org/dev/lib/inspect-types.html and it would seem that adding isgenerator would imply changing inspect.getmembers() and its documentation. Also, should one add a GeneratorType, perhaps as a subclass of FunctionType?
Michele Simionato wrote:
Georg Brandl
writes: I'd say, go ahead and write a patch including docs, and I think there's no problem with accepting it (as long as it comes before beta1).
I was having a look at http://docs.python.org/dev/lib/inspect-types.html and it would seem that adding isgenerator would imply changing inspect.getmembers() and its documentation.
Yep.
Also, should one add a GeneratorType, perhaps as a subclass of FunctionType?
Add GeneratorType where? There is already one in the types module. Georg
Georg Brandl
Also, should one add a GeneratorType, perhaps as a subclass of FunctionType?
Add GeneratorType where? There is already one in the types module.
Yep, this is the crux. types.GeneratorType refers to generator objects, which in an improper sense are "instances" of a "generator function". I.e. def g(): yield 1 # this is a generator go = g() # this is a generator object I want isgenerator(g) == True, but isgenerator(go) == False. So, what should be the class of g ? Maybe we can keep FunctionType and don't bother. Michele Simionato
Michele Simionato wrote:
Georg Brandl
writes: Also, should one add a GeneratorType, perhaps as a subclass of FunctionType?
Add GeneratorType where? There is already one in the types module.
Yep, this is the crux. types.GeneratorType refers to generator objects, which in an improper sense are "instances" of a "generator function". I.e.
def g(): yield 1 # this is a generator
go = g() # this is a generator object
I want isgenerator(g) == True, but isgenerator(go) == False.
Ah, ok. But then I would name the function differently, perhaps isgeneratorfunc().
So, what should be the class of g ? Maybe we can keep FunctionType and don't bother.
I would say, keep FunctionType. There's no real need to know the exact type except for inspecting, and for that, the new function in inspect can be used. Georg
"Michele Simionato"
Yep, this is the crux. types.GeneratorType refers to generator objects, which in an improper sense are "instances" of a "generator function". I.e.
def g(): yield 1 # this is a generator
go = g() # this is a generator object
That terminology does not work. For every other type, an x is an x object and vice versa. I think most of us call functions which return generators a generator function when the distinction needs to be made. A generator is a type in the conceptual class 'iterator'. Terry Jan Reedy
"Michele Simionato"
Neal Norwitz
writes: I wonder whether a check shouldn't just return (co_flags & 0x20), which is CO_GENERATOR.
Makes more sense.
Okay, but my point is that the final user should not be expected to know about those implementation details. The one obvious way to me is to have an inspect.isgenerator, analogous to inspect.isfunction, inspect.ismethod, etc. The typical use case is in writing a documentation/debugging tool. Now I was writing a decorator that needed to discriminate in the case it was decorating a regular function versus a generator.
To me, another obvious way is isinstance(object, gentype) where gentype = type(i for i in []) # for instance which should also be in types module. tjr
Terry Reedy
To me, another obvious way is isinstance(object, gentype) where gentype = type(i for i in []) # for instance which should also be in types module.
No, that check would match generator objects, not generators tout court. On a related notes, inspect.isfunction gives True on a generator, such as def g(): yield None This could confuse people, however I am inclined to leave things as they are. Any thoughts? Michele Simionato
"Michele Simionato"
No, that check would match generator objects, not generators tout court.
tout court?? is not English or commonly used at least in America
On a related notes, inspect.isfunction gives True on a generator, such as
def g(): yield None
Ok, you mean generator function, which produces generators, not generators themselves. So what you want is a new isgenfunc function. That makes more sense, in a sense, since I can see that you would want to wrap genfuncs differently from regular funcs. But then I wonder why you don't use a different decorator since you know when you are writing a generator function. tjr
Terry Reedy
tout court?? is not English or commonly used at least in America
It is French: http://encarta.msn.com/dictionary_561508877/tout_court.html I thought it was common in English too, but clearly I was mistaken.
Ok, you mean generator function, which produces generators, not generators themselves. So what you want is a new isgenfunc function. That makes more sense, in a sense, since I can see that you would want to wrap genfuncs differently from regular funcs. But then I wonder why you don't use a different decorator since you know when you are writing a generator function.
Because in a later refactoring I may want to replace a function with a generator function or viceversa, and I don't want to use a different decorator. The application I had in mind was a Web framework where you can write something like @expose def page(self): return 'Hello World!' or @expose def page(self): yield 'Hello ' yield 'World!' indifferently. I seem to remember CherryPy has something like that. Michele Simionato
At 09:26 AM 6/1/2006 +0000, Michele Simionato wrote:
Terry Reedy
writes: To me, another obvious way is isinstance(object, gentype) where gentype = type(i for i in []) # for instance which should also be in types module.
No, that check would match generator objects, not generators tout court. On a related notes, inspect.isfunction gives True on a generator, such as
def g(): yield None
This could confuse people, however I am inclined to leave things as they are. Any thoughts?
Yes, I think the whole concept of inspecting for this is broken. *Any* function can return a generator-iterator. A generator function is just a function that happens to always return one. In other words, the confusion is in the idea of introspecting for this in the first place, not that generator functions are of FunctionType. The best way to avoid the confusion is to avoid thinking that you can distinguish one type of function from another without explicit guidance from the function's author. I'm -0 on having an isgenfunc(), but -1 on changing isfunction. +1 on making the code flags available. -1 on changing any other inspect stuff to handle generators specially. They are not special and should not be treated specially - they are just functions that happen to always return generator-iterators -- and that is an *implementation detail* of the function. Pushing that information out to introspection or doc is a bad idea in the general case.
Phillip J. Eby
I think the whole concept of inspecting for this is broken. *Any* function can return a generator-iterator. A generator function is just a function that happens to always return one. In other words, the confusion is in the idea of introspecting for this in the first place, not that generator functions are of FunctionType. The best way to avoid the confusion is to avoid thinking that you can distinguish one type of function from another without explicit guidance from the function's author.
Nolo contendere. I am convinced and I am taking back my feature request. Michele Simionato
participants (6)
-
Fredrik Lundh
-
Georg Brandl
-
Michele Simionato
-
Neal Norwitz
-
Phillip J. Eby
-
Terry Reedy