From 5c259e2afe5c985dd27c45525561a2906d629d03 Mon Sep 17 00:00:00 2001 From: mpj Date: Thu, 19 Nov 2009 10:34:37 +0000 Subject: [PATCH] Implemented in a "Clean Code" manner with verbose option Design is inspired by Google's Clean Code talks and is aimed at testability, though currently there are no tests. --- sphinx-to-github | 244 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 194 insertions(+), 50 deletions(-) diff --git a/sphinx-to-github b/sphinx-to-github index 9ee7b37..71e9cdd 100755 --- a/sphinx-to-github +++ b/sphinx-to-github @@ -1,55 +1,199 @@ #! /usr/bin/env python +from optparse import OptionParser import os import sys -import re - -dir = sys.argv[1] - -def convert_file(file, convert_dict): - print "Converting: %s" % file - text = open(file).read() - for old_link in convert_dict: - new_link = convert_dict[old_link] - text = text.replace(old_link, new_link) - open(file, "w").write(text) - -def remove_underscores(path): - i = path.find("_") - if i != -1: - path = path[:i] + path[i+1:] - return remove_underscores(path) - else: - return path - -def strip_beginning_dotslash(path): - if "./" == path[:2]: - path = path[2:] - return path - -# build up the global dictionary with files we need to convert: -d = {} -for old_root, dirs, files in os.walk(dir): - if "_" in old_root: - new_root = remove_underscores(old_root) - assert new_root != old_root - os.mkdir(new_root) - for file in files: - old_path = os.path.join(old_root, file) - new_path = os.path.join(new_root, file) - os.rename(old_path, new_path) - new_path = strip_beginning_dotslash(new_path) - old_path = strip_beginning_dotslash(old_path) - d[old_path] = new_path - -# delete the old "_" directories -for old_root, dirs, files in os.walk(dir, topdown=False): - if "_" in old_root: - os.rmdir(old_root) - -# convert all html files: -for root, dirs, files in os.walk(dir): - for file in files: - if file.endswith(".html"): - convert_file(os.path.join(root, file), d) + + +class NoDirectoriesError(Exception): + "Error thrown when no directories starting with an underscore are found" + +class Replacer(object): + "Encapsulates a simple text replace" + + def __init__(self, from_, to): + + self.from_ = from_ + self.to = to + + def process(self, text): + + return text.replace( self.from_, self.to ) + +class FileHandler(object): + "Applies a series of replacements the contents of a file inplace" + + def __init__(self, name, replacers): + + self.name = name + self.replacers = replacers + + def process(self): + + text = open(self.name).read() + + for replacer in self.replacers: + text = replacer.process( text ) + + open(self.name, "w").write(text) + +class DirectoryHandler(object): + "Encapsulates renaming a directory by removing its first character" + + def __init__(self, name, root): + + self.name = name + self.new_name = name[1:] + self.root = root + os.sep + + def path(self): + + return os.path.join(self.root, self.name) + + def relative_path(self, directory, filename): + + path = directory.replace(self.root, "", 1) + return os.path.join(path, filename) + + def new_relative_path(self, directory, filename): + + path = self.relative_path(directory, filename) + return path.replace(self.name, self.new_name, 1) + + def process(self): + + from_ = os.path.join(self.root, self.name) + to = os.path.join(self.root, self.new_name) + os.rename(from_, to) + +class VerboseDirectoryHandler(DirectoryHandler): + + def __init__(self, name, root, stream): + + DirectoryHandler.__init__(self, name, root) + + self.stream = stream + + def process(self): + + self.stream.write( + "Renaming directory '%s' -> '%s'\n" % (self.name, self.new_name) + ) + + DirectoryHandler.process(self) + +class Layout(object): + """ + Applies a set of operations which result in the layout + of a directory changing + """ + + def __init__(self, directory_handlers, file_handlers): + + self.directory_handlers = directory_handlers + self.file_handlers = file_handlers + + def process(self): + + for handler in self.file_handlers: + handler.process() + + for handler in self.directory_handlers: + handler.process() + + +class LayoutFactory(object): + "Creates a layout object" + + def __init__(self, verbose, stream): + + self.verbose = verbose + self.output_stream = stream + + def create_layout(self, path): + + contents = os.listdir(path) + + # Build list of directories to process + directories = [d for d in contents if self.is_underscore_dir(path, d)] + if self.verbose: + underscore_directories = [ + VerboseDirectoryHandler(d, path, self.output_stream) + for d in directories + ] + else: + underscore_directories = [ + DirectoryHandler(d, path) for d in directories + ] + + if not underscore_directories: + raise NoDirectoriesError() + + # Build list of files that are in those directories + replacers = [] + for handler in underscore_directories: + for directory, dirs, files in os.walk(handler.path()): + for f in files: + replacers.append( + Replacer( + handler.relative_path(directory, f), + handler.new_relative_path(directory, f) + ) + ) + + # Build list of handlers to process all files + filelist = [] + for root, dirs, files in os.walk(path): + for f in files: + if f.endswith(".html"): + filelist.append( + FileHandler(os.path.join(root, f), replacers) + ) + + return Layout(underscore_directories, filelist) + + @staticmethod + def is_underscore_dir(path, directory): + + return (os.path.isdir(os.path.join(path, directory)) + and directory.startswith("_")) + + + +def main(args): + + usage = "usage: %prog [options] " + parser = OptionParser(usage=usage) + parser.add_option("-v","--verbose", action="store_true", + dest="verbose", default=False, help="Provides verbose output") + opts, args = parser.parse_args(args) + + try: + path = args[0] + except IndexError: + sys.stderr.write( + "Error - Expecting path to html directory:" + "sphinx-to-github \n" + ) + return + + layout_factory = LayoutFactory(opts.verbose, sys.stdout) + + try: + layout = layout_factory.create_layout(path) + except NoDirectoriesError: + sys.stderr.write( + "Error - No top level directories starting with an underscore " + "were found in '%s'\n" % path + ) + return + + layout.process() + + + +if __name__ == "__main__": + main(sys.argv[1:]) + +