diff --git a/docs/the_black_code_style/future_style.md b/docs/the_black_code_style/future_style.md index d5faae3..1c11186 100644 --- a/docs/the_black_code_style/future_style.md +++ b/docs/the_black_code_style/future_style.md @@ -16,6 +16,8 @@ feature, it is demoted to the `--unstable` style. To avoid thrash when a feature demoted from the `--preview` to the `--unstable` style, users can use the `--enable-unstable-feature` flag to enable specific unstable features. +(labels/preview-features)= + Currently, the following features are included in the preview style: - `hex_codes_in_unicode_sequences`: normalize casing of Unicode escape characters in diff --git a/tests/test_docs.py b/tests/test_docs.py new file mode 100644 index 0000000..8050e0f --- /dev/null +++ b/tests/test_docs.py @@ -0,0 +1,74 @@ +""" + +Test that the docs are up to date. + +""" + +import re +from itertools import islice +from pathlib import Path +from typing import Optional, Sequence, Set + +import pytest + +from black.mode import UNSTABLE_FEATURES, Preview + +DOCS_PATH = Path("docs/the_black_code_style/future_style.md") + + +def check_feature_list( + lines: Sequence[str], expected_feature_names: Set[str], label: str +) -> Optional[str]: + start_index = lines.index(f"(labels/{label}-features)=\n") + if start_index == -1: + return ( + f"Could not find the {label} features list in {DOCS_PATH}. Ensure the" + " preview-features label is present." + ) + num_blank_lines_seen = 0 + seen_preview_feature_names = set() + for line in islice(lines, start_index + 1, None): + if not line.strip(): + num_blank_lines_seen += 1 + if num_blank_lines_seen == 3: + break + continue + if line.startswith("- "): + match = re.search(r"^- `([a-z\d_]+)`", line) + if match: + seen_preview_feature_names.add(match.group(1)) + + if seen_preview_feature_names - expected_feature_names: + extra = ", ".join(sorted(seen_preview_feature_names - expected_feature_names)) + return ( + f"The following features should not be in the list of {label} features:" + f" {extra}. Please remove them from the {label}-features label in" + f" {DOCS_PATH}" + ) + elif expected_feature_names - seen_preview_feature_names: + missing = ", ".join(sorted(expected_feature_names - seen_preview_feature_names)) + return ( + f"The following features are missing from the list of {label} features:" + f" {missing}. Please document them under the {label}-features label in" + f" {DOCS_PATH}" + ) + else: + return None + + +def test_feature_lists_are_up_to_date() -> None: + repo_root = Path(__file__).parent.parent + if not (repo_root / "docs").exists(): + pytest.skip("docs not found") + with (repo_root / DOCS_PATH).open(encoding="utf-8") as f: + future_style = f.readlines() + preview_error = check_feature_list( + future_style, + {feature.name for feature in set(Preview) - UNSTABLE_FEATURES}, + "preview", + ) + assert preview_error is None, preview_error + unstable_error = check_feature_list( + future_style, {feature.name for feature in UNSTABLE_FEATURES}, "unstable" + ) + assert unstable_error is None, unstable_error