update_docs.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. # Copyright 2013 The Chromium Authors. All rights reserved.
  2. # Use of this source code is governed by a BSD-style license that can be
  3. # found in the LICENSE file.
  4. import logging
  5. import optparse
  6. import os
  7. import pkgutil
  8. import pydoc
  9. import re
  10. import sys
  11. import telemetry
  12. from telemetry.core import util
  13. telemetry_dir = util.GetTelemetryDir()
  14. docs_dir = os.path.join(telemetry_dir, 'docs', 'pydoc')
  15. def RemoveAllDocs():
  16. for dirname, _, filenames in os.walk(docs_dir):
  17. for filename in filenames:
  18. os.remove(os.path.join(dirname, filename))
  19. def GenerateHTMLForModule(module):
  20. html = pydoc.html.page(pydoc.describe(module),
  21. pydoc.html.document(module, module.__name__))
  22. # pydoc writes out html with links in a variety of funky ways. We need
  23. # to fix them up.
  24. assert not telemetry_dir.endswith(os.sep)
  25. links = re.findall('(<a href="(.+?)">(.+?)</a>)', html)
  26. for link_match in links:
  27. link, href, link_text = link_match
  28. if not href.startswith('file:'):
  29. continue
  30. new_href = href.replace('file:', '')
  31. new_href = new_href.replace(telemetry_dir, '..')
  32. new_href = new_href.replace(os.sep, '/')
  33. new_link_text = link_text.replace(telemetry_dir + os.sep, '')
  34. new_link = '<a href="%s">%s</a>' % (new_href, new_link_text)
  35. html = html.replace(link, new_link)
  36. # pydoc writes out html with absolute path file links. This is not suitable
  37. # for checked in documentation. So, fix up the HTML after it is generated.
  38. #html = re.sub('href="file:%s' % telemetry_dir, 'href="..', html)
  39. #html = re.sub(telemetry_dir + os.sep, '', html)
  40. return html
  41. def WriteHTMLForModule(module):
  42. page = GenerateHTMLForModule(module)
  43. path = os.path.join(docs_dir, '%s.html' % module.__name__)
  44. with open(path, 'w') as f:
  45. sys.stderr.write('Wrote %s\n' % os.path.relpath(path))
  46. f.write(page)
  47. def GetAllModulesToDocument(module):
  48. modules = [module]
  49. for _, modname, _ in pkgutil.walk_packages(
  50. module.__path__, module.__name__ + '.'):
  51. if modname.endswith('_unittest'):
  52. logging.debug("skipping %s due to being a unittest", modname)
  53. continue
  54. module = __import__(modname, fromlist=[""])
  55. name, _ = os.path.splitext(module.__file__)
  56. if not os.path.exists(name + '.py'):
  57. logging.info("skipping %s due to being an orphan .pyc", module.__file__)
  58. continue
  59. modules.append(module)
  60. return modules
  61. class AlreadyDocumentedModule(object):
  62. def __init__(self, filename):
  63. self.filename = filename
  64. @property
  65. def name(self):
  66. basename = os.path.basename(self.filename)
  67. return os.path.splitext(basename)[0]
  68. @property
  69. def contents(self):
  70. with open(self.filename, 'r') as f:
  71. return f.read()
  72. def GetAlreadyDocumentedModules():
  73. modules = []
  74. for dirname, _, filenames in os.walk(docs_dir):
  75. for filename in filenames:
  76. path = os.path.join(dirname, filename)
  77. modules.append(AlreadyDocumentedModule(path))
  78. return modules
  79. def IsUpdateDocsNeeded():
  80. already_documented_modules = GetAlreadyDocumentedModules()
  81. already_documented_modules_by_name = dict(
  82. (module.name, module) for module in already_documented_modules)
  83. current_modules = GetAllModulesToDocument(telemetry)
  84. # Quick check: if the names of modules has changed, we definitely need
  85. # an update.
  86. already_documented_module_names = set(
  87. m.name for m in already_documented_modules)
  88. current_module_names = set([m.__name__ for m in current_modules])
  89. if current_module_names != already_documented_module_names:
  90. return True
  91. # Generate the new docs and compare aganist the old. If changed, then a
  92. # an update is needed.
  93. for current_module in current_modules:
  94. already_documented_module = already_documented_modules_by_name[
  95. current_module.__name__]
  96. current_html = GenerateHTMLForModule(current_module)
  97. if current_html != already_documented_module.contents:
  98. return True
  99. return False
  100. def Main(args):
  101. parser = optparse.OptionParser()
  102. parser.add_option(
  103. '-v', '--verbose', action='count', dest='verbosity',
  104. help='Increase verbosity level (repeat as needed)')
  105. options, args = parser.parse_args(args)
  106. if options.verbosity >= 2:
  107. logging.getLogger().setLevel(logging.DEBUG)
  108. elif options.verbosity:
  109. logging.getLogger().setLevel(logging.INFO)
  110. else:
  111. logging.getLogger().setLevel(logging.WARNING)
  112. assert os.path.isdir(docs_dir), '%s does not exist' % docs_dir
  113. RemoveAllDocs()
  114. old_cwd = os.getcwd()
  115. try:
  116. os.chdir(telemetry_dir)
  117. for module in GetAllModulesToDocument(telemetry):
  118. WriteHTMLForModule(module)
  119. finally:
  120. os.chdir(old_cwd)