1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
|
# -*- coding: utf-8 -*-
"""
sphinx.cmdline
~~~~~~~~~~~~~~
sphinx-build command-line handling.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
import sys
import optparse
import traceback
from os import path
from six import text_type, binary_type
from docutils.utils import SystemMessage
from sphinx import __display_version__
from sphinx.errors import SphinxError
from sphinx.application import Sphinx
from sphinx.util import Tee, format_exception_cut_frames, save_traceback
from sphinx.util.console import red, nocolor, color_terminal # type: ignore
from sphinx.util.docutils import docutils_namespace
from sphinx.util.osutil import abspath, fs_encoding
from sphinx.util.pycompat import terminal_safe
if False:
# For type annotation
from typing import Any, IO, List, Union # NOQA
USAGE = """\
Sphinx v%s
Usage: %%prog [options] sourcedir outdir [filenames...]
Filename arguments:
without -a and without filenames, write new and changed files.
with -a, write all files.
with filenames, write these.
""" % __display_version__
EPILOG = """\
For more information, visit <http://sphinx-doc.org/>.
"""
class MyFormatter(optparse.IndentedHelpFormatter):
def format_usage(self, usage):
# type: (Any) -> Any
return usage
def format_help(self, formatter):
# type: (Any) -> unicode
result = [] # type: List[unicode]
if self.description: # type: ignore
result.append(self.format_description(formatter))
if self.option_list: # type: ignore
result.append(self.format_option_help(formatter)) # type: ignore
return "\n".join(result)
def handle_exception(app, opts, exception, stderr=sys.stderr):
# type: (Sphinx, Any, Union[Exception, KeyboardInterrupt], IO) -> None
if opts.pdb:
import pdb
print(red('Exception occurred while building, starting debugger:'),
file=stderr)
traceback.print_exc()
pdb.post_mortem(sys.exc_info()[2])
else:
print(file=stderr)
if opts.verbosity or opts.traceback:
traceback.print_exc(None, stderr)
print(file=stderr)
if isinstance(exception, KeyboardInterrupt):
print('interrupted!', file=stderr)
elif isinstance(exception, SystemMessage):
print(red('reST markup error:'), file=stderr)
print(terminal_safe(exception.args[0]), file=stderr)
elif isinstance(exception, SphinxError):
print(red('%s:' % exception.category), file=stderr)
print(terminal_safe(text_type(exception)), file=stderr)
elif isinstance(exception, UnicodeError):
print(red('Encoding error:'), file=stderr)
print(terminal_safe(text_type(exception)), file=stderr)
tbpath = save_traceback(app)
print(red('The full traceback has been saved in %s, if you want '
'to report the issue to the developers.' % tbpath),
file=stderr)
elif isinstance(exception, RuntimeError) and 'recursion depth' in str(exception):
print(red('Recursion error:'), file=stderr)
print(terminal_safe(text_type(exception)), file=stderr)
print(file=stderr)
print('This can happen with very large or deeply nested source '
'files. You can carefully increase the default Python '
'recursion limit of 1000 in conf.py with e.g.:', file=stderr)
print(' import sys; sys.setrecursionlimit(1500)', file=stderr)
else:
print(red('Exception occurred:'), file=stderr)
print(format_exception_cut_frames().rstrip(), file=stderr)
tbpath = save_traceback(app)
print(red('The full traceback has been saved in %s, if you '
'want to report the issue to the developers.' % tbpath),
file=stderr)
print('Please also report this if it was a user error, so '
'that a better error message can be provided next time.',
file=stderr)
print('A bug report can be filed in the tracker at '
'<https://github.com/sphinx-doc/sphinx/issues>. Thanks!',
file=stderr)
def main(argv):
# type: (List[unicode]) -> int
parser = optparse.OptionParser(USAGE, epilog=EPILOG, formatter=MyFormatter())
parser.add_option('--version', action='store_true', dest='version',
help='show version information and exit')
group = parser.add_option_group('General options')
group.add_option('-b', metavar='BUILDER', dest='builder', default='html',
help='builder to use; default is html')
group.add_option('-a', action='store_true', dest='force_all',
help='write all files; default is to only write new and '
'changed files')
group.add_option('-E', action='store_true', dest='freshenv',
help='don\'t use a saved environment, always read '
'all files')
group.add_option('-d', metavar='PATH', default=None, dest='doctreedir',
help='path for the cached environment and doctree files '
'(default: outdir/.doctrees)')
group.add_option('-j', metavar='N', default=1, type='int', dest='jobs',
help='build in parallel with N processes where possible')
# this option never gets through to this point (it is intercepted earlier)
# group.add_option('-M', metavar='BUILDER', dest='make_mode',
# help='"make" mode -- as used by Makefile, like '
# '"sphinx-build -M html"')
group = parser.add_option_group('Build configuration options')
group.add_option('-c', metavar='PATH', dest='confdir',
help='path where configuration file (conf.py) is located '
'(default: same as sourcedir)')
group.add_option('-C', action='store_true', dest='noconfig',
help='use no config file at all, only -D options')
group.add_option('-D', metavar='setting=value', action='append',
dest='define', default=[],
help='override a setting in configuration file')
group.add_option('-A', metavar='name=value', action='append',
dest='htmldefine', default=[],
help='pass a value into HTML templates')
group.add_option('-t', metavar='TAG', action='append',
dest='tags', default=[],
help='define tag: include "only" blocks with TAG')
group.add_option('-n', action='store_true', dest='nitpicky',
help='nit-picky mode, warn about all missing references')
group = parser.add_option_group('Console output options')
group.add_option('-v', action='count', dest='verbosity', default=0,
help='increase verbosity (can be repeated)')
group.add_option('-q', action='store_true', dest='quiet',
help='no output on stdout, just warnings on stderr')
group.add_option('-Q', action='store_true', dest='really_quiet',
help='no output at all, not even warnings')
group.add_option('--color', dest='color',
action='store_const', const='yes', default='auto',
help='Do emit colored output (default: auto-detect)')
group.add_option('-N', '--no-color', dest='color',
action='store_const', const='no',
help='Do not emit colored output (default: auot-detect)')
group.add_option('-w', metavar='FILE', dest='warnfile',
help='write warnings (and errors) to given file')
group.add_option('-W', action='store_true', dest='warningiserror',
help='turn warnings into errors')
group.add_option('-T', action='store_true', dest='traceback',
help='show full traceback on exception')
group.add_option('-P', action='store_true', dest='pdb',
help='run Pdb on exception')
# parse options
try:
opts, args = parser.parse_args(list(argv[1:]))
except SystemExit as err:
return err.code
# handle basic options
if opts.version:
print('Sphinx (sphinx-build) %s' % __display_version__)
return 0
# get paths (first and second positional argument)
try:
srcdir = abspath(args[0])
confdir = abspath(opts.confdir or srcdir)
if opts.noconfig:
confdir = None
if not path.isdir(srcdir):
print('Error: Cannot find source directory `%s\'.' % srcdir,
file=sys.stderr)
return 1
if not opts.noconfig and not path.isfile(path.join(confdir, 'conf.py')):
print('Error: Config directory doesn\'t contain a conf.py file.',
file=sys.stderr)
return 1
outdir = abspath(args[1])
if srcdir == outdir:
print('Error: source directory and destination directory are same.',
file=sys.stderr)
return 1
except IndexError:
parser.print_help()
return 1
except UnicodeError:
print(
'Error: Multibyte filename not supported on this filesystem '
'encoding (%r).' % fs_encoding, file=sys.stderr)
return 1
# handle remaining filename arguments
filenames = args[2:]
errored = False
for filename in filenames:
if not path.isfile(filename):
print('Error: Cannot find file %r.' % filename, file=sys.stderr)
errored = True
if errored:
return 1
# likely encoding used for command-line arguments
try:
locale = __import__('locale') # due to submodule of the same name
likely_encoding = locale.getpreferredencoding()
except Exception:
likely_encoding = None
if opts.force_all and filenames:
print('Error: Cannot combine -a option and filenames.', file=sys.stderr)
return 1
if opts.color == 'no' or (opts.color == 'auto' and not color_terminal()):
nocolor()
doctreedir = abspath(opts.doctreedir or path.join(outdir, '.doctrees'))
status = sys.stdout
warning = sys.stderr
error = sys.stderr
if opts.quiet:
status = None
if opts.really_quiet:
status = warning = None
if warning and opts.warnfile:
try:
warnfp = open(opts.warnfile, 'w')
except Exception as exc:
print('Error: Cannot open warning file %r: %s' %
(opts.warnfile, exc), file=sys.stderr)
sys.exit(1)
warning = Tee(warning, warnfp) # type: ignore
error = warning
confoverrides = {}
for val in opts.define:
try:
key, val = val.split('=', 1)
except ValueError:
print('Error: -D option argument must be in the form name=value.',
file=sys.stderr)
return 1
if likely_encoding and isinstance(val, binary_type):
try:
val = val.decode(likely_encoding)
except UnicodeError:
pass
confoverrides[key] = val
for val in opts.htmldefine:
try:
key, val = val.split('=')
except ValueError:
print('Error: -A option argument must be in the form name=value.',
file=sys.stderr)
return 1
try:
val = int(val)
except ValueError:
if likely_encoding and isinstance(val, binary_type):
try:
val = val.decode(likely_encoding)
except UnicodeError:
pass
confoverrides['html_context.%s' % key] = val
if opts.nitpicky:
confoverrides['nitpicky'] = True
app = None
try:
with docutils_namespace():
app = Sphinx(srcdir, confdir, outdir, doctreedir, opts.builder,
confoverrides, status, warning, opts.freshenv,
opts.warningiserror, opts.tags, opts.verbosity, opts.jobs)
app.build(opts.force_all, filenames)
return app.statuscode
except (Exception, KeyboardInterrupt) as exc:
handle_exception(app, opts, exc, error)
return 1
|