Skip to content

Commit

Permalink
Add a tool to move rst files and images
Browse files Browse the repository at this point in the history
The program moves a list of rst files along with their image
dependencies to a target directory. It then outputs redirects
in the format the ReadTheDocs backend expects.
  • Loading branch information
NathanLovato committed Oct 11, 2020
1 parent 9a63228 commit 5e3a7c8
Showing 1 changed file with 108 additions and 0 deletions.
108 changes: 108 additions & 0 deletions _tools/move_rst_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import re
from argparse import ArgumentParser
from os.path import isfile, isdir, join, realpath, split, dirname, relpath
import os
import shutil


def parse_and_get_arguments():
"""Creates and returns an object with parsed arguments, using argparse."""
parser: ArgumentParser = ArgumentParser(
prog="move_rst_files",
description="Moves reST documents and their dependencies from one folder to another. Outputs required redirections in the ReadTheDocs backend.",
)
parser.add_argument(
"documents", nargs="+", help="Paths of documents to move.",
)
parser.add_argument(
"output_path", help="Path to the target output directory.",
)
return parser.parse_args()


def find_project_root_path(document_path):
"""Returns the path to the repository's root directory by looking for the file conf.py, starting
from the path of any file in the project."""
full_path = realpath(document_path)
dirpath = split(full_path)[0]

root_path = ""
current = dirpath
iterations = 0
while root_path == "":
if isfile(join(current, "conf.py")):
root_path = current
else:
current = split(current)[0]
if current == "":
break
iterations += 1
if iterations > 20:
break
return root_path


def find_images(document):
"""Returns the list of image filepaths used by the `document`."""
images = []
for line in document:
match = re.match(r"\.\. image::\s+(img\/.+)", line)
if match:
images.append(match[1])
return list(set(images))


def find_document_dependencies(documents):
"""For each document path in `documents`, finds all pictures it depends on and returns a dict with the form { document: [images] }."""
data = {}
for path in documents:
with open(path, "r") as rst_file:
images = find_images(rst_file)
data[path] = images
return data


def move_documents(paths, output_path):
"""Moves .rst files and all their image dependencies to `output_path`"""
data = find_document_dependencies(paths)
for path in data:
directory = dirname(path)
shutil.move(path, output_path)
for image in data[path]:
image_in_path = join(directory, image)
image_out_path = join(output_path, image)
image_out_dirpath = dirname(image_out_path)
if not isdir(image_out_dirpath):
os.makedirs(image_out_dirpath)
shutil.move(image_in_path, image_out_path)


def print_redirects(paths):
"""Prints redirects we need to make on the ReadTheDocs backend with the form "input -> output".
Moving the file /learning/features/viewports/viewports.rst to /tutorials/viewports/viewports.rst
Requires the following redirect:
/learning/features/viewports/viewports.html -> /tutorials/viewports/viewports.html
"""
redirects = ""
project_root_path = find_project_root_path(paths[0])
out_path_relative = relpath(args.output_path, project_root_path)
for document in paths:
in_path_relative = relpath(document, project_root_path)
in_directory, filename_rst = split(in_path_relative)
filename_html = filename_rst.rsplit(".rst", 1)[0] + ".html"

in_path = join(in_directory, filename_html)
out_path = join(out_path_relative, filename_html)
redirects += in_path + " -> " + out_path + "\n"
print(redirects)


if __name__ == "__main__":
args = parse_and_get_arguments()
assert isdir(args.output_path)

documents = [
path for path in args.documents if isfile(path) and path.endswith(".rst")
]
move_documents(documents, args.output_path)
print_redirects(documents)

0 comments on commit 5e3a7c8

Please sign in to comment.