1""" 2Functions for analyzing/parsing docstrings 3""" 4import logging 5import re 6 7log = logging.getLogger(__name__) 8 9 10def strip_rst(docs): 11 """ 12 Strip/replace reStructuredText directives in docstrings 13 """ 14 for func, docstring in docs.items(): 15 log.debug("Stripping docstring for %s", func) 16 if not docstring: 17 continue 18 docstring_new = docstring 19 for regex, repl in ( 20 (r" *.. code-block:: \S+\n{1,2}", ""), 21 (".. note::", "Note:"), 22 (".. warning::", "Warning:"), 23 (".. versionadded::", "New in version"), 24 (".. versionchanged::", "Changed in version"), 25 ): 26 try: 27 docstring_new = re.sub(regex, repl, docstring_new) 28 except Exception: # pylint: disable=broad-except 29 log.debug( 30 "Exception encountered while matching regex %r to " 31 "docstring for function %s", 32 regex, 33 func, 34 exc_info=True, 35 ) 36 if docstring != docstring_new: 37 docs[func] = docstring_new 38 return docs 39 40 41def parse_docstring(docstring): 42 """ 43 Parse a docstring into its parts. 44 45 Currently only parses dependencies, can be extended to parse whatever is 46 needed. 47 48 Parses into a dictionary: 49 { 50 'full': full docstring, 51 'deps': list of dependencies (empty list if none) 52 } 53 """ 54 # First try with regex search for :depends: 55 ret = {"full": docstring} 56 regex = r"([ \t]*):depends:[ \t]+- (\w+)[^\n]*\n(\1[ \t]+- (\w+)[^\n]*\n)*" 57 match = re.search(regex, docstring, re.M) 58 if match: 59 deps = [] 60 regex = r"- (\w+)" 61 for line in match.group(0).strip().splitlines(): 62 deps.append(re.search(regex, line).group(1)) 63 ret["deps"] = deps 64 return ret 65 # Try searching for a one-liner instead 66 else: 67 txt = "Required python modules: " 68 data = docstring.splitlines() 69 dep_list = list(x for x in data if x.strip().startswith(txt)) 70 if not dep_list: 71 ret["deps"] = [] 72 return ret 73 deps = dep_list[0].replace(txt, "").strip().split(", ") 74 ret["deps"] = deps 75 return ret 76