move_rst_files.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import re
  2. from argparse import ArgumentParser
  3. from os.path import isfile, isdir, join, realpath, split, dirname, relpath
  4. import os
  5. import shutil
  6. def parse_and_get_arguments():
  7. """Creates and returns an object with parsed arguments, using argparse."""
  8. parser: ArgumentParser = ArgumentParser(
  9. prog="move_rst_files",
  10. description="Moves reST documents and their dependencies from one folder to another. Outputs required redirections in the ReadTheDocs backend.",
  11. )
  12. parser.add_argument(
  13. "documents", nargs="+", help="Paths of documents to move.",
  14. )
  15. parser.add_argument(
  16. "output_path", help="Path to the target output directory.",
  17. )
  18. return parser.parse_args()
  19. def find_project_root_path(document_path):
  20. """Returns the path to the repository's root directory by looking for the file conf.py, starting
  21. from the path of any file in the project."""
  22. full_path = realpath(document_path)
  23. dirpath = split(full_path)[0]
  24. root_path = ""
  25. current = dirpath
  26. iterations = 0
  27. while root_path == "":
  28. if isfile(join(current, "conf.py")):
  29. root_path = current
  30. else:
  31. current = split(current)[0]
  32. if current == "":
  33. break
  34. iterations += 1
  35. if iterations > 20:
  36. break
  37. return root_path
  38. def find_images(document):
  39. """Returns the list of image filepaths used by the `document`."""
  40. images = []
  41. for line in document:
  42. match = re.match(r"\.\. image::\s+(img\/.+)", line)
  43. if match:
  44. images.append(match[1])
  45. return list(set(images))
  46. def find_document_dependencies(documents):
  47. """For each document path in `documents`, finds all pictures it depends on and returns a dict with the form { document: [images] }."""
  48. data = {}
  49. for path in documents:
  50. with open(path, "r") as rst_file:
  51. images = find_images(rst_file)
  52. data[path] = images
  53. return data
  54. def move_documents(paths, output_path):
  55. """Moves .rst files and all their image dependencies to `output_path`"""
  56. data = find_document_dependencies(paths)
  57. for path in data:
  58. directory = dirname(path)
  59. shutil.move(path, output_path)
  60. for image in data[path]:
  61. image_in_path = join(directory, image)
  62. image_out_path = join(output_path, image)
  63. image_out_dirpath = dirname(image_out_path)
  64. if not isdir(image_out_dirpath):
  65. os.makedirs(image_out_dirpath)
  66. shutil.move(image_in_path, image_out_path)
  67. def print_redirects(paths):
  68. """Prints redirects we need to make on the ReadTheDocs backend with the form "input -> output".
  69. Moving the file /learning/features/viewports/viewports.rst to /tutorials/viewports/viewports.rst
  70. Requires the following redirect:
  71. /learning/features/viewports/viewports.html -> /tutorials/viewports/viewports.html
  72. """
  73. redirects = ""
  74. project_root_path = find_project_root_path(paths[0])
  75. out_path_relative = relpath(args.output_path, project_root_path)
  76. for document in paths:
  77. in_path_relative = relpath(document, project_root_path)
  78. in_directory, filename_rst = split(in_path_relative)
  79. filename_html = filename_rst.rsplit(".rst", 1)[0] + ".html"
  80. in_path = join(in_directory, filename_html)
  81. out_path = join(out_path_relative, filename_html)
  82. redirects += in_path + " -> " + out_path + "\n"
  83. print(redirects)
  84. if __name__ == "__main__":
  85. args = parse_and_get_arguments()
  86. assert isdir(args.output_path)
  87. documents = [
  88. path for path in args.documents if isfile(path) and path.endswith(".rst")
  89. ]
  90. move_documents(documents, args.output_path)
  91. print_redirects(documents)