55import argparse
66import os
77import shlex
8+ import sys
89
910import colorama
1011from livereload import Server
1112
13+ # This isn't public API, but there aren't many better options
14+ from sphinx .cmd .build import get_parser as sphinx_get_parser
15+
1216from sphinx_autobuild import __version__
13- from sphinx_autobuild .build import SPHINX_BUILD_OPTIONS , Builder
17+ from sphinx_autobuild .build import Builder
1418from sphinx_autobuild .ignore import Ignore
1519from sphinx_autobuild .utils import find_free_port
1620
1721
18- def _get_build_args (args ):
19- build_args = []
20- arg_dict = vars (args ) # Convert the args namespace to a dictionary
21- for opt , meta in SPHINX_BUILD_OPTIONS :
22- arg = opt .removeprefix ("-" ) # remove leading '-'
23- val = arg_dict .get (arg )
24- if val is None :
25- continue
26- if meta is None :
27- build_args .extend ([opt ] * val )
28- else :
29- for v in val :
30- build_args .extend ([opt , v ])
22+ def _parse_args (argv ):
23+ # Parse once with the Sphinx parser to emit errors
24+ # and capture the ``-d`` and ``-w`` options.
25+ # NOTE:
26+ # The Sphinx parser is not considered to be public API,
27+ # but as this is a first-party project, we can cheat a little bit.
28+ sphinx_args = _get_sphinx_build_parser ().parse_args (argv .copy ())
29+ print (f"{ sphinx_args .filenames = } " )
3130
32- build_args .extend ([os .path .realpath (args .sourcedir ), os .path .realpath (args .outdir )])
33- build_args .extend (args .filenames )
31+ # Parse a second time with just our parser
32+ parser = _get_parser ()
33+ args , build_args = parser .parse_known_args (argv .copy ())
3434
35- return build_args
35+ # Copy needed settings
36+ args .sourcedir = sphinx_args .sourcedir
37+ args .outdir = sphinx_args .outputdir
38+ args .doctree_dir = sphinx_args .doctreedir
39+ args .warnings_file = sphinx_args .warnfile
3640
41+ return args , build_args
3742
38- def get_parser ():
39- """Get the application's argument parser.
4043
41- Note: this also handles SPHINX_BUILD_OPTIONS, which later get forwarded to
42- sphinx-build as-is.
43- """
44+ def _get_sphinx_build_parser ():
45+ # NOTE:
46+ # sphinx.cmd.build.get_parser is not considered to be public API,
47+ # but as this is a first-party project, we can cheat a little bit.
48+ sphinx_build_parser = sphinx_get_parser ()
49+ sphinx_build_parser .description = None
50+ sphinx_build_parser .epilog = None
51+ sphinx_build_parser .prog = "sphinx-autobuild"
52+ for action in sphinx_build_parser ._actions :
53+ if hasattr (action , "version" ):
54+ # Fix the version
55+ action .version = f"%(prog)s { __version__ } "
56+ break
57+ _add_autobuild_arguments (sphinx_build_parser )
4458
45- class RawTextArgumentDefaultsHelpFormatter (
46- argparse .ArgumentDefaultsHelpFormatter , argparse .RawTextHelpFormatter
47- ):
48- pass
59+ return sphinx_build_parser
4960
50- parser = argparse .ArgumentParser (
51- formatter_class = RawTextArgumentDefaultsHelpFormatter
52- )
61+
62+ def _get_parser ():
63+ """Get the application's argument parser."""
64+
65+ parser = argparse .ArgumentParser (allow_abbrev = False )
5366 parser .add_argument (
67+ "--version" , action = "version" , version = f"sphinx-autobuild { __version__ } "
68+ )
69+ _add_autobuild_arguments (parser )
70+
71+ return parser
72+
73+
74+ def _add_autobuild_arguments (parser ):
75+ group = parser .add_argument_group ('autobuild options' )
76+ group .add_argument (
5477 "--port" ,
5578 type = int ,
5679 default = 8000 ,
5780 help = "port to serve documentation on. 0 means find and use a free port" ,
5881 )
59- parser .add_argument (
82+ group .add_argument (
6083 "--host" ,
6184 type = str ,
6285 default = "127.0.0.1" ,
6386 help = "hostname to serve documentation on" ,
6487 )
65- parser .add_argument (
88+ group .add_argument (
6689 "--re-ignore" ,
6790 action = "append" ,
6891 default = [],
6992 help = "regular expression for files to ignore, when watching for changes" ,
7093 )
71- parser .add_argument (
94+ group .add_argument (
7295 "--ignore" ,
7396 action = "append" ,
7497 default = [],
7598 help = "glob expression for files to ignore, when watching for changes" ,
7699 )
77- parser .add_argument (
100+ group .add_argument (
78101 "--no-initial" ,
79102 dest = "no_initial_build" ,
80103 action = "store_true" ,
81104 default = False ,
82105 help = "skip the initial build" ,
83106 )
84- parser .add_argument (
107+ group .add_argument (
85108 "--open-browser" ,
86109 dest = "openbrowser" ,
87110 action = "store_true" ,
88111 default = False ,
89112 help = "open the browser after building documentation" ,
90113 )
91- parser .add_argument (
114+ group .add_argument (
92115 "--delay" ,
93116 dest = "delay" ,
94117 type = int ,
95118 default = 5 ,
96119 help = "how long to wait before opening the browser" ,
97120 )
98- parser .add_argument (
121+ group .add_argument (
99122 "--watch" ,
100123 action = "append" ,
101124 metavar = "DIR" ,
102125 default = [],
103126 help = "additional directories to watch" ,
104127 dest = "additional_watched_dirs" ,
105128 )
106- parser .add_argument (
129+ group .add_argument (
107130 "--pre-build" ,
108131 action = "append" ,
109132 metavar = "COMMAND" ,
110133 default = [],
111134 help = "additional command(s) to run prior to building the documentation" ,
112135 )
113- parser .add_argument (
114- "--version" , action = "version" , version = f"sphinx-autobuild { __version__ } "
115- )
116-
117- sphinx_arguments = ", " .join (
118- arg if meta is None else f"{ arg } ={ meta } "
119- for arg , meta in SPHINX_BUILD_OPTIONS
120- )
121- sphinx_parser = parser .add_argument_group (
122- "sphinx's arguments" ,
123- (
124- "The following arguments are forwarded as-is to Sphinx. Please look at "
125- f"`sphinx --help` for more information.\n { sphinx_arguments } "
126- ),
127- )
128-
129- for arg , meta in SPHINX_BUILD_OPTIONS :
130- if meta is None :
131- sphinx_parser .add_argument (
132- arg , action = "count" , help = argparse .SUPPRESS
133- )
134- else :
135- sphinx_parser .add_argument (
136- arg ,
137- action = "append" ,
138- help = argparse .SUPPRESS ,
139- metavar = meta ,
140- )
141-
142- parser .add_argument ("sourcedir" , help = "source directory" )
143- parser .add_argument ("outdir" , help = "output directory for built documentation" )
144- parser .add_argument (
145- "filenames" , nargs = "*" , help = "specific files to rebuild on each run"
146- )
147- return parser
136+ return group
148137
149138
150139def _get_ignore_handler (ignore , regex_based , out_dir , doctree_dir , warnings_file ):
@@ -162,8 +151,7 @@ def main():
162151 """Actual application logic."""
163152 colorama .init ()
164153
165- parser = get_parser ()
166- args = parser .parse_args ()
154+ args , build_args = _parse_args (sys .argv [1 :])
167155
168156 srcdir = os .path .realpath (args .sourcedir )
169157 outdir = os .path .realpath (args .outdir )
@@ -173,7 +161,6 @@ def main():
173161 port_num = args .port or find_free_port ()
174162 server = Server ()
175163
176- build_args = _get_build_args (args )
177164 pre_build_commands = list (map (shlex .split , args .pre_build ))
178165 builder = Builder (
179166 server .watcher ,
@@ -184,7 +171,7 @@ def main():
184171 )
185172
186173 ignore_handler = _get_ignore_handler (args .ignore , args .re_ignore , outdir ,
187- args .w , args .d )
174+ args .warnings_file , args .doctree_dir )
188175 server .watch (srcdir , builder , ignore = ignore_handler )
189176 for dirpath in args .additional_watched_dirs :
190177 dirpath = os .path .realpath (dirpath )
0 commit comments