introspecting functions
C++-siggers, I have looked into how pydoc works. It doesn't look too bad. Pydoc does a large part of its work using the inspect module to look at code objects. 'Real' Python functions have a func_code property. Example:
urlparse.urlsplit.func_code.co_varnames ('url', 'scheme', 'allow_fragments', 'i', 'cached', 'c', 'netloc', 'fragment', 'tuple', 'key', 'query')
And the code for urlsplit is: def urlsplit(url, scheme='', allow_fragments=1): key = url, scheme, allow_fragments cached = _parse_cache.get(key, None) if cached: return cached if len(_parse_cache) >= MAX_CACHE_SIZE: # avoid runaway growth clear_cache() netloc = query = fragment = '' i = url.find(':') if i > 0: if url[:i] == 'http': # optimize the common case scheme = url[:i].lower() url = url[i+1:] if url[:2] == '//': i = url.find('/', 2) ...... and on it goes. Anyway, the first expression gives me all the variable names in the function. The first three .co_varnames appear to be the function argument names (.co_argcount). You can also retrieve the filename the function was compiled from and the bytecode for the function. I don't know what the consequences of using a fake func_code are and I would have played with it more but I don't know how to best add a func_code to my boost::python classes, but it's an interesting idea. You could probably figure out how to implement the equivalent of def foo(bar=1, baz=2) for the C++ for example. Whoah! Pydoc actually parses the bytecodes for classes in order to document what the default variable values are (not what the variable names are though). It mightn't be outrageous to compile lots of little def wrappedfunction(a, b=None): pass in order to make some bytecode though, or even replace : pass with : call_boost_function(). - Daniel Holth
Daniel Holth <dholth@fastmail.fm> writes:
C++-siggers,
I have looked into how pydoc works. It doesn't look too bad. Pydoc does a large part of its work using the inspect module to look at code objects.
'Real' Python functions have a func_code property. Example:
<snip>
Anyway, the first expression gives me all the variable names in the function. The first three .co_varnames appear to be the function argument names (.co_argcount). You can also retrieve the filename the function was compiled from and the bytecode for the function.
I don't know what the consequences of using a fake func_code are and I would have played with it more but I don't know how to best add a func_code to my boost::python classes,
Just try with your own callable object in a new-style class: class bplfunc(object): def __init__(self): self.func_code = ... def __call__(...) ... class myclass(object): some_method = bplfunc() Throw it at PyDoc and see what's needed to get it to respond in a reasonable way. If you can get that sorted out I'm sure we can implement something equivalent in Boost.Python. A note: I think I'd want to represent the C++ argument types in func_code.co_varnames. When no arg(...) expressions are used in wrapping to supply variable names, it'd be just the types. Otherwise, it'd be the types + names. Seeing the types would at least give a very strong hint about usage.
but it's an interesting idea. You could probably figure out how to implement the equivalent of def foo(bar=1, baz=2) for the C++ for example.
Whoah! Pydoc actually parses the bytecodes for classes in order to document what the default variable values are
You're kidding! Why not just use func_defaults??
(not what the variable names are though). It mightn't be outrageous to compile lots of little def wrappedfunction(a, b=None): pass in order to make some bytecode though, or even replace : pass with : call_boost_function().
I'd be shocked if there was actually any bytecode needed. -- Dave Abrahams Boost Consulting www.boost-consulting.com
participants (2)
-
Daniel Holth -
David Abrahams