scripts/create_api_docs.py
#!/usr/bin/env python
"""Generating rst file for api documentation."""
import os
from shutil import rmtree
import sys
__copyright__ = "Copyright 2016, The InaSAFE Project"
__license__ = "GPL version 3"
__email__ = "info@inasafe.org"
__revision__ = '$Format:%H$'
# Text
INDEX_HEADER = 'API documentation\n'
INDEX_HEADER += '=================\n'
INDEX_HEADER += 'This is the API documentation for the InaSAFE project.\n'
INDEX_HEADER += 'You can find out more about the InaSAFE project by visiting\n'
INDEX_HEADER += '`inasafe.org <http://www.inasafe.org/>`_.\n\n'
# known packages/directories/modules that don't have python files in it.
EXCLUDED_PACKAGES = [
'i18n', 'test', 'converter_data', 'ui', 'resources', 'fixtures']
def create_top_level_index_entry(title, max_depth, subtitles):
"""Function for creating a text entry in index.rst for its content.
:param title : Title for the content.
:type title: str
:param max_depth : Value for max_depth in the top level index content.
:type max_depth: int
:param subtitles : list of subtitles that is available.
:type subtitles: list
:return: A text for the content of top level index.
:rtype: str
"""
return_text = title + '\n'
dash = '-' * len(title) + '\n'
return_text += dash + '\n'
return_text += '.. toctree::' + '\n'
return_text += ' :maxdepth: ' + str(max_depth) + '\n\n'
for subtitle in subtitles:
return_text += ' ' + subtitle + '\n\n'
return return_text
def create_package_level_rst_index_file(
package_name, max_depth, modules, inner_packages=None):
"""Function for creating text for index for a package.
:param package_name: name of the package
:type package_name: str
:param max_depth: Value for max_depth in the index file.
:type max_depth: int
:param modules: list of module in the package.
:type modules: list
:return: A text for the content of the index file.
:rtype: str
"""
if inner_packages is None:
inner_packages = []
return_text = 'Package::' + package_name
dash = '=' * len(return_text)
return_text += '\n' + dash + '\n\n'
return_text += '.. toctree::' + '\n'
return_text += ' :maxdepth: ' + str(max_depth) + '\n\n'
upper_package = package_name.split('.')[-1]
for module in modules:
if module in EXCLUDED_PACKAGES:
continue
return_text += ' ' + upper_package + os.sep + module[:-3] + '\n'
for inner_package in inner_packages:
if inner_package in EXCLUDED_PACKAGES:
continue
return_text += ' ' + upper_package + os.sep + inner_package + '\n'
return return_text
def create_module_rst_file(module_name):
"""Function for creating content in each .rst file for a module.
:param module_name: name of the module.
:type module_name: str
:returns: A content for auto module.
:rtype: str
"""
return_text = 'Module: ' + module_name
dash = '=' * len(return_text)
return_text += '\n' + dash + '\n\n'
return_text += '.. automodule:: ' + module_name + '\n'
return_text += ' :members:\n\n'
return return_text
def create_dirs(path):
"""Shorter function for creating directory."""
if not os.path.exists(path):
os.makedirs(path)
def write_rst_file(file_directory, file_name, content):
"""Shorter procedure for creating rst file.
:param file_directory: Directory of the filename.
:type file_directory: str
:param file_name: Name of the file.
:type file_name: str
:param content: The content of the file.
:type content: str
"""
create_dirs(os.path.split(os.path.join(file_directory, file_name))[0])
try:
fl = open(os.path.join(file_directory, file_name + '.rst'), 'w+')
fl.write(content)
fl.close()
except Exception as e:
print(('Creating %s failed' % os.path.join(
file_directory, file_name + '.rst'), e))
def get_python_files_from_list(files, excluded_files=None):
"""Return list of python file from files, without excluded files.
:param files: List of files.
:type files: list
:param excluded_files: List of excluded file names.
:type excluded_files: list, None
:returns: List of python file without the excluded file, not started with
test.
:rtype: list
"""
if excluded_files is None:
excluded_files = ['__init__.py']
python_files = []
for fl in files:
if (fl.endswith('.py')
and fl not in excluded_files
and not fl.startswith('test')):
python_files.append(fl)
return python_files
def create_top_level_index(api_docs_path, packages, max_depth=2):
"""Create the top level index page (writing to file)
:param api_docs_path: Path to the api-docs of inasafe documentation.
:type api_docs_path: str
:param packages: List of packages which want to be extracted their api/
:type packages: list
:param max_depth: The maximum depth of tree in the api docs.
:type max_depth: int
"""
page_text = INDEX_HEADER
for package in packages:
# Write top level index file entries for safe
text = create_top_level_index_entry(
title='Package %s' % package,
max_depth=max_depth,
subtitles=[package])
page_text += '%s\n' % text
write_rst_file(api_docs_path, 'index', page_text)
def get_inasafe_code_path(custom_inasafe_path=None):
"""Determine the path to inasafe location.
:param custom_inasafe_path: Custom inasafe project location.
:type custom_inasafe_path: str
:returns: Path to inasafe source code.
:rtype: str
"""
inasafe_code_path = os.path.abspath(
os.path.join(os.path.dirname(__file__), '..'))
if custom_inasafe_path is not None:
inasafe_code_path = custom_inasafe_path
return inasafe_code_path
def clean_api_docs_dirs():
"""Empty previous api-docs directory.
:returns: Path to api-docs directory.
:rtype: str
"""
inasafe_docs_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__), '..', 'docs', 'api-docs'))
if os.path.exists(inasafe_docs_path):
rmtree(inasafe_docs_path)
create_dirs(inasafe_docs_path)
return inasafe_docs_path
def create_api_docs(code_path, api_docs_path, max_depth=2):
"""Function for generating .rst file for all .py file in dir_path folder.
:param code_path: Path of the source code.
:type code_path: str
:param api_docs_path: Path of the api documentation directory.
:type api_docs_path: str
:param max_depth: Maximum depth for the index.
:type max_depth: int
"""
base_path = os.path.split(code_path)[0]
for package, subpackages, candidate_files in os.walk(code_path):
# Checking __init__.py file
if '__init__.py' not in candidate_files:
continue
# Creating directory for the package
package_relative_path = package.replace(base_path + os.sep, '')
index_package_path = os.path.join(
api_docs_path, package_relative_path)
# calculate dir one up from package to store the index in
index_base_path, package_base_name = os.path.split(index_package_path)
if package_base_name in EXCLUDED_PACKAGES:
continue
full_package_name = package_relative_path.replace(os.sep, '.')
new_rst_dir = os.path.join(api_docs_path, package_relative_path)
create_dirs(new_rst_dir)
# Create index_file for the directory
modules = get_python_files_from_list(candidate_files)
index_file_text = create_package_level_rst_index_file(
package_name=full_package_name,
max_depth=max_depth,
modules=modules,
inner_packages=subpackages)
write_rst_file(
file_directory=index_base_path,
file_name=package_base_name,
content=index_file_text)
# Creating .rst file for each .py file
for module in modules:
module = module[:-3] # strip .py off the end
py_module_text = create_module_rst_file(
'%s.%s' % (full_package_name, module))
write_rst_file(
file_directory=new_rst_dir,
file_name=module,
content=py_module_text)
def usage():
"""Helper function for telling how to use the script."""
print('Usage:')
print(('python %s [optional path to inasafe directory]' % sys.argv[0]))
def main():
if len(sys.argv) > 2:
usage()
sys.exit()
elif len(sys.argv) == 2:
print(('Building rst files from %s' % sys.argv[1]))
inasafe_code_path = os.path.abspath(sys.argv[1])
else:
inasafe_code_path = None
print('Please make sure there is no unused source '
'code file in inasafe-dev')
inasafe_code_path = get_inasafe_code_path(inasafe_code_path)
print('Cleaning api docs...')
api_docs_path = clean_api_docs_dirs()
max_depth = 2
packages = ['safe', 'realtime']
# creating top level index for api-docs
print('Creating top level index page...')
create_top_level_index(api_docs_path, packages, max_depth)
for package in packages:
print('Creating api docs for package %s...' % package)
package_code_path = os.path.join(inasafe_code_path, package)
create_api_docs(
code_path=package_code_path,
api_docs_path=api_docs_path,
max_depth=max_depth)
print('Done.')
if __name__ == '__main__':
main()