diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 51d25ba6..468fb14a 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -66,6 +66,9 @@ jobs: - name: Install Node dependencies run: | npm install -g markdownlint-cli jshint csslint + - name: Check with black + if: always() + run: python -m tox -e black && git diff --exit-code - name: Check with flake8 if: always() run: python -m tox -e flake8 diff --git a/mkdocs/__main__.py b/mkdocs/__main__.py index b4d77304..7e603e33 100644 --- a/mkdocs/__main__.py +++ b/mkdocs/__main__.py @@ -51,7 +51,7 @@ class ColorFormatter(logging.Formatter): 'CRITICAL': 'red', 'ERROR': 'red', 'WARNING': 'yellow', - 'DEBUG': 'blue' + 'DEBUG': 'blue', } text_wrapper = textwrap.TextWrapper( @@ -59,8 +59,8 @@ class ColorFormatter(logging.Formatter): replace_whitespace=False, break_long_words=False, break_on_hyphens=False, - initial_indent=' '*12, - subsequent_indent=' '*12 + initial_indent=' ' * 12, + subsequent_indent=' ' * 12, ) def format(self, record): @@ -70,17 +70,14 @@ class ColorFormatter(logging.Formatter): prefix = click.style(prefix, fg=self.colors[record.levelname]) if self.text_wrapper.width: # Only wrap text if a terminal width was detected - msg = '\n'.join( - self.text_wrapper.fill(line) - for line in message.splitlines() - ) + msg = '\n'.join(self.text_wrapper.fill(line) for line in message.splitlines()) # Prepend prefix after wrapping so that color codes don't affect length return prefix + msg[12:] return prefix + message class State: - ''' Maintain logging level.''' + """Maintain logging level.""" def __init__(self, log_name='mkdocs', level=logging.INFO): self.logger = logging.getLogger(log_name) @@ -99,34 +96,42 @@ pass_state = click.make_pass_decorator(State, ensure=True) clean_help = "Remove old files from the site_dir before building (the default)." config_help = "Provide a specific MkDocs config" -dev_addr_help = ("IP address and port to serve documentation locally (default: " - "localhost:8000)") -strict_help = ("Enable strict mode. This will cause MkDocs to abort the build " - "on any warnings.") +dev_addr_help = "IP address and port to serve documentation locally (default: localhost:8000)" +strict_help = "Enable strict mode. This will cause MkDocs to abort the build on any warnings." theme_help = "The theme to use when building your documentation." theme_choices = utils.get_theme_names() site_dir_help = "The directory to output the result of the documentation build." use_directory_urls_help = "Use directory URLs when building pages (the default)." reload_help = "Enable the live reloading in the development server (this is the default)" no_reload_help = "Disable the live reloading in the development server." -dirty_reload_help = "Enable the live reloading in the development server, but only re-build files that have changed" -commit_message_help = ("A commit message to use when committing to the " - "GitHub Pages remote branch. Commit {sha} and MkDocs {version} are available as expansions") -remote_branch_help = ("The remote branch to commit to for GitHub Pages. This " - "overrides the value specified in config") -remote_name_help = ("The remote name to commit to for GitHub Pages. This " - "overrides the value specified in config") +dirty_reload_help = ( + "Enable the live reloading in the development server, but only re-build files that have changed" +) +commit_message_help = ( + "A commit message to use when committing to the " + "GitHub Pages remote branch. Commit {sha} and MkDocs {version} are available as expansions" +) +remote_branch_help = ( + "The remote branch to commit to for GitHub Pages. This " + "overrides the value specified in config" +) +remote_name_help = ( + "The remote name to commit to for GitHub Pages. This overrides the value specified in config" +) force_help = "Force the push to the repository." no_history_help = "Replace the whole Git history with one new commit." -ignore_version_help = "Ignore check that build is not being deployed with an older version of MkDocs." -watch_theme_help = ("Include the theme in list of files to watch for live reloading. " - "Ignored when live reload is not used.") +ignore_version_help = ( + "Ignore check that build is not being deployed with an older version of MkDocs." +) +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 or file to watch for live reloading. " - "Can be supplied multiple times.") +watch_help = "A directory or file to watch for live reloading. Can be supplied multiple times." -def add_options(opts): +def add_options(*opts): def inner(f): for i in reversed(opts): f = i(f) @@ -140,11 +145,15 @@ def verbose_option(f): state = ctx.ensure_object(State) if value: state.stream.setLevel(logging.DEBUG) - return click.option('-v', '--verbose', - is_flag=True, - expose_value=False, - help='Enable verbose output', - callback=callback)(f) + + return click.option( + '-v', + '--verbose', + is_flag=True, + expose_value=False, + help='Enable verbose output', + callback=callback, + )(f) def quiet_option(f): @@ -152,15 +161,19 @@ def quiet_option(f): state = ctx.ensure_object(State) if value: state.stream.setLevel(logging.ERROR) - return click.option('-q', '--quiet', - is_flag=True, - expose_value=False, - help='Silence warnings', - callback=callback)(f) + + return click.option( + '-q', + '--quiet', + is_flag=True, + expose_value=False, + help='Silence warnings', + callback=callback, + )(f) -common_options = add_options([quiet_option, verbose_option]) -common_config_options = add_options([ +common_options = add_options(quiet_option, verbose_option) +common_config_options = add_options( click.option('-f', '--config-file', type=click.File('rb'), help=config_help), # Don't override config value if user did not specify --strict flag # Conveniently, load_config drops None values @@ -168,8 +181,13 @@ common_config_options = add_options([ click.option('-t', '--theme', type=click.Choice(theme_choices), help=theme_help), # As with --strict, set the default to None so that this doesn't incorrectly # override the config file - click.option('--use-directory-urls/--no-directory-urls', is_flag=True, default=None, help=use_directory_urls_help) -]) + click.option( + '--use-directory-urls/--no-directory-urls', + is_flag=True, + default=None, + help=use_directory_urls_help, + ), +) PYTHON_VERSION = f"{sys.version_info.major}.{sys.version_info.minor}" @@ -179,8 +197,9 @@ PKG_DIR = os.path.dirname(os.path.abspath(__file__)) @click.group(context_settings={'help_option_names': ['-h', '--help']}) @click.version_option( __version__, - '-V', '--version', - message=f'%(prog)s, version %(version)s from { PKG_DIR } (Python { PYTHON_VERSION })' + '-V', + '--version', + message=f'%(prog)s, version %(version)s from { PKG_DIR } (Python { PYTHON_VERSION })', ) @common_options def cli(): @@ -195,12 +214,15 @@ 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, type=click.Path(exists=True), multiple=True, default=[]) +@click.option( + '-w', '--watch', help=watch_help, type=click.Path(exists=True), multiple=True, default=[] +) @common_config_options @common_options def serve_command(dev_addr, livereload, watch, **kwargs): """Run the builtin development server""" from mkdocs.commands import serve + _enable_warnings() serve.serve(dev_addr=dev_addr, livereload=livereload, watch=watch, **kwargs) @@ -213,6 +235,7 @@ def serve_command(dev_addr, livereload, watch, **kwargs): def build_command(clean, **kwargs): """Build the MkDocs documentation""" from mkdocs.commands import build + _enable_warnings() build.build(config.load_config(**kwargs), dirty=not clean) @@ -229,15 +252,14 @@ def build_command(clean, **kwargs): @common_config_options @click.option('-d', '--site-dir', type=click.Path(), help=site_dir_help) @common_options -def gh_deploy_command(clean, message, remote_branch, remote_name, force, no_history, ignore_version, shell, **kwargs): +def gh_deploy_command( + clean, message, remote_branch, remote_name, force, no_history, ignore_version, shell, **kwargs +): """Deploy your documentation to GitHub Pages""" from mkdocs.commands import build, gh_deploy + _enable_warnings() - cfg = config.load_config( - remote_branch=remote_branch, - remote_name=remote_name, - **kwargs - ) + cfg = config.load_config(remote_branch=remote_branch, remote_name=remote_name, **kwargs) build.build(cfg, dirty=not clean) gh_deploy.gh_deploy( cfg, @@ -245,7 +267,7 @@ def gh_deploy_command(clean, message, remote_branch, remote_name, force, no_hist force=force, no_history=no_history, ignore_version=ignore_version, - shell=shell + shell=shell, ) @@ -255,6 +277,7 @@ def gh_deploy_command(clean, message, remote_branch, remote_name, force, no_hist def new_command(project_directory): """Create a new MkDocs project""" from mkdocs.commands import new + new.new(project_directory) diff --git a/mkdocs/commands/babel.py b/mkdocs/commands/babel.py index bbfa9e82..0cdbbada 100644 --- a/mkdocs/commands/babel.py +++ b/mkdocs/commands/babel.py @@ -4,14 +4,14 @@ from pkg_resources import EntryPoint from babel.messages import frontend as babel -DEFAULT_MAPPING_FILE = path.normpath(path.join( - path.abspath(path.dirname(__file__)), '../themes/babel.cfg' -)) +DEFAULT_MAPPING_FILE = path.normpath( + path.join(path.abspath(path.dirname(__file__)), '../themes/babel.cfg') +) class ThemeMixin: def get_theme_dir(self): - ''' Validate theme option and return path to theme's root obtained from entry point. ''' + """Validate theme option and return path to theme's root obtained from entry point.""" entry_points = EntryPoint.parse_map(self.distribution.entry_points, self.distribution) if 'mkdocs.themes' not in entry_points: raise DistutilsOptionError("no mkdocs.themes are defined in entry_points") diff --git a/mkdocs/commands/build.py b/mkdocs/commands/build.py index 144c6230..b191aac2 100644 --- a/mkdocs/commands/build.py +++ b/mkdocs/commands/build.py @@ -14,7 +14,8 @@ import mkdocs class DuplicateFilter: - ''' Avoid logging duplicate messages. ''' + """Avoid logging duplicate messages.""" + def __init__(self): self.msgs = set() @@ -46,15 +47,11 @@ def get_context(nav, files, config, page=None, base_url=''): return { 'nav': nav, 'pages': files, - 'base_url': base_url, - 'extra_css': extra_css, 'extra_javascript': extra_javascript, - 'mkdocs_version': mkdocs.__version__, 'build_date_utc': utils.get_build_datetime(), - 'config': config, 'page': page, } @@ -90,15 +87,13 @@ def _build_template(name, template, files, config, nav): output = template.render(context) # Run `post_template` plugin events. - output = config['plugins'].run_event( - 'post_template', output, template_name=name, config=config - ) + output = config['plugins'].run_event('post_template', output, template_name=name, config=config) return output def _build_theme_template(template_name, env, files, config, nav): - """ Build a template using the theme environment. """ + """Build a template using the theme environment.""" log.debug(f"Building theme template: {template_name}") @@ -119,14 +114,16 @@ def _build_theme_template(template_name, env, files, config, nav): gz_filename = f'{output_path}.gz' with open(gz_filename, 'wb') as f: timestamp = utils.get_build_timestamp() - with gzip.GzipFile(fileobj=f, filename=gz_filename, mode='wb', mtime=timestamp) as gz_buf: + with gzip.GzipFile( + fileobj=f, filename=gz_filename, mode='wb', mtime=timestamp + ) as gz_buf: gz_buf.write(output.encode('utf-8')) else: log.info(f"Template skipped: '{template_name}' generated empty output.") def _build_extra_template(template_name, files, config, nav): - """ Build user templates which are not part of the theme. """ + """Build user templates which are not part of the theme.""" log.debug(f"Building extra template: {template_name}") @@ -151,7 +148,7 @@ def _build_extra_template(template_name, files, config, nav): def _populate_page(page, config, files, dirty=False): - """ Read page content from docs_dir and render Markdown. """ + """Read page content from docs_dir and render Markdown.""" try: # When --dirty is used, only read the page if the file has been modified since the @@ -160,9 +157,7 @@ def _populate_page(page, config, files, dirty=False): return # Run the `pre_page` plugin event - page = config['plugins'].run_event( - 'pre_page', page, config=config, files=files - ) + page = config['plugins'].run_event('pre_page', page, config=config, files=files) page.read_source(config) @@ -187,7 +182,7 @@ def _populate_page(page, config, files, dirty=False): def _build_page(page, config, doc_files, nav, env, dirty=False): - """ Pass a Page to theme template and write output to site_dir. """ + """Pass a Page to theme template and write output to site_dir.""" try: # When --dirty is used, only build the page if the file has been modified since the @@ -217,13 +212,13 @@ def _build_page(page, config, doc_files, nav, env, dirty=False): output = template.render(context) # Run `post_page` plugin events. - output = config['plugins'].run_event( - 'post_page', output, page=page, config=config - ) + output = config['plugins'].run_event('post_page', output, page=page, config=config) # Write the output file. if output.strip(): - utils.write_file(output.encode('utf-8', errors='xmlcharrefreplace'), page.file.abs_dest_path) + utils.write_file( + output.encode('utf-8', errors='xmlcharrefreplace'), page.file.abs_dest_path + ) else: log.info(f"Page skipped: '{page.file.src_path}'. Generated empty output.") @@ -239,7 +234,7 @@ def _build_page(page, config, doc_files, nav, env, dirty=False): def build(config, live_server=False, dirty=False): - """ Perform a full site build. """ + """Perform a full site build.""" logger = logging.getLogger('mkdocs') @@ -251,6 +246,7 @@ def build(config, live_server=False, dirty=False): try: from time import time + start = time() # Run `config` plugin events. @@ -264,8 +260,10 @@ def build(config, live_server=False, dirty=False): utils.clean_directory(config['site_dir']) else: # pragma: no cover # Warn user about problems that may occur with --dirty option - log.warning("A 'dirty' build is being performed, this will likely lead to inaccurate navigation and other" - " links within your site. This option is designed for site development purposes only.") + log.warning( + "A 'dirty' build is being performed, this will likely lead to inaccurate navigation and other" + " links within your site. This option is designed for site development purposes only." + ) if not live_server: # pragma: no cover log.info(f"Building documentation to directory: {config['site_dir']}") @@ -292,9 +290,7 @@ def build(config, live_server=False, dirty=False): _populate_page(file.page, config, files, dirty) # Run `env` plugin events. - env = config['plugins'].run_event( - 'env', env, config=config, files=files - ) + env = config['plugins'].run_event('env', env, config=config, files=files) # Start writing files to site_dir now that all data is gathered. Note that order matters. Files # with lower precedence get written first so that files with higher precedence can overwrite them. @@ -336,6 +332,6 @@ def build(config, live_server=False, dirty=False): def site_directory_contains_stale_files(site_directory): - """ Check if the site directory contains stale files from a previous build. """ + """Check if the site directory contains stale files from a previous build.""" return True if os.path.exists(site_directory) and os.listdir(site_directory) else False diff --git a/mkdocs/commands/gh_deploy.py b/mkdocs/commands/gh_deploy.py index 2748447e..4b754e43 100644 --- a/mkdocs/commands/gh_deploy.py +++ b/mkdocs/commands/gh_deploy.py @@ -18,7 +18,7 @@ def _is_cwd_git_repo(): proc = subprocess.Popen( ['git', 'rev-parse', '--is-inside-work-tree'], stdout=subprocess.PIPE, - stderr=subprocess.PIPE + stderr=subprocess.PIPE, ) except FileNotFoundError: log.error("Could not find git - is it installed and on your path?") @@ -29,8 +29,12 @@ def _is_cwd_git_repo(): def _get_current_sha(repo_path): - proc = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], cwd=repo_path or None, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc = subprocess.Popen( + ['git', 'rev-parse', '--short', 'HEAD'], + cwd=repo_path or None, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) stdout, _ = proc.communicate() sha = stdout.decode('utf-8').strip() @@ -42,8 +46,11 @@ def _get_remote_url(remote_name): # No CNAME found. We will use the origin URL to determine the GitHub # pages location. remote = f"remote.{remote_name}.url" - proc = subprocess.Popen(["git", "config", "--get", remote], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc = subprocess.Popen( + ["git", "config", "--get", remote], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) stdout, _ = proc.communicate() url = stdout.decode('utf-8').strip() @@ -60,8 +67,11 @@ def _get_remote_url(remote_name): def _check_version(branch): - proc = subprocess.Popen(['git', 'show', '-s', '--format=%s', f'refs/heads/{branch}'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc = subprocess.Popen( + ['git', 'show', '-s', '--format=%s', f'refs/heads/{branch}'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) stdout, _ = proc.communicate() msg = stdout.decode('utf-8').strip() @@ -84,11 +94,12 @@ def _check_version(branch): raise Abort('Deployment Aborted!') -def gh_deploy(config, message=None, force=False, no_history=False, ignore_version=False, shell=False): +def gh_deploy( + config, message=None, force=False, no_history=False, ignore_version=False, shell=False +): if not _is_cwd_git_repo(): - log.error('Cannot deploy - this directory does not appear to be a git ' - 'repository') + log.error('Cannot deploy - this directory does not appear to be a git repository') remote_branch = config['remote_branch'] remote_name = config['remote_name'] @@ -101,8 +112,11 @@ def gh_deploy(config, message=None, force=False, no_history=False, ignore_versio sha = _get_current_sha(os.path.dirname(config.config_file_path)) message = message.format(version=mkdocs.__version__, sha=sha) - log.info("Copying '%s' to '%s' branch and pushing to GitHub.", - config['site_dir'], config['remote_branch']) + log.info( + "Copying '%s' to '%s' branch and pushing to GitHub.", + config['site_dir'], + config['remote_branch'], + ) try: ghp_import.ghp_import( @@ -114,7 +128,7 @@ def gh_deploy(config, message=None, force=False, no_history=False, ignore_versio force=force, use_shell=shell, no_history=no_history, - nojekyll=True + nojekyll=True, ) except ghp_import.GhpError as e: log.error(f"Failed to deploy to GitHub with error: \n{e.message}") @@ -126,10 +140,13 @@ def gh_deploy(config, message=None, force=False, no_history=False, ignore_versio # This GitHub pages repository has a CNAME configured. with open(cname_file) as f: cname_host = f.read().strip() - log.info(f'Based on your CNAME file, your documentation should be ' - f'available shortly at: http://{cname_host}') - log.info('NOTE: Your DNS records must be configured appropriately for ' - 'your CNAME URL to work.') + log.info( + f'Based on your CNAME file, your documentation should be ' + f'available shortly at: http://{cname_host}' + ) + log.info( + 'NOTE: Your DNS records must be configured appropriately for your CNAME URL to work.' + ) return host, path = _get_remote_url(remote_name) @@ -140,6 +157,6 @@ def gh_deploy(config, message=None, force=False, no_history=False, ignore_versio else: username, repo = path.split('/', 1) if repo.endswith('.git'): - repo = repo[:-len('.git')] + repo = repo[: -len('.git')] url = f'https://{username}.github.io/{repo}/' log.info(f"Your documentation should shortly be available at: {url}") diff --git a/mkdocs/commands/serve.py b/mkdocs/commands/serve.py index fec06fa2..b5cf1d58 100644 --- a/mkdocs/commands/serve.py +++ b/mkdocs/commands/serve.py @@ -14,8 +14,17 @@ from mkdocs.livereload import LiveReloadServer log = logging.getLogger(__name__) -def serve(config_file=None, dev_addr=None, strict=None, theme=None, - theme_dir=None, livereload='livereload', watch_theme=False, watch=[], **kwargs): +def serve( + config_file=None, + dev_addr=None, + strict=None, + theme=None, + theme_dir=None, + livereload='livereload', + watch_theme=False, + watch=[], + **kwargs, +): """ Start the MkDocs development server @@ -41,7 +50,7 @@ def serve(config_file=None, dev_addr=None, strict=None, theme=None, theme=theme, theme_dir=theme_dir, site_dir=site_dir, - **kwargs + **kwargs, ) # combine CLI watch arguments with config file values @@ -63,7 +72,9 @@ def serve(config_file=None, dev_addr=None, strict=None, theme=None, config = builder() host, port = config['dev_addr'] - server = LiveReloadServer(builder=builder, host=host, port=port, root=site_dir, mount_path=mount_path(config)) + server = LiveReloadServer( + builder=builder, host=host, port=port, root=site_dir, mount_path=mount_path(config) + ) def error_handler(code): if code in (404, 500): diff --git a/mkdocs/commands/setup.py b/mkdocs/commands/setup.py index b0512d0d..f68e3683 100644 --- a/mkdocs/commands/setup.py +++ b/mkdocs/commands/setup.py @@ -3,8 +3,9 @@ try: compile_catalog, extract_messages, init_catalog, - update_catalog + update_catalog, ) + babel_cmdclass = { 'compile_catalog': compile_catalog, 'extract_messages': extract_messages, diff --git a/mkdocs/config/__init__.py b/mkdocs/config/__init__.py index b9780de6..02827893 100644 --- a/mkdocs/config/__init__.py +++ b/mkdocs/config/__init__.py @@ -1,4 +1,3 @@ from mkdocs.config.base import load_config, Config -__all__ = [load_config.__name__, - Config.__name__] +__all__ = [load_config.__name__, Config.__name__] diff --git a/mkdocs/config/base.py b/mkdocs/config/base.py index 8f7f8bef..74c23573 100644 --- a/mkdocs/config/base.py +++ b/mkdocs/config/base.py @@ -66,10 +66,8 @@ class Config(UserDict): except ValidationError as e: failed.append((key, e)) - for key in (set(self.keys()) - self._schema_keys): - warnings.append(( - key, f"Unrecognised configuration name: {key}" - )) + for key in set(self.keys()) - self._schema_keys: + warnings.append((key, f"Unrecognised configuration name: {key}")) return failed, warnings @@ -120,19 +118,20 @@ class Config(UserDict): return failed, warnings def load_dict(self, patch): - """ Load config options from a dictionary. """ + """Load config options from a dictionary.""" if not isinstance(patch, dict): raise exceptions.ConfigurationError( "The configuration is invalid. The expected type was a key " "value mapping (a python dict) but we got an object of type: " - f"{type(patch)}") + f"{type(patch)}" + ) self.user_configs.append(patch) self.data.update(patch) def load_file(self, config_file): - """ Load config options from the open file descriptor of a YAML file. """ + """Load config options from the open file descriptor of a YAML file.""" try: return self.load_dict(utils.yaml_load(config_file)) except YAMLError as e: @@ -177,8 +176,7 @@ def _open_config_file(config_file): except FileNotFoundError: continue else: - raise exceptions.ConfigurationError( - f"Config file '{paths_to_try[0]}' does not exist.") + raise exceptions.ConfigurationError(f"Config file '{paths_to_try[0]}' does not exist.") else: log.debug(f"Loading configuration file: {config_file}") # Ensure file descriptor is at beginning @@ -214,6 +212,7 @@ def load_config(config_file=None, **kwargs): # Initialize the config with the default schema. from mkdocs.config.defaults import get_schema + cfg = Config(schema=get_schema(), config_file_path=options['config_file_path']) # load the config file cfg.load_file(fd) @@ -233,9 +232,7 @@ def load_config(config_file=None, **kwargs): log.debug(f"Config value: '{key}' = {value!r}") if len(errors) > 0: - raise exceptions.Abort( - f"Aborted with {len(errors)} Configuration Errors!" - ) + raise exceptions.Abort(f"Aborted with {len(errors)} Configuration Errors!") elif cfg['strict'] and len(warnings) > 0: raise exceptions.Abort( f"Aborted with {len(warnings)} Configuration Warnings in 'strict' mode!" diff --git a/mkdocs/config/config_options.py b/mkdocs/config/config_options.py index 2074c7c0..5e654295 100644 --- a/mkdocs/config/config_options.py +++ b/mkdocs/config/config_options.py @@ -12,7 +12,6 @@ from mkdocs.config.base import Config, ValidationError class BaseConfigOption: - def __init__(self): self.warnings = [] self.default = None @@ -72,6 +71,7 @@ class ConfigItems(BaseConfigOption): Validates a list of mappings that all must match the same set of options. """ + def __init__(self, *config_options, **kwargs): BaseConfigOption.__init__(self) self.item_config = SubConfig(*config_options) @@ -88,8 +88,9 @@ class ConfigItems(BaseConfigOption): return () if not isinstance(value, Sequence): - raise ValidationError(f'Expected a sequence of mappings, but a ' - f'{type(value)} was given.') + raise ValidationError( + f'Expected a sequence of mappings, but a ' f'{type(value)} was given.' + ) return [self.item_config.validate(item) for item in value] @@ -149,8 +150,10 @@ class Type(OptionallyRequired): if not isinstance(value, self._type): msg = f"Expected type: {self._type} but received: {type(value)}" elif self.length is not None and len(value) != self.length: - msg = (f"Expected type: {self._type} with length {self.length}" - f" but received: {value} with length {len(value)}") + msg = ( + f"Expected type: {self._type} with length {self.length}" + f" but received: {value} with length {len(value)}" + ) else: return value @@ -287,10 +290,10 @@ class IpAddress(OptionallyRequired): host = config[key_name].host if key_name == 'dev_addr' and host in ['0.0.0.0', '::']: self.warnings.append( - (f"The use of the IP address '{host}' suggests a production environment " - "or the use of a proxy to connect to the MkDocs server. However, " - "the MkDocs' server is intended for local development purposes only. " - "Please use a third party production-ready server instead.") + f"The use of the IP address '{host}' suggests a production environment " + "or the use of a proxy to connect to the MkDocs server. However, " + "the MkDocs' server is intended for local development purposes only. " + "Please use a third party production-ready server instead." ) @@ -319,8 +322,7 @@ class URL(OptionallyRequired): parsed_url = parsed_url._replace(path=f'{parsed_url.path}/') return urlunsplit(parsed_url) - raise ValidationError( - "The URL isn't valid, it should include the http:// (scheme)") + raise ValidationError("The URL isn't valid, it should include the http:// (scheme)") class RepoURL(URL): @@ -373,7 +375,9 @@ class FilesystemObject(Type): self.config_dir = None def pre_validation(self, config, key_name): - self.config_dir = os.path.dirname(config.config_file_path) if config.config_file_path else None + self.config_dir = ( + os.path.dirname(config.config_file_path) if config.config_file_path else None + ) def run_validation(self, value): value = super().run_validation(value) @@ -390,6 +394,7 @@ class Dir(FilesystemObject): Validate a path to a directory, optionally verifying that it exists. """ + existence_test = staticmethod(os.path.isdir) name = 'directory' @@ -400,9 +405,10 @@ class Dir(FilesystemObject): # Validate that the dir is not the parent dir of the config file. if os.path.dirname(config.config_file_path) == config[key_name]: raise ValidationError( - (f"The '{key_name}' should not be the parent directory of the" - " config file. Use a child directory instead so that the" - f" '{key_name}' is a sibling of the config file.")) + f"The '{key_name}' should not be the parent directory of the" + f" config file. Use a child directory instead so that the" + f" '{key_name}' is a sibling of the config file." + ) class File(FilesystemObject): @@ -411,6 +417,7 @@ class File(FilesystemObject): Validate a path to a file, optionally verifying that it exists. """ + existence_test = staticmethod(os.path.isfile) name = 'file' @@ -427,7 +434,9 @@ class ListOfPaths(OptionallyRequired): super().__init__(default, required) def pre_validation(self, config, key_name): - self.config_dir = os.path.dirname(config.config_file_path) if config.config_file_path else None + self.config_dir = ( + os.path.dirname(config.config_file_path) if config.config_file_path else None + ) def run_validation(self, value): if not isinstance(value, list): @@ -455,24 +464,26 @@ class SiteDir(Dir): def post_validation(self, config, key_name): super().post_validation(config, key_name) + docs_dir = config['docs_dir'] + site_dir = config['site_dir'] # Validate that the docs_dir and site_dir don't contain the # other as this will lead to copying back and forth on each # and eventually make a deep nested mess. - if (config['docs_dir'] + os.sep).startswith(config['site_dir'].rstrip(os.sep) + os.sep): + if (docs_dir + os.sep).startswith(site_dir.rstrip(os.sep) + os.sep): raise ValidationError( - ("The 'docs_dir' should not be within the 'site_dir' as this " - "can mean the source files are overwritten by the output or " - "it will be deleted if --clean is passed to mkdocs build." - "(site_dir: '{}', docs_dir: '{}')" - ).format(config['site_dir'], config['docs_dir'])) - elif (config['site_dir'] + os.sep).startswith(config['docs_dir'].rstrip(os.sep) + os.sep): + f"The 'docs_dir' should not be within the 'site_dir' as this " + f"can mean the source files are overwritten by the output or " + f"it will be deleted if --clean is passed to mkdocs build." + f"(site_dir: '{site_dir}', docs_dir: '{docs_dir}')" + ) + elif (site_dir + os.sep).startswith(docs_dir.rstrip(os.sep) + os.sep): raise ValidationError( - ("The 'site_dir' should not be within the 'docs_dir' as this " - "leads to the build directory being copied into itself and " - "duplicate nested files in the 'site_dir'." - "(site_dir: '{}', docs_dir: '{}')" - ).format(config['site_dir'], config['docs_dir'])) + f"The 'site_dir' should not be within the 'docs_dir' as this " + f"leads to the build directory being copied into itself and " + f"duplicate nested files in the 'site_dir'." + f"(site_dir: '{site_dir}', docs_dir: '{docs_dir}')" + ) class Theme(BaseConfigOption): @@ -507,13 +518,17 @@ class Theme(BaseConfigOption): raise ValidationError("No theme name set.") - raise ValidationError(f'Invalid type "{type(value)}". Expected a string or key/value pairs.') + raise ValidationError( + f'Invalid type "{type(value)}". Expected a string or key/value pairs.' + ) def post_validation(self, config, key_name): theme_config = config[key_name] if not theme_config['name'] and 'custom_dir' not in theme_config: - raise ValidationError("At least one of 'theme.name' or 'theme.custom_dir' must be defined.") + raise ValidationError( + "At least one of 'theme.name' or 'theme.custom_dir' must be defined." + ) # Ensure custom_dir is an absolute path if 'custom_dir' in theme_config and not os.path.isabs(theme_config['custom_dir']): @@ -521,8 +536,11 @@ class Theme(BaseConfigOption): theme_config['custom_dir'] = os.path.join(config_dir, theme_config['custom_dir']) if 'custom_dir' in theme_config and not os.path.isdir(theme_config['custom_dir']): - raise ValidationError("The path set in {name}.custom_dir ('{path}') does not exist.". - format(path=theme_config['custom_dir'], name=key_name)) + raise ValidationError( + "The path set in {name}.custom_dir ('{path}') does not exist.".format( + path=theme_config['custom_dir'], name=key_name + ) + ) if 'locale' in theme_config and not isinstance(theme_config['locale'], str): raise ValidationError(f"'{theme_config['name']}.locale' must be a string.") @@ -559,11 +577,15 @@ class Nav(OptionallyRequired): pass elif isinstance(value, dict): if len(value) != 1: - raise ValidationError(f"Expected nav item to be a dict of size 1, got {self._repr_item(value)}") + raise ValidationError( + f"Expected nav item to be a dict of size 1, got {self._repr_item(value)}" + ) for subnav in value.values(): self.run_validation(subnav, top=False) else: - raise ValidationError(f"Expected nav item to be a string or dict, got {self._repr_item(value)}") + raise ValidationError( + f"Expected nav item to be a string or dict, got {self._repr_item(value)}" + ) @classmethod def _repr_item(cls, value): @@ -595,7 +617,8 @@ class MarkdownExtensions(OptionallyRequired): a dict item must be a valid Markdown extension name and the value must be a dict of config options for that extension. Extension configs are set on the private setting passed to `configkey`. The `builtins` keyword accepts a list of extensions which cannot be overridden by - the user. However, builtins can be duplicated to define config options for them if desired. """ + the user. However, builtins can be duplicated to define config options for them if desired.""" + def __init__(self, builtins=None, configkey='mdx_configs', **kwargs): super().__init__(**kwargs) self.builtins = builtins or [] @@ -647,7 +670,9 @@ class MarkdownExtensions(OptionallyRequired): stack.insert(0, frame) tb = ''.join(traceback.format_list(stack)) - raise ValidationError(f"Failed to load extension '{ext}'.\n{tb}{type(e).__name__}: {e}") + raise ValidationError( + f"Failed to load extension '{ext}'.\n{tb}{type(e).__name__}: {e}" + ) return extensions @@ -705,15 +730,13 @@ class Plugins(OptionallyRequired): if not issubclass(Plugin, plugins.BasePlugin): raise ValidationError( f'{Plugin.__module__}.{Plugin.__name__} must be a subclass of' - f' {plugins.BasePlugin.__module__}.{plugins.BasePlugin.__name__}') + f' {plugins.BasePlugin.__module__}.{plugins.BasePlugin.__name__}' + ) plugin = Plugin() errors, warnings = plugin.load_config(config, self.config_file_path) self.warnings.extend(warnings) - errors_message = '\n'.join( - f"Plugin '{name}' value: '{x}'. Error: {y}" - for x, y in errors - ) + errors_message = '\n'.join(f"Plugin '{name}' value: '{x}'. Error: {y}" for x, y in errors) if errors_message: raise ValidationError(errors_message) return plugin diff --git a/mkdocs/config/defaults.py b/mkdocs/config/defaults.py index 95d3c440..79f346d0 100644 --- a/mkdocs/config/defaults.py +++ b/mkdocs/config/defaults.py @@ -10,116 +10,91 @@ from mkdocs.config import config_options def get_schema(): - return ( - + schema = { # Reserved for internal use, stores the mkdocs.yml config file. - ('config_file_path', config_options.Type(str)), - + 'config_file_path': config_options.Type(str), # The title to use for the documentation - ('site_name', config_options.Type(str, required=True)), - + 'site_name': config_options.Type(str, required=True), # Defines the structure of the navigation. - ('nav', config_options.Nav()), - ('pages', config_options.Deprecated(removed=True, moved_to='nav')), - + 'nav': config_options.Nav(), + 'pages': config_options.Deprecated(removed=True, moved_to='nav'), # The full URL to where the documentation will be hosted - ('site_url', config_options.URL(is_dir=True)), - + 'site_url': config_options.URL(is_dir=True), # A description for the documentation project that will be added to the # HTML meta tags. - ('site_description', config_options.Type(str)), + 'site_description': config_options.Type(str), # The name of the author to add to the HTML meta tags - ('site_author', config_options.Type(str)), - + 'site_author': config_options.Type(str), # The MkDocs theme for the documentation. - ('theme', config_options.Theme(default='mkdocs')), - + 'theme': config_options.Theme(default='mkdocs'), # The directory containing the documentation markdown. - ('docs_dir', config_options.Dir(default='docs', exists=True)), - + 'docs_dir': config_options.Dir(default='docs', exists=True), # The directory where the site will be built to - ('site_dir', config_options.SiteDir(default='site')), - + 'site_dir': config_options.SiteDir(default='site'), # A copyright notice to add to the footer of documentation. - ('copyright', config_options.Type(str)), - + 'copyright': config_options.Type(str), # set of values for Google analytics containing the account IO and domain, # this should look like, ['UA-27795084-5', 'mkdocs.org'] - ('google_analytics', config_options.Deprecated( + 'google_analytics': config_options.Deprecated( message=( 'The configuration option {} has been deprecated and ' 'will be removed in a future release of MkDocs. See the ' 'options available on your theme for an alternative.' ), - option_type=config_options.Type(list, length=2) - )), - + option_type=config_options.Type(list, length=2), + ), # The address on which to serve the live reloading docs server. - ('dev_addr', config_options.IpAddress(default='127.0.0.1:8000')), - + 'dev_addr': config_options.IpAddress(default='127.0.0.1:8000'), # If `True`, use `/index.hmtl` style files with hyperlinks to # the directory.If `False`, use `.html style file with # hyperlinks to the file. # True generates nicer URLs, but False is useful if browsing the output on # a filesystem. - ('use_directory_urls', config_options.Type(bool, default=True)), - + 'use_directory_urls': config_options.Type(bool, default=True), # Specify a link to the project source repo to be included # in the documentation pages. - ('repo_url', config_options.RepoURL()), - + 'repo_url': config_options.RepoURL(), # A name to use for the link to the project source repo. # Default, If repo_url is unset then None, otherwise # "GitHub", "Bitbucket" or "GitLab" for known url or Hostname # for unknown urls. - ('repo_name', config_options.Type(str)), - + 'repo_name': config_options.Type(str), # Specify a URI to the docs dir in the project source repo, relative to the # repo_url. When set, a link directly to the page in the source repo will # be added to the generated HTML. If repo_url is not set also, this option # is ignored. - ('edit_uri', config_options.Type(str)), - + 'edit_uri': config_options.Type(str), # Specify which css or javascript files from the docs directory should be # additionally included in the site. - ('extra_css', config_options.Type(list, default=[])), - ('extra_javascript', config_options.Type(list, default=[])), - + 'extra_css': config_options.Type(list, default=[]), + 'extra_javascript': config_options.Type(list, default=[]), # Similar to the above, but each template (HTML or XML) will be build with # Jinja2 and the global context. - ('extra_templates', config_options.Type(list, default=[])), - + 'extra_templates': config_options.Type(list, default=[]), # PyMarkdown extension names. - ('markdown_extensions', config_options.MarkdownExtensions( - builtins=['toc', 'tables', 'fenced_code'], - configkey='mdx_configs', default=[])), - + 'markdown_extensions': config_options.MarkdownExtensions( + builtins=['toc', 'tables', 'fenced_code'], configkey='mdx_configs', default=[] + ), # PyMarkdown Extension Configs. For internal use only. - ('mdx_configs', config_options.Private()), - + 'mdx_configs': config_options.Private(), # enabling strict mode causes MkDocs to stop the build when a problem is # encountered rather than display an error. - ('strict', config_options.Type(bool, default=False)), - + 'strict': config_options.Type(bool, default=False), # the remote branch to commit to when using gh-deploy - ('remote_branch', config_options.Type( - str, default='gh-pages')), - + 'remote_branch': config_options.Type(str, default='gh-pages'), # the remote name to push to when using gh-deploy - ('remote_name', config_options.Type(str, default='origin')), - + 'remote_name': config_options.Type(str, default='origin'), # extra is a mapping/dictionary of data that is passed to the template. # This allows template authors to require extra configuration that not # relevant to all themes and doesn't need to be explicitly supported by # MkDocs itself. A good example here would be including the current # project version. - ('extra', config_options.SubConfig()), - + 'extra': config_options.SubConfig(), # a list of plugins. Each item may contain a string name or a key value pair. # 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'])), - + 'plugins': config_options.Plugins(default=['search']), # a list of extra paths to watch while running `mkdocs serve` - ('watch', config_options.ListOfPaths(default=[])) - ) + 'watch': config_options.ListOfPaths(default=[]), + } + return tuple(schema.items()) diff --git a/mkdocs/contrib/search/__init__.py b/mkdocs/contrib/search/__init__.py index 66eb92d8..a5549f8d 100644 --- a/mkdocs/contrib/search/__init__.py +++ b/mkdocs/contrib/search/__init__.py @@ -11,7 +11,7 @@ base_path = os.path.dirname(os.path.abspath(__file__)) class LangOption(config_options.OptionallyRequired): - """ Validate Language(s) provided in config are known languages. """ + """Validate Language(s) provided in config are known languages.""" def get_lunr_supported_lang(self, lang): for lang_part in lang.split("_"): @@ -40,14 +40,14 @@ class LangOption(config_options.OptionallyRequired): class SearchPlugin(BasePlugin): - """ Add a search feature to MkDocs. """ + """Add a search feature to MkDocs.""" config_scheme = ( ('lang', LangOption()), ('separator', config_options.Type(str, default=r'[\s\-]+')), ('min_search_length', config_options.Type(int, default=3)), ('prebuild_index', config_options.Choice((False, True, 'node', 'python'), default=False)), - ('indexing', config_options.Choice(('full', 'sections', 'titles'), default='full')) + ('indexing', config_options.Choice(('full', 'sections', 'titles'), default='full')), ) def on_config(self, config, **kwargs): @@ -95,10 +95,10 @@ class SearchPlugin(BasePlugin): files.append('lunr.stemmer.support.js') if len(self.config['lang']) > 1: files.append('lunr.multi.js') - if ('ja' in self.config['lang'] or 'jp' in self.config['lang']): + if 'ja' in self.config['lang'] or 'jp' in self.config['lang']: files.append('tinyseg.js') for lang in self.config['lang']: - if (lang != 'en'): + if lang != 'en': files.append(f'lunr.{lang}.js') for filename in files: diff --git a/mkdocs/contrib/search/search_index.py b/mkdocs/contrib/search/search_index.py index bef81393..e610bb82 100644 --- a/mkdocs/contrib/search/search_index.py +++ b/mkdocs/contrib/search/search_index.py @@ -8,6 +8,7 @@ from html.parser import HTMLParser try: from lunr import lunr + haslunrpy = True except ImportError: haslunrpy = False @@ -44,11 +45,7 @@ class SearchIndex: text = text.replace('\u00a0', ' ') text = re.sub(r'[ \t\n\r\f\v]+', ' ', text.strip()) - self._entries.append({ - 'title': title, - 'text': text, - 'location': loc - }) + self._entries.append({'title': title, 'text': text, 'location': loc}) def add_entry_from_context(self, page): """ @@ -70,11 +67,7 @@ class SearchIndex: # Create an entry for the full page. text = parser.stripped_html.rstrip('\n') if self.config['indexing'] == 'full' else '' - self._add_entry( - title=page.title, - text=text, - loc=url - ) + self._add_entry(title=page.title, text=text, loc=url) if self.config['indexing'] in ['full', 'sections']: for section in parser.data: @@ -91,28 +84,23 @@ class SearchIndex: text = ' '.join(section.text) if self.config['indexing'] == 'full' else '' if toc_item is not None: - self._add_entry( - title=toc_item.title, - text=text, - loc=abs_url + toc_item.url - ) + self._add_entry(title=toc_item.title, text=text, loc=abs_url + toc_item.url) def generate_search_index(self): """python to json conversion""" - page_dicts = { - 'docs': self._entries, - 'config': self.config - } + page_dicts = {'docs': self._entries, 'config': self.config} data = json.dumps(page_dicts, sort_keys=True, separators=(',', ':'), default=str) if self.config['prebuild_index'] in (True, 'node'): try: - script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'prebuild-index.js') + script_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), 'prebuild-index.js' + ) p = subprocess.Popen( ['node', script_path], stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE + stderr=subprocess.PIPE, ) idx, err = p.communicate(data.encode('utf-8')) if not err: @@ -127,8 +115,11 @@ class SearchIndex: elif self.config['prebuild_index'] == 'python': if haslunrpy: idx = lunr( - ref='location', fields=('title', 'text'), documents=self._entries, - languages=self.config['lang']) + ref='location', + fields=('title', 'text'), + documents=self._entries, + languages=self.config['lang'], + ) page_dicts['index'] = idx.serialize() data = json.dumps(page_dicts, sort_keys=True, separators=(',', ':')) else: @@ -154,11 +145,7 @@ class ContentSection: self.title = title def __eq__(self, other): - return ( - self.text == other.text and - self.id == other.id and - self.title == other.title - ) + return self.text == other.text and self.id == other.id and self.title == other.title class ContentParser(HTMLParser): diff --git a/mkdocs/exceptions.py b/mkdocs/exceptions.py index c72175df..4ce39ba2 100644 --- a/mkdocs/exceptions.py +++ b/mkdocs/exceptions.py @@ -7,6 +7,7 @@ class MkDocsException(ClickException): class Abort(MkDocsException): """Abort the build""" + def show(self, **kwargs): echo(self.format_message()) diff --git a/mkdocs/livereload/__init__.py b/mkdocs/livereload/__init__.py index 9feb27f7..8f9b7bff 100644 --- a/mkdocs/livereload/__init__.py +++ b/mkdocs/livereload/__init__.py @@ -219,7 +219,7 @@ class LiveReloadServer(socketserver.ThreadingMixIn, wsgiref.simple_server.WSGISe return [b"%d" % self._visible_epoch] if path.startswith(self.mount_path): - rel_file_path = path[len(self.mount_path):] + rel_file_path = path[len(self.mount_path) :] if path.endswith("/"): rel_file_path += "index.html" diff --git a/mkdocs/localization.py b/mkdocs/localization.py index 6f772ec3..54a711df 100644 --- a/mkdocs/localization.py +++ b/mkdocs/localization.py @@ -7,9 +7,11 @@ from mkdocs.config.base import ValidationError try: from babel.core import Locale, UnknownLocaleError from babel.support import Translations, NullTranslations + has_babel = True except ImportError: # pragma: no cover from mkdocs.utils.babel_stub import Locale, UnknownLocaleError + has_babel = False diff --git a/mkdocs/plugins.py b/mkdocs/plugins.py index 4cc978b9..4ea25ab6 100644 --- a/mkdocs/plugins.py +++ b/mkdocs/plugins.py @@ -15,14 +15,28 @@ log = logging.getLogger('mkdocs.plugins') EVENTS = ( - 'config', 'pre_build', 'files', 'nav', 'env', 'pre_template', 'template_context', - 'post_template', 'pre_page', 'page_read_source', 'page_markdown', - 'page_content', 'page_context', 'post_page', 'post_build', 'serve', 'build_error' + 'config', + 'pre_build', + 'files', + 'nav', + 'env', + 'pre_template', + 'template_context', + 'post_template', + 'pre_page', + 'page_read_source', + 'page_markdown', + 'page_content', + 'page_context', + 'post_page', + 'post_build', + 'serve', + 'build_error', ) def get_plugins(): - """ Return a dict of all installed Plugins as {name: EntryPoint}. """ + """Return a dict of all installed Plugins as {name: EntryPoint}.""" plugins = importlib_metadata.entry_points(group='mkdocs.plugins') @@ -48,7 +62,7 @@ class BasePlugin: config = {} def load_config(self, options, config_file_path=None): - """ Load config from a dict of options. Returns a tuple of (errors, warnings).""" + """Load config from a dict of options. Returns a tuple of (errors, warnings).""" self.config = Config(schema=self.config_scheme, config_file_path=config_file_path) self.config.load_dict(options) @@ -70,7 +84,7 @@ class PluginCollection(OrderedDict): self.events = {x: [] for x in EVENTS} def _register_event(self, event_name, method): - """ Register a method for an event. """ + """Register a method for an event.""" self.events[event_name].append(method) def __setitem__(self, key, value, **kwargs): @@ -78,7 +92,8 @@ class PluginCollection(OrderedDict): raise TypeError( f'{self.__module__}.{self.__name__} only accepts values which' f' are instances of {BasePlugin.__module__}.{BasePlugin.__name__}' - ' subclasses') + ' subclasses' + ) super().__setitem__(key, value, **kwargs) # Register all of the event methods defined for this Plugin. for event_name in (x for x in dir(value) if x.startswith('on_')): diff --git a/mkdocs/structure/files.py b/mkdocs/structure/files.py index 4ab26988..39a68d80 100644 --- a/mkdocs/structure/files.py +++ b/mkdocs/structure/files.py @@ -10,7 +10,8 @@ log = logging.getLogger(__name__) class Files: - """ A collection of File objects. """ + """A collection of File objects.""" + def __init__(self, files): self._files = files @@ -28,45 +29,46 @@ class Files: return {file.src_path: file for file in self._files} def get_file_from_path(self, path): - """ Return a File instance with File.src_path equal to path. """ + """Return a File instance with File.src_path equal to path.""" return self.src_paths.get(os.path.normpath(path)) def append(self, file): - """ Append file to Files collection. """ + """Append file to Files collection.""" self._files.append(file) def remove(self, file): - """ Remove file from Files collection. """ + """Remove file from Files collection.""" self._files.remove(file) def copy_static_files(self, dirty=False): - """ Copy static files from source to destination. """ + """Copy static files from source to destination.""" for file in self: if not file.is_documentation_page(): file.copy_file(dirty) def documentation_pages(self): - """ Return iterable of all Markdown page file objects. """ + """Return iterable of all Markdown page file objects.""" return [file for file in self if file.is_documentation_page()] def static_pages(self): - """ Return iterable of all static page file objects. """ + """Return iterable of all static page file objects.""" return [file for file in self if file.is_static_page()] def media_files(self): - """ Return iterable of all file objects which are not documentation or static pages. """ + """Return iterable of all file objects which are not documentation or static pages.""" return [file for file in self if file.is_media_file()] def javascript_files(self): - """ Return iterable of all javascript file objects. """ + """Return iterable of all javascript file objects.""" return [file for file in self if file.is_javascript()] def css_files(self): - """ Return iterable of all CSS file objects. """ + """Return iterable of all CSS file objects.""" return [file for file in self if file.is_css()] def add_files_from_theme(self, env, config): - """ Retrieve static files from Jinja environment and add to collection. """ + """Retrieve static files from Jinja environment and add to collection.""" + def filter(name): # '.*' filters dot files/dirs at root level whereas '*/.*' filters nested levels patterns = ['.*', '*/.*', '*.py', '*.pyc', '*.html', '*readme*', 'mkdocs_theme.yml'] @@ -78,6 +80,7 @@ class Files: if fnmatch.fnmatch(name.lower(), pattern): return False return True + for path in env.list_templates(filter_func=filter): # Theme files do not override docs_dir files path = os.path.normpath(path) @@ -85,7 +88,9 @@ class Files: for dir in config['theme'].dirs: # Find the first theme dir which contains path if os.path.isfile(os.path.join(dir, path)): - self.append(File(path, dir, config['site_dir'], config['use_directory_urls'])) + self.append( + File(path, dir, config['site_dir'], config['use_directory_urls']) + ) break @@ -121,6 +126,7 @@ class File: File.url The url of the destination file relative to the destination directory as a string. """ + def __init__(self, path, src_dir, dest_dir, use_directory_urls): self.page = None self.src_path = os.path.normpath(path) @@ -132,10 +138,10 @@ class File: def __eq__(self, other): return ( - isinstance(other, self.__class__) and - self.src_path == other.src_path and - self.abs_src_path == other.abs_src_path and - self.url == other.url + isinstance(other, self.__class__) + and self.src_path == other.src_path + and self.abs_src_path == other.abs_src_path + and self.url == other.url ) def __repr__(self): @@ -145,13 +151,13 @@ class File: ) def _get_stem(self): - """ Return the name of the file without it's extension. """ + """Return the name of the file without it's extension.""" filename = os.path.basename(self.src_path) stem, ext = os.path.splitext(filename) return 'index' if stem in ('index', 'README') else stem def _get_dest_path(self, use_directory_urls): - """ Return destination path based on source path. """ + """Return destination path based on source path.""" if self.is_documentation_page(): parent, filename = os.path.split(self.src_path) if not use_directory_urls or self.name == 'index': @@ -164,7 +170,7 @@ class File: return self.src_path def _get_url(self, use_directory_urls): - """ Return url based in destination path. """ + """Return url based in destination path.""" url = self.dest_path.replace(os.path.sep, '/') dirname, filename = os.path.split(url) if use_directory_urls and filename == 'index.html': @@ -175,11 +181,11 @@ class File: return urlquote(url) def url_relative_to(self, other): - """ Return url for file relative to other file. """ + """Return url for file relative to other file.""" return utils.get_relative_url(self.url, other.url if isinstance(other, File) else other) def copy_file(self, dirty=False): - """ Copy source file to destination, ensuring parent directories exist. """ + """Copy source file to destination, ensuring parent directories exist.""" if dirty and not self.is_modified(): log.debug(f"Skip copying unmodified file: '{self.src_path}'") else: @@ -192,28 +198,28 @@ class File: return True def is_documentation_page(self): - """ Return True if file is a Markdown page. """ + """Return True if file is a Markdown page.""" return utils.is_markdown_file(self.src_path) def is_static_page(self): - """ Return True if file is a static page (html, xml, json). """ + """Return True if file is a static page (html, xml, json).""" return self.src_path.endswith(('.html', '.htm', '.xml', '.json')) def is_media_file(self): - """ Return True if file is not a documentation or static page. """ + """Return True if file is not a documentation or static page.""" return not (self.is_documentation_page() or self.is_static_page()) def is_javascript(self): - """ Return True if file is a JavaScript file. """ + """Return True if file is a JavaScript file.""" return self.src_path.endswith(('.js', '.javascript')) def is_css(self): - """ Return True if file is a CSS file. """ + """Return True if file is a CSS file.""" return self.src_path.endswith('.css') def get_files(config): - """ Walk the `docs_dir` and return a Files collection. """ + """Walk the `docs_dir` and return a Files collection.""" files = [] exclude = ['.*', '/templates'] @@ -234,15 +240,19 @@ def get_files(config): continue # Skip README.md if an index file also exists in dir if filename == 'README.md' and 'index.md' in filenames: - log.warning(f"Both index.md and README.md found. Skipping README.md from {source_dir}") + log.warning( + f"Both index.md and README.md found. Skipping README.md from {source_dir}" + ) continue - files.append(File(path, config['docs_dir'], config['site_dir'], config['use_directory_urls'])) + files.append( + File(path, config['docs_dir'], config['site_dir'], config['use_directory_urls']) + ) return Files(files) def _sort_files(filenames): - """ Always sort `index` or `README` as first filename in list. """ + """Always sort `index` or `README` as first filename in list.""" def key(f): if os.path.splitext(f)[0] in ['index', 'README']: @@ -253,7 +263,7 @@ def _sort_files(filenames): def _filter_paths(basename, path, is_dir, exclude): - """ .gitignore style file filtering. """ + """.gitignore style file filtering.""" for item in exclude: # Items ending in '/' apply only to directories. if item.endswith('/') and not is_dir: diff --git a/mkdocs/structure/nav.py b/mkdocs/structure/nav.py index b8883773..e5433868 100644 --- a/mkdocs/structure/nav.py +++ b/mkdocs/structure/nav.py @@ -44,11 +44,11 @@ class Section: return f"Section(title='{self.title}')" def _get_active(self): - """ Return active status of section. """ + """Return active status of section.""" return self.__active def _set_active(self, value): - """ Set active status of section and ancestors. """ + """Set active status of section and ancestors.""" self.__active = bool(value) if self.parent is not None: self.parent.active = bool(value) @@ -96,7 +96,7 @@ class Link: def get_navigation(files, config): - """ Build site navigation from config and files.""" + """Build site navigation from config and files.""" nav_config = config['nav'] or nest_paths(f.src_path for f in files.documentation_pages()) items = _data_to_navigation(nav_config, files, config) if not isinstance(items, list): @@ -114,7 +114,8 @@ def get_navigation(files, config): log.info( 'The following pages exist in the docs directory, but are not ' 'included in the "nav" configuration:\n - {}'.format( - '\n - '.join([file.src_path for file in missing_from_config])) + '\n - '.join([file.src_path for file in missing_from_config]) + ) ) # Any documentation files not found in the nav should still have an associated page, so we # create them here. The Page object will automatically be assigned to `file.page` during @@ -126,9 +127,7 @@ def get_navigation(files, config): for link in links: scheme, netloc, path, query, fragment = urlsplit(link.url) if scheme or netloc: - log.debug( - f"An external link to '{link.url}' is included in the 'nav' configuration." - ) + log.debug(f"An external link to '{link.url}' is included in the 'nav' configuration.") elif link.url.startswith('/'): log.debug( f"An absolute path to '{link.url}' is included in the 'nav' " @@ -147,15 +146,15 @@ def _data_to_navigation(data, files, config): if isinstance(data, dict): return [ _data_to_navigation((key, value), files, config) - if isinstance(value, str) else - Section(title=key, children=_data_to_navigation(value, files, config)) + if isinstance(value, str) + else Section(title=key, children=_data_to_navigation(value, files, config)) for key, value in data.items() ] elif isinstance(data, list): return [ _data_to_navigation(item, files, config)[0] - if isinstance(item, dict) and len(item) == 1 else - _data_to_navigation(item, files, config) + if isinstance(item, dict) and len(item) == 1 + else _data_to_navigation(item, files, config) for item in data ] title, path = data if isinstance(data, tuple) else (None, data) diff --git a/mkdocs/structure/pages.py b/mkdocs/structure/pages.py index db50ddb2..a1a68ae7 100644 --- a/mkdocs/structure/pages.py +++ b/mkdocs/structure/pages.py @@ -44,9 +44,9 @@ class Page: def __eq__(self, other): return ( - isinstance(other, self.__class__) and - self.title == other.title and - self.file == other.file + isinstance(other, self.__class__) + and self.title == other.title + and self.file == other.file ) def __repr__(self): @@ -58,11 +58,11 @@ class Page: return '{}{}'.format(' ' * depth, repr(self)) def _get_active(self): - """ Return active status of page. """ + """Return active status of page.""" return self.__active def _set_active(self, value): - """ Set active status of page and ancestors. """ + """Set active status of page and ancestors.""" self.__active = bool(value) if self.parent is not None: self.parent.active = bool(value) @@ -112,9 +112,7 @@ class Page: self.edit_url = None def read_source(self, config): - source = config['plugins'].run_event( - 'page_read_source', page=self, config=config - ) + source = config['plugins'].run_event('page_read_source', page=self, config=config) if source is None: try: with open(self.file.abs_src_path, encoding='utf-8-sig', errors='strict') as f: @@ -164,13 +162,11 @@ class Page: Convert the Markdown source file to HTML as per the config. """ - extensions = [ - _RelativePathExtension(self.file, files) - ] + config['markdown_extensions'] + extensions = [_RelativePathExtension(self.file, files)] + config['markdown_extensions'] md = markdown.Markdown( extensions=extensions, - extension_configs=config['mdx_configs'] or {} + extension_configs=config['mdx_configs'] or {}, ) self.content = md.convert(self.markdown) self.toc = get_toc(getattr(md, 'toc_tokens', [])) @@ -205,8 +201,15 @@ class _RelativePathTreeprocessor(Treeprocessor): def path_to_url(self, url): scheme, netloc, path, query, fragment = urlsplit(url) - if (scheme or netloc or not path or url.startswith('/') or url.startswith('\\') - or AMP_SUBSTITUTE in url or '.' not in os.path.split(path)[-1]): + if ( + scheme + or netloc + or not path + or url.startswith('/') + or url.startswith('\\') + or AMP_SUBSTITUTE in url + or '.' not in os.path.split(path)[-1] + ): # Ignore URLs unless they are a relative link to a source file. # AMP_SUBSTITUTE is used internally by Markdown only for email. # No '.' in the last part of a path indicates path does not point to a file. diff --git a/mkdocs/structure/toc.py b/mkdocs/structure/toc.py index 72250ffe..9541b036 100644 --- a/mkdocs/structure/toc.py +++ b/mkdocs/structure/toc.py @@ -19,6 +19,7 @@ class TableOfContents: """ Represents the table of contents for a given page. """ + def __init__(self, items): self.items = items @@ -36,6 +37,7 @@ class AnchorLink: """ A single entry in the table of contents. """ + def __init__(self, title, id, level): self.title, self.id, self.level = title, id, level self.children = [] diff --git a/mkdocs/tests/babel_cmd_tests.py b/mkdocs/tests/babel_cmd_tests.py index 1a47738a..59dfa6c8 100644 --- a/mkdocs/tests/babel_cmd_tests.py +++ b/mkdocs/tests/babel_cmd_tests.py @@ -9,14 +9,13 @@ BASE_DIR = path.normpath(path.join(path.abspath(path.dirname(__file__)), '../../ class ThemeMixinTests(unittest.TestCase): - def test_dict_entry_point(self): inst = babel.ThemeMixin() inst.distribution = Distribution() inst.distribution.entry_points = { 'mkdocs.themes': [ - 'mkdocs = mkdocs.themes.mkdocs' - ] + 'mkdocs = mkdocs.themes.mkdocs', + ], } inst.theme = 'mkdocs' self.assertEqual(inst.get_theme_dir(), path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs')) @@ -36,8 +35,8 @@ class ThemeMixinTests(unittest.TestCase): inst.distribution = Distribution() inst.distribution.entry_points = { 'mkdocs.themes': [ - 'mkdocs = mkdocs.themes.mkdocs' - ] + 'mkdocs = mkdocs.themes.mkdocs', + ], } inst.theme = None self.assertEqual(inst.get_theme_dir(), path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs')) @@ -49,10 +48,12 @@ class ThemeMixinTests(unittest.TestCase): 'mkdocs.themes': [ 'mkdocs = mkdocs.themes.mkdocs', 'readthedocs = mkdocs.themes.readthedocs', - ] + ], } inst.theme = 'readthedocs' - self.assertEqual(inst.get_theme_dir(), path.join(BASE_DIR, 'mkdocs', 'themes', 'readthedocs')) + self.assertEqual( + inst.get_theme_dir(), path.join(BASE_DIR, 'mkdocs', 'themes', 'readthedocs') + ) def test_multiple_entry_points_no_default(self): inst = babel.ThemeMixin() @@ -61,7 +62,7 @@ class ThemeMixinTests(unittest.TestCase): 'mkdocs.themes': [ 'mkdocs = mkdocs.themes.mkdocs', 'readthedocs = mkdocs.themes.readthedocs', - ] + ], } inst.theme = None self.assertRaises(DistutilsOptionError, inst.get_theme_dir) @@ -78,15 +79,14 @@ class ThemeMixinTests(unittest.TestCase): inst.distribution = Distribution() inst.distribution.entry_points = { 'mkdocs.themes': [ - 'mkdocs = mkdocs.themes.mkdocs' - ] + 'mkdocs = mkdocs.themes.mkdocs', + ], } inst.theme = 'undefined' self.assertRaises(DistutilsOptionError, inst.get_theme_dir) class CommandTests(unittest.TestCase): - def test_compile_catalog(self): dist = Distribution() dist.entry_points = ''' @@ -136,7 +136,9 @@ class CommandTests(unittest.TestCase): cmd.theme = 'mkdocs' cmd.finalize_options() self.assertEqual(cmd.input_paths, [path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs')]) - self.assertEqual(cmd.output_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot')) + self.assertEqual( + cmd.output_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot') + ) self.assertEqual(cmd.mapping_file, babel.DEFAULT_MAPPING_FILE) self.assertEqual(cmd.project, 'foo') self.assertEqual(cmd.version, '1.2') @@ -153,7 +155,9 @@ class CommandTests(unittest.TestCase): cmd.finalize_options() self.assertEqual(cmd.theme, 'mkdocs') self.assertEqual(cmd.input_paths, [path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs')]) - self.assertEqual(cmd.output_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot')) + self.assertEqual( + cmd.output_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot') + ) def test_extract_messages_ingore_theme(self): dist = Distribution(dict(name='foo', version='1.2')) @@ -182,7 +186,9 @@ class CommandTests(unittest.TestCase): cmd.input_paths = 'mkdocs/tests' cmd.finalize_options() self.assertEqual(cmd.input_paths, ['mkdocs/tests']) - self.assertEqual(cmd.output_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot')) + self.assertEqual( + cmd.output_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot') + ) def test_extract_messages_ingore_theme_for_output(self): dist = Distribution(dict(name='foo', version='1.2')) @@ -209,7 +215,9 @@ class CommandTests(unittest.TestCase): cmd.theme = 'mkdocs' cmd.locale = 'en' cmd.finalize_options() - self.assertEqual(cmd.input_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot')) + self.assertEqual( + cmd.input_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot') + ) self.assertEqual(cmd.output_dir, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/locales')) def test_init_catalog_default_theme(self): @@ -224,7 +232,9 @@ class CommandTests(unittest.TestCase): self.assertIsNone(cmd.theme) cmd.finalize_options() self.assertEqual(cmd.theme, 'mkdocs') - self.assertEqual(cmd.input_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot')) + self.assertEqual( + cmd.input_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot') + ) self.assertEqual(cmd.output_dir, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/locales')) def test_init_catalog_ignore_theme(self): @@ -270,7 +280,9 @@ class CommandTests(unittest.TestCase): cmd.locale = 'en' cmd.output_dir = 'foo/bar' cmd.finalize_options() - self.assertEqual(cmd.input_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot')) + self.assertEqual( + cmd.input_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot') + ) self.assertEqual(cmd.output_dir, 'foo/bar') def test_update_catalog(self): @@ -283,7 +295,9 @@ class CommandTests(unittest.TestCase): cmd.initialize_options() cmd.theme = 'mkdocs' cmd.finalize_options() - self.assertEqual(cmd.input_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot')) + self.assertEqual( + cmd.input_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot') + ) self.assertEqual(cmd.output_dir, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/locales')) def test_update_catalog_default_theme(self): @@ -298,7 +312,9 @@ class CommandTests(unittest.TestCase): self.assertIsNone(cmd.theme) cmd.finalize_options() self.assertEqual(cmd.theme, 'mkdocs') - self.assertEqual(cmd.input_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot')) + self.assertEqual( + cmd.input_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot') + ) self.assertEqual(cmd.output_dir, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/locales')) def test_update_catalog_ignore_theme(self): @@ -344,5 +360,7 @@ class CommandTests(unittest.TestCase): cmd.locale = 'en' cmd.output_dir = 'foo/bar' cmd.finalize_options() - self.assertEqual(cmd.input_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot')) + self.assertEqual( + cmd.input_file, path.join(BASE_DIR, 'mkdocs', 'themes', 'mkdocs/messages.pot') + ) self.assertEqual(cmd.output_dir, 'foo/bar') diff --git a/mkdocs/tests/base.py b/mkdocs/tests/base.py index 1270db3a..3fbb4943 100644 --- a/mkdocs/tests/base.py +++ b/mkdocs/tests/base.py @@ -14,17 +14,15 @@ def dedent(text): def get_markdown_toc(markdown_source): - """ Return TOC generated by Markdown parser from Markdown source text. """ + """Return TOC generated by Markdown parser from Markdown source text.""" md = markdown.Markdown(extensions=['toc']) md.convert(markdown_source) return md.toc_tokens def load_config(**cfg): - """ Helper to build a simple config for testing. """ - path_base = os.path.join( - os.path.abspath(os.path.dirname(__file__)), 'integration', 'minimal' - ) + """Helper to build a simple config for testing.""" + path_base = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'integration', 'minimal') cfg = cfg or {} if 'site_name' not in cfg: cfg['site_name'] = 'Example' @@ -33,7 +31,9 @@ def load_config(**cfg): if 'docs_dir' not in cfg: # Point to an actual dir to avoid a 'does not exist' error on validation. cfg['docs_dir'] = os.path.join(path_base, 'docs') - conf = config.Config(schema=config_defaults.get_schema(), config_file_path=cfg['config_file_path']) + conf = config.Config( + schema=config_defaults.get_schema(), config_file_path=cfg['config_file_path'] + ) conf.load_dict(cfg) errors_warnings = conf.validate() @@ -78,7 +78,9 @@ def tempdir(files=None, **kw): pth = os.path.join(td, path) utils.write_file(content.encode(encoding='utf-8'), pth) return fn(self, td, *args) + return wrapper + return decorator diff --git a/mkdocs/tests/build_tests.py b/mkdocs/tests/build_tests.py index 266309d5..c99c7e44 100644 --- a/mkdocs/tests/build_tests.py +++ b/mkdocs/tests/build_tests.py @@ -13,9 +13,11 @@ from mkdocs.utils import meta def build_page(title, path, config, md_src=''): - """ Helper which returns a Page object. """ + """Helper which returns a Page object.""" - files = Files([File(path, config['docs_dir'], config['site_dir'], config['use_directory_urls'])]) + files = Files( + [File(path, config['docs_dir'], config['site_dir'], config['use_directory_urls'])] + ) page = Page(title, list(files)[0], config) # Fake page.read_source() page.markdown, page.meta = meta.get_data(md_src) @@ -23,7 +25,6 @@ def build_page(title, path, config, md_src=''): class BuildTests(PathAssertionMixin, unittest.TestCase): - def _get_env_with_null_translations(self, config): env = config['theme'].get_env() env.add_extension('jinja2.ext.i18n') @@ -34,24 +35,26 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): def test_context_base_url_homepage(self): nav_cfg = [ - {'Home': 'index.md'} + {'Home': 'index.md'}, ] cfg = load_config(nav=nav_cfg, use_directory_urls=False) - files = Files([ + fs = [ File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - ]) + ] + files = Files(fs) nav = get_navigation(files, cfg) context = build.get_context(nav, files, cfg, nav.pages[0]) self.assertEqual(context['base_url'], '.') def test_context_base_url_homepage_use_directory_urls(self): nav_cfg = [ - {'Home': 'index.md'} + {'Home': 'index.md'}, ] cfg = load_config(nav=nav_cfg) - files = Files([ + fs = [ File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - ]) + ] + files = Files(fs) nav = get_navigation(files, cfg) context = build.get_context(nav, files, cfg, nav.pages[0]) self.assertEqual(context['base_url'], '.') @@ -59,13 +62,14 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): def test_context_base_url_nested_page(self): nav_cfg = [ {'Home': 'index.md'}, - {'Nested': 'foo/bar.md'} + {'Nested': 'foo/bar.md'}, ] cfg = load_config(nav=nav_cfg, use_directory_urls=False) - files = Files([ + fs = [ File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) - ]) + File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ] + files = Files(fs) nav = get_navigation(files, cfg) context = build.get_context(nav, files, cfg, nav.pages[1]) self.assertEqual(context['base_url'], '..') @@ -73,13 +77,14 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): def test_context_base_url_nested_page_use_directory_urls(self): nav_cfg = [ {'Home': 'index.md'}, - {'Nested': 'foo/bar.md'} + {'Nested': 'foo/bar.md'}, ] cfg = load_config(nav=nav_cfg) - files = Files([ + fs = [ File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) - ]) + File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ] + files = Files(fs) nav = get_navigation(files, cfg) context = build.get_context(nav, files, cfg, nav.pages[1]) self.assertEqual(context['base_url'], '../..') @@ -116,17 +121,18 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): def test_context_extra_css_js_from_homepage(self): nav_cfg = [ - {'Home': 'index.md'} + {'Home': 'index.md'}, ] cfg = load_config( nav=nav_cfg, extra_css=['style.css'], extra_javascript=['script.js'], - use_directory_urls=False + use_directory_urls=False, ) - files = Files([ + fs = [ File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - ]) + ] + files = Files(fs) nav = get_navigation(files, cfg) context = build.get_context(nav, files, cfg, nav.pages[0]) self.assertEqual(context['extra_css'], ['style.css']) @@ -135,18 +141,19 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): def test_context_extra_css_js_from_nested_page(self): nav_cfg = [ {'Home': 'index.md'}, - {'Nested': 'foo/bar.md'} + {'Nested': 'foo/bar.md'}, ] cfg = load_config( nav=nav_cfg, extra_css=['style.css'], extra_javascript=['script.js'], - use_directory_urls=False + use_directory_urls=False, ) - files = Files([ + fs = [ File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) - ]) + File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ] + files = Files(fs) nav = get_navigation(files, cfg) context = build.get_context(nav, files, cfg, nav.pages[1]) self.assertEqual(context['extra_css'], ['../style.css']) @@ -155,17 +162,18 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): def test_context_extra_css_js_from_nested_page_use_directory_urls(self): nav_cfg = [ {'Home': 'index.md'}, - {'Nested': 'foo/bar.md'} + {'Nested': 'foo/bar.md'}, ] cfg = load_config( nav=nav_cfg, extra_css=['style.css'], - extra_javascript=['script.js'] + extra_javascript=['script.js'], ) - files = Files([ + fs = [ File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) - ]) + File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ] + files = Files(fs) nav = get_navigation(files, cfg) context = build.get_context(nav, files, cfg, nav.pages[1]) self.assertEqual(context['extra_css'], ['../../style.css']) @@ -213,7 +221,9 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): build._build_theme_template('missing.html', env, mock.Mock(), cfg, mock.Mock()) self.assertEqual( cm.output, - ["WARNING:mkdocs.commands.build:Template skipped: 'missing.html' not found in theme directories."] + [ + "WARNING:mkdocs.commands.build:Template skipped: 'missing.html' not found in theme directories." + ], ) mock_write_file.assert_not_called() mock_build_template.assert_not_called() @@ -227,7 +237,7 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): build._build_theme_template('main.html', env, mock.Mock(), cfg, mock.Mock()) self.assertEqual( cm.output, - ["INFO:mkdocs.commands.build:Template skipped: 'main.html' generated empty output."] + ["INFO:mkdocs.commands.build:Template skipped: 'main.html' generated empty output."], ) mock_write_file.assert_not_called() mock_build_template.assert_called_once() @@ -237,48 +247,54 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): @mock.patch('mkdocs.commands.build.open', mock.mock_open(read_data='template content')) def test_build_extra_template(self): cfg = load_config() - files = Files([ + fs = [ File('foo.html', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - ]) + ] + files = Files(fs) build._build_extra_template('foo.html', files, cfg, mock.Mock()) @mock.patch('mkdocs.commands.build.open', mock.mock_open(read_data='template content')) def test_skip_missing_extra_template(self): cfg = load_config() - files = Files([ + fs = [ File('foo.html', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - ]) + ] + files = Files(fs) with self.assertLogs('mkdocs', level='INFO') as cm: build._build_extra_template('missing.html', files, cfg, mock.Mock()) self.assertEqual( cm.output, - ["WARNING:mkdocs.commands.build:Template skipped: 'missing.html' not found in docs_dir."] + [ + "WARNING:mkdocs.commands.build:Template skipped: 'missing.html' not found in docs_dir." + ], ) @mock.patch('mkdocs.commands.build.open', side_effect=OSError('Error message.')) def test_skip_ioerror_extra_template(self, mock_open): cfg = load_config() - files = Files([ + fs = [ File('foo.html', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - ]) + ] + files = Files(fs) with self.assertLogs('mkdocs', level='INFO') as cm: build._build_extra_template('foo.html', files, cfg, mock.Mock()) self.assertEqual( cm.output, - ["WARNING:mkdocs.commands.build:Error reading template 'foo.html': Error message."] + ["WARNING:mkdocs.commands.build:Error reading template 'foo.html': Error message."], ) @mock.patch('mkdocs.commands.build.open', mock.mock_open(read_data='')) def test_skip_extra_template_empty_output(self): cfg = load_config() - files = Files([ + fs = [ File('foo.html', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - ]) + ] + files = Files(fs) with self.assertLogs('mkdocs', level='INFO') as cm: build._build_extra_template('foo.html', files, cfg, mock.Mock()) self.assertEqual( cm.output, - ["INFO:mkdocs.commands.build:Template skipped: 'foo.html' generated empty output."] + ["INFO:mkdocs.commands.build:Template skipped: 'foo.html' generated empty output."], ) # Test build._populate_page @@ -298,7 +314,9 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): page = Page('Foo', file, cfg) build._populate_page(page, cfg, Files([file]), dirty=True) self.assertTrue(page.markdown.startswith('# Welcome to MkDocs')) - self.assertTrue(page.content.startswith('

Welcome to MkDocs

')) + self.assertTrue( + page.content.startswith('

Welcome to MkDocs

') + ) @tempdir(files={'index.md': 'page content'}) @tempdir(files={'index.html': '

page content

'}) @@ -321,15 +339,18 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): with self.assertRaises(OSError): build._populate_page(page, cfg, Files([file])) self.assertEqual( - cm.output, [ + cm.output, + [ 'ERROR:mkdocs.structure.pages:File not found: missing.md', - "ERROR:mkdocs.commands.build:Error reading page 'missing.md': Error message." - ] + "ERROR:mkdocs.commands.build:Error reading page 'missing.md': Error message.", + ], ) mock_open.assert_called_once() @tempdir(files={'index.md': 'page content'}) - @mock.patch('mkdocs.plugins.PluginCollection.run_event', side_effect=PluginError('Error message.')) + @mock.patch( + 'mkdocs.plugins.PluginCollection.run_event', side_effect=PluginError('Error message.') + ) def test_populate_page_read_plugin_error(self, docs_dir, mock_open): cfg = load_config(docs_dir=docs_dir) file = File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) @@ -338,9 +359,8 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): with self.assertRaises(PluginError): build._populate_page(page, cfg, Files([file])) self.assertEqual( - cm.output, [ - "ERROR:mkdocs.commands.build:Error reading page 'index.md':" - ] + cm.output, + ["ERROR:mkdocs.commands.build:Error reading page 'index.md':"], ) mock_open.assert_called_once() @@ -349,7 +369,8 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): @tempdir() def test_build_page(self, site_dir): cfg = load_config(site_dir=site_dir, nav=['index.md'], plugins=[]) - files = Files([File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + fs = [File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])] + files = Files(fs) nav = get_navigation(files, cfg) page = files.documentation_pages()[0].page # Fake populate page @@ -363,13 +384,16 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): @mock.patch('jinja2.environment.Template.render', return_value='') def test_build_page_empty(self, site_dir, render_mock): cfg = load_config(site_dir=site_dir, nav=['index.md'], plugins=[]) - files = Files([File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + fs = [File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])] + files = Files(fs) nav = get_navigation(files, cfg) with self.assertLogs('mkdocs', level='INFO') as cm: - build._build_page(files.documentation_pages()[0].page, cfg, files, nav, cfg['theme'].get_env()) + build._build_page( + files.documentation_pages()[0].page, cfg, files, nav, cfg['theme'].get_env() + ) self.assertEqual( cm.output, - ["INFO:mkdocs.commands.build:Page skipped: 'index.md'. Generated empty output."] + ["INFO:mkdocs.commands.build:Page skipped: 'index.md'. Generated empty output."], ) self.assertPathNotFile(site_dir, 'index.html') render_mock.assert_called_once() @@ -379,34 +403,41 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): @mock.patch('mkdocs.utils.write_file') def test_build_page_dirty_modified(self, site_dir, docs_dir, mock_write_file): cfg = load_config(docs_dir=docs_dir, site_dir=site_dir, nav=['index.md'], plugins=[]) - files = Files([File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + fs = [File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])] + files = Files(fs) nav = get_navigation(files, cfg) page = files.documentation_pages()[0].page # Fake populate page page.title = 'Title' page.markdown = 'new page content' page.content = '

new page content

' - build._build_page(page, cfg, files, nav, self._get_env_with_null_translations(cfg), dirty=True) + build._build_page( + page, cfg, files, nav, self._get_env_with_null_translations(cfg), dirty=True + ) mock_write_file.assert_not_called() @tempdir(files={'testing.html': '

page content

'}) @mock.patch('mkdocs.utils.write_file') def test_build_page_dirty_not_modified(self, site_dir, mock_write_file): cfg = load_config(site_dir=site_dir, nav=['testing.md'], plugins=[]) - files = Files([File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + fs = [File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])] + files = Files(fs) nav = get_navigation(files, cfg) page = files.documentation_pages()[0].page # Fake populate page page.title = 'Title' page.markdown = 'page content' page.content = '

page content

' - build._build_page(page, cfg, files, nav, self._get_env_with_null_translations(cfg), dirty=True) + build._build_page( + page, cfg, files, nav, self._get_env_with_null_translations(cfg), dirty=True + ) mock_write_file.assert_called_once() @tempdir() def test_build_page_custom_template(self, site_dir): cfg = load_config(site_dir=site_dir, nav=['index.md'], plugins=[]) - files = Files([File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + fs = [File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])] + files = Files(fs) nav = get_navigation(files, cfg) page = files.documentation_pages()[0].page # Fake populate page @@ -421,7 +452,8 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): @mock.patch('mkdocs.utils.write_file', side_effect=OSError('Error message.')) def test_build_page_error(self, site_dir, mock_write_file): cfg = load_config(site_dir=site_dir, nav=['index.md'], plugins=[]) - files = Files([File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + fs = [File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])] + files = Files(fs) nav = get_navigation(files, cfg) page = files.documentation_pages()[0].page # Fake populate page @@ -430,24 +462,21 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): page.content = '

page content

' with self.assertLogs('mkdocs', level='ERROR') as cm: with self.assertRaises(OSError): - build._build_page( - page, - cfg, - files, - nav, - self._get_env_with_null_translations(cfg) - ) + build._build_page(page, cfg, files, nav, self._get_env_with_null_translations(cfg)) self.assertEqual( cm.output, - ["ERROR:mkdocs.commands.build:Error building page 'index.md': Error message."] + ["ERROR:mkdocs.commands.build:Error building page 'index.md': Error message."], ) mock_write_file.assert_called_once() @tempdir() - @mock.patch('mkdocs.plugins.PluginCollection.run_event', side_effect=PluginError('Error message.')) + @mock.patch( + 'mkdocs.plugins.PluginCollection.run_event', side_effect=PluginError('Error message.') + ) def test_build_page_plugin_error(self, site_dir, mock_write_file): cfg = load_config(site_dir=site_dir, nav=['index.md'], plugins=[]) - files = Files([File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + fs = [File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])] + files = Files(fs) nav = get_navigation(files, cfg) page = files.documentation_pages()[0].page # Fake populate page @@ -459,20 +488,22 @@ class BuildTests(PathAssertionMixin, unittest.TestCase): build._build_page(page, cfg, files, nav, cfg['theme'].get_env()) self.assertEqual( cm.output, - ["ERROR:mkdocs.commands.build:Error building page 'index.md':"] + ["ERROR:mkdocs.commands.build:Error building page 'index.md':"], ) mock_write_file.assert_called_once() # Test build.build - @tempdir(files={ - 'index.md': 'page content', - 'empty.md': '', - 'img.jpg': '', - 'static.html': 'content', - '.hidden': 'content', - '.git/hidden': 'content' - }) + @tempdir( + files={ + 'index.md': 'page content', + 'empty.md': '', + 'img.jpg': '', + 'static.html': 'content', + '.hidden': 'content', + '.git/hidden': 'content', + } + ) @tempdir() def test_copying_media(self, site_dir, docs_dir): cfg = load_config(docs_dir=docs_dir, site_dir=site_dir) diff --git a/mkdocs/tests/cli_tests.py b/mkdocs/tests/cli_tests.py index 40397d72..b84f127c 100644 --- a/mkdocs/tests/cli_tests.py +++ b/mkdocs/tests/cli_tests.py @@ -11,15 +11,13 @@ from mkdocs import __main__ as cli class CLITests(unittest.TestCase): - def setUp(self): self.runner = CliRunner() @mock.patch('mkdocs.commands.serve.serve', autospec=True) def test_serve_default(self, mock_serve): - result = self.runner.invoke( - cli.cli, ["serve"], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ["serve"], catch_exceptions=False) self.assertEqual(result.exit_code, 0) mock_serve.assert_called_once_with( @@ -30,14 +28,15 @@ class CLITests(unittest.TestCase): theme=None, use_directory_urls=None, watch_theme=False, - watch=() + watch=(), ) @mock.patch('mkdocs.commands.serve.serve', autospec=True) def test_serve_config_file(self, mock_serve): result = self.runner.invoke( - cli.cli, ["serve", "--config-file", "mkdocs.yml"], catch_exceptions=False) + cli.cli, ["serve", "--config-file", "mkdocs.yml"], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_serve.call_count, 1) @@ -50,7 +49,8 @@ class CLITests(unittest.TestCase): def test_serve_dev_addr(self, mock_serve): result = self.runner.invoke( - cli.cli, ["serve", '--dev-addr', '0.0.0.0:80'], catch_exceptions=False) + cli.cli, ["serve", '--dev-addr', '0.0.0.0:80'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) mock_serve.assert_called_once_with( @@ -61,14 +61,13 @@ class CLITests(unittest.TestCase): theme=None, use_directory_urls=None, watch_theme=False, - watch=() + watch=(), ) @mock.patch('mkdocs.commands.serve.serve', autospec=True) def test_serve_strict(self, mock_serve): - result = self.runner.invoke( - cli.cli, ["serve", '--strict'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ["serve", '--strict'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) mock_serve.assert_called_once_with( @@ -79,14 +78,15 @@ class CLITests(unittest.TestCase): theme=None, use_directory_urls=None, watch_theme=False, - watch=() + watch=(), ) @mock.patch('mkdocs.commands.serve.serve', autospec=True) def test_serve_theme(self, mock_serve): result = self.runner.invoke( - cli.cli, ["serve", '--theme', 'readthedocs'], catch_exceptions=False) + cli.cli, ["serve", '--theme', 'readthedocs'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) mock_serve.assert_called_once_with( @@ -97,14 +97,15 @@ class CLITests(unittest.TestCase): theme='readthedocs', use_directory_urls=None, watch_theme=False, - watch=() + watch=(), ) @mock.patch('mkdocs.commands.serve.serve', autospec=True) def test_serve_use_directory_urls(self, mock_serve): result = self.runner.invoke( - cli.cli, ["serve", '--use-directory-urls'], catch_exceptions=False) + cli.cli, ["serve", '--use-directory-urls'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) mock_serve.assert_called_once_with( @@ -115,14 +116,15 @@ class CLITests(unittest.TestCase): theme=None, use_directory_urls=True, watch_theme=False, - watch=() + watch=(), ) @mock.patch('mkdocs.commands.serve.serve', autospec=True) def test_serve_no_directory_urls(self, mock_serve): result = self.runner.invoke( - cli.cli, ["serve", '--no-directory-urls'], catch_exceptions=False) + cli.cli, ["serve", '--no-directory-urls'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) mock_serve.assert_called_once_with( @@ -133,14 +135,13 @@ class CLITests(unittest.TestCase): theme=None, use_directory_urls=False, watch_theme=False, - watch=() + watch=(), ) @mock.patch('mkdocs.commands.serve.serve', autospec=True) def test_serve_livereload(self, mock_serve): - result = self.runner.invoke( - cli.cli, ["serve", '--livereload'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ["serve", '--livereload'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) mock_serve.assert_called_once_with( @@ -151,14 +152,13 @@ class CLITests(unittest.TestCase): theme=None, use_directory_urls=None, watch_theme=False, - watch=() + watch=(), ) @mock.patch('mkdocs.commands.serve.serve', autospec=True) def test_serve_no_livereload(self, mock_serve): - result = self.runner.invoke( - cli.cli, ["serve", '--no-livereload'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ["serve", '--no-livereload'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) mock_serve.assert_called_once_with( @@ -169,14 +169,13 @@ class CLITests(unittest.TestCase): theme=None, use_directory_urls=None, watch_theme=False, - watch=() + watch=(), ) @mock.patch('mkdocs.commands.serve.serve', autospec=True) def test_serve_dirtyreload(self, mock_serve): - result = self.runner.invoke( - cli.cli, ["serve", '--dirtyreload'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ["serve", '--dirtyreload'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) mock_serve.assert_called_once_with( @@ -187,14 +186,13 @@ class CLITests(unittest.TestCase): theme=None, use_directory_urls=None, watch_theme=False, - watch=() + watch=(), ) @mock.patch('mkdocs.commands.serve.serve', autospec=True) def test_serve_watch_theme(self, mock_serve): - result = self.runner.invoke( - cli.cli, ["serve", '--watch-theme'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ["serve", '--watch-theme'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) mock_serve.assert_called_once_with( @@ -205,15 +203,14 @@ class CLITests(unittest.TestCase): theme=None, use_directory_urls=None, watch_theme=True, - watch=() + watch=(), ) @mock.patch('mkdocs.config.load_config', autospec=True) @mock.patch('mkdocs.commands.build.build', autospec=True) def test_build_defaults(self, mock_build, mock_load_config): - result = self.runner.invoke( - cli.cli, ['build'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ['build'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) @@ -225,7 +222,7 @@ class CLITests(unittest.TestCase): strict=None, theme=None, use_directory_urls=None, - site_dir=None + site_dir=None, ) handler = logging._handlers.get('MkDocsStreamHandler') self.assertEqual(handler.level, logging.INFO) @@ -234,8 +231,7 @@ class CLITests(unittest.TestCase): @mock.patch('mkdocs.commands.build.build', autospec=True) def test_build_clean(self, mock_build, mock_load_config): - result = self.runner.invoke( - cli.cli, ['build', '--clean'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ['build', '--clean'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) @@ -247,8 +243,7 @@ class CLITests(unittest.TestCase): @mock.patch('mkdocs.commands.build.build', autospec=True) def test_build_dirty(self, mock_build, mock_load_config): - result = self.runner.invoke( - cli.cli, ['build', '--dirty'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ['build', '--dirty'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) @@ -261,7 +256,8 @@ class CLITests(unittest.TestCase): def test_build_config_file(self, mock_build, mock_load_config): result = self.runner.invoke( - cli.cli, ['build', '--config-file', 'mkdocs.yml'], catch_exceptions=False) + cli.cli, ['build', '--config-file', 'mkdocs.yml'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) @@ -275,8 +271,7 @@ class CLITests(unittest.TestCase): @mock.patch('mkdocs.commands.build.build', autospec=True) def test_build_strict(self, mock_build, mock_load_config): - result = self.runner.invoke( - cli.cli, ['build', '--strict'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ['build', '--strict'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) @@ -285,7 +280,7 @@ class CLITests(unittest.TestCase): strict=True, theme=None, use_directory_urls=None, - site_dir=None + site_dir=None, ) @mock.patch('mkdocs.config.load_config', autospec=True) @@ -293,7 +288,8 @@ class CLITests(unittest.TestCase): def test_build_theme(self, mock_build, mock_load_config): result = self.runner.invoke( - cli.cli, ['build', '--theme', 'readthedocs'], catch_exceptions=False) + cli.cli, ['build', '--theme', 'readthedocs'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) @@ -302,7 +298,7 @@ class CLITests(unittest.TestCase): strict=None, theme='readthedocs', use_directory_urls=None, - site_dir=None + site_dir=None, ) @mock.patch('mkdocs.config.load_config', autospec=True) @@ -310,7 +306,8 @@ class CLITests(unittest.TestCase): def test_build_use_directory_urls(self, mock_build, mock_load_config): result = self.runner.invoke( - cli.cli, ['build', '--use-directory-urls'], catch_exceptions=False) + cli.cli, ['build', '--use-directory-urls'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) @@ -319,7 +316,7 @@ class CLITests(unittest.TestCase): strict=None, theme=None, use_directory_urls=True, - site_dir=None + site_dir=None, ) @mock.patch('mkdocs.config.load_config', autospec=True) @@ -327,7 +324,8 @@ class CLITests(unittest.TestCase): def test_build_no_directory_urls(self, mock_build, mock_load_config): result = self.runner.invoke( - cli.cli, ['build', '--no-directory-urls'], catch_exceptions=False) + cli.cli, ['build', '--no-directory-urls'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) @@ -336,7 +334,7 @@ class CLITests(unittest.TestCase): strict=None, theme=None, use_directory_urls=False, - site_dir=None + site_dir=None, ) @mock.patch('mkdocs.config.load_config', autospec=True) @@ -344,7 +342,8 @@ class CLITests(unittest.TestCase): def test_build_site_dir(self, mock_build, mock_load_config): result = self.runner.invoke( - cli.cli, ['build', '--site-dir', 'custom'], catch_exceptions=False) + cli.cli, ['build', '--site-dir', 'custom'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) @@ -360,8 +359,7 @@ class CLITests(unittest.TestCase): @mock.patch('mkdocs.commands.build.build', autospec=True) def test_build_verbose(self, mock_build, mock_load_config): - result = self.runner.invoke( - cli.cli, ['build', '--verbose'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ['build', '--verbose'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) @@ -372,8 +370,7 @@ class CLITests(unittest.TestCase): @mock.patch('mkdocs.commands.build.build', autospec=True) def test_build_quiet(self, mock_build, mock_load_config): - result = self.runner.invoke( - cli.cli, ['build', '--quiet'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ['build', '--quiet'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) @@ -383,8 +380,7 @@ class CLITests(unittest.TestCase): @mock.patch('mkdocs.commands.new.new', autospec=True) def test_new(self, mock_new): - result = self.runner.invoke( - cli.cli, ["new", "project"], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ["new", "project"], catch_exceptions=False) self.assertEqual(result.exit_code, 0) mock_new.assert_called_once_with('project') @@ -394,8 +390,7 @@ class CLITests(unittest.TestCase): @mock.patch('mkdocs.commands.gh_deploy.gh_deploy', autospec=True) def test_gh_deploy_defaults(self, mock_gh_deploy, mock_build, mock_load_config): - result = self.runner.invoke( - cli.cli, ['gh-deploy'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ['gh-deploy'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) @@ -417,7 +412,7 @@ class CLITests(unittest.TestCase): strict=None, theme=None, use_directory_urls=None, - site_dir=None + site_dir=None, ) @mock.patch('mkdocs.config.load_config', autospec=True) @@ -425,8 +420,7 @@ class CLITests(unittest.TestCase): @mock.patch('mkdocs.commands.gh_deploy.gh_deploy', autospec=True) def test_gh_deploy_clean(self, mock_gh_deploy, mock_build, mock_load_config): - result = self.runner.invoke( - cli.cli, ['gh-deploy', '--clean'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ['gh-deploy', '--clean'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) @@ -440,8 +434,7 @@ class CLITests(unittest.TestCase): @mock.patch('mkdocs.commands.gh_deploy.gh_deploy', autospec=True) def test_gh_deploy_dirty(self, mock_gh_deploy, mock_build, mock_load_config): - result = self.runner.invoke( - cli.cli, ['gh-deploy', '--dirty'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ['gh-deploy', '--dirty'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) @@ -456,7 +449,8 @@ class CLITests(unittest.TestCase): def test_gh_deploy_config_file(self, mock_gh_deploy, mock_build, mock_load_config): result = self.runner.invoke( - cli.cli, ['gh-deploy', '--config-file', 'mkdocs.yml'], catch_exceptions=False) + cli.cli, ['gh-deploy', '--config-file', 'mkdocs.yml'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) @@ -473,7 +467,8 @@ class CLITests(unittest.TestCase): def test_gh_deploy_message(self, mock_gh_deploy, mock_build, mock_load_config): result = self.runner.invoke( - cli.cli, ['gh-deploy', '--message', 'A commit message'], catch_exceptions=False) + cli.cli, ['gh-deploy', '--message', 'A commit message'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) @@ -489,7 +484,8 @@ class CLITests(unittest.TestCase): def test_gh_deploy_remote_branch(self, mock_gh_deploy, mock_build, mock_load_config): result = self.runner.invoke( - cli.cli, ['gh-deploy', '--remote-branch', 'foo'], catch_exceptions=False) + cli.cli, ['gh-deploy', '--remote-branch', 'foo'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) @@ -501,7 +497,7 @@ class CLITests(unittest.TestCase): strict=None, theme=None, use_directory_urls=None, - site_dir=None + site_dir=None, ) @mock.patch('mkdocs.config.load_config', autospec=True) @@ -510,7 +506,8 @@ class CLITests(unittest.TestCase): def test_gh_deploy_remote_name(self, mock_gh_deploy, mock_build, mock_load_config): result = self.runner.invoke( - cli.cli, ['gh-deploy', '--remote-name', 'foo'], catch_exceptions=False) + cli.cli, ['gh-deploy', '--remote-name', 'foo'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) @@ -522,7 +519,7 @@ class CLITests(unittest.TestCase): strict=None, theme=None, use_directory_urls=None, - site_dir=None + site_dir=None, ) @mock.patch('mkdocs.config.load_config', autospec=True) @@ -530,8 +527,7 @@ class CLITests(unittest.TestCase): @mock.patch('mkdocs.commands.gh_deploy.gh_deploy', autospec=True) def test_gh_deploy_force(self, mock_gh_deploy, mock_build, mock_load_config): - result = self.runner.invoke( - cli.cli, ['gh-deploy', '--force'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ['gh-deploy', '--force'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) @@ -547,7 +543,8 @@ class CLITests(unittest.TestCase): def test_gh_deploy_ignore_version(self, mock_gh_deploy, mock_build, mock_load_config): result = self.runner.invoke( - cli.cli, ['gh-deploy', '--ignore-version'], catch_exceptions=False) + cli.cli, ['gh-deploy', '--ignore-version'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) @@ -562,8 +559,7 @@ class CLITests(unittest.TestCase): @mock.patch('mkdocs.commands.gh_deploy.gh_deploy', autospec=True) def test_gh_deploy_strict(self, mock_gh_deploy, mock_build, mock_load_config): - result = self.runner.invoke( - cli.cli, ['gh-deploy', '--strict'], catch_exceptions=False) + result = self.runner.invoke(cli.cli, ['gh-deploy', '--strict'], catch_exceptions=False) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) @@ -575,7 +571,7 @@ class CLITests(unittest.TestCase): strict=True, theme=None, use_directory_urls=None, - site_dir=None + site_dir=None, ) @mock.patch('mkdocs.config.load_config', autospec=True) @@ -584,7 +580,8 @@ class CLITests(unittest.TestCase): def test_gh_deploy_theme(self, mock_gh_deploy, mock_build, mock_load_config): result = self.runner.invoke( - cli.cli, ['gh-deploy', '--theme', 'readthedocs'], catch_exceptions=False) + cli.cli, ['gh-deploy', '--theme', 'readthedocs'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) @@ -596,7 +593,7 @@ class CLITests(unittest.TestCase): strict=None, theme='readthedocs', use_directory_urls=None, - site_dir=None + site_dir=None, ) @mock.patch('mkdocs.config.load_config', autospec=True) @@ -605,7 +602,8 @@ class CLITests(unittest.TestCase): def test_gh_deploy_use_directory_urls(self, mock_gh_deploy, mock_build, mock_load_config): result = self.runner.invoke( - cli.cli, ['gh-deploy', '--use-directory-urls'], catch_exceptions=False) + cli.cli, ['gh-deploy', '--use-directory-urls'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) @@ -617,7 +615,7 @@ class CLITests(unittest.TestCase): strict=None, theme=None, use_directory_urls=True, - site_dir=None + site_dir=None, ) @mock.patch('mkdocs.config.load_config', autospec=True) @@ -626,7 +624,8 @@ class CLITests(unittest.TestCase): def test_gh_deploy_no_directory_urls(self, mock_gh_deploy, mock_build, mock_load_config): result = self.runner.invoke( - cli.cli, ['gh-deploy', '--no-directory-urls'], catch_exceptions=False) + cli.cli, ['gh-deploy', '--no-directory-urls'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) @@ -638,7 +637,7 @@ class CLITests(unittest.TestCase): strict=None, theme=None, use_directory_urls=False, - site_dir=None + site_dir=None, ) @mock.patch('mkdocs.config.load_config', autospec=True) @@ -647,7 +646,8 @@ class CLITests(unittest.TestCase): def test_gh_deploy_site_dir(self, mock_gh_deploy, mock_build, mock_load_config): result = self.runner.invoke( - cli.cli, ['gh-deploy', '--site-dir', 'custom'], catch_exceptions=False) + cli.cli, ['gh-deploy', '--site-dir', 'custom'], catch_exceptions=False + ) self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) @@ -659,5 +659,5 @@ class CLITests(unittest.TestCase): strict=None, theme=None, use_directory_urls=None, - site_dir='custom' + site_dir='custom', ) diff --git a/mkdocs/tests/config/base_tests.py b/mkdocs/tests/config/base_tests.py index 0f1f8d54..b9a37822 100644 --- a/mkdocs/tests/config/base_tests.py +++ b/mkdocs/tests/config/base_tests.py @@ -9,20 +9,26 @@ from mkdocs.config.config_options import BaseConfigOption class ConfigBaseTests(unittest.TestCase): - def test_unrecognised_keys(self): c = base.Config(schema=defaults.get_schema()) - c.load_dict({ - 'not_a_valid_config_option': "test" - }) + c.load_dict( + { + 'not_a_valid_config_option': "test", + } + ) failed, warnings = c.validate() - self.assertEqual(warnings, [ - ('not_a_valid_config_option', - 'Unrecognised configuration name: not_a_valid_config_option') - ]) + self.assertEqual( + warnings, + [ + ( + 'not_a_valid_config_option', + 'Unrecognised configuration name: not_a_valid_config_option', + ) + ], + ) def test_missing_required(self): @@ -197,8 +203,7 @@ class ConfigBaseTests(unittest.TestCase): config_file = tempfile.NamedTemporaryFile('w', delete=False) try: - config_file.write( - "site_dir: output\nsite_uri: https://www.mkdocs.org\n") + config_file.write("site_dir: output\nsite_uri: https://www.mkdocs.org\n") config_file.flush() config_file.close() @@ -212,7 +217,7 @@ class ConfigBaseTests(unittest.TestCase): def pre_validation(self, config, key_name): raise base.ValidationError('pre_validation error') - c = base.Config(schema=(('invalid_option', InvalidConfigOption()), )) + c = base.Config(schema=(('invalid_option', InvalidConfigOption()),)) errors, warnings = c.validate() @@ -227,7 +232,7 @@ class ConfigBaseTests(unittest.TestCase): def run_validation(self, value): raise base.ValidationError('run_validation error') - c = base.Config(schema=(('invalid_option', InvalidConfigOption()), )) + c = base.Config(schema=(('invalid_option', InvalidConfigOption()),)) errors, warnings = c.validate() @@ -242,7 +247,7 @@ class ConfigBaseTests(unittest.TestCase): def post_validation(self, config, key_name): raise base.ValidationError('post_validation error') - c = base.Config(schema=(('invalid_option', InvalidConfigOption()), )) + c = base.Config(schema=(('invalid_option', InvalidConfigOption()),)) errors, warnings = c.validate() @@ -253,7 +258,8 @@ class ConfigBaseTests(unittest.TestCase): self.assertEqual(len(warnings), 0) def test_pre_and_run_validation_errors(self): - """ A pre_validation error does not stop run_validation from running. """ + """A pre_validation error does not stop run_validation from running.""" + class InvalidConfigOption(BaseConfigOption): def pre_validation(self, config, key_name): raise base.ValidationError('pre_validation error') @@ -261,7 +267,7 @@ class ConfigBaseTests(unittest.TestCase): def run_validation(self, value): raise base.ValidationError('run_validation error') - c = base.Config(schema=(('invalid_option', InvalidConfigOption()), )) + c = base.Config(schema=(('invalid_option', InvalidConfigOption()),)) errors, warnings = c.validate() @@ -275,7 +281,8 @@ class ConfigBaseTests(unittest.TestCase): self.assertEqual(len(warnings), 0) def test_run_and_post_validation_errors(self): - """ A run_validation error stops post_validation from running. """ + """A run_validation error stops post_validation from running.""" + class InvalidConfigOption(BaseConfigOption): def run_validation(self, value): raise base.ValidationError('run_validation error') @@ -283,7 +290,7 @@ class ConfigBaseTests(unittest.TestCase): def post_validation(self, config, key_name): raise base.ValidationError('post_validation error') - c = base.Config(schema=(('invalid_option', InvalidConfigOption()), )) + c = base.Config(schema=(('invalid_option', InvalidConfigOption()),)) errors, warnings = c.validate() @@ -304,16 +311,19 @@ class ConfigBaseTests(unittest.TestCase): def post_validation(self, config, key_name): self.warnings.append('post_validation warning') - c = base.Config(schema=(('invalid_option', InvalidConfigOption()), )) + c = base.Config(schema=(('invalid_option', InvalidConfigOption()),)) errors, warnings = c.validate() self.assertEqual(len(errors), 0) - self.assertEqual(warnings, [ - ('invalid_option', 'pre_validation warning'), - ('invalid_option', 'run_validation warning'), - ('invalid_option', 'post_validation warning'), - ]) + self.assertEqual( + warnings, + [ + ('invalid_option', 'pre_validation warning'), + ('invalid_option', 'run_validation warning'), + ('invalid_option', 'post_validation warning'), + ], + ) def test_load_from_file_with_relative_paths(self): """ diff --git a/mkdocs/tests/config/config_options_tests.py b/mkdocs/tests/config/config_options_tests.py index e344aec3..fd605545 100644 --- a/mkdocs/tests/config/config_options_tests.py +++ b/mkdocs/tests/config/config_options_tests.py @@ -12,7 +12,6 @@ from mkdocs.utils import yaml_load class OptionallyRequiredTest(unittest.TestCase): - def test_empty(self): option = config_options.OptionallyRequired() @@ -49,7 +48,6 @@ class OptionallyRequiredTest(unittest.TestCase): class TypeTest(unittest.TestCase): - def test_single_type(self): option = config_options.Type(str) @@ -79,7 +77,6 @@ class TypeTest(unittest.TestCase): class ChoiceTest(unittest.TestCase): - def test_valid_choice(self): option = config_options.Choice(('python', 'node')) value = option.validate('python') @@ -97,7 +94,6 @@ class ChoiceTest(unittest.TestCase): class DeprecatedTest(unittest.TestCase): - def test_deprecated_option_simple(self): option = config_options.Deprecated() option.pre_validation({'d': 'value'}, 'd') @@ -164,7 +160,6 @@ class DeprecatedTest(unittest.TestCase): class IpAddressTest(unittest.TestCase): - def test_valid_address(self): addr = '127.0.0.1:8000' @@ -256,7 +251,6 @@ class IpAddressTest(unittest.TestCase): class URLTest(unittest.TestCase): - def test_valid_url(self): option = config_options.URL() @@ -288,7 +282,6 @@ class URLTest(unittest.TestCase): class RepoURLTest(unittest.TestCase): - def test_repo_name_github(self): option = config_options.RepoURL() @@ -351,14 +344,12 @@ class RepoURLTest(unittest.TestCase): def test_repo_name_custom_and_empty_edit_uri(self): option = config_options.RepoURL() - config = {'repo_url': "https://github.com/mkdocs/mkdocs", - 'repo_name': 'mkdocs'} + config = {'repo_url': "https://github.com/mkdocs/mkdocs", 'repo_name': 'mkdocs'} option.post_validation(config, 'repo_url') self.assertEqual(config.get('edit_uri'), 'edit/master/docs/') class DirTest(unittest.TestCase): - def test_valid_dir(self): d = os.path.dirname(__file__) @@ -403,7 +394,7 @@ class DirTest(unittest.TestCase): ) test_config = { - 'dir': 'юникод' + 'dir': 'юникод', } cfg.load_dict(test_config) @@ -421,7 +412,7 @@ class DirTest(unittest.TestCase): ) test_config = { - 'dir': 'Übersicht'.encode(encoding=sys.getfilesystemencoding()) + 'dir': 'Übersicht'.encode(encoding=sys.getfilesystemencoding()), } cfg.load_dict(test_config) @@ -439,7 +430,7 @@ class DirTest(unittest.TestCase): ) test_config = { - 'dir': 'юникод'.encode(encoding='ISO 8859-5') + 'dir': 'юникод'.encode(encoding='ISO 8859-5'), } cfg.load_dict(test_config) @@ -457,7 +448,7 @@ class DirTest(unittest.TestCase): ) test_config = { - 'dir': 'foo' + 'dir': 'foo', } cfg.load_dict(test_config) @@ -476,7 +467,7 @@ class DirTest(unittest.TestCase): ) test_config = { - 'dir': '.' + 'dir': '.', } cfg.load_dict(test_config) @@ -488,7 +479,6 @@ class DirTest(unittest.TestCase): class ListOfPathsTest(unittest.TestCase): - def test_valid_path(self): paths = [os.path.dirname(__file__)] option = config_options.ListOfPaths() @@ -523,7 +513,7 @@ class ListOfPathsTest(unittest.TestCase): config_file_path=os.path.join(base_path, 'mkdocs.yml'), ) test_config = { - 'watch': ['foo'] + 'watch': ['foo'], } cfg.load_dict(test_config) fails, warns = cfg.validate() @@ -534,9 +524,8 @@ class ListOfPathsTest(unittest.TestCase): class SiteDirTest(unittest.TestCase): - def validate_config(self, config): - """ Given a config with values for site_dir and doc_dir, run site_dir post_validation. """ + """Given a config with values for site_dir and doc_dir, run site_dir post_validation.""" site_dir = config_options.SiteDir() docs_dir = config_options.Dir() @@ -571,7 +560,7 @@ class SiteDirTest(unittest.TestCase): {'docs_dir': 'docs', 'site_dir': ''}, {'docs_dir': '', 'site_dir': ''}, {'docs_dir': j('..', parent_dir, 'docs'), 'site_dir': 'docs'}, - {'docs_dir': 'docs', 'site_dir': '/'} + {'docs_dir': 'docs', 'site_dir': '/'}, ) for test_config in test_configs: @@ -594,7 +583,7 @@ class SiteDirTest(unittest.TestCase): self.validate_config(test_config) def test_common_prefix(self): - """ Legitimate settings with common prefixes should not fail validation. """ + """Legitimate settings with common prefixes should not fail validation.""" test_configs = ( {'docs_dir': 'docs', 'site_dir': 'docs-site'}, @@ -606,7 +595,6 @@ class SiteDirTest(unittest.TestCase): class ThemeTest(unittest.TestCase): - def test_theme_as_string(self): option = config_options.Theme() @@ -627,7 +615,7 @@ class ThemeTest(unittest.TestCase): def test_theme_as_simple_config(self): config = { - 'name': 'mkdocs' + 'name': 'mkdocs', } option = config_options.Theme() value = option.validate(config) @@ -639,7 +627,7 @@ class ThemeTest(unittest.TestCase): 'name': 'mkdocs', 'custom_dir': 'custom', 'static_templates': ['sitemap.html'], - 'show_sidebar': False + 'show_sidebar': False, } option = config_options.Theme() value = option.validate(config) @@ -648,7 +636,7 @@ class ThemeTest(unittest.TestCase): def test_theme_name_is_none(self): config = { - 'name': None + 'name': None, } option = config_options.Theme() value = option.validate(config) @@ -666,7 +654,7 @@ class ThemeTest(unittest.TestCase): def test_uninstalled_theme_as_config(self): config = { - 'name': 'mkdocs2' + 'name': 'mkdocs2', } option = config_options.Theme() with self.assertRaises(config_options.ValidationError): @@ -683,8 +671,8 @@ class ThemeTest(unittest.TestCase): config = { 'theme': { - 'name': None - } + 'name': None, + }, } option = config_options.Theme() with self.assertRaises(config_options.ValidationError): @@ -697,7 +685,7 @@ class ThemeTest(unittest.TestCase): 'theme': { 'name': None, 'custom_dir': abs_base_path + '/inexisting_custom_dir', - } + }, } option = config_options.Theme() with self.assertRaises(config_options.ValidationError): @@ -708,8 +696,8 @@ class ThemeTest(unittest.TestCase): config = { 'theme': { 'name': 'mkdocs', - 'locale': None - } + 'locale': None, + }, } option = config_options.Theme() with self.assertRaises(config_options.ValidationError): @@ -720,8 +708,8 @@ class ThemeTest(unittest.TestCase): config = { 'theme': { 'name': 'mkdocs', - 'locale': 0 - } + 'locale': 0, + }, } option = config_options.Theme() with self.assertRaises(config_options.ValidationError): @@ -732,8 +720,8 @@ class ThemeTest(unittest.TestCase): config = { 'theme': { 'name': 'mkdocs', - 'locale': 'fr' - } + 'locale': 'fr', + }, } option = config_options.Theme() option.post_validation(config, 'theme') @@ -741,19 +729,17 @@ class ThemeTest(unittest.TestCase): class NavTest(unittest.TestCase): - def test_old_format(self): option = config_options.Nav() with self.assertRaises(config_options.ValidationError) as cm: option.validate([['index.md']]) - self.assertEqual(str(cm.exception), "Expected nav item to be a string or dict, got a list: ['index.md']") + self.assertEqual( + str(cm.exception), "Expected nav item to be a string or dict, got a list: ['index.md']" + ) def test_provided_dict(self): option = config_options.Nav() - value = option.validate([ - 'index.md', - {"Page": "page.md"} - ]) + value = option.validate(['index.md', {"Page": "page.md"}]) self.assertEqual(['index.md', {'Page': 'page.md'}], value) option.post_validation({'extra_stuff': []}, 'extra_stuff') @@ -768,13 +754,17 @@ class NavTest(unittest.TestCase): self.assertEqual(option.warnings, []) def test_normal_nav(self): - nav = yaml_load(textwrap.dedent('''\ - - Home: index.md - - getting-started.md - - User Guide: - - Overview: user-guide/index.md - - Installation: user-guide/installation.md - ''').encode()) + nav = yaml_load( + textwrap.dedent( + '''\ + - Home: index.md + - getting-started.md + - User Guide: + - Overview: user-guide/index.md + - Installation: user-guide/installation.md + ''' + ).encode() + ) option = config_options.Nav() self.assertEqual(option.validate(nav), nav) @@ -796,7 +786,9 @@ class NavTest(unittest.TestCase): option = config_options.Nav() with self.assertRaises(config_options.ValidationError) as cm: option.validate([1]) - self.assertEqual(str(cm.exception), "Expected nav item to be a string or dict, got a int: 1") + self.assertEqual( + str(cm.exception), "Expected nav item to be a string or dict, got a int: 1" + ) def test_invalid_item_none(self): option = config_options.Nav() @@ -821,14 +813,18 @@ class NavTest(unittest.TestCase): nav = ['foo', {}] with self.assertRaises(config_options.ValidationError) as cm: option.validate(nav) - self.assertEqual(str(cm.exception), "Expected nav item to be a dict of size 1, got a dict: {}") + self.assertEqual( + str(cm.exception), "Expected nav item to be a dict of size 1, got a dict: {}" + ) def test_invalid_nested_list(self): option = config_options.Nav() nav = [{'aaa': [[{"bbb": "user-guide/index.md"}]]}] with self.assertRaises(config_options.ValidationError) as cm: option.validate(nav) - msg = "Expected nav item to be a string or dict, got a list: [{'bbb': 'user-guide/index.md'}]" + msg = ( + "Expected nav item to be a string or dict, got a list: [{'bbb': 'user-guide/index.md'}]" + ) self.assertEqual(str(cm.exception), msg) def test_invalid_children_oversized_dict(self): @@ -842,11 +838,12 @@ class NavTest(unittest.TestCase): def test_warns_for_dict(self): option = config_options.Nav() option.validate([{"a": {"b": "c.md", "d": "e.md"}}]) - self.assertEqual(option.warnings, ["Expected nav to be a list, got dict with keys ('b', 'd')"]) + self.assertEqual( + option.warnings, ["Expected nav to be a list, got dict with keys ('b', 'd')"] + ) class PrivateTest(unittest.TestCase): - def test_defined(self): option = config_options.Private() @@ -855,19 +852,21 @@ class PrivateTest(unittest.TestCase): class MarkdownExtensionsTest(unittest.TestCase): - @patch('markdown.Markdown') def test_simple_list(self, mockMd): option = config_options.MarkdownExtensions() config = { - 'markdown_extensions': ['foo', 'bar'] + 'markdown_extensions': ['foo', 'bar'], } config['markdown_extensions'] = option.validate(config['markdown_extensions']) option.post_validation(config, 'markdown_extensions') - self.assertEqual({ - 'markdown_extensions': ['foo', 'bar'], - 'mdx_configs': {} - }, config) + self.assertEqual( + { + 'markdown_extensions': ['foo', 'bar'], + 'mdx_configs': {}, + }, + config, + ) @patch('markdown.Markdown') def test_list_dicts(self, mockMd): @@ -876,18 +875,21 @@ class MarkdownExtensionsTest(unittest.TestCase): 'markdown_extensions': [ {'foo': {'foo_option': 'foo value'}}, {'bar': {'bar_option': 'bar value'}}, - {'baz': None} + {'baz': None}, ] } config['markdown_extensions'] = option.validate(config['markdown_extensions']) option.post_validation(config, 'markdown_extensions') - self.assertEqual({ - 'markdown_extensions': ['foo', 'bar', 'baz'], - 'mdx_configs': { - 'foo': {'foo_option': 'foo value'}, - 'bar': {'bar_option': 'bar value'} - } - }, config) + self.assertEqual( + { + 'markdown_extensions': ['foo', 'bar', 'baz'], + 'mdx_configs': { + 'foo': {'foo_option': 'foo value'}, + 'bar': {'bar_option': 'bar value'}, + }, + }, + config, + ) @patch('markdown.Markdown') def test_mixed_list(self, mockMd): @@ -895,17 +897,20 @@ class MarkdownExtensionsTest(unittest.TestCase): config = { 'markdown_extensions': [ 'foo', - {'bar': {'bar_option': 'bar value'}} + {'bar': {'bar_option': 'bar value'}}, ] } config['markdown_extensions'] = option.validate(config['markdown_extensions']) option.post_validation(config, 'markdown_extensions') - self.assertEqual({ - 'markdown_extensions': ['foo', 'bar'], - 'mdx_configs': { - 'bar': {'bar_option': 'bar value'} - } - }, config) + self.assertEqual( + { + 'markdown_extensions': ['foo', 'bar'], + 'mdx_configs': { + 'bar': {'bar_option': 'bar value'}, + }, + }, + config, + ) @patch('markdown.Markdown') def test_dict_of_dicts(self, mockMd): @@ -914,86 +919,104 @@ class MarkdownExtensionsTest(unittest.TestCase): 'markdown_extensions': { 'foo': {'foo_option': 'foo value'}, 'bar': {'bar_option': 'bar value'}, - 'baz': {} + 'baz': {}, } } config['markdown_extensions'] = option.validate(config['markdown_extensions']) option.post_validation(config, 'markdown_extensions') - self.assertEqual({ - 'markdown_extensions': ['foo', 'bar', 'baz'], - 'mdx_configs': { - 'foo': {'foo_option': 'foo value'}, - 'bar': {'bar_option': 'bar value'} - } - }, config) + self.assertEqual( + { + 'markdown_extensions': ['foo', 'bar', 'baz'], + 'mdx_configs': { + 'foo': {'foo_option': 'foo value'}, + 'bar': {'bar_option': 'bar value'}, + }, + }, + config, + ) @patch('markdown.Markdown') def test_builtins(self, mockMd): option = config_options.MarkdownExtensions(builtins=['meta', 'toc']) config = { - 'markdown_extensions': ['foo', 'bar'] + 'markdown_extensions': ['foo', 'bar'], } config['markdown_extensions'] = option.validate(config['markdown_extensions']) option.post_validation(config, 'markdown_extensions') - self.assertEqual({ - 'markdown_extensions': ['meta', 'toc', 'foo', 'bar'], - 'mdx_configs': {} - }, config) + self.assertEqual( + { + 'markdown_extensions': ['meta', 'toc', 'foo', 'bar'], + 'mdx_configs': {}, + }, + config, + ) def test_duplicates(self): option = config_options.MarkdownExtensions(builtins=['meta', 'toc']) config = { - 'markdown_extensions': ['meta', 'toc'] + 'markdown_extensions': ['meta', 'toc'], } config['markdown_extensions'] = option.validate(config['markdown_extensions']) option.post_validation(config, 'markdown_extensions') - self.assertEqual({ - 'markdown_extensions': ['meta', 'toc'], - 'mdx_configs': {} - }, config) + self.assertEqual( + { + 'markdown_extensions': ['meta', 'toc'], + 'mdx_configs': {}, + }, + config, + ) def test_builtins_config(self): option = config_options.MarkdownExtensions(builtins=['meta', 'toc']) config = { 'markdown_extensions': [ - {'toc': {'permalink': True}} - ] + {'toc': {'permalink': True}}, + ], } config['markdown_extensions'] = option.validate(config['markdown_extensions']) option.post_validation(config, 'markdown_extensions') - self.assertEqual({ - 'markdown_extensions': ['meta', 'toc'], - 'mdx_configs': {'toc': {'permalink': True}} - }, config) + self.assertEqual( + { + 'markdown_extensions': ['meta', 'toc'], + 'mdx_configs': {'toc': {'permalink': True}}, + }, + config, + ) @patch('markdown.Markdown') def test_configkey(self, mockMd): option = config_options.MarkdownExtensions(configkey='bar') config = { 'markdown_extensions': [ - {'foo': {'foo_option': 'foo value'}} + {'foo': {'foo_option': 'foo value'}}, ] } config['markdown_extensions'] = option.validate(config['markdown_extensions']) option.post_validation(config, 'markdown_extensions') - self.assertEqual({ - 'markdown_extensions': ['foo'], - 'bar': { - 'foo': {'foo_option': 'foo value'} - } - }, config) + self.assertEqual( + { + 'markdown_extensions': ['foo'], + 'bar': { + 'foo': {'foo_option': 'foo value'}, + }, + }, + config, + ) def test_none(self): option = config_options.MarkdownExtensions(default=[]) config = { - 'markdown_extensions': None + 'markdown_extensions': None, } config['markdown_extensions'] = option.validate(config['markdown_extensions']) option.post_validation(config, 'markdown_extensions') - self.assertEqual({ - 'markdown_extensions': [], - 'mdx_configs': {} - }, config) + self.assertEqual( + { + 'markdown_extensions': [], + 'mdx_configs': {}, + }, + config, + ) @patch('markdown.Markdown') def test_not_list(self, mockMd): @@ -1006,8 +1029,8 @@ class MarkdownExtensionsTest(unittest.TestCase): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': [ - {'foo': 'not a dict'} - ] + {'foo': 'not a dict'}, + ], } with self.assertRaises(config_options.ValidationError): option.validate(config['markdown_extensions']) @@ -1017,8 +1040,8 @@ class MarkdownExtensionsTest(unittest.TestCase): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': [ - ['not a dict'] - ] + ['not a dict'], + ], } with self.assertRaises(config_options.ValidationError): option.validate(config['markdown_extensions']) @@ -1028,8 +1051,8 @@ class MarkdownExtensionsTest(unittest.TestCase): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': [ - {'key1': 'value', 'key2': 'too many keys'} - ] + {'key1': 'value', 'key2': 'too many keys'}, + ], } with self.assertRaises(config_options.ValidationError): option.validate(config['markdown_extensions']) @@ -1037,7 +1060,7 @@ class MarkdownExtensionsTest(unittest.TestCase): def test_unknown_extension(self): option = config_options.MarkdownExtensions() config = { - 'markdown_extensions': ['unknown'] + 'markdown_extensions': ['unknown'], } with self.assertRaises(config_options.ValidationError): option.validate(config['markdown_extensions']) diff --git a/mkdocs/tests/config/config_tests.py b/mkdocs/tests/config/config_tests.py index 295a9bd4..fe24fd3a 100644 --- a/mkdocs/tests/config/config_tests.py +++ b/mkdocs/tests/config/config_tests.py @@ -38,11 +38,13 @@ class ConfigTests(unittest.TestCase): config.load_config(config_file='/path/that/is/not/real') def test_invalid_config(self): - file_contents = dedent(""" - - ['index.md', 'Introduction'] - - ['index.md', 'Introduction'] - - ['index.md', 'Introduction'] - """) + file_contents = dedent( + """ + - ['index.md', 'Introduction'] + - ['index.md', 'Introduction'] + - ['index.md', 'Introduction'] + """ + ) config_file = tempfile.NamedTemporaryFile('w', delete=False) try: config_file.write(file_contents) @@ -61,15 +63,15 @@ class ConfigTests(unittest.TestCase): """ expected_result = { 'site_name': 'Example', - 'nav': [ - {'Introduction': 'index.md'} - ], + 'nav': [{'Introduction': 'index.md'}], } - file_contents = dedent(""" - site_name: Example - nav: - - 'Introduction': 'index.md' - """) + file_contents = dedent( + """ + site_name: Example + nav: + - 'Introduction': 'index.md' + """ + ) with TemporaryDirectory() as temp_path: os.mkdir(os.path.join(temp_path, 'docs')) config_path = os.path.join(temp_path, 'mkdocs.yml') @@ -90,16 +92,18 @@ class ConfigTests(unittest.TestCase): {"theme": "readthedocs"}, # builtin theme {"theme": {'name': 'readthedocs'}}, # builtin as complex {"theme": {'name': None, 'custom_dir': mytheme}}, # custom only as complex - {"theme": {'name': 'readthedocs', 'custom_dir': custom}}, # builtin and custom as complex + { + "theme": {'name': 'readthedocs', 'custom_dir': custom} + }, # builtin and custom as complex { # user defined variables 'theme': { 'name': 'mkdocs', 'locale': 'fr', 'static_templates': ['foo.html'], 'show_sidebar': False, - 'some_var': 'bar' + 'some_var': 'bar', } - } + }, ] mkdocs_dir = os.path.abspath(os.path.dirname(mkdocs.__file__)) @@ -120,9 +124,10 @@ class ConfigTests(unittest.TestCase): 'hljs_languages': [], 'navigation_depth': 2, 'nav_style': 'primary', - 'shortcuts': {'help': 191, 'next': 78, 'previous': 80, 'search': 83} - } - }, { + 'shortcuts': {'help': 191, 'next': 78, 'previous': 80, 'search': 83}, + }, + }, + { 'dirs': [os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir], 'static_templates': ['404.html', 'sitemap.xml'], 'vars': { @@ -138,9 +143,10 @@ class ConfigTests(unittest.TestCase): 'sticky_navigation': True, 'logo': None, 'titles_only': False, - 'collapse_navigation': True - } - }, { + 'collapse_navigation': True, + }, + }, + { 'dirs': [os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir], 'static_templates': ['404.html', 'sitemap.xml'], 'vars': { @@ -156,13 +162,15 @@ class ConfigTests(unittest.TestCase): 'sticky_navigation': True, 'logo': None, 'titles_only': False, - 'collapse_navigation': True - } - }, { + 'collapse_navigation': True, + }, + }, + { 'dirs': [mytheme, mkdocs_templates_dir], 'static_templates': ['sitemap.xml'], - 'vars': {'locale': parse_locale('en')} - }, { + 'vars': {'locale': parse_locale('en')}, + }, + { 'dirs': [custom, os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir], 'static_templates': ['404.html', 'sitemap.xml'], 'vars': { @@ -178,9 +186,10 @@ class ConfigTests(unittest.TestCase): 'sticky_navigation': True, 'logo': None, 'titles_only': False, - 'collapse_navigation': True - } - }, { + 'collapse_navigation': True, + }, + }, + { 'dirs': [os.path.join(theme_dir, 'mkdocs'), mkdocs_templates_dir], 'static_templates': ['404.html', 'sitemap.xml', 'foo.html'], 'vars': { @@ -195,9 +204,9 @@ class ConfigTests(unittest.TestCase): 'hljs_languages': [], 'navigation_depth': 2, 'nav_style': 'primary', - 'shortcuts': {'help': 191, 'next': 78, 'previous': 80, 'search': 83} - } - } + 'shortcuts': {'help': 191, 'next': 78, 'previous': 80, 'search': 83}, + }, + }, ) for config_contents, result in zip(configs, results): @@ -212,25 +221,29 @@ class ConfigTests(unittest.TestCase): def test_empty_nav(self): conf = config.Config(schema=defaults.get_schema()) - conf.load_dict({ - 'site_name': 'Example', - 'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml') - }) + conf.load_dict( + { + 'site_name': 'Example', + 'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml'), + } + ) conf.validate() self.assertEqual(conf['nav'], None) def test_error_on_pages(self): conf = config.Config(schema=defaults.get_schema()) - conf.load_dict({ - 'site_name': 'Example', - 'pages': ['index.md', 'about.md'], - }) + conf.load_dict( + { + 'site_name': 'Example', + 'pages': ['index.md', 'about.md'], + } + ) errors, warnings = conf.validate() self.assertEqual(warnings, []) self.assertEqual(len(errors), 1) self.assertEqual( str(errors[0][1]), - "The configuration option 'pages' was removed from MkDocs. Use 'nav' instead." + "The configuration option 'pages' was removed from MkDocs. Use 'nav' instead.", ) def test_doc_dir_in_site_dir(self): @@ -247,7 +260,7 @@ class ConfigTests(unittest.TestCase): ) conf = { - 'config_file_path': j(os.path.abspath('..'), 'mkdocs.yml') + 'config_file_path': j(os.path.abspath('..'), 'mkdocs.yml'), } for test_config in test_configs: @@ -256,11 +269,13 @@ class ConfigTests(unittest.TestCase): patch.update(test_config) # Same as the default schema, but don't verify the docs_dir exists. - c = config.Config(schema=( - ('docs_dir', config_options.Dir(default='docs')), - ('site_dir', config_options.SiteDir(default='site')), - ('config_file_path', config_options.Type(str)) - )) + c = config.Config( + schema=( + ('docs_dir', config_options.Dir(default='docs')), + ('site_dir', config_options.SiteDir(default='site')), + ('config_file_path', config_options.Type(str)), + ) + ) c.load_dict(patch) errors, warnings = c.validate() diff --git a/mkdocs/tests/gh_deploy_tests.py b/mkdocs/tests/gh_deploy_tests.py index 4670e21a..45acd044 100644 --- a/mkdocs/tests/gh_deploy_tests.py +++ b/mkdocs/tests/gh_deploy_tests.py @@ -9,7 +9,6 @@ from mkdocs import __version__ class TestGitHubDeploy(unittest.TestCase): - @mock.patch('subprocess.Popen') def test_is_cwd_git_repo(self, mock_popeno): @@ -36,7 +35,7 @@ class TestGitHubDeploy(unittest.TestCase): mock_popeno().communicate.return_value = ( b'git@github.com:mkdocs/mkdocs.git\n', - b'' + b'', ) expected = ('git@', 'mkdocs/mkdocs.git') @@ -47,7 +46,7 @@ class TestGitHubDeploy(unittest.TestCase): mock_popeno().communicate.return_value = ( b'https://github.com/mkdocs/mkdocs.git\n', - b'' + b'', ) expected = ('https://', 'mkdocs/mkdocs.git') @@ -58,7 +57,7 @@ class TestGitHubDeploy(unittest.TestCase): mock_popeno().communicate.return_value = ( b'https://notgh.com/mkdocs/mkdocs.git\n', - b'' + b'', ) expected = (None, None) @@ -82,8 +81,9 @@ class TestGitHubDeploy(unittest.TestCase): @mock.patch('mkdocs.commands.gh_deploy._check_version') @mock.patch('ghp_import.ghp_import') @mock.patch('os.path.isfile', return_value=False) - def test_deploy_no_cname(self, mock_isfile, mock_import, check_version, get_remote, - get_sha, is_repo): + def test_deploy_no_cname( + self, mock_isfile, mock_import, check_version, get_remote, get_sha, is_repo + ): config = load_config( remote_branch='test', @@ -92,8 +92,9 @@ class TestGitHubDeploy(unittest.TestCase): @mock.patch('mkdocs.commands.gh_deploy._is_cwd_git_repo', return_value=True) @mock.patch('mkdocs.commands.gh_deploy._get_current_sha', return_value='shashas') - @mock.patch('mkdocs.commands.gh_deploy._get_remote_url', return_value=( - 'git@', 'mkdocs/mkdocs.git')) + @mock.patch( + 'mkdocs.commands.gh_deploy._get_remote_url', return_value=('git@', 'mkdocs/mkdocs.git') + ) @mock.patch('mkdocs.commands.gh_deploy._check_version') @mock.patch('ghp_import.ghp_import') def test_deploy_hostname(self, mock_import, check_version, get_remote, get_sha, is_repo): @@ -108,7 +109,9 @@ class TestGitHubDeploy(unittest.TestCase): @mock.patch('mkdocs.commands.gh_deploy._get_remote_url', return_value=(None, None)) @mock.patch('mkdocs.commands.gh_deploy._check_version') @mock.patch('ghp_import.ghp_import') - def test_deploy_ignore_version_default(self, mock_import, check_version, get_remote, get_sha, is_repo): + def test_deploy_ignore_version_default( + self, mock_import, check_version, get_remote, get_sha, is_repo + ): config = load_config( remote_branch='test', @@ -150,31 +153,42 @@ class TestGitHubDeploy(unittest.TestCase): class TestGitHubDeployLogs(unittest.TestCase): - @mock.patch('subprocess.Popen') def test_mkdocs_newer(self, mock_popeno): - mock_popeno().communicate.return_value = (b'Deployed 12345678 with MkDocs version: 0.1.2\n', b'') + mock_popeno().communicate.return_value = ( + b'Deployed 12345678 with MkDocs version: 0.1.2\n', + b'', + ) with self.assertLogs('mkdocs', level='INFO') as cm: gh_deploy._check_version('gh-pages') self.assertEqual( - cm.output, ['INFO:mkdocs.commands.gh_deploy:Previous deployment was done with MkDocs ' - f'version 0.1.2; you are deploying with a newer version ({__version__})'] + cm.output, + [ + 'INFO:mkdocs.commands.gh_deploy:Previous deployment was done with MkDocs ' + f'version 0.1.2; you are deploying with a newer version ({__version__})' + ], ) @mock.patch('subprocess.Popen') def test_mkdocs_older(self, mock_popeno): - mock_popeno().communicate.return_value = (b'Deployed 12345678 with MkDocs version: 10.1.2\n', b'') + mock_popeno().communicate.return_value = ( + b'Deployed 12345678 with MkDocs version: 10.1.2\n', + b'', + ) with self.assertLogs('mkdocs', level='ERROR') as cm: with self.assertRaises(Abort): gh_deploy._check_version('gh-pages') self.assertEqual( - cm.output, ['ERROR:mkdocs.commands.gh_deploy:Deployment terminated: Previous deployment was made with ' - f'MkDocs version 10.1.2; you are attempting to deploy with an older version ({__version__}).' - ' Use --ignore-version to deploy anyway.'] + cm.output, + [ + 'ERROR:mkdocs.commands.gh_deploy:Deployment terminated: Previous deployment was made with ' + f'MkDocs version 10.1.2; you are attempting to deploy with an older version ({__version__}).' + ' Use --ignore-version to deploy anyway.' + ], ) @mock.patch('subprocess.Popen') @@ -186,5 +200,7 @@ class TestGitHubDeployLogs(unittest.TestCase): gh_deploy._check_version('gh-pages') self.assertEqual( cm.output, - ['WARNING:mkdocs.commands.gh_deploy:Version check skipped: No version specified in previous deployment.'] + [ + 'WARNING:mkdocs.commands.gh_deploy:Version check skipped: No version specified in previous deployment.' + ], ) diff --git a/mkdocs/tests/integration.py b/mkdocs/tests/integration.py index ebbe8897..4b594f1c 100644 --- a/mkdocs/tests/integration.py +++ b/mkdocs/tests/integration.py @@ -31,21 +31,22 @@ TEST_PROJECTS = os.path.abspath(os.path.join(DIR, 'integration')) @click.command() -@click.option('--output', - help="The output directory to use when building themes", - type=click.Path(file_okay=False, writable=True), - required=True) +@click.option( + '--output', + help="The output directory to use when building themes", + type=click.Path(file_okay=False, writable=True), + required=True, +) def main(output=None): log.propagate = False stream = logging.StreamHandler() - formatter = logging.Formatter( - "\033[1m\033[1;32m *** %(message)s *** \033[0m") + formatter = logging.Formatter("\033[1m\033[1;32m *** %(message)s *** \033[0m") stream.setFormatter(formatter) log.addHandler(stream) log.setLevel(logging.DEBUG) - base_cmd = ['mkdocs', 'build', '-s', '-v', '--site-dir', ] + base_cmd = ['mkdocs', 'build', '-s', '-v', '--site-dir'] log.debug("Building installed themes.") for theme in sorted(MKDOCS_THEMES): @@ -60,7 +61,7 @@ def main(output=None): log.debug(f"Building test project: {project}") project_dir = os.path.join(TEST_PROJECTS, project) out = os.path.join(output, project) - command = base_cmd + [out, ] + command = base_cmd + [out] subprocess.check_call(command, cwd=project_dir) log.debug(f"Theme and integration builds are in {output}") diff --git a/mkdocs/tests/livereload_tests.py b/mkdocs/tests/livereload_tests.py index ecbbaecc..6d838c14 100644 --- a/mkdocs/tests/livereload_tests.py +++ b/mkdocs/tests/livereload_tests.py @@ -64,9 +64,7 @@ def do_request(server, content): return headers, content.decode() -SCRIPT_REGEX = ( - r'' -) +SCRIPT_REGEX = r'' class BuildTests(unittest.TestCase): diff --git a/mkdocs/tests/localization_tests.py b/mkdocs/tests/localization_tests.py index a7ceff96..d7312c6c 100644 --- a/mkdocs/tests/localization_tests.py +++ b/mkdocs/tests/localization_tests.py @@ -9,7 +9,6 @@ from mkdocs.config.base import ValidationError class LocalizationTests(unittest.TestCase): - def setUp(self): self.env = unittest.mock.Mock() @@ -42,7 +41,9 @@ class LocalizationTests(unittest.TestCase): def test_translations_found(self, tdir): translations = unittest.mock.Mock() - with unittest.mock.patch('mkdocs.localization.Translations.load', return_value=translations): + with unittest.mock.patch( + 'mkdocs.localization.Translations.load', return_value=translations + ): install_translations(self.env, parse_locale('en'), [tdir]) self.env.install_gettext_translations.assert_called_once_with(translations) diff --git a/mkdocs/tests/new_tests.py b/mkdocs/tests/new_tests.py index 735fdf5d..3406c296 100644 --- a/mkdocs/tests/new_tests.py +++ b/mkdocs/tests/new_tests.py @@ -8,7 +8,6 @@ from mkdocs.commands import new class NewTests(unittest.TestCase): - def test_new(self): tempdir = tempfile.mkdtemp() diff --git a/mkdocs/tests/plugin_tests.py b/mkdocs/tests/plugin_tests.py index fa0e3d08..e204c91f 100644 --- a/mkdocs/tests/plugin_tests.py +++ b/mkdocs/tests/plugin_tests.py @@ -21,24 +21,23 @@ class DummyPlugin(plugins.BasePlugin): ) def on_pre_page(self, content, **kwargs): - """ modify page content by prepending `foo` config value. """ + """modify page content by prepending `foo` config value.""" return f'{self.config["foo"]} {content}' def on_nav(self, item, **kwargs): - """ do nothing (return None) to not modify item. """ + """do nothing (return None) to not modify item.""" return None def on_page_read_source(self, **kwargs): - """ create new source by prepending `foo` config value to 'source'. """ + """create new source by prepending `foo` config value to 'source'.""" return f'{self.config["foo"]} source' def on_pre_build(self, **kwargs): - """ do nothing (return None). """ + """do nothing (return None).""" return None class TestPluginClass(unittest.TestCase): - def test_valid_plugin_options(self): test_dir = 'test' @@ -85,7 +84,6 @@ class TestPluginClass(unittest.TestCase): class TestPluginCollection(unittest.TestCase): - def test_set_plugin_on_collection(self): collection = plugins.PluginCollection() plugin = DummyPlugin() @@ -98,7 +96,9 @@ class TestPluginCollection(unittest.TestCase): collection['foo'] = plugin1 plugin2 = DummyPlugin() collection['bar'] = plugin2 - self.assertEqual([(k, v) for k, v in collection.items()], [('foo', plugin1), ('bar', plugin2)]) + self.assertEqual( + [(k, v) for k, v in collection.items()], [('foo', plugin1), ('bar', plugin2)] + ) def test_run_event_on_collection(self): collection = plugins.PluginCollection() @@ -115,8 +115,9 @@ class TestPluginCollection(unittest.TestCase): plugin2 = DummyPlugin() plugin2.load_config({'foo': 'second'}) collection['bar'] = plugin2 - self.assertEqual(collection.run_event('pre_page', 'page content'), - 'second new page content') + self.assertEqual( + collection.run_event('pre_page', 'page content'), 'second new page content' + ) def test_event_returns_None(self): collection = plugins.PluginCollection() @@ -214,7 +215,6 @@ MockEntryPoint.configure_mock(**{'name': 'sample', 'load.return_value': DummyPlu @mock.patch('importlib_metadata.entry_points', return_value=[MockEntryPoint]) class TestPluginConfig(unittest.TestCase): - def test_plugin_config_without_options(self, mock_class): cfg = {'plugins': ['sample']} @@ -234,12 +234,14 @@ class TestPluginConfig(unittest.TestCase): def test_plugin_config_with_options(self, mock_class): cfg = { - 'plugins': [{ - 'sample': { - 'foo': 'foo value', - 'bar': 42 + 'plugins': [ + { + 'sample': { + 'foo': 'foo value', + 'bar': 42, + }, } - }] + ], } option = config.config_options.Plugins() cfg['plugins'] = option.validate(cfg['plugins']) @@ -260,9 +262,9 @@ class TestPluginConfig(unittest.TestCase): 'plugins': { 'sample': { 'foo': 'foo value', - 'bar': 42 - } - } + 'bar': 42, + }, + }, } option = config.config_options.Plugins() cfg['plugins'] = option.validate(cfg['plugins']) @@ -335,13 +337,15 @@ class TestPluginConfig(unittest.TestCase): def test_plugin_config_multivalue_dict(self, mock_class): cfg = { - 'plugins': [{ - 'sample': { - 'foo': 'foo value', - 'bar': 42 - }, - 'extra_key': 'baz' - }] + 'plugins': [ + { + 'sample': { + 'foo': 'foo value', + 'bar': 42, + }, + 'extra_key': 'baz', + } + ], } option = config.config_options.Plugins() with self.assertRaises(config.base.ValidationError): @@ -350,7 +354,7 @@ class TestPluginConfig(unittest.TestCase): def test_plugin_config_not_string_or_dict(self, mock_class): cfg = { - 'plugins': [('not a string or dict',)] + 'plugins': [('not a string or dict',)], } option = config.config_options.Plugins() with self.assertRaises(config.base.ValidationError): @@ -359,9 +363,11 @@ class TestPluginConfig(unittest.TestCase): def test_plugin_config_options_not_dict(self, mock_class): cfg = { - 'plugins': [{ - 'sample': 'not a dict' - }] + 'plugins': [ + { + 'sample': 'not a dict', + } + ], } option = config.config_options.Plugins() with self.assertRaises(config.base.ValidationError): diff --git a/mkdocs/tests/search_tests.py b/mkdocs/tests/search_tests.py index a7a33917..654a4cae 100644 --- a/mkdocs/tests/search_tests.py +++ b/mkdocs/tests/search_tests.py @@ -18,7 +18,6 @@ def strip_whitespace(string): class SearchConfigTests(unittest.TestCase): - def test_lang_default(self): option = search.LangOption(default=['en']) value = option.validate(None) @@ -76,14 +75,13 @@ class SearchConfigTests(unittest.TestCase): class SearchPluginTests(unittest.TestCase): - def test_plugin_config_defaults(self): expected = { 'lang': None, 'separator': r'[\s\-]+', 'min_search_length': 3, 'prebuild_index': False, - 'indexing': 'full' + 'indexing': 'full', } plugin = search.SearchPlugin() errors, warnings = plugin.load_config({}) @@ -97,7 +95,7 @@ class SearchPluginTests(unittest.TestCase): 'separator': r'[\s\-]+', 'min_search_length': 3, 'prebuild_index': False, - 'indexing': 'full' + 'indexing': 'full', } plugin = search.SearchPlugin() errors, warnings = plugin.load_config({'lang': 'es'}) @@ -111,7 +109,7 @@ class SearchPluginTests(unittest.TestCase): 'separator': r'[\s\-\.]+', 'min_search_length': 3, 'prebuild_index': False, - 'indexing': 'full' + 'indexing': 'full', } plugin = search.SearchPlugin() errors, warnings = plugin.load_config({'separator': r'[\s\-\.]+'}) @@ -125,7 +123,7 @@ class SearchPluginTests(unittest.TestCase): 'separator': r'[\s\-]+', 'min_search_length': 2, 'prebuild_index': False, - 'indexing': 'full' + 'indexing': 'full', } plugin = search.SearchPlugin() errors, warnings = plugin.load_config({'min_search_length': 2}) @@ -139,7 +137,7 @@ class SearchPluginTests(unittest.TestCase): 'separator': r'[\s\-]+', 'min_search_length': 3, 'prebuild_index': True, - 'indexing': 'full' + 'indexing': 'full', } plugin = search.SearchPlugin() errors, warnings = plugin.load_config({'prebuild_index': True}) @@ -153,7 +151,7 @@ class SearchPluginTests(unittest.TestCase): 'separator': r'[\s\-]+', 'min_search_length': 3, 'prebuild_index': False, - 'indexing': 'titles' + 'indexing': 'titles', } plugin = search.SearchPlugin() errors, warnings = plugin.load_config({'indexing': 'titles'}) @@ -186,7 +184,9 @@ class SearchPluginTests(unittest.TestCase): def test_event_on_config_theme_locale(self): plugin = search.SearchPlugin() plugin.load_config({}) - result = plugin.on_config(load_config(theme={'name': 'mkdocs', 'locale': 'fr'}, extra_javascript=[])) + result = plugin.on_config( + load_config(theme={'name': 'mkdocs', 'locale': 'fr'}, extra_javascript=[]) + ) self.assertFalse(result['theme']['search_index_only']) self.assertFalse(result['theme']['include_search_page']) self.assertEqual(result['theme'].static_templates, {'404.html', 'sitemap.xml'}) @@ -197,18 +197,24 @@ class SearchPluginTests(unittest.TestCase): def test_event_on_config_include_search_page(self): plugin = search.SearchPlugin() plugin.load_config({}) - config = load_config(theme={'name': 'mkdocs', 'include_search_page': True}, extra_javascript=[]) + config = load_config( + theme={'name': 'mkdocs', 'include_search_page': True}, extra_javascript=[] + ) result = plugin.on_config(config) self.assertFalse(result['theme']['search_index_only']) self.assertTrue(result['theme']['include_search_page']) - self.assertEqual(result['theme'].static_templates, {'404.html', 'sitemap.xml', 'search.html'}) + self.assertEqual( + result['theme'].static_templates, {'404.html', 'sitemap.xml', 'search.html'} + ) self.assertEqual(len(result['theme'].dirs), 3) self.assertEqual(result['extra_javascript'], ['search/main.js']) def test_event_on_config_search_index_only(self): plugin = search.SearchPlugin() plugin.load_config({}) - config = load_config(theme={'name': 'mkdocs', 'search_index_only': True}, extra_javascript=[]) + config = load_config( + theme={'name': 'mkdocs', 'search_index_only': True}, extra_javascript=[] + ) result = plugin.on_config(config) self.assertTrue(result['theme']['search_index_only']) self.assertFalse(result['theme']['include_search_page']) @@ -263,7 +269,6 @@ class SearchPluginTests(unittest.TestCase): class SearchIndexTests(unittest.TestCase): - def test_html_stripping(self): stripper = search_index.ContentParser() @@ -279,11 +284,9 @@ class SearchIndexTests(unittest.TestCase): parser.feed('

Title

TEST') parser.close() - self.assertEqual(parser.data, [search_index.ContentSection( - text=["TEST"], - id_="title", - title="Title" - )]) + self.assertEqual( + parser.data, [search_index.ContentSection(text=["TEST"], id_="title", title="Title")] + ) def test_content_parser_no_id(self): @@ -292,11 +295,9 @@ class SearchIndexTests(unittest.TestCase): parser.feed("

Title

TEST") parser.close() - self.assertEqual(parser.data, [search_index.ContentSection( - text=["TEST"], - id_=None, - title="Title" - )]) + self.assertEqual( + parser.data, [search_index.ContentSection(text=["TEST"], id_=None, title="Title")] + ) def test_content_parser_content_before_header(self): @@ -305,11 +306,9 @@ class SearchIndexTests(unittest.TestCase): parser.feed("Content Before H1

Title

TEST") parser.close() - self.assertEqual(parser.data, [search_index.ContentSection( - text=["TEST"], - id_=None, - title="Title" - )]) + self.assertEqual( + parser.data, [search_index.ContentSection(text=["TEST"], id_=None, title="Title")] + ) def test_content_parser_no_sections(self): @@ -326,11 +325,13 @@ class SearchIndexTests(unittest.TestCase): index = search_index.SearchIndex() - md = dedent(""" - # Heading 1 - ## Heading 2 - ### Heading 3 - """) + md = dedent( + """ + # Heading 1 + ## Heading 2 + ### Heading 3 + """ + ) toc = get_toc(get_markdown_toc(md)) toc_item = index._find_toc_by_id(toc, "heading-1") @@ -364,23 +365,29 @@ class SearchIndexTests(unittest.TestCase): 'index.md', base_cfg['docs_dir'], base_cfg['site_dir'], - base_cfg['use_directory_urls']), - base_cfg), + base_cfg['use_directory_urls'], + ), + base_cfg, + ), Page( 'About', File( 'about.md', base_cfg['docs_dir'], base_cfg['site_dir'], - base_cfg['use_directory_urls']), - base_cfg) + base_cfg['use_directory_urls'], + ), + base_cfg, + ), ] - md = dedent(""" - # Heading 1 - ## Heading 2 - ### Heading 3 - """) + md = dedent( + """ + # Heading 1 + ## Heading 2 + ### Heading 3 + """ + ) toc = get_toc(get_markdown_toc(md)) full_content = ''.join(f"Heading{i}Content{i}" for i in range(1, 4)) @@ -420,12 +427,12 @@ class SearchIndexTests(unittest.TestCase): def test_search_indexing_options(self): def test_page(title, filename, config): test_page = Page( - title, File( - filename, - config['docs_dir'], - config['site_dir'], - config['use_directory_urls']), - config) + title, + File( + filename, config['docs_dir'], config['site_dir'], config['use_directory_urls'] + ), + config, + ) test_page.content = """

Heading 1

Content 1

@@ -433,29 +440,40 @@ class SearchIndexTests(unittest.TestCase):

Content 2

Heading 3

Content 3

""" - test_page.markdown = dedent(""" + test_page.markdown = dedent( + """ # Heading 1 ## Heading 2 - ### Heading 3""") + ### Heading 3""" + ) test_page.toc = get_toc(get_markdown_toc(test_page.markdown)) return test_page validate = { - 'full': (lambda data: - self.assertEqual(len(data[0]), 4) and - self.assertTrue([x for x in data[0][0] if x['title'] and x['text']])), - 'sections': (lambda data: - # Sanity - self.assertEqual(len(data[0]), 4) and - # Page - (self.assertEqual(data[0][0]['title'], data[1].title) and - self.assertTrue(data[0][0]['text'])) and - # Headings - self.assertTrue([x for x in data[0][1:] if x['title'] and not x['text']])), - 'titles': (lambda data: - # Sanity - self.assertEqual(len(data[0]), 1) and - self.assertFalse([x for x in data[0] if x['text']])) + 'full': ( + lambda data: self.assertEqual(len(data[0]), 4) + and self.assertTrue([x for x in data[0][0] if x['title'] and x['text']]) + ), + 'sections': ( + lambda data: + # Sanity + self.assertEqual(len(data[0]), 4) + and + # Page + ( + self.assertEqual(data[0][0]['title'], data[1].title) + and self.assertTrue(data[0][0]['text']) + ) + and + # Headings + self.assertTrue([x for x in data[0][1:] if x['title'] and not x['text']]) + ), + 'titles': ( + lambda data: + # Sanity + self.assertEqual(len(data[0]), 1) + and self.assertFalse([x for x in data[0] if x['text']]) + ), } for option in ['full', 'sections', 'titles']: @@ -471,7 +489,7 @@ class SearchIndexTests(unittest.TestCase): pages = [ test_page('Home', 'index.md', base_cfg), - test_page('About', 'about.md', base_cfg) + test_page('About', 'about.md', base_cfg), ] for page in pages: @@ -492,7 +510,7 @@ class SearchIndexTests(unittest.TestCase): expected = { 'docs': [], 'config': {'prebuild_index': True}, - 'index': {'mock': 'index'} + 'index': {'mock': 'index'}, } result = json.loads(index.generate_search_index()) self.assertEqual(mock_popen.call_count, 1) @@ -510,7 +528,7 @@ class SearchIndexTests(unittest.TestCase): index = search_index.SearchIndex(prebuild_index=True) expected = { 'docs': [], - 'config': {'prebuild_index': True} + 'config': {'prebuild_index': True}, } result = json.loads(index.generate_search_index()) self.assertEqual(mock_popen.call_count, 1) @@ -528,7 +546,7 @@ class SearchIndexTests(unittest.TestCase): index = search_index.SearchIndex(prebuild_index=True) expected = { 'docs': [], - 'config': {'prebuild_index': True} + 'config': {'prebuild_index': True}, } result = json.loads(index.generate_search_index()) self.assertEqual(mock_popen.call_count, 1) @@ -546,7 +564,7 @@ class SearchIndexTests(unittest.TestCase): index = search_index.SearchIndex(prebuild_index=True) expected = { 'docs': [], - 'config': {'prebuild_index': True} + 'config': {'prebuild_index': True}, } result = json.loads(index.generate_search_index()) self.assertEqual(mock_popen.call_count, 1) @@ -564,7 +582,7 @@ class SearchIndexTests(unittest.TestCase): index = search_index.SearchIndex(prebuild_index=False) expected = { 'docs': [], - 'config': {'prebuild_index': False} + 'config': {'prebuild_index': False}, } result = json.loads(index.generate_search_index()) self.assertEqual(mock_popen.call_count, 0) @@ -579,7 +597,7 @@ class SearchIndexTests(unittest.TestCase): expected = { 'docs': [], 'config': {'prebuild_index': 'python', 'lang': 'en'}, - 'index': {'mock': 'index'} + 'index': {'mock': 'index'}, } result = json.loads(index.generate_search_index()) self.assertEqual(mock_lunr.call_count, 1) @@ -591,7 +609,7 @@ class SearchIndexTests(unittest.TestCase): index = search_index.SearchIndex(prebuild_index='python', lang='en') expected = { 'docs': [], - 'config': {'prebuild_index': 'python', 'lang': 'en'} + 'config': {'prebuild_index': 'python', 'lang': 'en'}, } result = json.loads(index.generate_search_index()) self.assertEqual(result, expected) @@ -608,7 +626,7 @@ class SearchIndexTests(unittest.TestCase): expected = { 'docs': [], 'config': {'prebuild_index': 'node'}, - 'index': {'mock': 'index'} + 'index': {'mock': 'index'}, } result = json.loads(index.generate_search_index()) self.assertEqual(mock_popen.call_count, 1) diff --git a/mkdocs/tests/structure/file_tests.py b/mkdocs/tests/structure/file_tests.py index d3d3cfad..b3076007 100644 --- a/mkdocs/tests/structure/file_tests.py +++ b/mkdocs/tests/structure/file_tests.py @@ -7,49 +7,56 @@ from mkdocs.tests.base import load_config, tempdir, PathAssertionMixin class TestFiles(PathAssertionMixin, unittest.TestCase): - def test_file_eq(self): file = File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False) - self.assertTrue(file == File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)) + self.assertTrue( + file == File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False) + ) def test_file_ne(self): file = File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False) # Different filename - self.assertTrue(file != File('b.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)) + self.assertTrue( + file != File('b.md', '/path/to/docs', '/path/to/site', use_directory_urls=False) + ) # Different src_path - self.assertTrue(file != File('a.md', '/path/to/other', '/path/to/site', use_directory_urls=False)) + self.assertTrue( + file != File('a.md', '/path/to/other', '/path/to/site', use_directory_urls=False) + ) # Different URL - self.assertTrue(file != File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)) + self.assertTrue( + file != File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=True) + ) def test_sort_files(self): self.assertEqual( _sort_files(['b.md', 'bb.md', 'a.md', 'index.md', 'aa.md']), - ['index.md', 'a.md', 'aa.md', 'b.md', 'bb.md'] + ['index.md', 'a.md', 'aa.md', 'b.md', 'bb.md'], ) self.assertEqual( _sort_files(['b.md', 'index.html', 'a.md', 'index.md']), - ['index.html', 'index.md', 'a.md', 'b.md'] + ['index.html', 'index.md', 'a.md', 'b.md'], ) self.assertEqual( _sort_files(['a.md', 'index.md', 'b.md', 'index.html']), - ['index.md', 'index.html', 'a.md', 'b.md'] + ['index.md', 'index.html', 'a.md', 'b.md'], ) self.assertEqual( _sort_files(['.md', '_.md', 'a.md', 'index.md', '1.md']), - ['index.md', '.md', '1.md', '_.md', 'a.md'] + ['index.md', '.md', '1.md', '_.md', 'a.md'], ) self.assertEqual( _sort_files(['a.md', 'b.md', 'a.md']), - ['a.md', 'a.md', 'b.md'] + ['a.md', 'a.md', 'b.md'], ) self.assertEqual( _sort_files(['A.md', 'B.md', 'README.md']), - ['README.md', 'A.md', 'B.md'] + ['README.md', 'A.md', 'B.md'], ) def test_md_file(self): @@ -320,7 +327,7 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): File('foo/bar.html', '/path/to/docs', '/path/to/site', use_directory_urls=True), File('foo/bar.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True), File('foo/bar.js', '/path/to/docs', '/path/to/site', use_directory_urls=True), - File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=True) + File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=True), ] files = Files(fs) self.assertEqual([f for f in files], fs) @@ -342,38 +349,42 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): self.assertTrue(extra_file.src_path in files) self.assertEqual(files.documentation_pages(), [fs[0], fs[1], extra_file]) - @tempdir(files=[ - 'favicon.ico', - 'index.md' - ]) - @tempdir(files=[ - 'base.html', - 'favicon.ico', - 'style.css', - 'foo.md', - 'README', - '.ignore.txt', - '.ignore/file.txt', - 'foo/.ignore.txt', - 'foo/.ignore/file.txt' - ]) + @tempdir( + files=[ + 'favicon.ico', + 'index.md', + ] + ) + @tempdir( + files=[ + 'base.html', + 'favicon.ico', + 'style.css', + 'foo.md', + 'README', + '.ignore.txt', + '.ignore/file.txt', + 'foo/.ignore.txt', + 'foo/.ignore/file.txt', + ] + ) def test_add_files_from_theme(self, tdir, ddir): config = load_config(docs_dir=ddir, theme={'name': None, 'custom_dir': tdir}) env = config['theme'].get_env() files = get_files(config) self.assertEqual( [file.src_path for file in files], - ['index.md', 'favicon.ico'] + ['index.md', 'favicon.ico'], ) files.add_files_from_theme(env, config) self.assertEqual( [file.src_path for file in files], - ['index.md', 'favicon.ico', 'style.css'] + ['index.md', 'favicon.ico', 'style.css'], ) # Ensure theme file does not override docs_dir file self.assertEqual( files.get_file_from_path('favicon.ico').abs_src_path, - os.path.normpath(os.path.join(ddir, 'favicon.ico')) + os.path.normpath(os.path.join(ddir, 'favicon.ico')), ) def test_filter_paths(self): @@ -412,7 +423,7 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): 'foo/bar/baz/index.md', 'foo.md', 'foo/bar.md', - 'foo/bar/baz.md' + 'foo/bar/baz.md', ] to_file_urls = [ @@ -422,18 +433,18 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): 'foo/bar/baz/', 'foo/', 'foo/bar/', - 'foo/bar/baz/' + 'foo/bar/baz/', ] from_file = File('img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True) expected = [ - 'img.jpg', # img.jpg relative to . - '../img.jpg', # img.jpg relative to foo/ - '../../img.jpg', # img.jpg relative to foo/bar/ + 'img.jpg', # img.jpg relative to . + '../img.jpg', # img.jpg relative to foo/ + '../../img.jpg', # img.jpg relative to foo/bar/ '../../../img.jpg', # img.jpg relative to foo/bar/baz/ - '../img.jpg', # img.jpg relative to foo - '../../img.jpg', # img.jpg relative to foo/bar - '../../../img.jpg' # img.jpg relative to foo/bar/baz + '../img.jpg', # img.jpg relative to foo + '../../img.jpg', # img.jpg relative to foo/bar + '../../../img.jpg', # img.jpg relative to foo/bar/baz ] for i, filename in enumerate(to_files): @@ -445,13 +456,13 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): from_file = File('foo/img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True) expected = [ - 'foo/img.jpg', # foo/img.jpg relative to . - 'img.jpg', # foo/img.jpg relative to foo/ - '../img.jpg', # foo/img.jpg relative to foo/bar/ + 'foo/img.jpg', # foo/img.jpg relative to . + 'img.jpg', # foo/img.jpg relative to foo/ + '../img.jpg', # foo/img.jpg relative to foo/bar/ '../../img.jpg', # foo/img.jpg relative to foo/bar/baz/ - 'img.jpg', # foo/img.jpg relative to foo - '../img.jpg', # foo/img.jpg relative to foo/bar - '../../img.jpg' # foo/img.jpg relative to foo/bar/baz + 'img.jpg', # foo/img.jpg relative to foo + '../img.jpg', # foo/img.jpg relative to foo/bar + '../../img.jpg', # foo/img.jpg relative to foo/bar/baz ] for i, filename in enumerate(to_files): @@ -463,13 +474,13 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): from_file = File('index.html', '/path/to/docs', '/path/to/site', use_directory_urls=True) expected = [ - '.', # . relative to . - '..', # . relative to foo/ - '../..', # . relative to foo/bar/ + '.', # . relative to . + '..', # . relative to foo/ + '../..', # . relative to foo/bar/ '../../..', # . relative to foo/bar/baz/ - '..', # . relative to foo - '../..', # . relative to foo/bar - '../../..' # . relative to foo/bar/baz + '..', # . relative to foo + '../..', # . relative to foo/bar + '../../..', # . relative to foo/bar/baz ] for i, filename in enumerate(to_files): @@ -481,13 +492,13 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): from_file = File('file.md', '/path/to/docs', '/path/to/site', use_directory_urls=True) expected = [ - 'file/', # file relative to . - '../file/', # file relative to foo/ - '../../file/', # file relative to foo/bar/ + 'file/', # file relative to . + '../file/', # file relative to foo/ + '../../file/', # file relative to foo/bar/ '../../../file/', # file relative to foo/bar/baz/ - '../file/', # file relative to foo - '../../file/', # file relative to foo/bar - '../../../file/' # file relative to foo/bar/baz + '../file/', # file relative to foo + '../../file/', # file relative to foo/bar + '../../../file/', # file relative to foo/bar/baz ] for i, filename in enumerate(to_files): @@ -505,7 +516,7 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): 'foo/bar/baz/index.md', 'foo.md', 'foo/bar.md', - 'foo/bar/baz.md' + 'foo/bar/baz.md', ] to_file_urls = [ @@ -515,18 +526,18 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): 'foo/bar/baz/index.html', 'foo.html', 'foo/bar.html', - 'foo/bar/baz.html' + 'foo/bar/baz.html', ] from_file = File('img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=False) expected = [ - 'img.jpg', # img.jpg relative to . - '../img.jpg', # img.jpg relative to foo/ - '../../img.jpg', # img.jpg relative to foo/bar/ + 'img.jpg', # img.jpg relative to . + '../img.jpg', # img.jpg relative to foo/ + '../../img.jpg', # img.jpg relative to foo/bar/ '../../../img.jpg', # img.jpg relative to foo/bar/baz/ - 'img.jpg', # img.jpg relative to foo.html - '../img.jpg', # img.jpg relative to foo/bar.html - '../../img.jpg' # img.jpg relative to foo/bar/baz.html + 'img.jpg', # img.jpg relative to foo.html + '../img.jpg', # img.jpg relative to foo/bar.html + '../../img.jpg', # img.jpg relative to foo/bar/baz.html ] for i, filename in enumerate(to_files): @@ -538,13 +549,13 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): from_file = File('foo/img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=False) expected = [ - 'foo/img.jpg', # foo/img.jpg relative to . - 'img.jpg', # foo/img.jpg relative to foo/ - '../img.jpg', # foo/img.jpg relative to foo/bar/ + 'foo/img.jpg', # foo/img.jpg relative to . + 'img.jpg', # foo/img.jpg relative to foo/ + '../img.jpg', # foo/img.jpg relative to foo/bar/ '../../img.jpg', # foo/img.jpg relative to foo/bar/baz/ - 'foo/img.jpg', # foo/img.jpg relative to foo.html - 'img.jpg', # foo/img.jpg relative to foo/bar.html - '../img.jpg' # foo/img.jpg relative to foo/bar/baz.html + 'foo/img.jpg', # foo/img.jpg relative to foo.html + 'img.jpg', # foo/img.jpg relative to foo/bar.html + '../img.jpg', # foo/img.jpg relative to foo/bar/baz.html ] for i, filename in enumerate(to_files): @@ -556,13 +567,13 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): from_file = File('index.html', '/path/to/docs', '/path/to/site', use_directory_urls=False) expected = [ - 'index.html', # index.html relative to . - '../index.html', # index.html relative to foo/ - '../../index.html', # index.html relative to foo/bar/ + 'index.html', # index.html relative to . + '../index.html', # index.html relative to foo/ + '../../index.html', # index.html relative to foo/bar/ '../../../index.html', # index.html relative to foo/bar/baz/ - 'index.html', # index.html relative to foo.html - '../index.html', # index.html relative to foo/bar.html - '../../index.html' # index.html relative to foo/bar/baz.html + 'index.html', # index.html relative to foo.html + '../index.html', # index.html relative to foo/bar.html + '../../index.html', # index.html relative to foo/bar/baz.html ] for i, filename in enumerate(to_files): @@ -574,13 +585,13 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): from_file = File('file.html', '/path/to/docs', '/path/to/site', use_directory_urls=False) expected = [ - 'file.html', # file.html relative to . - '../file.html', # file.html relative to foo/ - '../../file.html', # file.html relative to foo/bar/ + 'file.html', # file.html relative to . + '../file.html', # file.html relative to foo/ + '../../file.html', # file.html relative to foo/bar/ '../../../file.html', # file.html relative to foo/bar/baz/ - 'file.html', # file.html relative to foo.html - '../file.html', # file.html relative to foo/bar.html - '../../file.html' # file.html relative to foo/bar/baz.html + 'file.html', # file.html relative to foo.html + '../file.html', # file.html relative to foo/bar.html + '../../file.html', # file.html relative to foo/bar/baz.html ] for i, filename in enumerate(to_files): @@ -590,17 +601,19 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): self.assertEqual(from_file.url_relative_to(file.url), expected[i]) self.assertEqual(from_file.url_relative_to(file), expected[i]) - @tempdir(files=[ - 'index.md', - 'readme.md', - 'bar.css', - 'bar.html', - 'bar.jpg', - 'bar.js', - 'bar.md', - '.dotfile', - 'templates/foo.html' - ]) + @tempdir( + files=[ + 'index.md', + 'readme.md', + 'bar.css', + 'bar.html', + 'bar.jpg', + 'bar.js', + 'bar.md', + '.dotfile', + 'templates/foo.html', + ] + ) def test_get_files(self, tdir): config = load_config(docs_dir=tdir, extra_css=['bar.css'], extra_javascript=['bar.js']) files = get_files(config) @@ -609,10 +622,12 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): self.assertEqual(len(files), len(expected)) self.assertEqual([f.src_path for f in files], expected) - @tempdir(files=[ - 'README.md', - 'foo.md' - ]) + @tempdir( + files=[ + 'README.md', + 'foo.md', + ] + ) def test_get_files_include_readme_without_index(self, tdir): config = load_config(docs_dir=tdir) files = get_files(config) @@ -621,11 +636,13 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): self.assertEqual(len(files), len(expected)) self.assertEqual([f.src_path for f in files], expected) - @tempdir(files=[ - 'index.md', - 'README.md', - 'foo.md' - ]) + @tempdir( + files=[ + 'index.md', + 'README.md', + 'foo.md', + ] + ) def test_get_files_exclude_readme_with_index(self, tdir): config = load_config(docs_dir=tdir) files = get_files(config) @@ -683,7 +700,7 @@ class TestFiles(PathAssertionMixin, unittest.TestCase): File('foo/bar.html', '/path/to/docs', '/path/to/site', use_directory_urls=True), File('foo/bar.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True), File('foo/bar.js', '/path/to/docs', '/path/to/site', use_directory_urls=True), - File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=True) + File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=True), ] files = Files(fs) self.assertEqual(len(files), 6) diff --git a/mkdocs/tests/structure/nav_tests.py b/mkdocs/tests/structure/nav_tests.py index 81849236..de9141ea 100644 --- a/mkdocs/tests/structure/nav_tests.py +++ b/mkdocs/tests/structure/nav_tests.py @@ -16,17 +16,22 @@ class SiteNavigationTests(unittest.TestCase): def test_simple_nav(self): nav_cfg = [ {'Home': 'index.md'}, - {'About': 'about.md'} + {'About': 'about.md'}, ] - expected = dedent(""" - Page(title='Home', url='/') - Page(title='About', url='/about/') - """) - cfg = load_config(nav=nav_cfg, site_url='http://example.com/') - files = Files( - [File(list(item.values())[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) - for item in nav_cfg] + expected = dedent( + """ + Page(title='Home', url='/') + Page(title='About', url='/about/') + """ ) + cfg = load_config(nav=nav_cfg, site_url='http://example.com/') + fs = [ + File( + list(item.values())[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'] + ) + for item in nav_cfg + ] + files = Files(fs) site_navigation = get_navigation(files, cfg) self.assertEqual(str(site_navigation).strip(), expected) self.assertEqual(len(site_navigation.items), 2) @@ -36,17 +41,22 @@ class SiteNavigationTests(unittest.TestCase): def test_nav_no_directory_urls(self): nav_cfg = [ {'Home': 'index.md'}, - {'About': 'about.md'} + {'About': 'about.md'}, ] - expected = dedent(""" - Page(title='Home', url='/index.html') - Page(title='About', url='/about.html') - """) - cfg = load_config(nav=nav_cfg, use_directory_urls=False, site_url='http://example.com/') - files = Files( - [File(list(item.values())[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) - for item in nav_cfg] + expected = dedent( + """ + Page(title='Home', url='/index.html') + Page(title='About', url='/about.html') + """ ) + cfg = load_config(nav=nav_cfg, use_directory_urls=False, site_url='http://example.com/') + fs = [ + File( + list(item.values())[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'] + ) + for item in nav_cfg + ] + files = Files(fs) site_navigation = get_navigation(files, cfg) self.assertEqual(str(site_navigation).strip(), expected) self.assertEqual(len(site_navigation.items), 2) @@ -55,16 +65,19 @@ class SiteNavigationTests(unittest.TestCase): def test_nav_missing_page(self): nav_cfg = [ - {'Home': 'index.md'} + {'Home': 'index.md'}, ] - expected = dedent(""" - Page(title='Home', url='/') - """) + expected = dedent( + """ + Page(title='Home', url='/') + """ + ) cfg = load_config(nav=nav_cfg, site_url='http://example.com/') - files = Files([ + fs = [ File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('page_not_in_nav.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) - ]) + File('page_not_in_nav.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ] + files = Files(fs) site_navigation = get_navigation(files, cfg) self.assertEqual(str(site_navigation).strip(), expected) self.assertEqual(len(site_navigation.items), 1) @@ -75,17 +88,20 @@ class SiteNavigationTests(unittest.TestCase): def test_nav_no_title(self): nav_cfg = [ 'index.md', - {'About': 'about.md'} + {'About': 'about.md'}, ] - expected = dedent(""" - Page(title=[blank], url='/') - Page(title='About', url='/about/') - """) + expected = dedent( + """ + Page(title=[blank], url='/') + Page(title='About', url='/about/') + """ + ) cfg = load_config(nav=nav_cfg, site_url='http://example.com/') - files = Files([ + fs = [ File(nav_cfg[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File(nav_cfg[1]['About'], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) - ]) + File(nav_cfg[1]['About'], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ] + files = Files(fs) site_navigation = get_navigation(files, cfg) self.assertEqual(str(site_navigation).strip(), expected) self.assertEqual(len(site_navigation.items), 2) @@ -95,15 +111,18 @@ class SiteNavigationTests(unittest.TestCase): nav_cfg = [ {'Home': 'index.md'}, {'Local': '/local.html'}, - {'External': 'http://example.com/external.html'} + {'External': 'http://example.com/external.html'}, ] - expected = dedent(""" - Page(title='Home', url='/') - Link(title='Local', url='/local.html') - Link(title='External', url='http://example.com/external.html') - """) + expected = dedent( + """ + Page(title='Home', url='/') + Link(title='Local', url='/local.html') + Link(title='External', url='http://example.com/external.html') + """ + ) cfg = load_config(nav=nav_cfg, site_url='http://example.com/') - files = Files([File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + fs = [File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])] + files = Files(fs) with self.assertLogs('mkdocs', level='DEBUG') as cm: site_navigation = get_navigation(files, cfg) self.assertEqual( @@ -112,8 +131,8 @@ class SiteNavigationTests(unittest.TestCase): "DEBUG:mkdocs.structure.nav:An absolute path to '/local.html' is included in the " "'nav' configuration, which presumably points to an external resource.", "DEBUG:mkdocs.structure.nav:An external link to 'http://example.com/external.html' " - "is included in the 'nav' configuration." - ] + "is included in the 'nav' configuration.", + ], ) self.assertEqual(str(site_navigation).strip(), expected) self.assertEqual(len(site_navigation.items), 3) @@ -123,15 +142,18 @@ class SiteNavigationTests(unittest.TestCase): nav_cfg = [ {'Home': 'index.md'}, {'Missing': 'missing.html'}, - {'Bad External': 'example.com'} + {'Bad External': 'example.com'}, ] - expected = dedent(""" - Page(title='Home', url='/') - Link(title='Missing', url='missing.html') - Link(title='Bad External', url='example.com') - """) + expected = dedent( + """ + Page(title='Home', url='/') + Link(title='Missing', url='missing.html') + Link(title='Bad External', url='example.com') + """ + ) cfg = load_config(nav=nav_cfg, site_url='http://example.com/') - files = Files([File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + fs = [File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])] + files = Files(fs) with self.assertLogs('mkdocs', level='WARNING') as cm: site_navigation = get_navigation(files, cfg) self.assertEqual( @@ -140,8 +162,8 @@ class SiteNavigationTests(unittest.TestCase): "WARNING:mkdocs.structure.nav:A relative path to 'missing.html' is included " "in the 'nav' configuration, which is not found in the documentation files", "WARNING:mkdocs.structure.nav:A relative path to 'example.com' is included " - "in the 'nav' configuration, which is not found in the documentation files" - ] + "in the 'nav' configuration, which is not found in the documentation files", + ], ) self.assertEqual(str(site_navigation).strip(), expected) self.assertEqual(len(site_navigation.items), 3) @@ -150,42 +172,53 @@ class SiteNavigationTests(unittest.TestCase): def test_indented_nav(self): nav_cfg = [ {'Home': 'index.md'}, - {'API Guide': [ - {'Running': 'api-guide/running.md'}, - {'Testing': 'api-guide/testing.md'}, - {'Debugging': 'api-guide/debugging.md'}, - {'Advanced': [ - {'Part 1': 'api-guide/advanced/part-1.md'}, - ]}, - ]}, - {'About': [ - {'Release notes': 'about/release-notes.md'}, - {'License': '/license.html'} - ]}, - {'External': 'https://example.com/'} + { + 'API Guide': [ + {'Running': 'api-guide/running.md'}, + {'Testing': 'api-guide/testing.md'}, + {'Debugging': 'api-guide/debugging.md'}, + { + 'Advanced': [ + {'Part 1': 'api-guide/advanced/part-1.md'}, + ] + }, + ] + }, + { + 'About': [ + {'Release notes': 'about/release-notes.md'}, + {'License': '/license.html'}, + ] + }, + {'External': 'https://example.com/'}, ] - expected = dedent(""" - Page(title='Home', url='/') - Section(title='API Guide') - Page(title='Running', url='/api-guide/running/') - Page(title='Testing', url='/api-guide/testing/') - Page(title='Debugging', url='/api-guide/debugging/') - Section(title='Advanced') - Page(title='Part 1', url='/api-guide/advanced/part-1/') - Section(title='About') - Page(title='Release notes', url='/about/release-notes/') - Link(title='License', url='/license.html') - Link(title='External', url='https://example.com/') - """) + expected = dedent( + """ + Page(title='Home', url='/') + Section(title='API Guide') + Page(title='Running', url='/api-guide/running/') + Page(title='Testing', url='/api-guide/testing/') + Page(title='Debugging', url='/api-guide/debugging/') + Section(title='Advanced') + Page(title='Part 1', url='/api-guide/advanced/part-1/') + Section(title='About') + Page(title='Release notes', url='/about/release-notes/') + Link(title='License', url='/license.html') + Link(title='External', url='https://example.com/') + """ + ) cfg = load_config(nav=nav_cfg, site_url='http://example.com/') - files = Files([ - File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('api-guide/running.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('api-guide/testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('api-guide/debugging.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('api-guide/advanced/part-1.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('about/release-notes.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - ]) + fs = [ + 'index.md', + 'api-guide/running.md', + 'api-guide/testing.md', + 'api-guide/debugging.md', + 'api-guide/advanced/part-1.md', + 'about/release-notes.md', + ] + files = Files( + [File(s, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) for s in fs] + ) site_navigation = get_navigation(files, cfg) self.assertEqual(str(site_navigation).strip(), expected) self.assertEqual(len(site_navigation.items), 4) @@ -196,23 +229,40 @@ class SiteNavigationTests(unittest.TestCase): self.assertIsNone(site_navigation.items[1].parent) self.assertEqual(site_navigation.items[1].ancestors, []) self.assertEqual(len(site_navigation.items[1].children), 4) - self.assertEqual(repr(site_navigation.items[1].children[0].parent), "Section(title='API Guide')") + self.assertEqual( + repr(site_navigation.items[1].children[0].parent), "Section(title='API Guide')" + ) self.assertEqual(site_navigation.items[1].children[0].ancestors, [site_navigation.items[1]]) - self.assertEqual(repr(site_navigation.items[1].children[1].parent), "Section(title='API Guide')") + self.assertEqual( + repr(site_navigation.items[1].children[1].parent), "Section(title='API Guide')" + ) self.assertEqual(site_navigation.items[1].children[1].ancestors, [site_navigation.items[1]]) - self.assertEqual(repr(site_navigation.items[1].children[2].parent), "Section(title='API Guide')") + self.assertEqual( + repr(site_navigation.items[1].children[2].parent), "Section(title='API Guide')" + ) self.assertEqual(site_navigation.items[1].children[2].ancestors, [site_navigation.items[1]]) - self.assertEqual(repr(site_navigation.items[1].children[3].parent), "Section(title='API Guide')") + self.assertEqual( + repr(site_navigation.items[1].children[3].parent), "Section(title='API Guide')" + ) self.assertEqual(site_navigation.items[1].children[3].ancestors, [site_navigation.items[1]]) self.assertEqual(len(site_navigation.items[1].children[3].children), 1) - self.assertEqual(repr(site_navigation.items[1].children[3].children[0].parent), "Section(title='Advanced')") - self.assertEqual(site_navigation.items[1].children[3].children[0].ancestors, - [site_navigation.items[1].children[3], site_navigation.items[1]]) + self.assertEqual( + repr(site_navigation.items[1].children[3].children[0].parent), + "Section(title='Advanced')", + ) + self.assertEqual( + site_navigation.items[1].children[3].children[0].ancestors, + [site_navigation.items[1].children[3], site_navigation.items[1]], + ) self.assertIsNone(site_navigation.items[2].parent) self.assertEqual(len(site_navigation.items[2].children), 2) - self.assertEqual(repr(site_navigation.items[2].children[0].parent), "Section(title='About')") + self.assertEqual( + repr(site_navigation.items[2].children[0].parent), "Section(title='About')" + ) self.assertEqual(site_navigation.items[2].children[0].ancestors, [site_navigation.items[2]]) - self.assertEqual(repr(site_navigation.items[2].children[1].parent), "Section(title='About')") + self.assertEqual( + repr(site_navigation.items[2].children[1].parent), "Section(title='About')" + ) self.assertEqual(site_navigation.items[2].children[1].ancestors, [site_navigation.items[2]]) self.assertIsNone(site_navigation.items[3].parent) self.assertEqual(site_navigation.items[3].ancestors, []) @@ -224,16 +274,21 @@ class SiteNavigationTests(unittest.TestCase): {'Contact': 'about/contact.md'}, {'License Title': 'about/sub/license.md'}, ] - expected = dedent(""" - Page(title='Home', url='/') - Page(title='Contact', url='/about/contact/') - Page(title='License Title', url='/about/sub/license/') - """) - cfg = load_config(nav=nav_cfg, site_url='http://example.com/') - files = Files( - [File(list(item.values())[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) - for item in nav_cfg] + expected = dedent( + """ + Page(title='Home', url='/') + Page(title='Contact', url='/about/contact/') + Page(title='License Title', url='/about/sub/license/') + """ ) + cfg = load_config(nav=nav_cfg, site_url='http://example.com/') + fs = [ + File( + list(item.values())[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'] + ) + for item in nav_cfg + ] + files = Files(fs) site_navigation = get_navigation(files, cfg) self.assertEqual(str(site_navigation).strip(), expected) self.assertEqual(len(site_navigation.items), 3) @@ -243,18 +298,22 @@ class SiteNavigationTests(unittest.TestCase): nav_cfg = [ 'index.md', 'about/contact.md', - 'about/sub/license.md' + 'about/sub/license.md', ] - expected = dedent(""" - Page(title=[blank], url='/') - Page(title=[blank], url='/about/contact/') - Page(title=[blank], url='/about/sub/license/') - """) + expected = dedent( + """ + Page(title=[blank], url='/') + Page(title=[blank], url='/about/contact/') + Page(title=[blank], url='/about/sub/license/') + """ + ) cfg = load_config(nav=nav_cfg, site_url='http://example.com/') - files = Files( - [File(item, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) for item in nav_cfg] - ) + fs = [ + File(item, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + for item in nav_cfg + ] + files = Files(fs) site_navigation = get_navigation(files, cfg) self.assertEqual(str(site_navigation).strip(), expected) self.assertEqual(len(site_navigation.items), 3) @@ -268,31 +327,38 @@ class SiteNavigationTests(unittest.TestCase): 'about\\contact.md', 'about\\sub\\license.md', ] - expected = dedent(""" - Page(title=[blank], url='/') - Page(title=[blank], url='/about/contact/') - Page(title=[blank], url='/about/sub/license/') - """) + expected = dedent( + """ + Page(title=[blank], url='/') + Page(title=[blank], url='/about/contact/') + Page(title=[blank], url='/about/sub/license/') + """ + ) cfg = load_config(nav=nav_cfg, site_url='http://example.com/') - files = Files( - [File(item, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) for item in nav_cfg] - ) + fs = [ + File(item, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + for item in nav_cfg + ] + files = Files(fs) site_navigation = get_navigation(files, cfg) self.assertEqual(str(site_navigation).strip(), expected) self.assertEqual(len(site_navigation.items), 3) self.assertEqual(len(site_navigation.pages), 3) def test_nav_from_files(self): - expected = dedent(""" - Page(title=[blank], url='/') - Page(title=[blank], url='/about/') - """) + expected = dedent( + """ + Page(title=[blank], url='/') + Page(title=[blank], url='/about/') + """ + ) cfg = load_config(site_url='http://example.com/') - files = Files([ + fs = [ File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('about.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) - ]) + File('about.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ] + files = Files(fs) site_navigation = get_navigation(files, cfg) self.assertEqual(str(site_navigation).strip(), expected) self.assertEqual(len(site_navigation.items), 2) @@ -300,28 +366,33 @@ class SiteNavigationTests(unittest.TestCase): self.assertEqual(repr(site_navigation.homepage), "Page(title=[blank], url='/')") def test_nav_from_nested_files(self): - expected = dedent(""" - Page(title=[blank], url='/') - Section(title='About') - Page(title=[blank], url='/about/license/') - Page(title=[blank], url='/about/release-notes/') - Section(title='Api guide') - Page(title=[blank], url='/api-guide/debugging/') - Page(title=[blank], url='/api-guide/running/') - Page(title=[blank], url='/api-guide/testing/') - Section(title='Advanced') - Page(title=[blank], url='/api-guide/advanced/part-1/') - """) + expected = dedent( + """ + Page(title=[blank], url='/') + Section(title='About') + Page(title=[blank], url='/about/license/') + Page(title=[blank], url='/about/release-notes/') + Section(title='Api guide') + Page(title=[blank], url='/api-guide/debugging/') + Page(title=[blank], url='/api-guide/running/') + Page(title=[blank], url='/api-guide/testing/') + Section(title='Advanced') + Page(title=[blank], url='/api-guide/advanced/part-1/') + """ + ) cfg = load_config(site_url='http://example.com/') - files = Files([ - File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('about/license.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('about/release-notes.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('api-guide/debugging.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('api-guide/running.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('api-guide/testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('api-guide/advanced/part-1.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - ]) + fs = [ + 'index.md', + 'about/license.md', + 'about/release-notes.md', + 'api-guide/debugging.md', + 'api-guide/running.md', + 'api-guide/testing.md', + 'api-guide/advanced/part-1.md', + ] + files = Files( + [File(s, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) for s in fs] + ) site_navigation = get_navigation(files, cfg) self.assertEqual(str(site_navigation).strip(), expected) self.assertEqual(len(site_navigation.items), 3) @@ -331,29 +402,38 @@ class SiteNavigationTests(unittest.TestCase): def test_active(self): nav_cfg = [ {'Home': 'index.md'}, - {'API Guide': [ - {'Running': 'api-guide/running.md'}, - {'Testing': 'api-guide/testing.md'}, - {'Debugging': 'api-guide/debugging.md'}, - {'Advanced': [ - {'Part 1': 'api-guide/advanced/part-1.md'}, - ]}, - ]}, - {'About': [ - {'Release notes': 'about/release-notes.md'}, - {'License': 'about/license.md'} - ]} + { + 'API Guide': [ + {'Running': 'api-guide/running.md'}, + {'Testing': 'api-guide/testing.md'}, + {'Debugging': 'api-guide/debugging.md'}, + { + 'Advanced': [ + {'Part 1': 'api-guide/advanced/part-1.md'}, + ] + }, + ] + }, + { + 'About': [ + {'Release notes': 'about/release-notes.md'}, + {'License': 'about/license.md'}, + ] + }, ] cfg = load_config(nav=nav_cfg, site_url='http://example.com/') - files = Files([ - File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('api-guide/running.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('api-guide/testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('api-guide/debugging.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('api-guide/advanced/part-1.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('about/release-notes.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - File('about/license.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), - ]) + fs = [ + 'index.md', + 'api-guide/running.md', + 'api-guide/testing.md', + 'api-guide/debugging.md', + 'api-guide/advanced/part-1.md', + 'about/release-notes.md', + 'about/license.md', + ] + files = Files( + [File(s, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) for s in fs] + ) site_navigation = get_navigation(files, cfg) # Confirm nothing is active self.assertTrue(all(page.active is False for page in site_navigation.pages)) @@ -381,15 +461,18 @@ class SiteNavigationTests(unittest.TestCase): def test_get_by_type_nested_sections(self): nav_cfg = [ - {'Section 1': [ - {'Section 2': [ - {'Page': 'page.md'} - ]} - ]} + { + 'Section 1': [ + { + 'Section 2': [ + {'Page': 'page.md'}, + ] + }, + ] + }, ] cfg = load_config(nav=nav_cfg, site_url='http://example.com/') - files = Files([ - File('page.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) - ]) + fs = [File('page.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])] + files = Files(fs) site_navigation = get_navigation(files, cfg) self.assertEqual(len(_get_by_type(site_navigation, Section)), 2) diff --git a/mkdocs/tests/structure/page_tests.py b/mkdocs/tests/structure/page_tests.py index 35c1886b..af666b11 100644 --- a/mkdocs/tests/structure/page_tests.py +++ b/mkdocs/tests/structure/page_tests.py @@ -11,7 +11,9 @@ from mkdocs.tests.base import load_config, dedent class PageTests(unittest.TestCase): - DOCS_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../integration/subpages/docs') + DOCS_DIR = os.path.join( + os.path.abspath(os.path.dirname(__file__)), '../integration/subpages/docs' + ) def test_homepage(self): cfg = load_config(docs_dir=self.DOCS_DIR) @@ -429,52 +431,24 @@ class PageTests(unittest.TestCase): def test_page_edit_url(self): configs = [ + {'repo_url': 'http://github.com/mkdocs/mkdocs'}, + {'repo_url': 'https://github.com/mkdocs/mkdocs/'}, + {'repo_url': 'http://example.com'}, + {'repo_url': 'http://example.com', 'edit_uri': 'edit/master'}, + {'repo_url': 'http://example.com', 'edit_uri': '/edit/master'}, + {'repo_url': 'http://example.com/foo/', 'edit_uri': '/edit/master/'}, + {'repo_url': 'http://example.com/foo', 'edit_uri': '/edit/master/'}, + {'repo_url': 'http://example.com/foo/', 'edit_uri': '/edit/master'}, + {'repo_url': 'http://example.com/foo/', 'edit_uri': 'edit/master/'}, + {'repo_url': 'http://example.com/foo', 'edit_uri': 'edit/master/'}, + {'repo_url': 'http://example.com', 'edit_uri': '?query=edit/master'}, + {'repo_url': 'http://example.com/', 'edit_uri': '?query=edit/master/'}, + {'repo_url': 'http://example.com', 'edit_uri': '#edit/master'}, + {'repo_url': 'http://example.com/', 'edit_uri': '#edit/master/'}, + {'repo_url': 'http://example.com', 'edit_uri': ''}, # Set to blank value { - 'repo_url': 'http://github.com/mkdocs/mkdocs' - }, - { - 'repo_url': 'https://github.com/mkdocs/mkdocs/' - }, { - 'repo_url': 'http://example.com' - }, { - 'repo_url': 'http://example.com', - 'edit_uri': 'edit/master' - }, { - 'repo_url': 'http://example.com', - 'edit_uri': '/edit/master' - }, { - 'repo_url': 'http://example.com/foo/', - 'edit_uri': '/edit/master/' - }, { - 'repo_url': 'http://example.com/foo', - 'edit_uri': '/edit/master/' - }, { - 'repo_url': 'http://example.com/foo/', - 'edit_uri': '/edit/master' - }, { - 'repo_url': 'http://example.com/foo/', - 'edit_uri': 'edit/master/' - }, { - 'repo_url': 'http://example.com/foo', - 'edit_uri': 'edit/master/' - }, { - 'repo_url': 'http://example.com', - 'edit_uri': '?query=edit/master' - }, { - 'repo_url': 'http://example.com/', - 'edit_uri': '?query=edit/master/' - }, { - 'repo_url': 'http://example.com', - 'edit_uri': '#edit/master' - }, { - 'repo_url': 'http://example.com/', - 'edit_uri': '#edit/master/' - }, { - 'repo_url': 'http://example.com', - 'edit_uri': '' # Set to blank value - }, { # Nothing defined - } + }, ] expected = [ @@ -493,7 +467,7 @@ class PageTests(unittest.TestCase): 'http://example.com#edit/master/testing.md', 'http://example.com/#edit/master/testing.md', None, - None + None, ] for i, c in enumerate(configs): @@ -505,47 +479,20 @@ class PageTests(unittest.TestCase): def test_nested_page_edit_url(self): configs = [ - { - 'repo_url': 'http://github.com/mkdocs/mkdocs' - }, - { - 'repo_url': 'https://github.com/mkdocs/mkdocs/' - }, { - 'repo_url': 'http://example.com' - }, { - 'repo_url': 'http://example.com', - 'edit_uri': 'edit/master' - }, { - 'repo_url': 'http://example.com', - 'edit_uri': '/edit/master' - }, { - 'repo_url': 'http://example.com/foo/', - 'edit_uri': '/edit/master/' - }, { - 'repo_url': 'http://example.com/foo', - 'edit_uri': '/edit/master/' - }, { - 'repo_url': 'http://example.com/foo/', - 'edit_uri': '/edit/master' - }, { - 'repo_url': 'http://example.com/foo/', - 'edit_uri': 'edit/master/' - }, { - 'repo_url': 'http://example.com/foo', - 'edit_uri': 'edit/master/' - }, { - 'repo_url': 'http://example.com', - 'edit_uri': '?query=edit/master' - }, { - 'repo_url': 'http://example.com/', - 'edit_uri': '?query=edit/master/' - }, { - 'repo_url': 'http://example.com', - 'edit_uri': '#edit/master' - }, { - 'repo_url': 'http://example.com/', - 'edit_uri': '#edit/master/' - } + {'repo_url': 'http://github.com/mkdocs/mkdocs'}, + {'repo_url': 'https://github.com/mkdocs/mkdocs/'}, + {'repo_url': 'http://example.com'}, + {'repo_url': 'http://example.com', 'edit_uri': 'edit/master'}, + {'repo_url': 'http://example.com', 'edit_uri': '/edit/master'}, + {'repo_url': 'http://example.com/foo/', 'edit_uri': '/edit/master/'}, + {'repo_url': 'http://example.com/foo', 'edit_uri': '/edit/master/'}, + {'repo_url': 'http://example.com/foo/', 'edit_uri': '/edit/master'}, + {'repo_url': 'http://example.com/foo/', 'edit_uri': 'edit/master/'}, + {'repo_url': 'http://example.com/foo', 'edit_uri': 'edit/master/'}, + {'repo_url': 'http://example.com', 'edit_uri': '?query=edit/master'}, + {'repo_url': 'http://example.com/', 'edit_uri': '?query=edit/master/'}, + {'repo_url': 'http://example.com', 'edit_uri': '#edit/master'}, + {'repo_url': 'http://example.com/', 'edit_uri': '#edit/master/'}, ] expected = [ @@ -562,13 +509,15 @@ class PageTests(unittest.TestCase): 'http://example.com?query=edit/master/sub1/non-index.md', 'http://example.com/?query=edit/master/sub1/non-index.md', 'http://example.com#edit/master/sub1/non-index.md', - 'http://example.com/#edit/master/sub1/non-index.md' + 'http://example.com/#edit/master/sub1/non-index.md', ] for i, c in enumerate(configs): c['docs_dir'] = self.DOCS_DIR cfg = load_config(**c) - fl = File('sub1/non-index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + fl = File( + 'sub1/non-index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'] + ) pg = Page('Foo', fl, cfg) self.assertEqual(pg.url, 'sub1/non-index/') self.assertEqual(pg.edit_url, expected[i]) @@ -576,47 +525,20 @@ class PageTests(unittest.TestCase): @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_nested_page_edit_url_windows(self): configs = [ - { - 'repo_url': 'http://github.com/mkdocs/mkdocs' - }, - { - 'repo_url': 'https://github.com/mkdocs/mkdocs/' - }, { - 'repo_url': 'http://example.com' - }, { - 'repo_url': 'http://example.com', - 'edit_uri': 'edit/master' - }, { - 'repo_url': 'http://example.com', - 'edit_uri': '/edit/master' - }, { - 'repo_url': 'http://example.com/foo/', - 'edit_uri': '/edit/master/' - }, { - 'repo_url': 'http://example.com/foo', - 'edit_uri': '/edit/master/' - }, { - 'repo_url': 'http://example.com/foo/', - 'edit_uri': '/edit/master' - }, { - 'repo_url': 'http://example.com/foo/', - 'edit_uri': 'edit/master/' - }, { - 'repo_url': 'http://example.com/foo', - 'edit_uri': 'edit/master/' - }, { - 'repo_url': 'http://example.com', - 'edit_uri': '?query=edit/master' - }, { - 'repo_url': 'http://example.com/', - 'edit_uri': '?query=edit/master/' - }, { - 'repo_url': 'http://example.com', - 'edit_uri': '#edit/master' - }, { - 'repo_url': 'http://example.com/', - 'edit_uri': '#edit/master/' - } + {'repo_url': 'http://github.com/mkdocs/mkdocs'}, + {'repo_url': 'https://github.com/mkdocs/mkdocs/'}, + {'repo_url': 'http://example.com'}, + {'repo_url': 'http://example.com', 'edit_uri': 'edit/master'}, + {'repo_url': 'http://example.com', 'edit_uri': '/edit/master'}, + {'repo_url': 'http://example.com/foo/', 'edit_uri': '/edit/master/'}, + {'repo_url': 'http://example.com/foo', 'edit_uri': '/edit/master/'}, + {'repo_url': 'http://example.com/foo/', 'edit_uri': '/edit/master'}, + {'repo_url': 'http://example.com/foo/', 'edit_uri': 'edit/master/'}, + {'repo_url': 'http://example.com/foo', 'edit_uri': 'edit/master/'}, + {'repo_url': 'http://example.com', 'edit_uri': '?query=edit/master'}, + {'repo_url': 'http://example.com/', 'edit_uri': '?query=edit/master/'}, + {'repo_url': 'http://example.com', 'edit_uri': '#edit/master'}, + {'repo_url': 'http://example.com/', 'edit_uri': '#edit/master/'}, ] expected = [ @@ -633,13 +555,15 @@ class PageTests(unittest.TestCase): 'http://example.com?query=edit/master/sub1/non-index.md', 'http://example.com/?query=edit/master/sub1/non-index.md', 'http://example.com#edit/master/sub1/non-index.md', - 'http://example.com/#edit/master/sub1/non-index.md' + 'http://example.com/#edit/master/sub1/non-index.md', ] for i, c in enumerate(configs): c['docs_dir'] = self.DOCS_DIR cfg = load_config(**c) - fl = File('sub1\\non-index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + fl = File( + 'sub1\\non-index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'] + ) pg = Page('Foo', fl, cfg) self.assertEqual(pg.url, 'sub1/non-index/') self.assertEqual(pg.edit_url, expected[i]) @@ -652,14 +576,19 @@ class PageTests(unittest.TestCase): self.assertEqual(pg.content, None) self.assertEqual(pg.toc, []) pg.render(cfg, [fl]) - self.assertTrue(pg.content.startswith( - '

Welcome to MkDocs

\n' - )) - self.assertEqual(str(pg.toc).strip(), dedent(""" - Welcome to MkDocs - #welcome-to-mkdocs - Commands - #commands - Project layout - #project-layout - """)) + self.assertTrue( + pg.content.startswith('

Welcome to MkDocs

\n') + ) + self.assertEqual( + str(pg.toc).strip(), + dedent( + """ + Welcome to MkDocs - #welcome-to-mkdocs + Commands - #commands + Project layout - #project-layout + """ + ), + ) def test_missing_page(self): cfg = load_config() @@ -670,7 +599,6 @@ class PageTests(unittest.TestCase): class SourceDateEpochTests(unittest.TestCase): - def setUp(self): self.default = os.environ.get('SOURCE_DATE_EPOCH', None) os.environ['SOURCE_DATE_EPOCH'] = '0' @@ -690,13 +618,18 @@ class SourceDateEpochTests(unittest.TestCase): class RelativePathExtensionTests(unittest.TestCase): - DOCS_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../integration/subpages/docs') + DOCS_DIR = os.path.join( + os.path.abspath(os.path.dirname(__file__)), '../integration/subpages/docs' + ) def get_rendered_result(self, files): cfg = load_config(docs_dir=self.DOCS_DIR) - fs = [] - for f in files: - fs.append(File(f.replace('/', os.sep), cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])) + fs = [ + File( + f.replace('/', os.sep), cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'] + ) + for f in files + ] pg = Page('Foo', fs[0], cfg) pg.read_source(cfg) pg.render(cfg, Files(fs)) @@ -706,105 +639,113 @@ class RelativePathExtensionTests(unittest.TestCase): def test_relative_html_link(self): self.assertEqual( self.get_rendered_result(['index.md', 'non-index.md']), - '

link

' # No trailing / + '

link

', # No trailing / ) @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](index.md)')) def test_relative_html_link_index(self): self.assertEqual( self.get_rendered_result(['non-index.md', 'index.md']), - '

link

' + '

link

', ) @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](sub2/index.md)')) def test_relative_html_link_sub_index(self): self.assertEqual( self.get_rendered_result(['index.md', 'sub2/index.md']), - '

link

' # No trailing / + '

link

', # No trailing / ) - @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](sub2/non-index.md)')) + @mock.patch( + 'mkdocs.structure.pages.open', mock.mock_open(read_data='[link](sub2/non-index.md)') + ) def test_relative_html_link_sub_page(self): self.assertEqual( self.get_rendered_result(['index.md', 'sub2/non-index.md']), - '

link

' # No trailing / + '

link

', # No trailing / ) @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](file%20name.md)')) def test_relative_html_link_with_encoded_space(self): self.assertEqual( self.get_rendered_result(['index.md', 'file name.md']), - '

link

' + '

link

', ) @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](file name.md)')) def test_relative_html_link_with_unencoded_space(self): self.assertEqual( self.get_rendered_result(['index.md', 'file name.md']), - '

link

' + '

link

', ) @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](../index.md)')) def test_relative_html_link_parent_index(self): self.assertEqual( self.get_rendered_result(['sub2/non-index.md', 'index.md']), - '

link

' + '

link

', ) - @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](non-index.md#hash)')) + @mock.patch( + 'mkdocs.structure.pages.open', mock.mock_open(read_data='[link](non-index.md#hash)') + ) def test_relative_html_link_hash(self): self.assertEqual( self.get_rendered_result(['index.md', 'non-index.md']), - '

link

' + '

link

', ) - @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](sub2/index.md#hash)')) + @mock.patch( + 'mkdocs.structure.pages.open', mock.mock_open(read_data='[link](sub2/index.md#hash)') + ) def test_relative_html_link_sub_index_hash(self): self.assertEqual( self.get_rendered_result(['index.md', 'sub2/index.md']), - '

link

' + '

link

', ) - @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](sub2/non-index.md#hash)')) + @mock.patch( + 'mkdocs.structure.pages.open', mock.mock_open(read_data='[link](sub2/non-index.md#hash)') + ) def test_relative_html_link_sub_page_hash(self): self.assertEqual( self.get_rendered_result(['index.md', 'sub2/non-index.md']), - '

link

' + '

link

', ) @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](#hash)')) def test_relative_html_link_hash_only(self): self.assertEqual( self.get_rendered_result(['index.md']), - '

link

' + '

link

', ) @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='![image](image.png)')) def test_relative_image_link_from_homepage(self): self.assertEqual( self.get_rendered_result(['index.md', 'image.png']), - '

image

' # no opening ./ + '

image

', # no opening ./ ) @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='![image](../image.png)')) def test_relative_image_link_from_subpage(self): self.assertEqual( self.get_rendered_result(['sub2/non-index.md', 'image.png']), - '

image

' + '

image

', ) @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='![image](image.png)')) def test_relative_image_link_from_sibling(self): self.assertEqual( self.get_rendered_result(['non-index.md', 'image.png']), - '

image

' + '

image

', ) @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='*__not__ a link*.')) def test_no_links(self): self.assertEqual( self.get_rendered_result(['index.md']), - '

not a link.

' + '

not a link.

', ) @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](non-existent.md)')) @@ -812,33 +753,43 @@ class RelativePathExtensionTests(unittest.TestCase): with self.assertLogs('mkdocs', level='WARNING') as cm: self.assertEqual( self.get_rendered_result(['index.md']), - '

link

' + '

link

', ) self.assertEqual( cm.output, - ["WARNING:mkdocs.structure.pages:Documentation file 'index.md' contains a link " - "to 'non-existent.md' which is not found in the documentation files."] + [ + "WARNING:mkdocs.structure.pages:Documentation file 'index.md' contains a link " + "to 'non-existent.md' which is not found in the documentation files." + ], ) - @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[external](http://example.com/index.md)')) + @mock.patch( + 'mkdocs.structure.pages.open', + mock.mock_open(read_data='[external](http://example.com/index.md)'), + ) def test_external_link(self): self.assertEqual( self.get_rendered_result(['index.md']), - '

external

' + '

external

', ) - @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[absolute link](/path/to/file.md)')) + @mock.patch( + 'mkdocs.structure.pages.open', mock.mock_open(read_data='[absolute link](/path/to/file.md)') + ) def test_absolute_link(self): self.assertEqual( self.get_rendered_result(['index.md']), - '

absolute link

' + '

absolute link

', ) - @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[absolute local path](\\image.png)')) + @mock.patch( + 'mkdocs.structure.pages.open', + mock.mock_open(read_data='[absolute local path](\\image.png)'), + ) def test_absolute_win_local_path(self): self.assertEqual( self.get_rendered_result(['index.md']), - '

absolute local path

' + '

absolute local path

', ) @mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='')) @@ -849,5 +800,5 @@ class RelativePathExtensionTests(unittest.TestCase): # The following is equivalent to: '

mail@example.com

' '

mail@' - 'example.com

' + 'example.com

', ) diff --git a/mkdocs/tests/structure/toc_tests.py b/mkdocs/tests/structure/toc_tests.py index 591d4f7b..44c9a4b8 100644 --- a/mkdocs/tests/structure/toc_tests.py +++ b/mkdocs/tests/structure/toc_tests.py @@ -6,135 +6,166 @@ from mkdocs.tests.base import dedent, get_markdown_toc class TableOfContentsTests(unittest.TestCase): - def test_indented_toc(self): - md = dedent(""" - # Heading 1 - ## Heading 2 - ### Heading 3 - """) - expected = dedent(""" - Heading 1 - #heading-1 - Heading 2 - #heading-2 - Heading 3 - #heading-3 - """) + md = dedent( + """ + # Heading 1 + ## Heading 2 + ### Heading 3 + """ + ) + expected = dedent( + """ + Heading 1 - #heading-1 + Heading 2 - #heading-2 + Heading 3 - #heading-3 + """ + ) toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) self.assertEqual(len(toc), 1) def test_indented_toc_html(self): - md = dedent(""" - # Heading 1 - ## Heading 2 - ## Heading 3 - """) - expected = dedent(""" - Heading 1 - #heading-1 - Heading 2 - #heading-2 - Heading 3 - #heading-3 - """) + md = dedent( + """ + # Heading 1 + ## Heading 2 + ## Heading 3 + """ + ) + expected = dedent( + """ + Heading 1 - #heading-1 + Heading 2 - #heading-2 + Heading 3 - #heading-3 + """ + ) toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) self.assertEqual(len(toc), 1) def test_flat_toc(self): - md = dedent(""" - # Heading 1 - # Heading 2 - # Heading 3 - """) - expected = dedent(""" - Heading 1 - #heading-1 - Heading 2 - #heading-2 - Heading 3 - #heading-3 - """) + md = dedent( + """ + # Heading 1 + # Heading 2 + # Heading 3 + """ + ) + expected = dedent( + """ + Heading 1 - #heading-1 + Heading 2 - #heading-2 + Heading 3 - #heading-3 + """ + ) toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) self.assertEqual(len(toc), 3) def test_flat_h2_toc(self): - md = dedent(""" - ## Heading 1 - ## Heading 2 - ## Heading 3 - """) - expected = dedent(""" - Heading 1 - #heading-1 - Heading 2 - #heading-2 - Heading 3 - #heading-3 - """) + md = dedent( + """ + ## Heading 1 + ## Heading 2 + ## Heading 3 + """ + ) + expected = dedent( + """ + Heading 1 - #heading-1 + Heading 2 - #heading-2 + Heading 3 - #heading-3 + """ + ) toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) self.assertEqual(len(toc), 3) def test_mixed_toc(self): - md = dedent(""" - # Heading 1 - ## Heading 2 - # Heading 3 - ### Heading 4 - ### Heading 5 - """) - expected = dedent(""" - Heading 1 - #heading-1 - Heading 2 - #heading-2 - Heading 3 - #heading-3 - Heading 4 - #heading-4 - Heading 5 - #heading-5 - """) + md = dedent( + """ + # Heading 1 + ## Heading 2 + # Heading 3 + ### Heading 4 + ### Heading 5 + """ + ) + expected = dedent( + """ + Heading 1 - #heading-1 + Heading 2 - #heading-2 + Heading 3 - #heading-3 + Heading 4 - #heading-4 + Heading 5 - #heading-5 + """ + ) toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) self.assertEqual(len(toc), 2) def test_mixed_html(self): - md = dedent(""" - # Heading 1 - ## Heading 2 - # Heading 3 - ### Heading 4 - ### Heading 5 - """) - expected = dedent(""" - Heading 1 - #heading-1 - Heading 2 - #heading-2 - Heading 3 - #heading-3 - Heading 4 - #heading-4 - Heading 5 - #heading-5 - """) + md = dedent( + """ + # Heading 1 + ## Heading 2 + # Heading 3 + ### Heading 4 + ### Heading 5 + """ + ) + expected = dedent( + """ + Heading 1 - #heading-1 + Heading 2 - #heading-2 + Heading 3 - #heading-3 + Heading 4 - #heading-4 + Heading 5 - #heading-5 + """ + ) toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) self.assertEqual(len(toc), 2) def test_nested_anchor(self): - md = dedent(""" - # Heading 1 - ## Heading 2 - # Heading 3 - ### Heading 4 - ### Heading 5 - """) - expected = dedent(""" - Heading 1 - #heading-1 - Heading 2 - #heading-2 - Heading 3 - #heading-3 - Heading 4 - #heading-4 - Heading 5 - #heading-5 - """) + md = dedent( + """ + # Heading 1 + ## Heading 2 + # Heading 3 + ### Heading 4 + ### Heading 5 + """ + ) + expected = dedent( + """ + Heading 1 - #heading-1 + Heading 2 - #heading-2 + Heading 3 - #heading-3 + Heading 4 - #heading-4 + Heading 5 - #heading-5 + """ + ) toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) self.assertEqual(len(toc), 2) def test_entityref(self): - md = dedent(""" - # Heading & 1 - ## Heading > 2 - ### Heading < 3 - """) - expected = dedent(""" - Heading & 1 - #heading-1 - Heading > 2 - #heading-2 - Heading < 3 - #heading-3 - """) + md = dedent( + """ + # Heading & 1 + ## Heading > 2 + ### Heading < 3 + """ + ) + expected = dedent( + """ + Heading & 1 - #heading-1 + Heading > 2 - #heading-2 + Heading < 3 - #heading-3 + """ + ) toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) self.assertEqual(len(toc), 1) @@ -147,13 +178,15 @@ class TableOfContentsTests(unittest.TestCase): self.assertEqual(len(toc), 1) def test_level(self): - md = dedent(""" - # Heading 1 - ## Heading 1.1 - ### Heading 1.1.1 - ### Heading 1.1.2 - ## Heading 1.2 - """) + md = dedent( + """ + # Heading 1 + ## Heading 1.1 + ### Heading 1.1.1 + ### Heading 1.1.2 + ## Heading 1.2 + """ + ) toc = get_toc(get_markdown_toc(md)) def get_level_sequence(items): diff --git a/mkdocs/tests/theme_tests.py b/mkdocs/tests/theme_tests.py index 296540af..b4c71553 100644 --- a/mkdocs/tests/theme_tests.py +++ b/mkdocs/tests/theme_tests.py @@ -14,31 +14,33 @@ theme_dir = os.path.abspath(os.path.join(mkdocs_dir, 'themes')) def get_vars(theme): - """ Return dict of theme vars. """ + """Return dict of theme vars.""" return {k: theme[k] for k in iter(theme)} class ThemeTests(unittest.TestCase): - def test_simple_theme(self): theme = Theme(name='mkdocs') self.assertEqual( theme.dirs, - [os.path.join(theme_dir, 'mkdocs'), mkdocs_templates_dir] + [os.path.join(theme_dir, 'mkdocs'), mkdocs_templates_dir], ) self.assertEqual(theme.static_templates, {'404.html', 'sitemap.xml'}) - self.assertEqual(get_vars(theme), { - 'locale': parse_locale('en'), - 'include_search_page': False, - 'search_index_only': False, - 'analytics': {'gtag': None}, - 'highlightjs': True, - 'hljs_style': 'github', - 'hljs_languages': [], - 'navigation_depth': 2, - 'nav_style': 'primary', - 'shortcuts': {'help': 191, 'next': 78, 'previous': 80, 'search': 83} - }) + self.assertEqual( + get_vars(theme), + { + 'locale': parse_locale('en'), + 'include_search_page': False, + 'search_index_only': False, + 'analytics': {'gtag': None}, + 'highlightjs': True, + 'hljs_style': 'github', + 'hljs_languages': [], + 'navigation_depth': 2, + 'nav_style': 'primary', + 'shortcuts': {'help': 191, 'next': 78, 'previous': 80, 'search': 83}, + }, + ) def test_custom_dir(self): custom = tempfile.mkdtemp() @@ -48,8 +50,8 @@ class ThemeTests(unittest.TestCase): [ custom, os.path.join(theme_dir, 'mkdocs'), - mkdocs_templates_dir - ] + mkdocs_templates_dir, + ], ) def test_custom_dir_only(self): @@ -57,14 +59,14 @@ class ThemeTests(unittest.TestCase): theme = Theme(name=None, custom_dir=custom) self.assertEqual( theme.dirs, - [custom, mkdocs_templates_dir] + [custom, mkdocs_templates_dir], ) def static_templates(self): theme = Theme(name='mkdocs', static_templates='foo.html') self.assertEqual( theme.static_templates, - {'404.html', 'sitemap.xml', 'foo.html'} + {'404.html', 'sitemap.xml', 'foo.html'}, ) def test_vars(self): @@ -85,10 +87,12 @@ class ThemeTests(unittest.TestCase): self.assertEqual(theme.static_templates, {'sitemap.xml'}) def test_inherited_theme(self): - m = mock.Mock(side_effect=[ - {'extends': 'readthedocs', 'static_templates': ['child.html']}, - {'static_templates': ['parent.html']} - ]) + m = mock.Mock( + side_effect=[ + {'extends': 'readthedocs', 'static_templates': ['child.html']}, + {'static_templates': ['parent.html']}, + ] + ) with mock.patch('mkdocs.utils.yaml_load', m) as m: theme = Theme(name='mkdocs') self.assertEqual(m.call_count, 2) @@ -97,9 +101,7 @@ class ThemeTests(unittest.TestCase): [ os.path.join(theme_dir, 'mkdocs'), os.path.join(theme_dir, 'readthedocs'), - mkdocs_templates_dir - ] - ) - self.assertEqual( - theme.static_templates, {'sitemap.xml', 'child.html', 'parent.html'} + mkdocs_templates_dir, + ], ) + self.assertEqual(theme.static_templates, {'sitemap.xml', 'child.html', 'parent.html'}) diff --git a/mkdocs/tests/utils/babel_stub_tests.py b/mkdocs/tests/utils/babel_stub_tests.py index 59cb35f0..722d3b32 100644 --- a/mkdocs/tests/utils/babel_stub_tests.py +++ b/mkdocs/tests/utils/babel_stub_tests.py @@ -3,7 +3,6 @@ from mkdocs.utils.babel_stub import Locale, UnknownLocaleError class BabelStubTests(unittest.TestCase): - def test_locale_language_only(self): locale = Locale('es') self.assertEqual(locale.language, 'es') diff --git a/mkdocs/tests/utils/utils_tests.py b/mkdocs/tests/utils/utils_tests.py index 1a79fcf0..0cb8142f 100644 --- a/mkdocs/tests/utils/utils_tests.py +++ b/mkdocs/tests/utils/utils_tests.py @@ -45,7 +45,7 @@ class UtilsTests(unittest.TestCase): 'index.markdown': True, 'index.MARKDOWN': False, 'index.txt': False, - 'indexmd': False + 'indexmd': False, } for path, expected_result in expected_results.items(): is_markdown = utils.is_markdown_file(path) @@ -85,7 +85,8 @@ class UtilsTests(unittest.TestCase): ('a', ''): 'a', ('a', '..'): 'a', ('a', 'b'): '../a', - ('a', 'b/..'): '../a', # The dots are considered a file. Documenting a long-standing bug. + # The dots are considered a file. Documenting a long-standing bug: + ('a', 'b/..'): '../a', ('a', 'b/../..'): 'a', ('a/..../b', 'a/../b'): '../a/..../b', ('a/я/b', 'a/я/c'): '../b', @@ -114,55 +115,67 @@ class UtilsTests(unittest.TestCase): 'https://media.cdn.org/jq.js': [ 'https://media.cdn.org/jq.js', 'https://media.cdn.org/jq.js', - 'https://media.cdn.org/jq.js' + 'https://media.cdn.org/jq.js', ], 'http://media.cdn.org/jquery.js': [ 'http://media.cdn.org/jquery.js', 'http://media.cdn.org/jquery.js', - 'http://media.cdn.org/jquery.js' + 'http://media.cdn.org/jquery.js', ], '//media.cdn.org/jquery.js': [ '//media.cdn.org/jquery.js', '//media.cdn.org/jquery.js', - '//media.cdn.org/jquery.js' + '//media.cdn.org/jquery.js', ], 'media.cdn.org/jquery.js': [ 'media.cdn.org/jquery.js', 'media.cdn.org/jquery.js', - '../media.cdn.org/jquery.js' + '../media.cdn.org/jquery.js', ], 'local/file/jquery.js': [ 'local/file/jquery.js', 'local/file/jquery.js', - '../local/file/jquery.js' + '../local/file/jquery.js', ], 'local\\windows\\file\\jquery.js': [ 'local/windows/file/jquery.js', 'local/windows/file/jquery.js', - '../local/windows/file/jquery.js' + '../local/windows/file/jquery.js', ], 'image.png': [ 'image.png', 'image.png', - '../image.png' + '../image.png', ], 'style.css?v=20180308c': [ 'style.css?v=20180308c', 'style.css?v=20180308c', - '../style.css?v=20180308c' + '../style.css?v=20180308c', ], '#some_id': [ '#some_id', '#some_id', - '#some_id' - ] + '#some_id', + ], } cfg = load_config(use_directory_urls=False) pages = [ - Page('Home', File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg), - Page('About', File('about.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg), - Page('FooBar', File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg) + Page( + 'Home', + File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + cfg, + ), + Page( + 'About', + File('about.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + cfg, + ), + Page( + 'FooBar', + File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + cfg, + ), ] for i, page in enumerate(pages): @@ -175,55 +188,67 @@ class UtilsTests(unittest.TestCase): 'https://media.cdn.org/jq.js': [ 'https://media.cdn.org/jq.js', 'https://media.cdn.org/jq.js', - 'https://media.cdn.org/jq.js' + 'https://media.cdn.org/jq.js', ], 'http://media.cdn.org/jquery.js': [ 'http://media.cdn.org/jquery.js', 'http://media.cdn.org/jquery.js', - 'http://media.cdn.org/jquery.js' + 'http://media.cdn.org/jquery.js', ], '//media.cdn.org/jquery.js': [ '//media.cdn.org/jquery.js', '//media.cdn.org/jquery.js', - '//media.cdn.org/jquery.js' + '//media.cdn.org/jquery.js', ], 'media.cdn.org/jquery.js': [ 'media.cdn.org/jquery.js', '../media.cdn.org/jquery.js', - '../../media.cdn.org/jquery.js' + '../../media.cdn.org/jquery.js', ], 'local/file/jquery.js': [ 'local/file/jquery.js', '../local/file/jquery.js', - '../../local/file/jquery.js' + '../../local/file/jquery.js', ], 'local\\windows\\file\\jquery.js': [ 'local/windows/file/jquery.js', '../local/windows/file/jquery.js', - '../../local/windows/file/jquery.js' + '../../local/windows/file/jquery.js', ], 'image.png': [ 'image.png', '../image.png', - '../../image.png' + '../../image.png', ], 'style.css?v=20180308c': [ 'style.css?v=20180308c', '../style.css?v=20180308c', - '../../style.css?v=20180308c' + '../../style.css?v=20180308c', ], '#some_id': [ '#some_id', '#some_id', - '#some_id' - ] + '#some_id', + ], } cfg = load_config(use_directory_urls=True) pages = [ - Page('Home', File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg), - Page('About', File('about.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg), - Page('FooBar', File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg) + Page( + 'Home', + File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + cfg, + ), + Page( + 'About', + File('about.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + cfg, + ), + Page( + 'FooBar', + File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + cfg, + ), ] for i, page in enumerate(pages): @@ -233,14 +258,12 @@ class UtilsTests(unittest.TestCase): def test_reduce_list(self): self.assertEqual( utils.reduce_list([1, 2, 3, 4, 5, 5, 2, 4, 6, 7, 8]), - [1, 2, 3, 4, 5, 6, 7, 8] + [1, 2, 3, 4, 5, 6, 7, 8], ) def test_get_themes(self): - self.assertEqual( - sorted(utils.get_theme_names()), - ['mkdocs', 'readthedocs']) + self.assertEqual(sorted(utils.get_theme_names()), ['mkdocs', 'readthedocs']) @mock.patch('importlib_metadata.entry_points', autospec=True) def test_get_theme_dir(self, mock_iter): @@ -289,9 +312,7 @@ class UtilsTests(unittest.TestCase): mock_iter.return_value = [theme1, theme2] - self.assertEqual( - sorted(utils.get_theme_names()), - sorted(['mkdocs2', ])) + self.assertEqual(sorted(utils.get_theme_names()), sorted(['mkdocs2'])) @mock.patch('importlib_metadata.entry_points', autospec=True) def test_get_themes_error(self, mock_iter): @@ -315,29 +336,37 @@ class UtilsTests(unittest.TestCase): j = os.path.join - result = utils.nest_paths([ - 'index.md', - j('user-guide', 'configuration.md'), - j('user-guide', 'styling-your-docs.md'), - j('user-guide', 'writing-your-docs.md'), - j('about', 'contributing.md'), - j('about', 'license.md'), - j('about', 'release-notes.md'), - ]) + result = utils.nest_paths( + [ + 'index.md', + j('user-guide', 'configuration.md'), + j('user-guide', 'styling-your-docs.md'), + j('user-guide', 'writing-your-docs.md'), + j('about', 'contributing.md'), + j('about', 'license.md'), + j('about', 'release-notes.md'), + ] + ) self.assertEqual( result, [ 'index.md', - {'User guide': [ - j('user-guide', 'configuration.md'), - j('user-guide', 'styling-your-docs.md'), - j('user-guide', 'writing-your-docs.md')]}, - {'About': [ - j('about', 'contributing.md'), - j('about', 'license.md'), - j('about', 'release-notes.md')]} - ] + { + 'User guide': [ + j('user-guide', 'configuration.md'), + j('user-guide', 'styling-your-docs.md'), + j('user-guide', 'writing-your-docs.md'), + ] + }, + { + 'About': [ + j('about', 'contributing.md'), + j('about', 'license.md'), + j('about', 'release-notes.md'), + ] + }, + ], ) def test_unicode_yaml(self): @@ -383,15 +412,15 @@ class UtilsTests(unittest.TestCase): 'baz': { 'sub1': 'replaced', 'sub2': 2, - 'sub3': 'new' + 'sub3': 'new', }, 'deep1': { 'deep2-1': { 'deep3-1': 'replaced', - 'deep3-2': 'bar' + 'deep3-2': 'bar', }, - 'deep2-2': 'baz' - } + 'deep2-2': 'baz', + }, } with open(os.path.join(tdir, 'base.yml')) as fd: result = utils.yaml_load(fd) @@ -411,8 +440,8 @@ class UtilsTests(unittest.TestCase): ] dst_paths = [ 'foo.txt', - 'foo/', # ensure src filename is appended - 'foo/bar/baz.txt' # ensure missing dirs are created + 'foo/', # ensure src filename is appended + 'foo/bar/baz.txt', # ensure missing dirs are created ] expected = [ 'foo.txt', @@ -459,7 +488,9 @@ class UtilsTests(unittest.TestCase): os.chmod(src, stat.S_IRUSR) utils.copy_file(src, dst_dir) self.assertTrue(os.path.isfile(os.path.join(dst_dir, expected[i]))) - self.assertNotEqual(os.stat(src).st_mode, os.stat(os.path.join(dst_dir, expected[i])).st_mode) + self.assertNotEqual( + os.stat(src).st_mode, os.stat(os.path.join(dst_dir, expected[i])).st_mode + ) # While src was read-only, dst must remain writable self.assertTrue(os.access(os.path.join(dst_dir, expected[i]), os.W_OK)) finally: @@ -492,9 +523,9 @@ class UtilsTests(unittest.TestCase): 'title': 'Foo Bar', 'date': '2018-07-10', 'summary': 'Line one Line two', - 'tags': 'foo bar' - } - ) + 'tags': 'foo bar', + }, + ), ) def test_mm_meta_data_blank_first_line(self): @@ -524,9 +555,9 @@ class UtilsTests(unittest.TestCase): 'Title': 'Foo Bar', 'Date': datetime.date(2018, 7, 10), 'Summary': 'Line one Line two', - 'Tags': ['foo', 'bar'] - } - ) + 'Tags': ['foo', 'bar'], + }, + ), ) def test_yaml_meta_data_not_dict(self): diff --git a/mkdocs/theme.py b/mkdocs/theme.py index 04ae3c4d..a83ac27a 100644 --- a/mkdocs/theme.py +++ b/mkdocs/theme.py @@ -56,8 +56,11 @@ class Theme: def __repr__(self): return "{}(name='{}', dirs={}, static_templates={}, {})".format( - self.__class__.__name__, self.name, self.dirs, list(self.static_templates), - ', '.join(f'{k}={v!r}' for k, v in self._vars.items()) + self.__class__.__name__, + self.name, + self.dirs, + list(self.static_templates), + ', '.join(f'{k}={v!r}' for k, v in self._vars.items()), ) def __getitem__(self, key): @@ -73,7 +76,7 @@ class Theme: return iter(self._vars) def _load_theme_config(self, name): - """ Recursively load theme and any parent themes. """ + """Recursively load theme and any parent themes.""" theme_dir = utils.get_theme_dir(name) self.dirs.append(theme_dir) @@ -107,7 +110,7 @@ class Theme: self._vars.update(theme_config) def get_env(self): - """ Return a Jinja environment for the theme. """ + """Return a Jinja environment for the theme.""" loader = jinja2.FileSystemLoader(self.dirs) # No autoreload because editing a template in the middle of a build is not useful. diff --git a/mkdocs/utils/__init__.py b/mkdocs/utils/__init__.py index 4006d1b7..17e52d19 100644 --- a/mkdocs/utils/__init__.py +++ b/mkdocs/utils/__init__.py @@ -30,12 +30,12 @@ markdown_extensions = ( '.mdown', '.mkdn', '.mkd', - '.md' + '.md', ) def get_yaml_loader(loader=yaml.Loader): - """ Wrap PyYaml's loader so we can extend it to suit our needs. """ + """Wrap PyYaml's loader so we can extend it to suit our needs.""" class Loader(loader): """ @@ -51,7 +51,7 @@ def get_yaml_loader(loader=yaml.Loader): def yaml_load(source, loader=None): - """ Return dict of source YAML file using loader, recursively deep merging inherited parent. """ + """Return dict of source YAML file using loader, recursively deep merging inherited parent.""" Loader = loader or get_yaml_loader() result = yaml.load(source, Loader=Loader) if result is not None and 'INHERIT' in result: @@ -59,7 +59,8 @@ def yaml_load(source, loader=None): abspath = os.path.normpath(os.path.join(os.path.dirname(source.name), relpath)) if not os.path.exists(abspath): raise exceptions.ConfigurationError( - f"Inherited config file '{relpath}' does not exist at '{abspath}'.") + f"Inherited config file '{relpath}' does not exist at '{abspath}'." + ) log.debug(f"Loading inherited configuration file: {abspath}") with open(abspath, 'rb') as fd: parent = yaml_load(fd, Loader) @@ -116,7 +117,7 @@ def get_build_date(): def reduce_list(data_set): - """ Reduce duplicate items in a list and preserve order """ + """Reduce duplicate items in a list and preserve order""" return list(dict.fromkeys(data_set)) @@ -181,7 +182,7 @@ def get_url_path(path, use_directory_urls=True): path = get_html_path(path) url = '/' + path.replace(os.path.sep, '/') if use_directory_urls: - return url[:-len('index.html')] + return url[: -len('index.html')] return url @@ -255,7 +256,7 @@ def get_relative_url(url, other): def normalize_url(path, page=None, base=''): - """ Return a URL relative to the given page or using the base. """ + """Return a URL relative to the given page or using the base.""" path, is_abs = _get_norm_url(path) if is_abs: return path @@ -288,14 +289,14 @@ def path_to_url(path): def get_theme_dir(name): - """ Return the directory of an installed theme by name. """ + """Return the directory of an installed theme by name.""" theme = get_themes()[name] return os.path.dirname(os.path.abspath(theme.load().__file__)) def get_themes(): - """ Return a dict of all installed themes as {name: EntryPoint}. """ + """Return a dict of all installed themes as {name: EntryPoint}.""" themes = {} eps = set(importlib_metadata.entry_points(group='mkdocs.themes')) @@ -326,7 +327,7 @@ def get_theme_names(): def dirname_to_title(dirname): - """ Return a page tile obtained from a directory name. """ + """Return a page tile obtained from a directory name.""" title = dirname title = title.replace('-', ' ').replace('_', ' ') # Capitalize if the dirname was all lowercase, otherwise leave it as-is. @@ -402,7 +403,7 @@ def nest_paths(paths): class CountHandler(logging.NullHandler): - """ Counts all logged messages >= level. """ + """Counts all logged messages >= level.""" def __init__(self, **kwargs): self.counts = defaultdict(int) diff --git a/mkdocs/utils/filters.py b/mkdocs/utils/filters.py index c3049db9..d959784c 100644 --- a/mkdocs/utils/filters.py +++ b/mkdocs/utils/filters.py @@ -8,5 +8,5 @@ from mkdocs.utils import normalize_url @contextfilter def url_filter(context, value): - """ A Template filter to normalize URLs. """ + """A Template filter to normalize URLs.""" return normalize_url(value, page=context['page'], base=context['base_url']) diff --git a/mkdocs/utils/meta.py b/mkdocs/utils/meta.py index d208589d..714fb184 100644 --- a/mkdocs/utils/meta.py +++ b/mkdocs/utils/meta.py @@ -36,6 +36,7 @@ Extracts, parses and transforms MultiMarkdown style data from documents. import re import yaml + try: from yaml import CSafeLoader as SafeLoader except ImportError: # pragma: no cover @@ -64,7 +65,7 @@ def get_data(doc): try: data = yaml.load(m.group(1), SafeLoader) if isinstance(data, dict): - doc = doc[m.end():].lstrip('\n') + doc = doc[m.end() :].lstrip('\n') else: data = {} except Exception: diff --git a/requirements/project-min.txt b/requirements/project-min.txt index 00719753..7dc9825c 100644 --- a/requirements/project-min.txt +++ b/requirements/project-min.txt @@ -1,5 +1,5 @@ babel==2.9.0 -click==3.3 +click==8.0 Jinja2==2.10.2 markupsafe<=2.0.1 Markdown==3.3 diff --git a/requirements/project.txt b/requirements/project.txt index 2e1be0c8..bcf8e8d3 100644 --- a/requirements/project.txt +++ b/requirements/project.txt @@ -1,5 +1,5 @@ babel>=2.9.0 -click>=7.0 +click>=8.0 Jinja2>=2.10.2 Markdown>=3.3,<3.4 PyYAML>=5.2 diff --git a/requirements/test.txt b/requirements/test.txt index 9c4e8691..d38598f9 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,2 +1,3 @@ coverage +black flake8 diff --git a/setup.cfg b/setup.cfg index 1e6ff981..41194d3a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,10 @@ [metadata] license_file = LICENSE +[flake8] +max-line-length = 119 +extend-ignore = E203 + [compile_catalog] statistics = True domain = messages diff --git a/tox.ini b/tox.ini index e36af039..62799a82 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = py{36,37,38,39,310,py3}-{unittests,min-req,integration,integration-no-babel}, - flake8, markdown-lint, linkchecker, jshint, csslint, nobabel, codespell + black, flake8, markdown-lint, linkchecker, jshint, csslint, nobabel, codespell [testenv] passenv = LANG @@ -16,9 +16,13 @@ commands= py{36,37,38,39,310,py3}-unittests: {envbindir}/coverage report --show-missing py{36,37,38,39,310,py3}-integration: {envpython} -m mkdocs.tests.integration --output={envtmpdir}/builds +[testenv:black] +deps=-rrequirements/test.txt +commands={envbindir}/black -l100 -tpy36 --skip-string-normalization mkdocs + [testenv:flake8] deps=-rrequirements/test.txt -commands={envbindir}/flake8 mkdocs --max-line-length=119 +commands={envbindir}/flake8 mkdocs [testenv:markdown-lint] whitelist_externals=