Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support custom directories to watch when running mkdocs serve #2642

Merged
merged 18 commits into from Nov 7, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/user-guide/configuration.md
Expand Up @@ -355,6 +355,22 @@ extra:

## Preview controls

## Live Reloading

### watch

Determines additional directories to watch when running `mkdocs serve`.
Configuration is a YAML list.

```yaml
watch:
- directory_a
- directory_b
```

Allows a custom default to be set without the need to pass it through the `-w`/`--watch`
option every time the `mkdocs serve` command is called.

### use_directory_urls

This setting controls the style used for linking to pages within the
Expand Down
7 changes: 5 additions & 2 deletions mkdocs/__main__.py
Expand Up @@ -99,6 +99,8 @@ def __init__(self, log_name='mkdocs', level=logging.INFO):
watch_theme_help = ("Include the theme in list of files to watch for live reloading. "
"Ignored when live reload is not used.")
shell_help = "Use the shell when invoking Git."
watch_help = ("A directory to watch for live reloading. "
steven-terrana marked this conversation as resolved.
Show resolved Hide resolved
"Can be supplied multiple times.")


def add_options(opts):
Expand Down Expand Up @@ -170,11 +172,12 @@ def cli():
@click.option('--no-livereload', 'livereload', flag_value='no-livereload', help=no_reload_help)
@click.option('--dirtyreload', 'livereload', flag_value='dirty', help=dirty_reload_help)
@click.option('--watch-theme', help=watch_theme_help, is_flag=True)
@click.option('-w', '--watch', help=watch_help, multiple=True, default=[])
steven-terrana marked this conversation as resolved.
Show resolved Hide resolved
@common_config_options
@common_options
def serve_command(dev_addr, livereload, **kwargs):
def serve_command(dev_addr, livereload, watch, **kwargs):
"""Run the builtin development server"""
serve.serve(dev_addr=dev_addr, livereload=livereload, **kwargs)
serve.serve(dev_addr=dev_addr, livereload=livereload, watch=watch, **kwargs)


@cli.command(name="build")
Expand Down
12 changes: 11 additions & 1 deletion mkdocs/commands/serve.py
Expand Up @@ -13,7 +13,7 @@


def serve(config_file=None, dev_addr=None, strict=None, theme=None,
theme_dir=None, livereload='livereload', watch_theme=False, **kwargs):
theme_dir=None, livereload='livereload', watch_theme=False, watch=[], **kwargs):
"""
Start the MkDocs development server

Expand Down Expand Up @@ -41,6 +41,10 @@ def builder():
site_dir=site_dir,
**kwargs
)

# combine CLI watch arguments with config file values
config["watch"].extend(watch)

# Override a few config settings after validation
config['site_url'] = 'http://{}{}'.format(config['dev_addr'], mount_path(config))

Expand Down Expand Up @@ -74,6 +78,12 @@ def error_handler(code):
for d in config['theme'].dirs:
server.watch(d)

# convert to set in case CLI params match config file settings
watch_list = dict.fromkeys(config["watch"])
for item in watch_list:
log.info(f"Watching additional path: {item}")
server.watch(item)

# Run `serve` plugin events.
server = config['plugins'].run_event('serve', server, config=config, builder=builder)

Expand Down
3 changes: 3 additions & 0 deletions mkdocs/config/defaults.py
Expand Up @@ -120,4 +120,7 @@ def get_schema():
# A key value pair should be the string name (as the key) and a dict of config
# options (as the value).
('plugins', config_options.Plugins(default=['search'])),

# a list of extra directories to watch while running `mkdocs serve`
('watch', config_options.Type(list, default=[]))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the path will have to be relativized to the location of mkdocs.yml, as here:

if self.config_dir and not os.path.isabs(value):
value = os.path.join(self.config_dir, value)

Well, I think that's what would be the most logical as per what I've seen from MkDocs so far.
Feel free to argue otherwise.


This would probably require a new type in config_optons.py. ListOfPaths or something.
During validation it could reuse FilesystemObject.

I would recommend turning it non-abstract:

 class FilesystemObject(Type):
+    existence_test = staticmethod(os.path.exists)
+    name = 'path'

Relevant implementation example:

class Nav(OptionallyRequired):


Note that we probably should not check for existence of the path (exists=False should be kept). Not sure though.

Copy link
Contributor

@oprypin oprypin Oct 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: we should, in fact, check for existence (just set exists=True), otherwise it throws an OSError anyway after startup

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify why this is important:

watch:
    - foo

mkdocs serve --config-file=subdir/mkdocs.yml -w bar

I want this to watch the foo that is adjacent to mkdocs.yml.
The difference in the output being:

-Watching paths for changes: 'subdir/docs', 'subdir/mkdocs.yml', 'foo', 'bar'
+Watching paths for changes: 'subdir/docs', 'subdir/mkdocs.yml', 'subdir/foo', 'bar'

(-w flag not relativized intentionally)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm leaving this to you btw.

And some tests for the new config option would be nice :D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

root@08e90cd25d84:/# mkdocs serve --config-file=mkdocs/mkdocs.yml -w home
INFO     -  Building documentation...
INFO     -  Cleaning site directory
INFO     -  Documentation built in 1.09 seconds
INFO     -  [01:17:16] Watching paths for changes: 'mkdocs/docs', 'mkdocs/mkdocs.yml', 'mkdocs/foo', 'home'

seems to be working :).

agree this needs some tests. i'll have that soon. might take a day or two to carve out some more time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unit tests for ListOfPaths added

Copy link
Sponsor Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll argue in favor of this 🙂
As a data point, see this mkdocstrings issue.
That ListOfPaths type could be very useful to resolve it!

)
30 changes: 20 additions & 10 deletions mkdocs/tests/cli_tests.py
Expand Up @@ -29,7 +29,8 @@ def test_serve_default(self, mock_serve):
strict=None,
theme=None,
use_directory_urls=None,
watch_theme=False
watch_theme=False,
watch=()
)

@mock.patch('mkdocs.commands.serve.serve', autospec=True)
Expand Down Expand Up @@ -59,7 +60,8 @@ def test_serve_dev_addr(self, mock_serve):
strict=None,
theme=None,
use_directory_urls=None,
watch_theme=False
watch_theme=False,
watch=()
)

@mock.patch('mkdocs.commands.serve.serve', autospec=True)
Expand All @@ -76,7 +78,8 @@ def test_serve_strict(self, mock_serve):
strict=True,
theme=None,
use_directory_urls=None,
watch_theme=False
watch_theme=False,
watch=()
)

@mock.patch('mkdocs.commands.serve.serve', autospec=True)
Expand All @@ -93,7 +96,8 @@ def test_serve_theme(self, mock_serve):
strict=None,
theme='readthedocs',
use_directory_urls=None,
watch_theme=False
watch_theme=False,
watch=()
)

@mock.patch('mkdocs.commands.serve.serve', autospec=True)
Expand All @@ -110,7 +114,8 @@ def test_serve_use_directory_urls(self, mock_serve):
strict=None,
theme=None,
use_directory_urls=True,
watch_theme=False
watch_theme=False,
watch=()
)

@mock.patch('mkdocs.commands.serve.serve', autospec=True)
Expand All @@ -127,7 +132,8 @@ def test_serve_no_directory_urls(self, mock_serve):
strict=None,
theme=None,
use_directory_urls=False,
watch_theme=False
watch_theme=False,
watch=()
)

@mock.patch('mkdocs.commands.serve.serve', autospec=True)
Expand All @@ -144,7 +150,8 @@ def test_serve_livereload(self, mock_serve):
strict=None,
theme=None,
use_directory_urls=None,
watch_theme=False
watch_theme=False,
watch=()
)

@mock.patch('mkdocs.commands.serve.serve', autospec=True)
Expand All @@ -161,7 +168,8 @@ def test_serve_no_livereload(self, mock_serve):
strict=None,
theme=None,
use_directory_urls=None,
watch_theme=False
watch_theme=False,
watch=()
)

@mock.patch('mkdocs.commands.serve.serve', autospec=True)
Expand All @@ -178,7 +186,8 @@ def test_serve_dirtyreload(self, mock_serve):
strict=None,
theme=None,
use_directory_urls=None,
watch_theme=False
watch_theme=False,
watch=()
)

@mock.patch('mkdocs.commands.serve.serve', autospec=True)
Expand All @@ -195,7 +204,8 @@ def test_serve_watch_theme(self, mock_serve):
strict=None,
theme=None,
use_directory_urls=None,
watch_theme=True
watch_theme=True,
watch=()
)

@mock.patch('mkdocs.config.load_config', autospec=True)
Expand Down