File: render.py

package info (click to toggle)
django-anymail 13.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,552 kB
  • sloc: python: 28,882; makefile: 132; javascript: 33; sh: 9
file content (109 lines) | stat: -rw-r--r-- 3,556 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#!/usr/bin/env python
# Render a README file (roughly) as it would appear on PyPI

import argparse
import sys
from importlib.metadata import PackageNotFoundError, metadata
from pathlib import Path
from typing import Dict, Optional

import readme_renderer.rst
from docutils.core import publish_string
from docutils.utils import SystemMessage

# Docutils template.txt in our directory:
DEFAULT_TEMPLATE_FILE = Path(__file__).with_name("template.txt").absolute()


def get_package_readme(package: str) -> str:
    # Note: "description" was added to metadata in Python 3.10
    return metadata(package)["description"]


class ReadMeHTMLWriter(readme_renderer.rst.Writer):
    translator_class = readme_renderer.rst.ReadMeHTMLTranslator

    def interpolation_dict(self) -> Dict[str, str]:
        result = super().interpolation_dict()
        # clean the same parts as readme_renderer.rst.render:
        clean = readme_renderer.rst.clean
        result["docinfo"] = clean(result["docinfo"])
        result["body"] = result["fragment"] = clean(result["fragment"])
        return result


def render(source_text: str, warning_stream=sys.stderr) -> Optional[str]:
    # Adapted from readme_renderer.rst.render
    settings = readme_renderer.rst.SETTINGS.copy()
    settings.update(
        {
            "warning_stream": warning_stream,
            "template": DEFAULT_TEMPLATE_FILE,
            # Input and output are text str (we handle decoding/encoding):
            "input_encoding": "unicode",
            "output_encoding": "unicode",
            # Exit with error on docutils warning or above.
            # (There's discussion of having readme_renderer ignore warnings;
            # this ensures they'll be treated as errors here.)
            "halt_level": 2,  # (docutils.utils.Reporter.WARNING_LEVEL)
            # Report all docutils warnings or above.
            # (The readme_renderer default suppresses this output.)
            "report_level": 2,  # (docutils.utils.Reporter.WARNING_LEVEL)
        }
    )

    writer = ReadMeHTMLWriter()

    try:
        return publish_string(
            source_text,
            writer=writer,
            settings_overrides=settings,
        )
    except SystemMessage:
        warning_stream.write("Error rendering readme source.\n")
        return None


def main(argv=None):
    parser = argparse.ArgumentParser(
        description="Render readme file as it would appear on PyPI"
    )
    input_group = parser.add_mutually_exclusive_group(required=True)
    input_group.add_argument(
        "-p", "--package", help="Source readme from package's metadata"
    )
    input_group.add_argument(
        "-i",
        "--input",
        help="Source readme.rst file ('-' for stdin)",
        type=argparse.FileType("r"),
    )
    parser.add_argument(
        "-o",
        "--output",
        help="Output file (default: stdout)",
        type=argparse.FileType("w"),
        default="-",
    )

    args = parser.parse_args(argv)
    if args.package:
        try:
            source_text = get_package_readme(args.package)
        except PackageNotFoundError:
            print(f"Package not installed: {args.package!r}", file=sys.stderr)
            sys.exit(2)
        if source_text is None:
            print(f"No metadata readme for {args.package!r}", file=sys.stderr)
            sys.exit(2)
    else:
        source_text = args.input.read()
    rendered = render(source_text)
    if rendered is None:
        sys.exit(2)
    args.output.write(rendered)


if __name__ == "__main__":
    main()