3 from __future__
import absolute_import
5 from optparse
import OptionParser
14 sys
.stdout
.write(msg
+ '\n')
17 class DirHelper(object):
18 def __init__(self
, is_dir
, list_dir
, walk
, rmtree
):
20 self
.list_dir
= list_dir
25 class FileSystemHelper(object):
26 def __init__(self
, open_
, path_join
, move
, exists
):
28 self
.path_join
= path_join
33 class Replacer(object):
34 "Encapsulates a simple text replace"
36 def __init__(self
, from_
, to
):
40 def process(self
, text
):
41 return text
.replace(self
.from_
, self
.to
)
44 class FileHandler(object):
45 "Applies a series of replacements the contents of a file inplace"
47 def __init__(self
, name
, replacers
, opener
):
49 self
.replacers
= replacers
53 text
= self
.opener(self
.name
, "r").read()
55 for replacer
in self
.replacers
:
56 text
= replacer
.process(text
)
58 self
.opener(self
.name
, "w").write(text
)
61 class Remover(object):
62 def __init__(self
, exists
, remove
):
66 def __call__(self
, name
):
71 class ForceRename(object):
72 def __init__(self
, renamer
, remove
):
73 self
.renamer
= renamer
76 def __call__(self
, from_
, to
):
78 self
.renamer(from_
, to
)
81 class VerboseRename(object):
82 def __init__(self
, renamer
, stream
):
83 self
.renamer
= renamer
86 def __call__(self
, from_
, to
):
88 "Renaming directory '%s' -> '%s'\n"
89 % (os
.path
.basename(from_
), os
.path
.basename(to
))
92 self
.renamer(from_
, to
)
95 class DirectoryHandler(object):
96 "Encapsulates renaming a directory by removing its first character"
98 def __init__(self
, name
, root
, renamer
):
100 self
.new_name
= name
[1:]
101 self
.root
= str(root
)
102 self
.renamer
= renamer
105 return os
.path
.join(self
.root
, self
.name
)
107 def relative_path(self
, directory
, filename
):
108 path
= directory
.replace(self
.root
, "", 1)
109 return os
.path
.join(path
, filename
)
111 def new_relative_path(self
, directory
, filename
):
112 path
= self
.relative_path(directory
, filename
)
113 return path
.replace(self
.name
, self
.new_name
, 1)
116 from_
= os
.path
.join(self
.root
, self
.name
)
117 to
= os
.path
.join(self
.root
, self
.new_name
)
118 self
.renamer(from_
, to
)
121 class HandlerFactory(object):
122 def create_file_handler(self
, name
, replacers
, opener
):
123 return FileHandler(name
, replacers
, opener
)
125 def create_dir_handler(self
, name
, root
, renamer
):
126 return DirectoryHandler(name
, root
, renamer
)
129 class OperationsFactory(object):
130 def create_force_rename(self
, renamer
, remover
):
131 return ForceRename(renamer
, remover
)
133 def create_verbose_rename(self
, renamer
, stream
):
134 return VerboseRename(renamer
, stream
)
136 def create_replacer(self
, from_
, to
):
137 return Replacer(from_
, to
)
139 def create_remover(self
, exists
, remove
):
140 return Remover(exists
, remove
)
143 class Layout(object):
145 Applies a set of operations which result in the layout
146 of a directory changing
149 def __init__(self
, directory_handlers
, file_handlers
):
150 self
.directory_handlers
= directory_handlers
151 self
.file_handlers
= file_handlers
154 for handler
in self
.file_handlers
:
157 for handler
in self
.directory_handlers
:
161 class NullLayout(object):
163 Layout class that does nothing when asked to process
170 class LayoutFactory(object):
171 "Creates a layout object"
183 self
.operations_factory
= operations_factory
184 self
.handler_factory
= handler_factory
186 self
.file_helper
= file_helper
187 self
.dir_helper
= dir_helper
189 self
.verbose
= verbose
190 self
.output_stream
= stream
193 def create_layout(self
, path
):
194 contents
= self
.dir_helper
.list_dir(path
)
196 renamer
= self
.file_helper
.move
199 remove
= self
.operations_factory
.create_remover(
200 self
.file_helper
.exists
, self
.dir_helper
.rmtree
202 renamer
= self
.operations_factory
.create_force_rename(renamer
, remove
)
205 renamer
= self
.operations_factory
.create_verbose_rename(
206 renamer
, self
.output_stream
209 # Build list of directories to process
210 directories
= [d
for d
in contents
if self
.is_underscore_dir(path
, d
)]
211 underscore_directories
= [
212 self
.handler_factory
.create_dir_handler(d
, path
, renamer
)
216 if not underscore_directories
:
218 self
.output_stream
.write(
219 "No top level directories starting with an underscore "
220 "were found in '%s'\n" % path
224 # Build list of files that are in those directories
226 for handler
in underscore_directories
:
227 for directory
, _
, files
in self
.dir_helper
.walk(handler
.path()):
230 self
.operations_factory
.create_replacer(
231 handler
.relative_path(directory
, f
),
232 handler
.new_relative_path(directory
, f
),
236 # Build list of handlers to process all files
238 for root
, _
, files
in self
.dir_helper
.walk(path
):
240 if f
.endswith(".html"):
242 self
.handler_factory
.create_file_handler(
243 self
.file_helper
.path_join(root
, f
),
245 self
.file_helper
.open_
,
248 if f
.endswith(".js"):
250 self
.handler_factory
.create_file_handler(
251 self
.file_helper
.path_join(root
, f
),
253 self
.operations_factory
.create_replacer(
254 "'_sources/'", "'sources/'"
257 self
.file_helper
.open_
,
261 return Layout(underscore_directories
, filelist
)
263 def is_underscore_dir(self
, path
, directory
):
264 return self
.dir_helper
.is_dir(
265 self
.file_helper
.path_join(path
, directory
)
266 ) and directory
.startswith("_")
269 def sphinx_extension(app
, exception
):
270 "Wrapped up as a Sphinx Extension"
272 if app
.builder
.name
not in ("html", "dirhtml"):
275 if not app
.config
.sphinx_to_github
:
276 if app
.config
.sphinx_to_github_verbose
:
277 stdout("Sphinx-to-github: Disabled, doing nothing.")
281 if app
.config
.sphinx_to_github_verbose
:
282 msg
= "Sphinx-to-github: " "Exception raised in main build, doing nothing."
286 dir_helper
= DirHelper(os
.path
.isdir
, os
.listdir
, os
.walk
, shutil
.rmtree
)
288 file_helper
= FileSystemHelper(
289 lambda f
, mode
: codecs
.open(f
, mode
, app
.config
.sphinx_to_github_encoding
),
295 operations_factory
= OperationsFactory()
296 handler_factory
= HandlerFactory()
298 layout_factory
= LayoutFactory(
303 app
.config
.sphinx_to_github_verbose
,
308 layout
= layout_factory
.create_layout(app
.outdir
)
313 "Setup function for Sphinx Extension"
315 app
.add_config_value("sphinx_to_github", True, '')
316 app
.add_config_value("sphinx_to_github_verbose", True, '')
317 app
.add_config_value("sphinx_to_github_encoding", 'utf-8', '')
319 app
.connect("build-finished", sphinx_extension
)
323 usage
= "usage: %prog [options] <html directory>"
324 parser
= OptionParser(usage
=usage
)
331 help="Provides verbose output",
339 help="Encoding for reading and writing files",
341 opts
, args
= parser
.parse_args(args
)
347 "Error - Expecting path to html directory:" "sphinx-to-github <path>\n"
351 dir_helper
= DirHelper(os
.path
.isdir
, os
.listdir
, os
.walk
, shutil
.rmtree
)
353 file_helper
= FileSystemHelper(
354 lambda f
, mode
: codecs
.open(f
, mode
, opts
.encoding
),
360 operations_factory
= OperationsFactory()
361 handler_factory
= HandlerFactory()
363 layout_factory
= LayoutFactory(
373 layout
= layout_factory
.create_layout(path
)
377 if __name__
== "__main__":