##// END OF EJS Templates
added gh-pages command for GitHub hosted sphinx docs
added gh-pages command for GitHub hosted sphinx docs

File last commit:

r3255:7355c69f
r3255:7355c69f
Show More
sphinxtogithub.py
381 lines | 9.9 KiB | text/x-python | PythonLexer
#! /usr/bin/env python
"""
sphinxtogithub extension Copyright Michael Jones
BSD License
Original at: https://github.com/michaeljones/sphinx-to-github
"""
from optparse import OptionParser
import os
import sys
import shutil
class DirHelper(object):
def __init__(self, is_dir, list_dir, walk, rmtree):
self.is_dir = is_dir
self.list_dir = list_dir
self.walk = walk
self.rmtree = rmtree
class FileSystemHelper(object):
def __init__(self, open_, path_join, move, exists):
self.open_ = open_
self.path_join = path_join
self.move = move
self.exists = exists
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, opener):
self.name = name
self.replacers = replacers
self.opener = opener
def process(self):
text = self.opener(self.name).read()
for replacer in self.replacers:
text = replacer.process( text )
self.opener(self.name, "w").write(text)
class Remover(object):
def __init__(self, exists, remove):
self.exists = exists
self.remove = remove
def __call__(self, name):
if self.exists(name):
self.remove(name)
class ForceRename(object):
def __init__(self, renamer, remove):
self.renamer = renamer
self.remove = remove
def __call__(self, from_, to):
self.remove(to)
self.renamer(from_, to)
class VerboseRename(object):
def __init__(self, renamer, stream):
self.renamer = renamer
self.stream = stream
def __call__(self, from_, to):
self.stream.write(
"Renaming directory '%s' -> '%s'\n"
% (os.path.basename(from_), os.path.basename(to))
)
self.renamer(from_, to)
class DirectoryHandler(object):
"Encapsulates renaming a directory by removing its first character"
def __init__(self, name, root, renamer):
self.name = name
self.new_name = name[1:]
self.root = root + os.sep
self.renamer = renamer
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)
self.renamer(from_, to)
class HandlerFactory(object):
def create_file_handler(self, name, replacers, opener):
return FileHandler(name, replacers, opener)
def create_dir_handler(self, name, root, renamer):
return DirectoryHandler(name, root, renamer)
class OperationsFactory(object):
def create_force_rename(self, renamer, remover):
return ForceRename(renamer, remover)
def create_verbose_rename(self, renamer, stream):
return VerboseRename(renamer, stream)
def create_replacer(self, from_, to):
return Replacer(from_, to)
def create_remover(self, exists, remove):
return Remover(exists, remove)
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 NullLayout(object):
"""
Layout class that does nothing when asked to process
"""
def process(self):
pass
class LayoutFactory(object):
"Creates a layout object"
def __init__(self, operations_factory, handler_factory, file_helper, dir_helper, verbose, stream, force):
self.operations_factory = operations_factory
self.handler_factory = handler_factory
self.file_helper = file_helper
self.dir_helper = dir_helper
self.verbose = verbose
self.output_stream = stream
self.force = force
def create_layout(self, path):
contents = self.dir_helper.list_dir(path)
renamer = self.file_helper.move
if self.force:
remove = self.operations_factory.create_remover(self.file_helper.exists, self.dir_helper.rmtree)
renamer = self.operations_factory.create_force_rename(renamer, remove)
if self.verbose:
renamer = self.operations_factory.create_verbose_rename(renamer, self.output_stream)
# Build list of directories to process
directories = [d for d in contents if self.is_underscore_dir(path, d)]
underscore_directories = [
self.handler_factory.create_dir_handler(d, path, renamer)
for d in directories
]
if not underscore_directories:
if self.verbose:
self.output_stream.write(
"No top level directories starting with an underscore "
"were found in '%s'\n" % path
)
return NullLayout()
# Build list of files that are in those directories
replacers = []
for handler in underscore_directories:
for directory, dirs, files in self.dir_helper.walk(handler.path()):
for f in files:
replacers.append(
self.operations_factory.create_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 self.dir_helper.walk(path):
for f in files:
if f.endswith(".html"):
filelist.append(
self.handler_factory.create_file_handler(
self.file_helper.path_join(root, f),
replacers,
self.file_helper.open_)
)
if f.endswith(".js"):
filelist.append(
self.handler_factory.create_file_handler(
self.file_helper.path_join(root, f),
[self.operations_factory.create_replacer("'_sources/'", "'sources/'")],
self.file_helper.open_
)
)
return Layout(underscore_directories, filelist)
def is_underscore_dir(self, path, directory):
return (self.dir_helper.is_dir(self.file_helper.path_join(path, directory))
and directory.startswith("_"))
def sphinx_extension(app, exception):
"Wrapped up as a Sphinx Extension"
if not app.builder.name in ("html", "dirhtml"):
return
if not app.config.sphinx_to_github:
if app.config.sphinx_to_github_verbose:
print "Sphinx-to-github: Disabled, doing nothing."
return
if exception:
if app.config.sphinx_to_github_verbose:
print "Sphinx-to-github: Exception raised in main build, doing nothing."
return
dir_helper = DirHelper(
os.path.isdir,
os.listdir,
os.walk,
shutil.rmtree
)
file_helper = FileSystemHelper(
open,
os.path.join,
shutil.move,
os.path.exists
)
operations_factory = OperationsFactory()
handler_factory = HandlerFactory()
layout_factory = LayoutFactory(
operations_factory,
handler_factory,
file_helper,
dir_helper,
app.config.sphinx_to_github_verbose,
sys.stdout,
force=True
)
layout = layout_factory.create_layout(app.outdir)
layout.process()
def setup(app):
"Setup function for Sphinx Extension"
app.add_config_value("sphinx_to_github", True, '')
app.add_config_value("sphinx_to_github_verbose", True, '')
app.connect("build-finished", sphinx_extension)
def main(args):
usage = "usage: %prog [options] <html directory>"
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 <path>\n"
)
return
dir_helper = DirHelper(
os.path.isdir,
os.listdir,
os.walk,
shutil.rmtree
)
file_helper = FileSystemHelper(
open,
os.path.join,
shutil.move,
os.path.exists
)
operations_factory = OperationsFactory()
handler_factory = HandlerFactory()
layout_factory = LayoutFactory(
operations_factory,
handler_factory,
file_helper,
dir_helper,
opts.verbose,
sys.stdout,
force=False
)
layout = layout_factory.create_layout(path)
layout.process()
if __name__ == "__main__":
main(sys.argv[1:])