summaryrefslogtreecommitdiff
path: root/sphinx/cmdline.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/cmdline.py')
-rw-r--r--sphinx/cmdline.py398
1 files changed, 195 insertions, 203 deletions
diff --git a/sphinx/cmdline.py b/sphinx/cmdline.py
index ec24d902..6486e64b 100644
--- a/sphinx/cmdline.py
+++ b/sphinx/cmdline.py
@@ -8,13 +8,15 @@
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+from __future__ import print_function
import os
import sys
-import getopt
+import optparse
import traceback
from os import path
+from six import text_type, binary_type
from docutils.utils import SystemMessage
from sphinx import __version__
@@ -23,106 +25,138 @@ 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
from sphinx.util.osutil import abspath, fs_encoding
-from sphinx.util.pycompat import terminal_safe, bytes
+from sphinx.util.pycompat import terminal_safe
def usage(argv, msg=None):
if msg:
- print >>sys.stderr, msg
- print >>sys.stderr
- print >>sys.stderr, """\
+ print(msg, file=sys.stderr)
+ print(file=sys.stderr)
+
+USAGE = """\
Sphinx v%s
-Usage: %s [options] sourcedir outdir [filenames...]
-
-General options
-^^^^^^^^^^^^^^^
--b <builder> builder to use; default is html
--a write all files; default is to only write new and changed files
--E don't use a saved environment, always read all files
--d <path> path for the cached environment and doctree files
- (default: outdir/.doctrees)
--j <N> build in parallel with N processes where possible
--M <builder> "make" mode -- used by Makefile, like "sphinx-build -M html"
-
-Build configuration options
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
--c <path> path where configuration file (conf.py) is located
- (default: same as sourcedir)
--C use no config file at all, only -D options
--D <setting=value> override a setting in configuration file
--t <tag> define tag: include "only" blocks with <tag>
--A <name=value> pass a value into the templates, for HTML builder
--n nit-picky mode, warn about all missing references
-
-Console output options
-^^^^^^^^^^^^^^^^^^^^^^
--v increase verbosity (can be repeated)
--q no output on stdout, just warnings on stderr
--Q no output at all, not even warnings
--w <file> write warnings (and errors) to given file
--W turn warnings into errors
--T show full traceback on exception
--N do not emit colored output
--P run Pdb on exception
-
-Filename arguments
-^^^^^^^^^^^^^^^^^^
-* without -a and without filenames, write new and changed files.
-* with -a, write all files.
-* with filenames, write these.
-
-Standard options
-^^^^^^^^^^^^^^^^
--h, --help show this help and exit
---version show version information and exit
-""" % (__version__, argv[0])
+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.
+""" % __version__
+
+EPILOG = """\
+For more information, visit <http://sphinx-doc.org/>.
+"""
+
+
+class MyFormatter(optparse.IndentedHelpFormatter):
+ def format_usage(self, usage):
+ return usage
+
+ def format_help(self, formatter):
+ result = []
+ if self.description:
+ result.append(self.format_description(formatter))
+ if self.option_list:
+ result.append(self.format_option_help(formatter))
+ return "\n".join(result)
def main(argv):
if not color_terminal():
- # Windows' poor cmd box doesn't understand ANSI sequences
nocolor()
+ 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('-N', action='store_true', dest='nocolor',
+ help='do not emit colored output')
+ 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 = getopt.getopt(argv[1:], 'ab:t:d:c:CD:A:nNEqQWw:PThvj:',
- ['help', 'version'])
- except getopt.error, err:
- usage(argv, 'Error: %s' % err)
- return 1
+ opts, args = parser.parse_args(argv[1:])
+ except SystemExit as err:
+ return err.code
# handle basic options
- allopts = set(opt[0] for opt in opts)
- # help and version options
- if '-h' in allopts or '--help' in allopts:
- usage(argv)
- print >>sys.stderr
- print >>sys.stderr, 'For more information, see <http://sphinx-doc.org/>.'
- return 0
- if '--version' in allopts:
- print 'Sphinx (sphinx-build) %s' % __version__
+ if opts.version:
+ print('Sphinx (sphinx-build) %s' % __version__)
return 0
# get paths (first and second positional argument)
try:
- srcdir = confdir = abspath(args[0])
+ srcdir = abspath(args[0])
+ confdir = abspath(opts.confdir or srcdir)
+ if opts.noconfig:
+ confdir = None
if not path.isdir(srcdir):
- print >>sys.stderr, 'Error: Cannot find source directory `%s\'.' % (
- srcdir,)
+ print('Error: Cannot find source directory `%s\'.' % srcdir,
+ file=sys.stderr)
return 1
- if not path.isfile(path.join(srcdir, 'conf.py')) and \
- '-c' not in allopts and '-C' not in allopts:
- print >>sys.stderr, ('Error: Source directory doesn\'t '
- 'contain a conf.py file.')
+ 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])
except IndexError:
usage(argv, 'Error: Insufficient arguments.')
return 1
except UnicodeError:
- print >>sys.stderr, (
+ print(
'Error: Multibyte filename not supported on this filesystem '
- 'encoding (%r).' % fs_encoding)
+ 'encoding (%r).' % fs_encoding, file=sys.stderr)
return 1
# handle remaining filename arguments
@@ -130,7 +164,7 @@ def main(argv):
err = 0
for filename in filenames:
if not path.isfile(filename):
- print >>sys.stderr, 'Error: Cannot find file %r.' % filename
+ print('Error: Cannot find file %r.' % filename, file=sys.stderr)
err = 1
if err:
return 1
@@ -142,155 +176,113 @@ def main(argv):
except Exception:
likely_encoding = None
- buildername = None
- force_all = freshenv = warningiserror = use_pdb = False
- show_traceback = False
- verbosity = 0
- parallel = 0
+ if opts.force_all and filenames:
+ print('Error: Cannot combine -a option and filenames.', file=sys.stderr)
+ return 1
+
+ if opts.nocolor:
+ nocolor()
+
+ doctreedir = abspath(opts.doctreedir or path.join(outdir, '.doctrees'))
+
status = sys.stdout
warning = sys.stderr
error = sys.stderr
- warnfile = None
- confoverrides = {}
- tags = []
- doctreedir = path.join(outdir, '.doctrees')
- for opt, val in opts:
- if opt == '-b':
- buildername = val
- elif opt == '-a':
- if filenames:
- usage(argv, 'Error: Cannot combine -a option and filenames.')
- return 1
- force_all = True
- elif opt == '-t':
- tags.append(val)
- elif opt == '-d':
- doctreedir = abspath(val)
- elif opt == '-c':
- confdir = abspath(val)
- if not path.isfile(path.join(confdir, 'conf.py')):
- print >>sys.stderr, ('Error: Configuration directory '
- 'doesn\'t contain conf.py file.')
- return 1
- elif opt == '-C':
- confdir = None
- elif opt == '-D':
- try:
- key, val = val.split('=')
- except ValueError:
- print >>sys.stderr, ('Error: -D option argument must be '
- 'in the form name=value.')
- return 1
- try:
- val = int(val)
- except ValueError:
- if likely_encoding and isinstance(val, bytes):
- try:
- val = val.decode(likely_encoding)
- except UnicodeError:
- pass
- confoverrides[key] = val
- elif opt == '-A':
- try:
- key, val = val.split('=')
- except ValueError:
- print >>sys.stderr, ('Error: -A option argument must be '
- 'in the form name=value.')
- return 1
- try:
- val = int(val)
- except ValueError:
- if likely_encoding and isinstance(val, bytes):
- try:
- val = val.decode(likely_encoding)
- except UnicodeError:
- pass
- confoverrides['html_context.%s' % key] = val
- elif opt == '-n':
- confoverrides['nitpicky'] = True
- elif opt == '-N':
- nocolor()
- elif opt == '-E':
- freshenv = True
- elif opt == '-q':
- status = None
- elif opt == '-Q':
- status = None
- warning = None
- elif opt == '-W':
- warningiserror = True
- elif opt == '-w':
- warnfile = val
- elif opt == '-P':
- use_pdb = True
- elif opt == '-T':
- show_traceback = True
- elif opt == '-v':
- verbosity += 1
- show_traceback = True
- elif opt == '-j':
- try:
- parallel = int(val)
- except ValueError:
- print >>sys.stderr, ('Error: -j option argument must be an '
- 'integer.')
- return 1
-
- if warning and warnfile:
- warnfp = open(warnfile, 'w')
+
+ 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)
error = warning
- if not path.isdir(outdir):
- if status:
- print >>status, 'Making output directory...'
- os.makedirs(outdir)
+ confoverrides = {}
+ for val in opts.define:
+ try:
+ key, val = val.split('=')
+ 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:
- app = Sphinx(srcdir, confdir, outdir, doctreedir, buildername,
- confoverrides, status, warning, freshenv,
- warningiserror, tags, verbosity, parallel)
- app.build(force_all, filenames)
+ 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), err:
- if use_pdb:
+ except (Exception, KeyboardInterrupt) as err:
+ if opts.pdb:
import pdb
- print >>error, red('Exception occurred while building, '
- 'starting debugger:')
+ print(red('Exception occurred while building, starting debugger:'),
+ file=error)
traceback.print_exc()
pdb.post_mortem(sys.exc_info()[2])
else:
- print >>error
- if show_traceback:
+ print(file=error)
+ if opts.verbosity or opts.traceback:
traceback.print_exc(None, error)
- print >>error
+ print(file=error)
if isinstance(err, KeyboardInterrupt):
- print >>error, 'interrupted!'
+ print('interrupted!', file=error)
elif isinstance(err, SystemMessage):
- print >>error, red('reST markup error:')
- print >>error, terminal_safe(err.args[0])
+ print(red('reST markup error:'), file=error)
+ print(terminal_safe(err.args[0]), file=error)
elif isinstance(err, SphinxError):
- print >>error, red('%s:' % err.category)
- print >>error, terminal_safe(unicode(err))
+ print(red('%s:' % err.category), file=error)
+ print(terminal_safe(text_type(err)), file=error)
elif isinstance(err, UnicodeError):
- print >>error, red('Encoding error:')
- print >>error, terminal_safe(unicode(err))
+ print(red('Encoding error:'), file=error)
+ print(terminal_safe(text_type(err)), file=error)
tbpath = save_traceback(app)
- print >>error, red('The full traceback has been saved '
- 'in %s, if you want to report the '
- 'issue to the developers.' % tbpath)
+ print(red('The full traceback has been saved in %s, if you want '
+ 'to report the issue to the developers.' % tbpath),
+ file=error)
else:
- print >>error, red('Exception occurred:')
- print >>error, format_exception_cut_frames().rstrip()
+ print(red('Exception occurred:'), file=error)
+ print(format_exception_cut_frames().rstrip(), file=error)
tbpath = save_traceback(app)
- print >>error, red('The full traceback has been saved '
- 'in %s, if you want to report the '
- 'issue to the developers.' % tbpath)
- print >>error, ('Please also report this if it was a user '
- 'error, so that a better error message '
- 'can be provided next time.')
- print >>error, (
- 'A bug report can be filed in the tracker at '
- '<https://bitbucket.org/birkenfeld/sphinx/issues/>. Thanks!')
+ print(red('The full traceback has been saved in %s, if you '
+ 'want to report the issue to the developers.' % tbpath),
+ file=error)
+ print('Please also report this if it was a user error, so '
+ 'that a better error message can be provided next time.',
+ file=error)
+ print('A bug report can be filed in the tracker at '
+ '<https://bitbucket.org/birkenfeld/sphinx/issues/>. Thanks!',
+ file=error)
return 1