Source code for pyproject_parser.utils

#!/usr/bin/env python3
#
#  utils.py
"""
Utility functions.
"""
#
#  Copyright © 2021-2023 Dominic Davis-Foster <dominic@davis-foster.co.uk>
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to deal
#  in the Software without restriction, including without limitation the rights
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#  copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in all
#  copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
#  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
#  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
#  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
#  DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
#  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
#  OR OTHER DEALINGS IN THE SOFTWARE.
#

# stdlib
import functools
import io
import os
import sys
from typing import TYPE_CHECKING, Any, Dict, Optional

# 3rd party
import dom_toml
from dom_toml.parser import BadConfigError
from domdf_python_tools.paths import PathPlus
from domdf_python_tools.typing import PathLike

if TYPE_CHECKING:
	# this package
	from pyproject_parser.type_hints import ContentTypes

__all__ = ["render_markdown", "render_rst", "content_type_from_filename", "PyProjectDeprecationWarning"]


[docs]def render_markdown(content: str) -> None: """ Attempt to render the given content as :wikipedia:`Markdown`. .. extras-require:: readme :pyproject: :scope: function :param content: """ try: # 3rd party import cmarkgfm # type: ignore[import] # noqa: F401 import readme_renderer.markdown except ImportError: # pragma: no cover return rendering_result = readme_renderer.markdown.render(content, stream=sys.stderr) if rendering_result is None: # pragma: no cover raise BadConfigError("Error rendering README.")
[docs]def render_rst(content: str, filename: PathLike = "<string>") -> None: """ Attempt to render the given content as :wikipedia:`ReStructuredText`. .. extras-require:: readme :pyproject: :scope: function :param content: :param filename: The original filename. .. versionchanged:: 0.8.0 Added the ``filename`` argument. """ try: # 3rd party import docutils.core import readme_renderer.rst from docutils.utils import SystemMessage from docutils.writers.html4css1 import Writer except ImportError: # pragma: no cover return # Adapted from https://github.com/pypa/readme_renderer/blob/main/readme_renderer/rst.py#L106 settings = readme_renderer.rst.SETTINGS.copy() settings["warning_stream"] = io.StringIO() writer = Writer() writer.translator_class = readme_renderer.rst.ReadMeHTMLTranslator # type: ignore[assignment] try: parts = docutils.core.publish_parts(content, str(filename), writer=writer, settings_overrides=settings) if parts.get("docinfo", '') + parts.get("fragment", ''): # Success! return except SystemMessage: pass warning_stream: io.StringIO = settings["warning_stream"] # type: ignore[assignment] if not warning_stream.tell(): raise BadConfigError("Error rendering README: No content rendered from RST source.") else: sys.stderr.write(warning_stream.getvalue()) raise BadConfigError("Error rendering README.")
[docs]@functools.lru_cache() def content_type_from_filename(filename: PathLike) -> "ContentTypes": """ Return the inferred content type for the given (readme) filename. :param filename: """ filename = PathPlus(filename) if filename.suffix.lower() == ".md": return "text/markdown" elif filename.suffix.lower() == ".rst": return "text/x-rst" elif filename.suffix.lower() == ".txt": return "text/plain" raise ValueError(f"Unsupported extension for {filename.as_posix()!r}")
def render_readme( readme_file: PathLike, content_type: Optional["ContentTypes"] = None, encoding: str = "UTF-8", ) -> None: """ Attempts to render the given readme file. :param readme_file: :param content_type: The content-type of the readme. If :py:obj:`None` the type will be inferred from the file extension. :param encoding: The encoding to read the file with. """ readme_file = PathPlus(readme_file) if content_type is None: content_type = content_type_from_filename(filename=readme_file) content = readme_file.read_text(encoding=encoding) if int(os.environ.get("CHECK_README", 1)): if content_type == "text/markdown": render_markdown(content) elif content_type == "text/x-rst": render_rst(content, readme_file)
[docs]class PyProjectDeprecationWarning(Warning): """ Warning for the use of deprecated features in `pyproject.toml`. This is a user-facing warning which will be shown by default. For developer-facing warnings intended for direct consumers of this library, use a standard :class:`DeprecationWarning`. .. versionadded:: 0.5.0 """
def _load_toml(filename: PathLike, ) -> Dict[str, Any]: r""" Parse TOML from the given file. :param filename: The filename to read from to. :returns: A mapping containing the ``TOML`` data. """ return dom_toml.load(filename)