4 from optparse
import OptionParser
13 sys
.stdout
.write(msg
+ '\n')
17 def __init__(self
, is_dir
, list_dir
, walk
, rmtree
):
19 self
.list_dir
= list_dir
24 class FileSystemHelper
:
25 def __init__(self
, open_
, path_join
, move
, exists
):
27 self
.path_join
= path_join
33 """Encapsulates a simple text replace"""
35 def __init__(self
, from_
, to
):
39 def process(self
, text
):
40 return text
.replace(self
.from_
, self
.to
)
44 """Applies a series of replacements the contents of a file inplace"""
46 def __init__(self
, name
, replacers
, opener
):
48 self
.replacers
= replacers
52 text
= self
.opener(self
.name
, 'r').read()
54 for replacer
in self
.replacers
:
55 text
= replacer
.process(text
)
57 self
.opener(self
.name
, 'w').write(text
)
61 def __init__(self
, exists
, remove
):
65 def __call__(self
, name
):
71 def __init__(self
, renamer
, remove
):
72 self
.renamer
= renamer
75 def __call__(self
, from_
, to
):
77 self
.renamer(from_
, to
)
81 def __init__(self
, renamer
, stream
):
82 self
.renamer
= renamer
85 def __call__(self
, from_
, to
):
87 "Renaming directory '%s' -> '%s'\n"
88 % (os
.path
.basename(from_
), os
.path
.basename(to
))
91 self
.renamer(from_
, to
)
94 class DirectoryHandler
:
95 """Encapsulates renaming a directory by removing its first character"""
97 def __init__(self
, name
, root
, renamer
):
99 self
.new_name
= name
[1:]
100 self
.root
= str(root
) + os
.sep
101 self
.renamer
= renamer
104 return os
.path
.join(self
.root
, self
.name
)
106 def relative_path(self
, directory
, filename
):
107 path
= directory
.replace(self
.root
, '', 1)
108 return os
.path
.join(path
, filename
)
110 def new_relative_path(self
, directory
, filename
):
111 path
= self
.relative_path(directory
, filename
)
112 return path
.replace(self
.name
, self
.new_name
, 1)
115 from_
= os
.path
.join(self
.root
, self
.name
)
116 to
= os
.path
.join(self
.root
, self
.new_name
)
117 self
.renamer(from_
, to
)
120 class HandlerFactory
:
121 def create_file_handler(self
, name
, replacers
, opener
):
122 return FileHandler(name
, replacers
, opener
)
124 def create_dir_handler(self
, name
, root
, renamer
):
125 return DirectoryHandler(name
, root
, renamer
)
128 class OperationsFactory
:
129 def create_force_rename(self
, renamer
, remover
):
130 return ForceRename(renamer
, remover
)
132 def create_verbose_rename(self
, renamer
, stream
):
133 return VerboseRename(renamer
, stream
)
135 def create_replacer(self
, from_
, to
):
136 return Replacer(from_
, to
)
138 def create_remover(self
, exists
, remove
):
139 return Remover(exists
, remove
)
144 Applies a set of operations which result in the layout
145 of a directory changing
148 def __init__(self
, directory_handlers
, file_handlers
):
149 self
.directory_handlers
= directory_handlers
150 self
.file_handlers
= file_handlers
153 for handler
in self
.file_handlers
:
156 for handler
in self
.directory_handlers
:
162 Layout class that does nothing when asked to process
170 """Creates a layout object"""
182 self
.operations_factory
= operations_factory
183 self
.handler_factory
= handler_factory
185 self
.file_helper
= file_helper
186 self
.dir_helper
= dir_helper
188 self
.verbose
= verbose
189 self
.output_stream
= stream
192 def create_layout(self
, path
):
193 contents
= self
.dir_helper
.list_dir(path
)
195 renamer
= self
.file_helper
.move
198 remove
= self
.operations_factory
.create_remover(
199 self
.file_helper
.exists
, self
.dir_helper
.rmtree
201 renamer
= self
.operations_factory
.create_force_rename(renamer
, remove
)
204 renamer
= self
.operations_factory
.create_verbose_rename(
205 renamer
, self
.output_stream
208 # Build list of directories to process
209 directories
= [d
for d
in contents
if self
.is_underscore_dir(path
, d
)]
210 underscore_directories
= [
211 self
.handler_factory
.create_dir_handler(d
, path
, renamer
)
215 if not underscore_directories
:
217 self
.output_stream
.write(
218 'No top level directories starting with an underscore '
219 "were found in '%s'\n" % path
223 # Build list of files that are in those directories
225 for handler
in underscore_directories
:
226 for directory
, _
, files
in self
.dir_helper
.walk(handler
.path()):
229 self
.operations_factory
.create_replacer(
230 handler
.relative_path(directory
, f
),
231 handler
.new_relative_path(directory
, f
),
235 # Build list of handlers to process all files
237 for root
, _
, files
in self
.dir_helper
.walk(path
):
239 if f
.endswith('.html'):
241 self
.handler_factory
.create_file_handler(
242 self
.file_helper
.path_join(root
, f
),
244 self
.file_helper
.open_
,
247 if f
.endswith('.js'):
249 self
.handler_factory
.create_file_handler(
250 self
.file_helper
.path_join(root
, f
),
252 self
.operations_factory
.create_replacer(
253 "'_sources/'", "'sources/'"
256 self
.file_helper
.open_
,
260 return Layout(underscore_directories
, filelist
)
262 def is_underscore_dir(self
, path
, directory
):
263 return self
.dir_helper
.is_dir(
264 self
.file_helper
.path_join(path
, directory
)
265 ) and directory
.startswith('_')
268 def sphinx_extension(app
, exception
):
269 """Wrapped up as a Sphinx Extension"""
271 if app
.builder
.name
not in ('html', 'dirhtml'):
274 if not app
.config
.sphinx_to_github
:
275 if app
.config
.sphinx_to_github_verbose
:
276 stdout('Sphinx-to-github: Disabled, doing nothing.')
280 if app
.config
.sphinx_to_github_verbose
:
281 msg
= 'Sphinx-to-github: ' 'Exception raised in main build, doing nothing.'
285 dir_helper
= DirHelper(os
.path
.isdir
, os
.listdir
, os
.walk
, shutil
.rmtree
)
287 file_helper
= FileSystemHelper(
288 lambda f
, mode
: codecs
.open(f
, mode
, app
.config
.sphinx_to_github_encoding
),
294 operations_factory
= OperationsFactory()
295 handler_factory
= HandlerFactory()
297 layout_factory
= LayoutFactory(
302 app
.config
.sphinx_to_github_verbose
,
307 layout
= layout_factory
.create_layout(app
.outdir
)
312 """Setup function for Sphinx Extension"""
314 app
.add_config_value('sphinx_to_github', True, '')
315 app
.add_config_value('sphinx_to_github_verbose', True, '')
316 app
.add_config_value('sphinx_to_github_encoding', 'utf-8', '')
318 app
.connect('build-finished', sphinx_extension
)
322 usage
= 'usage: %prog [options] <html directory>'
323 parser
= OptionParser(usage
=usage
)
330 help='Provides verbose output',
338 help='Encoding for reading and writing files',
340 opts
, args
= parser
.parse_args(args
)
346 'Error - Expecting path to html directory:' 'sphinx-to-github <path>\n'
350 dir_helper
= DirHelper(os
.path
.isdir
, os
.listdir
, os
.walk
, shutil
.rmtree
)
352 file_helper
= FileSystemHelper(
353 lambda f
, mode
: codecs
.open(f
, mode
, opts
.encoding
),
359 operations_factory
= OperationsFactory()
360 handler_factory
= HandlerFactory()
362 layout_factory
= LayoutFactory(
372 layout
= layout_factory
.create_layout(path
)
376 if __name__
== '__main__':