Skip to content

Commit

Permalink
adds --exec_script flag to run pre-build command
Browse files Browse the repository at this point in the history
Now sphinx-autobuild has the ability to run a script before building.

If the script errors out, sphinx-autobuild will print out a warning, but
otherwise continue.

My use case for this feature:
In my previous workflow of manually rebuilding, I had a script that
would automatically add in an `automodule` directive in the test
documentation. If someone changes what a test script references, I want
the `automodule` directives to also update.
  • Loading branch information
Haris Godil committed Sep 17, 2018
1 parent e0f40b6 commit 3b01fdd
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 9 deletions.
20 changes: 18 additions & 2 deletions sphinx_autobuild/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

from livereload import Server

from subprocess import call

from watchdog.observers import Observer
from watchdog.observers.polling import PollingObserver
from watchdog.events import FileSystemEventHandler
Expand Down Expand Up @@ -129,12 +131,13 @@ class SphinxBuilder(object):
"""
Helper class to run sphinx-build command.
"""
def __init__(self, outdir, args, ignored=None, regex_ignored=None):
def __init__(self, outdir, args, ignored=None, regex_ignored=None, exec_script=None):
self._outdir = outdir
self._args = args
self._ignored = ignored or []
self._ignored.append(outdir)
self._regex_ignored = [re.compile(r) for r in regex_ignored or []]
self._exec_script = exec_script

def is_ignored(self, src_path):
path = self.get_relative_path(src_path)
Expand All @@ -159,10 +162,22 @@ def __call__(self, watcher, src_path):
self.build(path)

def build(self, path=None):
if self._exec_script:
self.execute_script()

self._build(path)

def execute_script(self):
code = call(self._exec_script, shell=True)
if code != 0:
sys.stdout.write("Warning: '{}' returned with status code {}\n".format(self._exec_script, code))

def _build(self, path):
if path:
pre = '+--------- {0} changed '.format(path)
else:
pre = '+--------- manually triggered build '

sys.stdout.write('\n')
sys.stdout.write(pre)
sys.stdout.write('-' * (81 - len(pre)))
Expand Down Expand Up @@ -230,6 +245,7 @@ def get_parser():
parser.add_argument('-H', '--host', type=str, default='127.0.0.1')
parser.add_argument('-r', '--re-ignore', action='append', default=[])
parser.add_argument('-i', '--ignore', action='append', default=[])
parser.add_argument('-x', '--exec_script', help='specify prebuild script')
parser.add_argument('--poll', dest='use_polling',
action='store_true', default=False)
parser.add_argument('--no-initial', dest='initial_build',
Expand Down Expand Up @@ -297,7 +313,7 @@ def main():
else:
portn = port_for.select_random()

builder = SphinxBuilder(outdir, build_args, ignored, re_ignore)
builder = SphinxBuilder(outdir, build_args, ignored, re_ignore, args.exec_script)
server = Server(
watcher=LivereloadWatchdogWatcher(use_polling=args.use_polling),
)
Expand Down
24 changes: 17 additions & 7 deletions sphinx_autobuild/test/test_autobuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,27 @@ def patched_args(sys_args, monkeypatch):
monkeypatch.setattr('sys.argv', sys_args)


@pytest.mark.parametrize('sys_args', (
['sphinx-autobuild', '/source', '/output'],
@pytest.mark.parametrize('sys_args, with_script', (
(['sphinx-autobuild', '/source', '/output'], False),
(['sphinx-autobuild', '/source', '/output',
'--exec_script', '/script'], True),
))
@mock.patch.object(observers.Observer, 'schedule')
@mock.patch.object(livereload.Server, 'serve')
@mock.patch('sphinx_autobuild.SphinxBuilder.build')
@mock.patch('sphinx_autobuild.SphinxBuilder.execute_script')
@mock.patch('sphinx_autobuild.SphinxBuilder._build')
@mock.patch('os.makedirs')
def test_autobuild(mock_makedirs, mock_builder, mock_serve, mock_schedule):
def test_autobuild(mock_makedirs, mock_builder, mock_execute_script,
mock_serve, mock_schedule, with_script):
"""
Test autobuild entry point.
"""
main()
mock_builder.assert_called_once_with()
mock_builder.assert_called_once_with(None)
if with_script:
mock_execute_script.assert_called_once_with()
else:
mock_execute_script.assert_not_called()
mock_makedirs.assert_called_once_with('/output')
mock_serve.assert_called_once_with(
host='127.0.0.1', root='/output', port=8000)
Expand All @@ -40,7 +48,8 @@ def test_autobuild(mock_makedirs, mock_builder, mock_serve, mock_schedule):
'--port', '8888',
'--host', 'example.org',
'--ignore', '/ignored',
'--watch', '/external'],
'--watch', '/external',
'--exec_script', '/script'],
))
@mock.patch.object(observers.Observer, 'schedule')
@mock.patch.object(livereload.Server, 'serve')
Expand All @@ -62,7 +71,8 @@ def test_autobuild_with_options(mock_makedirs,

# --ignore
mock_builder.assert_called_once_with(
'/output', ['/source', '/output'], ['/ignored'], DEFAULT_IGNORE_REGEX)
'/output', ['/source', '/output'], ['/ignored'],
DEFAULT_IGNORE_REGEX, '/script')

# --watch
calls = [call('/source', mock_builder.return_value),
Expand Down

0 comments on commit 3b01fdd

Please sign in to comment.