generate_function_list.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. # This file generates the modules.md content
  2. # This file should be run in an environment where there is one, and only one, Kamailio install built from source
  3. import os, json, sys
  4. class ModuleDocGenerator(object):
  5. PATH_GENERATED_JSON = "./generated_functions.json"
  6. PATH_GENERATED_MARKDOWN = "../docs/modules.md"
  7. # A source file in the tm module
  8. SEARCH_DOCS_TM_FILE = "tm_load.c"
  9. # Contains the output until it should be written to disk
  10. markdown_string = ""
  11. path_docs = ""
  12. def execute(self):
  13. path_kamctl = self.find("kamctl", "/")
  14. # Obtain the KEMI function list through KAMCTL RPC
  15. failed = os.system(path_kamctl + " rpc app_python.api_list > " + self.PATH_GENERATED_JSON)
  16. if failed != 0:
  17. print "ERR: Failed to execute KAMCTL"
  18. exit()
  19. functions_raw = json.load(open(self.PATH_GENERATED_JSON))
  20. # Validate that we got some methods back. 155 is an arbitrary large number.
  21. if functions_raw["result"]["msize"] < 155:
  22. print "ERR: Invalid JSON RPC response"
  23. exit()
  24. functions_parsed = self.parse_function_list(functions_raw["result"]["methods"])
  25. self.output_markdown(functions_parsed)
  26. print "Markdown doc created successfully at " + self.PATH_GENERATED_MARKDOWN
  27. def find(self, name, path):
  28. for root, dirs, files in os.walk(path):
  29. if name in files:
  30. return os.path.join(root, name)
  31. def parse_function_list(self, functions):
  32. data = {}
  33. for elem in functions:
  34. props = elem["func"]
  35. module = props["module"]
  36. # The core and hdr submodule are documented in a different section
  37. # TODO: What about the pvx module?
  38. if module == "" or module == "hdr":
  39. continue
  40. if module not in data:
  41. data[module] = []
  42. data[module].append({"name": props["name"], "return": props["ret"], "params": props["params"]})
  43. return data
  44. def output_markdown(self, data):
  45. self.markdown_header()
  46. for key in sorted(data):
  47. methods = data[key]
  48. methods = sorted(methods, key = lambda k: k['name'])
  49. self.markdown_section_heading(key)
  50. self.markdown_section_content(key, methods)
  51. self.markdown_write()
  52. return True
  53. def markdown_header(self):
  54. self.markdown_string += "<!-- This file is auto-generated. Any manual modifications will be deleted -->\n"'' \
  55. # TODO: Move the below markdown to a file in ../docs/modules/something.md
  56. self.markdown_string += "# KEMI Module Functions #\n"
  57. self.markdown_string += "The following sections lists all exported KEMI functions. More information regarding the function can be found by clicking the KEMI declaration which will take you the original modules documentation. \n"
  58. return True
  59. def markdown_section_heading(self, heading):
  60. self.markdown_string += "## " + heading + " ##\n"
  61. # TODO: Lookup if a file such as ../docs/modules/module.header.md exists and if so inject the markdown here
  62. return True
  63. def markdown_section_content(self, module, methods):
  64. module_prefix = module + "."
  65. for value in methods:
  66. self.markdown_string += "#### KSR." + module_prefix + value["name"] + "() ####\n"
  67. # Sanitize the return values
  68. if value["return"] == "none":
  69. return_value = "void"
  70. else:
  71. return_value = value["return"]
  72. # Sanitize the parameter values
  73. if value["params"] == "none":
  74. params_value = ""
  75. else:
  76. params_value = value["params"]
  77. docs_params = self.generate_param_definitions(params_value, module, value["name"])
  78. # Generate the output string for the markdown page
  79. self.markdown_string += "KEMI declaration: " "<a target='_blank' href='/docs/modules/devel/modules/" + module + ".html#" \
  80. + module + ".f." + value["name"] + "'> `" + return_value + " " + value["name"] \
  81. + "(" + params_value + ")` </a> \n\n"
  82. # If we found a definition in the Docs let's present it as well
  83. if params_value != docs_params:
  84. self.markdown_string += "Docs declaration: `" + return_value + " " + value[
  85. "name"] + "(" + docs_params + ")`\n"
  86. # TODO: Lookup if a file such as ../docs/modules/module.function.md exists and if so inject the markdown here
  87. return True
  88. def markdown_write(self):
  89. f = open(self.PATH_GENERATED_MARKDOWN, "w")
  90. f.write(self.markdown_string)
  91. f.close()
  92. return True
  93. def generate_param_definitions(self, params_kemi, module, function):
  94. if params_kemi == "":
  95. return ""
  96. # print params_kemi, module, function
  97. params_original = self.find_function_parameters(module, function)
  98. if params_original is None:
  99. return params_kemi
  100. # Remove redundant information
  101. if params_original == "str" or params_original == "str" or params_original == "int" or params_original == "integer" or params_original == "param":
  102. return ""
  103. return params_original
  104. def find_function_parameters(self, module, function):
  105. path_modules = self.locate_docs_path()
  106. path_readme = path_modules + module + "/README"
  107. if not os.path.isfile(path_readme):
  108. print "ERR: Could not find README for module: " + module
  109. return None
  110. with open(path_readme) as f:
  111. content = f.readlines()
  112. match = False
  113. for line in content:
  114. if line.find(function + "(") >= 0:
  115. match = True
  116. break
  117. elif line.find(function + " (") >= 0:
  118. match = True
  119. break
  120. if not match:
  121. print "ERR: Could not find definition for function: " + module + "." + function
  122. return None
  123. # Get all content between parenthesises
  124. params = line[line.find("(") + 1:line.find(")")]
  125. if params == "":
  126. return None
  127. return params
  128. def locate_docs_path(self):
  129. if self.path_docs == "":
  130. # We look for a file in the Kamailio source and work our way backwards from there
  131. path = self.find(self.SEARCH_DOCS_TM_FILE, "/")
  132. if path:
  133. strip_path = "tm/" + self.SEARCH_DOCS_TM_FILE
  134. path = path[:-len(strip_path)]
  135. self.path_docs = path
  136. return path
  137. else:
  138. print "ERR: Could not find the path to the Kamailio source"
  139. exit()
  140. else:
  141. return self.path_docs
  142. if __name__ == "__main__":
  143. reload(sys)
  144. sys.setdefaultencoding('utf8')
  145. fgen = ModuleDocGenerator()
  146. fgen.execute()