Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
12 kB · 411 lines
Python
at develop
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412#! /usr/bin/env python
# // BSD licence, modified to remove the organisation as there isn't one.# # Copyright (c) 2009, Michael Jones# All rights reserved.# # Redistribution and use in source and binary forms, with or without modification,# are permitted provided that the following conditions are met:# # * Redistributions of source code must retain the above copyright notice,# this list of conditions and the following disclaimer.# * Redistributions in binary form must reproduce the above copyright notice,# this list of conditions and the following disclaimer in the documentation# and/or other materials provided with the distribution.# * The names of its contributors may not be used to endorse or promote# products derived from this software without specific prior written# permission.# # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from optparse import OptionParser
import osimport sysimport shutilimport codecs
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, "r").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( lambda f, mode: codecs.open(f, mode, app.config.sphinx_to_github_encoding), 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.add_config_value("sphinx_to_github_encoding", 'utf-8', '')
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") parser.add_option("-e","--encoding", action="store", dest="encoding", default="utf-8", help="Encoding for reading and writing files") 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( lambda f, mode: codecs.open(f, mode, opts.encoding), 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:])