From b3992222b110ab0bde78ade55577f0899e0471b2 Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Mon, 16 Oct 2023 22:32:08 +0200 Subject: [PATCH] Move forward with some deprecations (#3425) --- mkdocs/livereload/__init__.py | 33 ++++++--------------- mkdocs/tests/livereload_tests.py | 22 -------------- mkdocs/theme.py | 32 +++++++++++++-------- mkdocs/utils/__init__.py | 49 ++------------------------------ 4 files changed, 32 insertions(+), 104 deletions(-) diff --git a/mkdocs/livereload/__init__.py b/mkdocs/livereload/__init__.py index f720a4fd..94246d1d 100644 --- a/mkdocs/livereload/__init__.py +++ b/mkdocs/livereload/__init__.py @@ -18,7 +18,6 @@ import threading import time import traceback import urllib.parse -import warnings import wsgiref.simple_server import wsgiref.util from typing import Any, BinaryIO, Callable, Iterable @@ -119,10 +118,8 @@ class LiveReloadServer(socketserver.ThreadingMixIn, wsgiref.simple_server.WSGISe self._visible_epoch = self._wanted_epoch # Latest fully built version of the site. self._epoch_cond = threading.Condition() # Must be held when accessing _visible_epoch. - self._to_rebuild: dict[ - Callable[[], None], bool - ] = {} # Used as an ordered set of functions to call. - self._rebuild_cond = threading.Condition() # Must be held when accessing _to_rebuild. + self._want_rebuild: bool = False + self._rebuild_cond = threading.Condition() # Must be held when accessing _want_rebuild. self._shutdown = False self.serve_thread = threading.Thread(target=lambda: self.serve_forever(shutdown_delay)) @@ -131,21 +128,11 @@ class LiveReloadServer(socketserver.ThreadingMixIn, wsgiref.simple_server.WSGISe self._watched_paths: dict[str, int] = {} self._watch_refs: dict[str, Any] = {} - def watch( - self, path: str, func: Callable[[], None] | None = None, recursive: bool = True - ) -> None: + def watch(self, path: str, func: None = None, *, recursive: bool = True) -> None: """Add the 'path' to watched paths, call the function and reload when any file changes under it.""" path = os.path.abspath(path) - if func is None or func is self.builder: - funct = self.builder - else: - funct = func - warnings.warn( - "Plugins should not pass the 'func' parameter of watch(). " - "The ability to execute custom callbacks will be removed soon.", - DeprecationWarning, - stacklevel=2, - ) + if not (func is None or func is self.builder): # type: ignore[unreachable] + raise TypeError("Plugins can no longer pass a 'func' parameter to watch().") if path in self._watched_paths: self._watched_paths[path] += 1 @@ -157,7 +144,7 @@ class LiveReloadServer(socketserver.ThreadingMixIn, wsgiref.simple_server.WSGISe return log.debug(str(event)) with self._rebuild_cond: - self._to_rebuild[funct] = True + self._want_rebuild = True self._rebuild_cond.notify_all() handler = watchdog.events.FileSystemEventHandler() @@ -193,7 +180,7 @@ class LiveReloadServer(socketserver.ThreadingMixIn, wsgiref.simple_server.WSGISe while True: with self._rebuild_cond: while not self._rebuild_cond.wait_for( - lambda: self._to_rebuild or self._shutdown, timeout=self.shutdown_delay + lambda: self._want_rebuild or self._shutdown, timeout=self.shutdown_delay ): # We could have used just one wait instead of a loop + timeout, but we need # occasional breaks, otherwise on Windows we can't receive KeyboardInterrupt. @@ -205,12 +192,10 @@ class LiveReloadServer(socketserver.ThreadingMixIn, wsgiref.simple_server.WSGISe log.debug("Waiting for file changes to stop happening") self._wanted_epoch = _timestamp() - funcs = list(self._to_rebuild) - self._to_rebuild.clear() + self._want_rebuild = False try: - for func in funcs: - func() + self.builder() except Exception as e: if isinstance(e, SystemExit): print(e, file=sys.stderr) # noqa: T201 diff --git a/mkdocs/tests/livereload_tests.py b/mkdocs/tests/livereload_tests.py index db6b791e..6a7a0c9f 100644 --- a/mkdocs/tests/livereload_tests.py +++ b/mkdocs/tests/livereload_tests.py @@ -181,28 +181,6 @@ class BuildTests(unittest.TestCase): with self.assertRaises(KeyError): server.unwatch(site_dir) - @tempdir({"foo.docs": "a"}) - @tempdir({"foo.site": "original"}) - def test_custom_action_warns(self, site_dir, docs_dir): - started_building = threading.Event() - - def rebuild(): - started_building.set() - content = Path(docs_dir, "foo.docs").read_text() - Path(site_dir, "foo.site").write_text(content * 5) - - with testing_server(site_dir) as server: - with self.assertWarnsRegex(DeprecationWarning, "func") as cm: - server.watch(docs_dir, rebuild) - time.sleep(0.01) - self.assertIn("livereload_tests.py", cm.filename) - - Path(docs_dir, "foo.docs").write_text("b") - self.assertTrue(started_building.wait(timeout=10)) - - _, output = do_request(server, "GET /foo.site") - self.assertEqual(output, "bbbbb") - @tempdir({"foo.docs": "docs1"}) @tempdir({"foo.extra": "extra1"}) @tempdir({"foo.site": "original"}) diff --git a/mkdocs/theme.py b/mkdocs/theme.py index 467d26ea..c9e8e408 100644 --- a/mkdocs/theme.py +++ b/mkdocs/theme.py @@ -2,6 +2,7 @@ from __future__ import annotations import logging import os +import warnings from typing import Any, Collection, MutableMapping import jinja2 @@ -37,8 +38,7 @@ class Theme(MutableMapping[str, Any]): self.name = name self._custom_dir = custom_dir _vars: dict[str, Any] = {'name': name, 'locale': 'en'} - # _vars is soft-deprecated, intentionally hide it from mypy. - setattr(self, '_vars', _vars) + self.__vars = _vars # MkDocs provided static templates are always included package_dir = os.path.abspath(os.path.dirname(__file__)) @@ -62,9 +62,9 @@ class Theme(MutableMapping[str, Any]): _vars.update(user_config) # Validate locale and convert to Locale object - _vars['locale'] = localization.parse_locale( - locale if locale is not None else _vars['locale'] - ) + if locale is None: + locale = _vars['locale'] + _vars['locale'] = localization.parse_locale(locale) name: str | None @@ -76,6 +76,14 @@ class Theme(MutableMapping[str, Any]): def custom_dir(self) -> str | None: return self._custom_dir + @property + def _vars(self) -> dict[str, Any]: + warnings.warn( + "Do not access Theme._vars, instead access the keys of Theme directly.", + DeprecationWarning, + ) + return self.__vars + dirs: list[str] static_templates: set[str] @@ -90,22 +98,22 @@ class Theme(MutableMapping[str, Any]): ) def __getitem__(self, key: str) -> Any: - return self._vars[key] # type: ignore[attr-defined] + return self.__vars[key] def __setitem__(self, key: str, value): - self._vars[key] = value # type: ignore[attr-defined] + self.__vars[key] = value def __delitem__(self, key: str): - del self._vars[key] # type: ignore[attr-defined] + del self.__vars[key] def __contains__(self, item: object) -> bool: - return item in self._vars # type: ignore[attr-defined] + return item in self.__vars def __len__(self): - return len(self._vars) # type: ignore[attr-defined] + return len(self.__vars) def __iter__(self): - return iter(self._vars) # type: ignore[attr-defined] + return iter(self.__vars) def _load_theme_config(self, name: str) -> None: """Recursively load theme and any parent themes.""" @@ -137,7 +145,7 @@ class Theme(MutableMapping[str, Any]): self._load_theme_config(parent_theme) self.static_templates.update(theme_config.pop('static_templates', [])) - self._vars.update(theme_config) # type: ignore[attr-defined] + self.__vars.update(theme_config) def get_env(self) -> jinja2.Environment: """Return a Jinja environment for the theme.""" diff --git a/mkdocs/utils/__init__.py b/mkdocs/utils/__init__.py index e3d2290a..60ef8eb2 100644 --- a/mkdocs/utils/__init__.py +++ b/mkdocs/utils/__init__.py @@ -44,16 +44,6 @@ markdown_extensions = ( ) -def modified_time(file_path): - warnings.warn( - "modified_time is never used in MkDocs and will be removed soon.", DeprecationWarning - ) - if os.path.exists(file_path): - return os.path.getmtime(file_path) - else: - return 0.0 - - def get_build_timestamp() -> int: """ Returns the number of seconds since the epoch. @@ -162,27 +152,6 @@ def clean_directory(directory: str) -> None: os.unlink(path) -def get_html_path(path): - warnings.warn( - "get_html_path is never used in MkDocs and will be removed soon.", DeprecationWarning - ) - path = os.path.splitext(path)[0] - if os.path.basename(path) == 'index': - return path + '.html' - return "/".join((path, 'index.html')) - - -def get_url_path(path, use_directory_urls=True): - warnings.warn( - "get_url_path is never used in MkDocs and will be removed soon.", DeprecationWarning - ) - path = get_html_path(path) - url = '/' + path.replace(os.sep, '/') - if use_directory_urls: - return url[: -len('index.html')] - return url - - def is_markdown_file(path: str) -> bool: """ Return True if the given file path is a Markdown file. @@ -192,20 +161,6 @@ def is_markdown_file(path: str) -> bool: return path.endswith(markdown_extensions) -def is_html_file(path): - warnings.warn( - "is_html_file is never used in MkDocs and will be removed soon.", DeprecationWarning - ) - return path.lower().endswith(('.html', '.htm')) - - -def is_template_file(path): - warnings.warn( - "is_template_file is never used in MkDocs and will be removed soon.", DeprecationWarning - ) - return path.lower().endswith(('.html', '.htm', '.xml')) - - _ERROR_TEMPLATE_RE = re.compile(r'^\d{3}\.html?$') @@ -297,7 +252,9 @@ def create_media_urls( def path_to_url(path): - """Soft-deprecated, do not use.""" + warnings.warn( + "path_to_url is never used in MkDocs and will be removed soon.", DeprecationWarning + ) return path.replace('\\', '/')