Hi all, I'm working on better documentation for the python interface to gnuradio. I'd like to get the inline help (python help function) returning useful responses and would like to get useful html documentation generated.
The current representation of the blocks in python makes this difficult. For example gnuradio.gr.head maps to gr_make_head which is a function that produces an instance of gr_head_sptr. It would be nicer for the sake of documentation generation if gnuradio.gr.head was a class that looked like gr_head_sptr. This patch wraps the swig generated gr_XXX_sptr classes in much the same way as is currently done for the top_block. I think this additional indirection is justified for the added simplicity it gives to the python interface. I ran "make check" and it didn't appear to break anything, which surprises me. It may be that I'm not "make check"ing correctly. Cheers, Ben
From ad47250a3d9874fe00c4693dc68f2d5fe146d1dd Mon Sep 17 00:00:00 2001 From: Ben Reynwar <b...@reynwar.net> Date: Mon, 20 Sep 2010 13:38:04 -0700 Subject: [PATCH] Added python wrapper class to wrap gr_XXX_sptr and gr_make_XXX together. gnuradio.gr.XXX was previously an alias for gr_make_XXX and generated instances of gnuradio.gr.gr_XXX_sptr. Now gnuradio.gr.XXX is a class, whose instances wrap the gr_XXX_sptr instances. This results in more useful information from the python command help(gnuradio.gr.XXX). It will also simplify documentation generation. --- gnuradio-core/src/lib/swig/Makefile.am | 2 + gnuradio-core/src/lib/swig/block_wrapper.py | 67 ++++++++++++++++++++ gnuradio-core/src/lib/swig/gnuradio_swig_python.py | 60 ++++++++++++++++-- gnuradio-core/src/lib/swig/gr_swig_block_magic.i | 8 +-- 4 files changed, 124 insertions(+), 13 deletions(-) create mode 100644 gnuradio-core/src/lib/swig/block_wrapper.py diff --git a/gnuradio-core/src/lib/swig/Makefile.am b/gnuradio-core/src/lib/swig/Makefile.am index 242f27d..93828ee 100644 --- a/gnuradio-core/src/lib/swig/Makefile.am +++ b/gnuradio-core/src/lib/swig/Makefile.am @@ -37,6 +37,8 @@ swiginclude_HEADERS = \ # of the split Python libraries. ourpythondir = $(grpythondir)/gr ourpython_PYTHON = gnuradio_swig_python.py +ourblockwrapperdir = $(grpythondir)/gr +ourblockwrapper_PYTHON = block_wrapper.py # ---------------------------------------------------------------- # FIXME As of swig 1.3.31, this still seems to be required... diff --git a/gnuradio-core/src/lib/swig/block_wrapper.py b/gnuradio-core/src/lib/swig/block_wrapper.py new file mode 100644 index 0000000..1173b04 --- /dev/null +++ b/gnuradio-core/src/lib/swig/block_wrapper.py @@ -0,0 +1,67 @@ +import types +from functools import wraps + +def _method_wrapper(m): + @wraps(m) + def method(self, *args, **kwargs): + return m(self.wrapped_inst, *args, **kwargs) + return method + +def block_wrapper(name, module, sptr_class, make_func): + """ + Creates a wrapper for a class. + + An instance of the original class is contained within each + instance of the new class. + + Changing an attribute in the wrapper does not change the + equivalent attribute in the wrapped instance. + + This method of wrapping is necessary for the block classes since + when the make_func is called it creates a new instance of the + sptr_class, calling the __new__ method. Therefore we cannot + simply override the __new__ method of the sptr_class and insert + make_func. The attributes are copied into the wrapper class + rather than simply using __getattr__ to aid documentation + generation. + """ + + # Create the wrapping class. + # We do it with type() so that we can set the docstring to that of + # sptr_class. Class docstrings cannot be edited once set. + + def __init__(self, *args, **kwargs): + self.wrapped_inst = make_func(*args, **kwargs) + + def __repr__(self): + return "<gr_block %s (%d)>" % (self.name(), self.unique_id()) + + wrapping_dict = {"__doc__": sptr_class.__doc__, + "__init__": __init__, + "__module__": module, + "__repr__": __repr__, + } + wrapper = type(name, (object,), wrapping_dict) + + for attr in dir(sptr_class): + # Don't wrap attributes that start with an underscore. + if attr[0] == '_': + continue + o = getattr(sptr_class, attr) + # Check to see if the attribute is a method that expects an + # instance of sptr_class as it's first argument. + # i.e. an unbound method associated with sptr_class + if isinstance(o, types.MethodType): + #Python2.x test (checks it is unbound associated with this class) + if ((hasattr(o, "im_class") and getattr(o, "im_class") == sptr_class + and getattr(o, "im_self") is None) or + #Python3.x test (checks it is not bound) + (hasattr(o, "__self__") and getattr(o, "__self__") is None)): + + method = _method_wrapper(o) + setattr(wrapper, attr, method) + setattr(method, "__doc__", o.__doc__) + continue + # It's not a method associated with sptr_class so we can directly add it. + setattr(wrapper, attr, getattr(sptr_class, attr)) + return wrapper diff --git a/gnuradio-core/src/lib/swig/gnuradio_swig_python.py b/gnuradio-core/src/lib/swig/gnuradio_swig_python.py index 5324b23..81f2973 100644 --- a/gnuradio-core/src/lib/swig/gnuradio_swig_python.py +++ b/gnuradio-core/src/lib/swig/gnuradio_swig_python.py @@ -20,9 +20,57 @@ # This file implements the old gnuradio_swig_python namespace -from gnuradio_swig_py_runtime import * -from gnuradio_swig_py_general import * -from gnuradio_swig_py_gengen import * -from gnuradio_swig_py_filter import * -from gnuradio_swig_py_io import * -from gnuradio_swig_py_hier import * +# The importing of objects from other modules has been modified so +# that gr_XXX_sptr and gr_make_XXX are wrapped together into a single +# class. + +import sys + +from block_wrapper import block_wrapper + +thismodule = sys.modules[__name__] + +import_modules = ["gnuradio_swig_py_runtime", + "gnuradio_swig_py_general", + "gnuradio_swig_py_gengen", + "gnuradio_swig_py_filter", + "gnuradio_swig_py_io", + "gnuradio_swig_py_hier",] + +def base_of_sptr(attr_name): + if attr_name[:3] == "gr_" and attr_name[-5:] == "_sptr": + return attr_name[3:-5] + else: + return None + +def base_of_make(attr_name): + if attr_name[:8] == "gr_make_": + return attr_name[8:] + else: + return None + +for im in import_modules: + cm = __import__(im, globals(), locals(), [], -1) + ans = dir(cm) + # Find all the sptr classes in this module + sptr_classes = dict([(base_of_sptr(an), an) + for an in ans if base_of_sptr(an) is not None]) + # and all the make functions + make_funcs = dict([(base_of_make(an), an) + for an in ans if base_of_make(an) is not None]) + # Find sptr class and make function pairs + common_bases = set(sptr_classes) & set(make_funcs) + paired_ans = set([sptr_classes[base] for base in common_bases]) + paired_ans |= set([make_funcs[base] for base in common_bases]) + # Lonely attibute names (not in pair) + lonely_ans = set(ans) - paired_ans + lonely_ans = [an for an in lonely_ans if an[0] != "_"] + # Wrap paired attribute names + for base in common_bases: + # Combine them into a wrapper class and add it to this module. + setattr(thismodule, base, + block_wrapper(base, "gnuradio.gr", getattr(cm,sptr_classes[base]), + getattr(cm, make_funcs[base]))) + # The remaining attributes that weren't paired are added to this module. + for an in lonely_ans: + setattr(thismodule, an, getattr(cm, an)) diff --git a/gnuradio-core/src/lib/swig/gr_swig_block_magic.i b/gnuradio-core/src/lib/swig/gr_swig_block_magic.i index 78e8380..215cd67 100644 --- a/gnuradio-core/src/lib/swig/gr_swig_block_magic.i +++ b/gnuradio-core/src/lib/swig/gr_swig_block_magic.i @@ -28,12 +28,6 @@ _GR_SWIG_BLOCK_MAGIC_HELPER(PKG, PKG ## _ ## BASE_NAME, BASE_NAME) class NAME; typedef boost::shared_ptr<NAME> NAME ## _sptr; %template(NAME ## _sptr) boost::shared_ptr<NAME>; -%rename(BASE_NAME) PKG ## _make_ ## BASE_NAME; - -%pythoncode %{ -NAME ## _sptr.block = lambda self: NAME ## _block (self) -NAME ## _sptr.__repr__ = lambda self: "<gr_block %s (%d)>" % (self.name(), self.unique_id ()) -%} - %ignore NAME; %enddef + -- 1.7.0.4
_______________________________________________ Patch-gnuradio mailing list Patch-gnuradio@gnu.org http://lists.gnu.org/mailman/listinfo/patch-gnuradio