doc.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. """
  2. Copyright (C) 2021 by
  3. The Salk Institute for Biological Studies
  4. Use of this source code is governed by an MIT-style
  5. license that can be found in the LICENSE file or at
  6. https://opensource.org/licenses/MIT.
  7. """
  8. import sys
  9. import os
  10. import yaml
  11. from constants import *
  12. from gen import indent_and_fix_rst_chars, yaml_type_to_py_type, get_default_or_unset_value_py
  13. def cat_to_title(cat):
  14. if cat == CATEGORY_CONSTANTS:
  15. return 'Enums and Constants'
  16. else:
  17. return cat.replace('_', ' ').capitalize()
  18. def write_cat_label(f, cat):
  19. f.write('.. _api-' + cat + ':\n\n')
  20. def gen_example_links(base_links):
  21. split_links = base_links.strip().split()
  22. n = len(split_links)
  23. if n == 0:
  24. return ''
  25. res = 'Example' + ('' if n == 1 else 's') + ': '
  26. for l in split_links:
  27. name = os.path.basename(os.path.dirname(l)) + '/' + os.path.basename(l)
  28. res += '`' + name + ' <' + EXAMPLES_BASE_URL + l + '>`_ '
  29. return res
  30. def write_h4(f, text, name, class_name):
  31. f.write('.. _' + class_name + '__' + name + ':\n\n')
  32. f.write(text + '\n')
  33. f.write('-' * len(text) + '\n\n')
  34. def get_method_declaration(method):
  35. res = method[KEY_NAME] + ' ('
  36. if KEY_PARAMS in method:
  37. num_params = len(method[KEY_PARAMS])
  38. for i in range(num_params):
  39. param = method[KEY_PARAMS][i]
  40. t = yaml_type_to_py_type(param[KEY_TYPE])
  41. res += param[KEY_NAME] + ': ' + t
  42. if KEY_DEFAULT in param:
  43. res += '=' + get_default_or_unset_value_py(param)
  44. if i != num_params - 1:
  45. res += ', '
  46. res += ')'
  47. if KEY_RETURN_TYPE in method:
  48. res += ' -> ' + yaml_type_to_py_type(method[KEY_RETURN_TYPE])
  49. return res
  50. def generate_class_documentation(f, class_name, class_def):
  51. f.write(class_name + '\n' + '='*len(class_name) + '\n\n')
  52. if KEY_DOC in class_def:
  53. f.write(class_def[KEY_DOC].strip() + '\n\n')
  54. if KEY_EXAMPLES in class_def:
  55. f.write(gen_example_links(class_def[KEY_EXAMPLES]) + '\n\n')
  56. if KEY_ITEMS in class_def and class_def[KEY_ITEMS]:
  57. f.write('Attributes:\n' + '*'*len('Attributes:') + '\n')
  58. num_items = len(class_def[KEY_ITEMS])
  59. for item in class_def[KEY_ITEMS]:
  60. t = yaml_type_to_py_type(item[KEY_TYPE])
  61. header = item[KEY_NAME] + ': ' + t
  62. write_h4(f, header, item[KEY_NAME], class_name)
  63. if KEY_DOC in item and item[KEY_DOC]:
  64. f.write(' | ' + indent_and_fix_rst_chars(item[KEY_DOC].strip(), ' | ') + '\n')
  65. if KEY_DEFAULT in item:
  66. f.write(' | - default argument value in constructor: ' + get_default_or_unset_value_py(item))
  67. f.write('\n')
  68. if KEY_EXAMPLES in item:
  69. f.write('\n | ' + gen_example_links(item[KEY_EXAMPLES]) + '\n\n')
  70. f.write('\n')
  71. if KEY_METHODS in class_def and class_def[KEY_METHODS]:
  72. f.write('\nMethods:\n' + '*'*len('nMethods:') + '\n')
  73. for method in class_def[KEY_METHODS]:
  74. method_name = method[KEY_NAME]
  75. header = get_method_declaration(method)
  76. write_h4(f, header, method_name, class_name)
  77. if KEY_DOC in method:
  78. f.write('\n | ' + indent_and_fix_rst_chars(method[KEY_DOC].strip(), ' | ') + '\n\n')
  79. if KEY_PARAMS in method:
  80. num_params = len(method[KEY_PARAMS])
  81. for param in method[KEY_PARAMS]:
  82. t = yaml_type_to_py_type(param[KEY_TYPE])
  83. f.write('* | ' + param[KEY_NAME] + ': ' + t)
  84. if KEY_DEFAULT in param:
  85. f.write(' = ' + get_default_or_unset_value_py(param))
  86. if KEY_DOC in param:
  87. f.write('\n | ' + indent_and_fix_rst_chars(param[KEY_DOC].strip(), ' | ') + '\n\n')
  88. else:
  89. f.write('\n')
  90. if KEY_EXAMPLES in method:
  91. f.write(' | ' + gen_example_links(method[KEY_EXAMPLES]) + '\n\n')
  92. f.write('\n')
  93. f.write('\n')
  94. def generate_documentation(data_classes):
  95. # generate constants
  96. with open(os.path.join(DOC_DIRECTORY, CATEGORY_CONSTANTS + EXT_RST), 'w') as f:
  97. write_cat_label(f, CATEGORY_CONSTANTS)
  98. f.write(
  99. '*******************\n' +
  100. cat_to_title(CATEGORY_CONSTANTS) + '\n' +
  101. '*******************\n\n'
  102. )
  103. # generate enums first, then constants
  104. enums = data_classes[KEY_ENUMS]
  105. for enum in enums:
  106. enum_name = enum[KEY_NAME]
  107. f.write(enum_name + '\n' + '='*len(enum_name) + '\n\n')
  108. if KEY_DOC in enum:
  109. f.write('\n | ' + indent_and_fix_rst_chars(enum[KEY_DOC].strip(), ' | ') + '\n\n')
  110. for value in enum[KEY_VALUES]:
  111. f.write('* | **' + value[KEY_NAME] + '** = ' + str(value[KEY_VALUE]) + '\n')
  112. if KEY_DOC in value:
  113. f.write(' | ' + indent_and_fix_rst_chars(value[KEY_DOC].strip(), ' | ') + '\n\n')
  114. f.write('\n')
  115. f.write('\n\n')
  116. c = 'Constants'
  117. f.write(c + '\n' + '='*len(c) + '\n\n')
  118. constants = data_classes[KEY_CONSTANTS]
  119. for const in constants:
  120. const_name = const[KEY_NAME]
  121. f.write('* | **' + const_name + '**: ' + yaml_type_to_py_type(const[KEY_TYPE]) + \
  122. ' = ' + str(const[KEY_VALUE]) +'\n')
  123. if KEY_DOC in const:
  124. f.write(' | ' + indent_and_fix_rst_chars(const[KEY_DOC].strip(), ' | ') + '\n\n')
  125. f.write('\n\n')
  126. # then generate classes into files by category
  127. for cat in CATEGORIES:
  128. if cat == CATEGORY_CONSTANTS:
  129. continue
  130. input_file = cat + EXT_RST
  131. with open(os.path.join(DOC_DIRECTORY, input_file), 'w') as f:
  132. write_cat_label(f, cat)
  133. cat_name = cat_to_title(cat)
  134. f.write('*'*len(cat_name) + '\n' + cat_name + '\n' + '*'*len(cat_name) + '\n')
  135. for key, value in sorted(data_classes.items()):
  136. if key != KEY_CONSTANTS and key != KEY_ENUMS and value[KEY_CATEGORY] == cat:
  137. generate_class_documentation(f, key, value)
  138. # and generate api.rst file
  139. with open(os.path.join(DOC_DIRECTORY, API_RST), 'w') as f:
  140. title = 'Python API Reference'
  141. f.write(
  142. title + '\n' +
  143. '='*len(title) + '\n\n'
  144. )
  145. f.write(
  146. '.. toctree::\n'
  147. ' :maxdepth: 2\n'
  148. ' :hidden:\n'
  149. ' :caption: Contents\n\n'
  150. )
  151. for cat in CATEGORIES:
  152. f.write(' ' + cat + '\n')
  153. f.write('\nThis section contains automatically generated documentation on Python classes, enums, '
  154. 'and constants provided by MCell.\n\n')
  155. for cat in CATEGORIES:
  156. f.write('- :ref:`api-' + cat + '`\n')