##// END OF EJS Templates
added gh-pages command for GitHub hosted sphinx docs
MinRK -
Show More
@@ -0,0 +1,381 b''
1 #! /usr/bin/env python
2 """
3 sphinxtogithub extension Copyright Michael Jones
4
5 BSD License
6
7 Original at: https://github.com/michaeljones/sphinx-to-github
8 """
9 from optparse import OptionParser
10 import os
11 import sys
12 import shutil
13
14
15 class DirHelper(object):
16
17 def __init__(self, is_dir, list_dir, walk, rmtree):
18
19 self.is_dir = is_dir
20 self.list_dir = list_dir
21 self.walk = walk
22 self.rmtree = rmtree
23
24 class FileSystemHelper(object):
25
26 def __init__(self, open_, path_join, move, exists):
27
28 self.open_ = open_
29 self.path_join = path_join
30 self.move = move
31 self.exists = exists
32
33 class Replacer(object):
34 "Encapsulates a simple text replace"
35
36 def __init__(self, from_, to):
37
38 self.from_ = from_
39 self.to = to
40
41 def process(self, text):
42
43 return text.replace( self.from_, self.to )
44
45 class FileHandler(object):
46 "Applies a series of replacements the contents of a file inplace"
47
48 def __init__(self, name, replacers, opener):
49
50 self.name = name
51 self.replacers = replacers
52 self.opener = opener
53
54 def process(self):
55
56 text = self.opener(self.name).read()
57
58 for replacer in self.replacers:
59 text = replacer.process( text )
60
61 self.opener(self.name, "w").write(text)
62
63 class Remover(object):
64
65 def __init__(self, exists, remove):
66 self.exists = exists
67 self.remove = remove
68
69 def __call__(self, name):
70
71 if self.exists(name):
72 self.remove(name)
73
74 class ForceRename(object):
75
76 def __init__(self, renamer, remove):
77
78 self.renamer = renamer
79 self.remove = remove
80
81 def __call__(self, from_, to):
82
83 self.remove(to)
84 self.renamer(from_, to)
85
86 class VerboseRename(object):
87
88 def __init__(self, renamer, stream):
89
90 self.renamer = renamer
91 self.stream = stream
92
93 def __call__(self, from_, to):
94
95 self.stream.write(
96 "Renaming directory '%s' -> '%s'\n"
97 % (os.path.basename(from_), os.path.basename(to))
98 )
99
100 self.renamer(from_, to)
101
102
103 class DirectoryHandler(object):
104 "Encapsulates renaming a directory by removing its first character"
105
106 def __init__(self, name, root, renamer):
107
108 self.name = name
109 self.new_name = name[1:]
110 self.root = root + os.sep
111 self.renamer = renamer
112
113 def path(self):
114
115 return os.path.join(self.root, self.name)
116
117 def relative_path(self, directory, filename):
118
119 path = directory.replace(self.root, "", 1)
120 return os.path.join(path, filename)
121
122 def new_relative_path(self, directory, filename):
123
124 path = self.relative_path(directory, filename)
125 return path.replace(self.name, self.new_name, 1)
126
127 def process(self):
128
129 from_ = os.path.join(self.root, self.name)
130 to = os.path.join(self.root, self.new_name)
131 self.renamer(from_, to)
132
133
134 class HandlerFactory(object):
135
136 def create_file_handler(self, name, replacers, opener):
137
138 return FileHandler(name, replacers, opener)
139
140 def create_dir_handler(self, name, root, renamer):
141
142 return DirectoryHandler(name, root, renamer)
143
144
145 class OperationsFactory(object):
146
147 def create_force_rename(self, renamer, remover):
148
149 return ForceRename(renamer, remover)
150
151 def create_verbose_rename(self, renamer, stream):
152
153 return VerboseRename(renamer, stream)
154
155 def create_replacer(self, from_, to):
156
157 return Replacer(from_, to)
158
159 def create_remover(self, exists, remove):
160
161 return Remover(exists, remove)
162
163
164 class Layout(object):
165 """
166 Applies a set of operations which result in the layout
167 of a directory changing
168 """
169
170 def __init__(self, directory_handlers, file_handlers):
171
172 self.directory_handlers = directory_handlers
173 self.file_handlers = file_handlers
174
175 def process(self):
176
177 for handler in self.file_handlers:
178 handler.process()
179
180 for handler in self.directory_handlers:
181 handler.process()
182
183
184 class NullLayout(object):
185 """
186 Layout class that does nothing when asked to process
187 """
188 def process(self):
189 pass
190
191 class LayoutFactory(object):
192 "Creates a layout object"
193
194 def __init__(self, operations_factory, handler_factory, file_helper, dir_helper, verbose, stream, force):
195
196 self.operations_factory = operations_factory
197 self.handler_factory = handler_factory
198
199 self.file_helper = file_helper
200 self.dir_helper = dir_helper
201
202 self.verbose = verbose
203 self.output_stream = stream
204 self.force = force
205
206 def create_layout(self, path):
207
208 contents = self.dir_helper.list_dir(path)
209
210 renamer = self.file_helper.move
211
212 if self.force:
213 remove = self.operations_factory.create_remover(self.file_helper.exists, self.dir_helper.rmtree)
214 renamer = self.operations_factory.create_force_rename(renamer, remove)
215
216 if self.verbose:
217 renamer = self.operations_factory.create_verbose_rename(renamer, self.output_stream)
218
219 # Build list of directories to process
220 directories = [d for d in contents if self.is_underscore_dir(path, d)]
221 underscore_directories = [
222 self.handler_factory.create_dir_handler(d, path, renamer)
223 for d in directories
224 ]
225
226 if not underscore_directories:
227 if self.verbose:
228 self.output_stream.write(
229 "No top level directories starting with an underscore "
230 "were found in '%s'\n" % path
231 )
232 return NullLayout()
233
234 # Build list of files that are in those directories
235 replacers = []
236 for handler in underscore_directories:
237 for directory, dirs, files in self.dir_helper.walk(handler.path()):
238 for f in files:
239 replacers.append(
240 self.operations_factory.create_replacer(
241 handler.relative_path(directory, f),
242 handler.new_relative_path(directory, f)
243 )
244 )
245
246 # Build list of handlers to process all files
247 filelist = []
248 for root, dirs, files in self.dir_helper.walk(path):
249 for f in files:
250 if f.endswith(".html"):
251 filelist.append(
252 self.handler_factory.create_file_handler(
253 self.file_helper.path_join(root, f),
254 replacers,
255 self.file_helper.open_)
256 )
257 if f.endswith(".js"):
258 filelist.append(
259 self.handler_factory.create_file_handler(
260 self.file_helper.path_join(root, f),
261 [self.operations_factory.create_replacer("'_sources/'", "'sources/'")],
262 self.file_helper.open_
263 )
264 )
265
266 return Layout(underscore_directories, filelist)
267
268 def is_underscore_dir(self, path, directory):
269
270 return (self.dir_helper.is_dir(self.file_helper.path_join(path, directory))
271 and directory.startswith("_"))
272
273
274
275 def sphinx_extension(app, exception):
276 "Wrapped up as a Sphinx Extension"
277
278 if not app.builder.name in ("html", "dirhtml"):
279 return
280
281 if not app.config.sphinx_to_github:
282 if app.config.sphinx_to_github_verbose:
283 print "Sphinx-to-github: Disabled, doing nothing."
284 return
285
286 if exception:
287 if app.config.sphinx_to_github_verbose:
288 print "Sphinx-to-github: Exception raised in main build, doing nothing."
289 return
290
291 dir_helper = DirHelper(
292 os.path.isdir,
293 os.listdir,
294 os.walk,
295 shutil.rmtree
296 )
297
298 file_helper = FileSystemHelper(
299 open,
300 os.path.join,
301 shutil.move,
302 os.path.exists
303 )
304
305 operations_factory = OperationsFactory()
306 handler_factory = HandlerFactory()
307
308 layout_factory = LayoutFactory(
309 operations_factory,
310 handler_factory,
311 file_helper,
312 dir_helper,
313 app.config.sphinx_to_github_verbose,
314 sys.stdout,
315 force=True
316 )
317
318 layout = layout_factory.create_layout(app.outdir)
319 layout.process()
320
321
322 def setup(app):
323 "Setup function for Sphinx Extension"
324
325 app.add_config_value("sphinx_to_github", True, '')
326 app.add_config_value("sphinx_to_github_verbose", True, '')
327
328 app.connect("build-finished", sphinx_extension)
329
330
331 def main(args):
332
333 usage = "usage: %prog [options] <html directory>"
334 parser = OptionParser(usage=usage)
335 parser.add_option("-v","--verbose", action="store_true",
336 dest="verbose", default=False, help="Provides verbose output")
337 opts, args = parser.parse_args(args)
338
339 try:
340 path = args[0]
341 except IndexError:
342 sys.stderr.write(
343 "Error - Expecting path to html directory:"
344 "sphinx-to-github <path>\n"
345 )
346 return
347
348 dir_helper = DirHelper(
349 os.path.isdir,
350 os.listdir,
351 os.walk,
352 shutil.rmtree
353 )
354
355 file_helper = FileSystemHelper(
356 open,
357 os.path.join,
358 shutil.move,
359 os.path.exists
360 )
361
362 operations_factory = OperationsFactory()
363 handler_factory = HandlerFactory()
364
365 layout_factory = LayoutFactory(
366 operations_factory,
367 handler_factory,
368 file_helper,
369 dir_helper,
370 opts.verbose,
371 sys.stdout,
372 force=False
373 )
374
375 layout = layout_factory.create_layout(path)
376 layout.process()
377
378
379
380 if __name__ == "__main__":
381 main(sys.argv[1:])
@@ -0,0 +1,31 b''
1 #!/usr/bin/env sh
2 # pick repo for gh-pages branch
3 repo=origin
4
5 if [ ! -d gh-pages ]; then
6 echo "setting up gh-pages subdir"
7 mkdir gh-pages || exit -1
8 cp -r ../.git gh-pages/ || exit -1
9 cd gh-pages || exit -1
10 init=0
11 git checkout $repo/gh-pages || init=1
12 if [ "$init" != "0" ]; then
13 echo "initializing gh-pages repo"
14 git symbolic-ref HEAD refs/heads/gh-pages || exit -1
15 rm .git/index || exit -1
16 git clean -fdx || exit -1
17 touch index.html
18 git add .
19 git commit -a -m 'init gh-pages' || exit -1
20 git push origin HEAD:gh-pages
21 fi
22 cd ..
23 fi
24 echo "updating local gh-pages with html build"
25 rsync -va build/html/ gh-pages/ --delete --exclude .git || exit -1
26 cd gh-pages
27 git add .
28 git commit -a || exit -1
29 echo "pushing to remote gh-pages"
30 # pwd
31 git push $repo HEAD:gh-pages
@@ -3,6 +3,7 b' build'
3 docs/dist
3 docs/dist
4 docs/build/*
4 docs/build/*
5 docs/source/api/generated
5 docs/source/api/generated
6 docs/gh-pages
6 *.py[co]
7 *.py[co]
7 build
8 build
8 *.egg-info
9 *.egg-info
@@ -102,3 +102,6 b' gitwash-update:'
102
102
103 nightly: dist
103 nightly: dist
104 rsync -avH --delete dist/ ipython:www/doc/nightly
104 rsync -avH --delete dist/ ipython:www/doc/nightly
105
106 gh-pages: html
107 sh update_ghpages.sh
@@ -20,7 +20,7 b' import sys, os'
20 # If your extensions are in another directory, add it here. If the directory
20 # If your extensions are in another directory, add it here. If the directory
21 # is relative to the documentation root, use os.path.abspath to make it
21 # is relative to the documentation root, use os.path.abspath to make it
22 # absolute, like shown here.
22 # absolute, like shown here.
23 sys.path.append(os.path.abspath('../sphinxext'))
23 sys.path.insert(0, os.path.abspath('../sphinxext'))
24
24
25 # Import support for ipython console session syntax highlighting (lives
25 # Import support for ipython console session syntax highlighting (lives
26 # in the sphinxext directory defined above)
26 # in the sphinxext directory defined above)
@@ -44,6 +44,7 b' extensions = ['
44 'inheritance_diagram',
44 'inheritance_diagram',
45 'ipython_console_highlighting',
45 'ipython_console_highlighting',
46 'numpydoc', # to preprocess docstrings
46 'numpydoc', # to preprocess docstrings
47 'sphinxtogithub',
47 ]
48 ]
48
49
49 # Add any paths that contain templates here, relative to this directory.
50 # Add any paths that contain templates here, relative to this directory.
General Comments 0
You need to be logged in to leave comments. Login now