diff --git a/TODO b/TODO index 5061301e..a91e7b53 100644 --- a/TODO +++ b/TODO @@ -29,86 +29,16 @@ op = Vips.Operation.new("black") help(op) -- want to get the description for an operation + but does not include our docstring, strange - op = Vips.Operation.new("black") + this has stuff from comments in Vips.py - can't look at op.description, since that's not set until build(), we need to - get the class and walk that - + help(Vips.Image) - try: + we should trim and rewrite comments - GType a = Vips.type_find("VipsObject", "black") - - in C, use this to get the class from a gtype: - - g_type_class_ref(gtype) - - from gi.repository import GObject - dir(GObject.GType) - ['__class__', '__delattr__', '__doc__', '__eq__', '__format__', - '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', - '__le__', '__lt__', '__ne__', '__new__', '__reduce__', - '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', - '__subclasshook__', 'children', 'depth', 'from_name', 'fundamental', - 'has_value_table', 'interfaces', 'is_a', 'is_abstract', 'is_classed', - 'is_deep_derivable', 'is_derivable', 'is_instantiatable', - 'is_interface', 'is_value_abstract', 'is_value_type', 'name', - 'parent', 'pytype'] - - a = Vips.type_find("VipsObject", "black") - dir(a) - ['__class__', '__delattr__', '__doc__', '__eq__', '__format__', - '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', - '__le__', '__lt__', '__ne__', '__new__', '__reduce__', - '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', - '__subclasshook__', 'children', 'depth', 'from_name', 'fundamental', - 'has_value_table', 'interfaces', 'is_a', 'is_abstract', 'is_classed', - 'is_deep_derivable', 'is_derivable', 'is_instantiatable', - 'is_interface', 'is_value_abstract', 'is_value_type', 'name', - 'parent', 'pytype'] - - >>> op = Vips.Operation.new("black") - >>> dir(op) - ['__class__', '__copy__', '__deepcopy__', '__delattr__', '__dict__', - '__doc__', '__eq__', '__format__', '__gdoc__', '__ge__', - '__getattribute__', '__gpointer__', '__grefcount__', '__gsignals__', - '__gt__', '__gtype__', '__hash__', '__info__', '__init__', '__le__', - '__lt__', '__module__', '__ne__', '__new__', '__reduce__', - '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', - '__subclasshook__', '_force_floating', '_ref', '_ref_sink', '_unref', - '_unsupported_data_method', '_unsupported_method', 'argument_isset', - 'argument_needsstring', 'argument_table', 'bind_property', - 'bind_property_full', 'build', 'chain', 'close', 'compat_control', - 'connect', 'connect_after', 'connect_object', 'connect_object_after', - 'constructed', 'description', 'disconnect', 'disconnect_by_func', - 'do_build', 'do_close', 'do_get_flags', 'do_invalidate', - 'do_output_to_arg', 'do_postbuild', 'do_postclose', 'do_preclose', - 'do_rewind', 'emit', 'emit_stop_by_name', 'force_floating', - 'found_hash', 'freeze_notify', 'g_type_instance', - 'get_argument_flags', 'get_argument_priority', - 'get_argument_to_string', 'get_data', 'get_flags', 'get_properties', - 'get_property', 'get_qdata', 'handler_block', 'handler_block_by_func', - 'handler_disconnect', 'handler_is_connected', 'handler_unblock', - 'handler_unblock_by_func', 'hash', 'interface_find_property', - 'interface_install_property', 'interface_list_properties', - 'invalidate', 'is_floating', 'local_cb', 'local_memory', 'new', - 'new_from_string', 'nickname', 'notify', 'notify_by_pspec', - 'parent_instance', 'parent_object', 'pixels', 'postclose', 'preclose', - 'print_all', 'print_dump', 'print_name', 'print_summary', - 'print_summary_class', 'props', 'qdata', 'ref', 'ref_count', - 'ref_sink', 'replace_data', 'replace_qdata', 'rewind', 'run_dispose', - 'sanity', 'sanity_all', 'set_argument_from_string', 'set_data', - 'set_from_string', 'set_properties', 'set_property', 'set_required', - 'set_static', 'static_object', 'steal_data', 'steal_qdata', - 'stop_emission', 'stop_emission_by_name', 'thaw_notify', 'unref', - 'unref_outputs', 'watch_closure', 'weak_ref'] - - - - - +- python fitsload has both "access" and "sequential" as kwargs, is this right? + why do we need both? - test other arg types diff --git a/libvips/include/vips/object.h b/libvips/include/vips/object.h index 5ebb7920..ea8655c5 100644 --- a/libvips/include/vips/object.h +++ b/libvips/include/vips/object.h @@ -639,6 +639,8 @@ void vips_object_rewind( VipsObject *object ); void vips_object_unref_outputs( VipsObject *object ); +const char *vips_object_get_description( VipsObject *object ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index f47280b2..4f6e73e4 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -2974,3 +2974,26 @@ vips_object_unref_outputs( VipsObject *object ) (void) vips_argument_map( object, vips_object_unref_outputs_sub, NULL, NULL ); } + +/** + * vips_object_get_description: + * @object: object to fetch description from + * + * Fetch the object description. Useful for language bindings. + * + * @object.description is only avaliable after _build(), which can be too + * late. This function fetches from the instance, if possible, but falls back + * to the class description if we are too early. + * + * Returns: the object description + */ +const char * +vips_object_get_description( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + + if( object->description ) + return( object->description ) ; + else + return( class->description ) ; +} diff --git a/python/Vips.py b/python/Vips.py index a4c2aebe..d4001905 100644 --- a/python/Vips.py +++ b/python/Vips.py @@ -157,6 +157,13 @@ class Argument: return value + def description(self): + result = self.name + result += " " * (10 - len(self.name)) + " - " + self.prop.blurb + result += ", " + self.prop.value_type.name + + return result + Vips.Argument = Argument # search a list recursively for a Vips.Image object @@ -391,15 +398,61 @@ def generate_docstring(name): not x.isset] required_output = [x for x in args if x.flags & enm.OUTPUT and - not x.flags & enm.REQUIRED] + x.flags & enm.REQUIRED] optional_output = [x for x in args if x.flags & enm.OUTPUT and not x.flags & enm.REQUIRED] - result = "usage:\n" + # find the first required input image, if any ... we will be a member + # function of this instance + member_x = None + for i in range(0, len(required_input)): + x = required_input[i] + if GObject.type_is_a(vips_type_image, x.prop.value_type): + member_x = x + break + result = op.get_description() + "\n" + result += "usage:\n" + + result += " " + ", ".join([x.name for x in required_output]) + " = " + if member_x: + result += member_x.name + "." + name + "(" + else: + result += "Vips.Image." + name + "(" + result += ", ".join([x.name for x in required_input + if x != member_x]) + if len(optional_input) > 0: + result += ", " + result += ", ".join([x.name + " = " + x.prop.value_type.name + for x in optional_input]) + result += ")\n" + + result += "\n" + + result += "where:\n" + for x in required_output: + result += " " + x.description() + "\n" + + result += "\n" + + result += "required parameters:\n" for x in required_input: - result += x.name + "\n" + result += " " + x.description() + "\n" + + if len(optional_input) > 0: + result += "\n" + + result += "optional parameters:\n" + for x in optional_input: + result += " " + x.description() + "\n" + + if len(optional_output) > 0: + result += "\n" + + result += "extra output options:\n" + for x in optional_output: + result += " " + x.description() + "\n" return result @@ -412,6 +465,13 @@ def smap(func, x): else: return func(x) +# decorator to set docstring +def add_doc(value): + def _doc(func): + func.__doc__ = value + return func + return _doc + class Image(Vips.Image): """This is a test docstring in Vips.py ... does this get attached to the class we are overriding? @@ -455,9 +515,9 @@ class Image(Vips.Image): if name in dir(self.props): return getattr(self.props, name) + @add_doc(generate_docstring(name)) def call_function(*args, **kwargs): return _call_instance(self, name, args, kwargs) - call_function.__doc__ = generate_docstring(name) return call_function @@ -788,12 +848,6 @@ class_methods = [ "fitsload", "openexrload"] -def add_doc(value): - def _doc(func): - func.__doc__ = value - return func - return _doc - def generate_class_method(name): @classmethod @add_doc(generate_docstring(name))