-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathwebsite_macros.py
114 lines (94 loc) · 3.7 KB
/
website_macros.py
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
110
111
112
113
114
# pyright: reportUnusedFunction=false
"""Macros and variables for the mkdocs-macros plugin."""
import builtins
import csv
import datetime as dt
import shutil
import subprocess
import types
from abc import abstractmethod
from collections.abc import Callable
from enum import Enum
from pathlib import Path
from typing import Any, Protocol, TypeGuard, TypeVar, get_args, get_origin
import attrs
import markdown
import typeshed_stats.gather
import typeshed_stats.serialize
CF = TypeVar("CF", bound=Callable[..., Any])
class Env(Protocol):
"""Protocol representing the `mkdocs_macros.MacrosPlugin` object."""
variables: dict[str, Any]
conf: dict[str, Any]
@abstractmethod
def macro(self, /, func: CF) -> CF:
"""Register a function as a macro."""
def define_env(env: Env) -> None:
"""Define environment variables."""
# Variables needed for cli.md
help_result = subprocess.run(
["typeshed-stats", "--help"], text=True, capture_output=True
)
env.variables["cli_help"] = help_result.stdout + "\n"
# Variables needed for serialize.md
env.variables["serialize"] = typeshed_stats.serialize
# Variables needed for gather.md
@env.macro
def is_enum(x: object) -> TypeGuard[type[Enum]]:
return isinstance(x, type) and issubclass(x, Enum)
@env.macro
def get_field_description(typ: object) -> str:
"""Get a description of the type of a field in an `attrs` class.
A helper function for the `gather.md.jinja` template.
"""
if isinstance(typ, types.UnionType):
return r" \| ".join(map(get_field_description, get_args(typ)))
if isinstance(typ, types.GenericAlias):
return (
get_field_description(get_origin(typ))
+ "["
+ ", ".join(map(get_field_description, get_args(typ)))
+ "]"
)
if isinstance(typ, type):
typ_name = typ.__name__
if typ_name in typeshed_stats.gather.__all__:
return f"[`{typ_name}`][typeshed_stats.gather.{typ_name}]"
if typ_name in dir(builtins):
return f"[`{typ_name}`][{typ_name}]"
if typ is Path:
return "[`Path`][pathlib.Path]"
if typ is types.NoneType:
return "[`None`][None]"
return f"`{typ_name}`"
return f"`{typ}`"
env.variables.update({"gather": typeshed_stats.gather, "attrs": attrs})
# Variables needed for stats.md and stats-csv.md
stats_as_csv: list[dict[str, str | int]] = []
with Path("examples", "example.csv").open(newline="", encoding="utf-8") as csvfile:
for line in csv.DictReader(csvfile):
row: dict[str, str | int] = {}
for key, val in line.items():
if val.isdigit():
row[key] = int(val)
elif key == "upstream_url":
row[key] = "(unknown)" if val == "-" else f"[{val}]({val})"
else:
row[key] = val
stats_as_csv.append(row)
@env.macro
def is_int(x: object) -> TypeGuard[int]:
return isinstance(x, int)
env.variables.update({
"last_update_time": dt.datetime.now(dt.UTC).strftime("%H:%M UTC on %Y-%m-%d"),
"num_packages": len(stats_as_csv),
"formatted_stats": Path("examples", "example.md").read_text(encoding="utf-8"),
"num_lines": sum(int(s["number_of_lines"]) for s in stats_as_csv),
"stats_as_csv": stats_as_csv,
"markdown": markdown,
})
def on_post_build(env: Env) -> None:
"""Define post-build actions."""
shutil.copyfile(
Path("examples", "example.csv"), Path(env.conf["site_dir"], "stats_as_csv.csv")
)