@@ -85,7 +85,7 @@ def header(level, text):
8585
8686
8787def paragraph (text ):
88- return tag (text , "p" )
88+ return tag (inject_class_links ( text ) , "p" )
8989
9090
9191def strong (text ):
@@ -121,15 +121,15 @@ def header_link(text, header_id):
121121 return tag (text , "a" , {"href" :"#" + header_id })
122122
123123
124- def class_link (cls ):
124+ def class_link (cls , text ):
125125 """ Generates an intra-document link for the given class. Example:
126126 >>> from labelbox import Project
127- >>> class_link(Project)
128- >>> <a href="#class_labelbox_schema_project">Project </a>
127+ >>> class_link(Project, "blah" )
128+ >>> <a href="#class_labelbox_schema_project">blah </a>
129129 """
130130 header_id = "class_" + labelbox .utils .snake_case (qual_class_name (cls ).
131131 replace ("." , "_" ))
132- return header_link (cls . __name__ , header_id )
132+ return header_link (text , header_id )
133133
134134
135135def inject_class_links (text ):
@@ -141,15 +141,15 @@ def inject_class_links(text):
141141 matches = list (re .finditer (pattern , text ))
142142 for match in reversed (matches ):
143143 start , end = match .span ()
144- text = text [:start ] + class_link (cls ) + text [end :]
144+ text = text [:start ] + class_link (cls , match . group () ) + text [end :]
145145 return text
146146
147147
148148def is_method (attribute ):
149149 """ Determines if the given attribute is most likely a method. It's
150150 approximative since from Python 3 there are no more unbound methods. """
151151 return inspect .isfunction (attribute ) and "." in attribute .__qualname__ \
152- and inspect .getfullargspec (attribute ).args [0 ] == 'self'
152+ and inspect .getfullargspec (attribute ).args [: 1 ] == [ 'self' ]
153153
154154
155155def preprocess_docstring (docstring ):
@@ -230,26 +230,51 @@ def process(collection, f):
230230
231231 return "" .join (result )
232232
233+ def parse_maybe_block (text ):
234+ """ Adapts to text. Calls `parse_block` if there is a codeblock
235+ indented, otherwise just joins lines into a single line and
236+ reduces whitespace.
237+ """
238+ if text is None :
239+ return ""
240+ if re .findall (r"\n\s+>>>" , text ):
241+ return parse_block ()
242+ return re .sub (r"\s+" , " " , text ).strip ()
243+
233244 parts = (("Args: " , parse_list (args )),
234- ("Kwargs: " , parse_block (kwargs )),
235- ("Returns: " , parse_block (returns )),
245+ ("Kwargs: " , parse_maybe_block (kwargs )),
246+ ("Returns: " , parse_maybe_block (returns )),
236247 ("Raises: " , parse_list (raises )))
237248
238249 return parse_block (docstring ) + unordered_list ([
239250 strong (name ) + item for name , item in parts if bool (item )])
240251
241252
242- def generate_methods (cls ):
243- """ Generates HelpDocs style documentation for all the methods
244- of the given class.
253+ def generate_functions (cls , predicate ):
254+ """ Generates HelpDocs style documentation for the functions
255+ of the given class that satisfy the given predicate. The functions
256+ also must not being with "_", with the exception of Client.__init__.
257+
258+ Args:
259+ cls (type): The class being generated.
260+ predicate (callable): A callable accepting a single argument
261+ (class attribute) and returning a bool indicating if
262+ that attribute should be included in documentation
263+ generation.
264+ Return:
265+ Textual documentation of functions belonging to the given
266+ class that satisfy the given predicate.
245267 """
246268 text = []
247- for attr_name in dir (cls ):
248- attr = getattr (cls , attr_name )
249- if ((is_method (attr ) and not attr_name .startswith ("_" )) or
250- (cls == labelbox .Client and attr_name == "__init__" )):
251- text .append (paragraph (generate_signature (attr )))
252- text .append (preprocess_docstring (attr .__doc__ ))
269+ for name , attr in cls .__dict__ .items ():
270+ if predicate (attr ):
271+ # static and class methods gave the __func__ attribute
272+ # with the original definition that we need.
273+ attr = getattr (attr , "__func__" , attr )
274+ if not name .startswith ("_" ) or (cls == labelbox .Client and
275+ name == "__init__" ):
276+ text .append (paragraph (generate_signature (attr )))
277+ text .append (preprocess_docstring (attr .__doc__ ))
253278
254279 return "" .join (text )
255280
@@ -319,29 +344,35 @@ def generate_class(cls, schema_class):
319344 text .append (generate_fields (cls ))
320345 text .append (header (3 , "Relationships" ))
321346 text .append (generate_relationships (cls ))
322- methods = generate_methods (cls ).strip ()
323- if len (methods ):
324- text .append (header (3 , "Methods" ))
325- text .append (methods )
347+
348+ for name , predicate in (
349+ ("Static Methods" , lambda attr : type (attr ) == staticmethod ),
350+ ("Class Methods" , lambda attr : type (attr ) == classmethod ),
351+ ("Object Methods" , is_method )):
352+ functions = generate_functions (cls , predicate ).strip ()
353+ if len (functions ):
354+ text .append (header (3 , name ))
355+ text .append (functions )
356+
326357 return "\n " .join (text )
327358
328359
329- def generate_all (general_classes , schema_classes , error_classes ):
360+ def generate_all ():
330361 """ Generates the full HelpDocs API documentation article body. """
331362 text = []
332363 text .append (header (3 , "General Classes" ))
333- text .append (unordered_list ([qual_class_name (cls ) for cls in general_classes ]))
364+ text .append (unordered_list ([qual_class_name (cls ) for cls in GENERAL_CLASSES ]))
334365 text .append (header (3 , "Data Classes" ))
335- text .append (unordered_list ([qual_class_name (cls ) for cls in schema_classes ]))
366+ text .append (unordered_list ([qual_class_name (cls ) for cls in SCHEMA_CLASSES ]))
336367 text .append (header (3 , "Error Classes" ))
337- text .append (unordered_list ([qual_class_name (cls ) for cls in error_classes ]))
368+ text .append (unordered_list ([qual_class_name (cls ) for cls in ERROR_CLASSES ]))
338369
339370 text .append (header (1 , "General classes" ))
340- text .extend (generate_class (cls , False ) for cls in general_classes )
371+ text .extend (generate_class (cls , False ) for cls in GENERAL_CLASSES )
341372 text .append (header (1 , "Data Classes" ))
342- text .extend (generate_class (cls , True ) for cls in schema_classes )
373+ text .extend (generate_class (cls , True ) for cls in SCHEMA_CLASSES )
343374 text .append (header (1 , "Error Classes" ))
344- text .extend (generate_class (cls , False ) for cls in error_classes )
375+ text .extend (generate_class (cls , False ) for cls in ERROR_CLASSES )
345376 return "\n " .join (text )
346377
347378
@@ -353,7 +384,7 @@ def main():
353384
354385 args = argp .parse_args ()
355386
356- body = generate_all (GENERAL_CLASSES , SCHEMA_CLASSES , ERROR_CLASSES )
387+ body = generate_all ()
357388
358389 if args .helpdocs_api_key is not None :
359390 url = "https://api.helpdocs.io/v1/article/zg9hp7yx3u?key=" + \
0 commit comments