100 lines
2.7 KiB
Python
100 lines
2.7 KiB
Python
"""
|
|
Contrast Tests
|
|
~~~~~~~~~~
|
|
|
|
Pygments styles should be accessible to people with suboptimal vision.
|
|
This test ensures that the minimum contrast of styles does not degrade,
|
|
and that every rule of a new style fulfills the WCAG AA standard.[1]
|
|
|
|
[1]: https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
|
|
import pygments.styles
|
|
import pygments.token
|
|
import wcag_contrast_ratio
|
|
|
|
JSON_FILENAME = os.path.join(os.path.dirname(__file__), "min_contrasts.json")
|
|
WCAG_AA_CONTRAST = 4.5
|
|
|
|
|
|
def hex2rgb(hexstr):
|
|
hexstr = hexstr.lstrip("#")
|
|
r = int(hexstr[:2], 16) / 255
|
|
g = int(hexstr[2:4], 16) / 255
|
|
b = int(hexstr[4:], 16) / 255
|
|
return (r, g, b)
|
|
|
|
|
|
def get_style_contrasts(style_cls):
|
|
return [
|
|
(
|
|
round(
|
|
wcag_contrast_ratio.rgb(
|
|
hex2rgb(style["bgcolor"] or style_cls.background_color),
|
|
hex2rgb(style["color"] or "#000000")
|
|
# we default to black because browsers also do
|
|
),
|
|
1,
|
|
),
|
|
ttype,
|
|
)
|
|
for ttype, style in style_cls.list_styles()
|
|
if ttype != pygments.token.Whitespace
|
|
]
|
|
|
|
|
|
def builtin_styles():
|
|
for style_name in pygments.styles.STYLE_MAP:
|
|
yield (style_name, pygments.styles.get_style_by_name(style_name))
|
|
|
|
|
|
def min_contrasts():
|
|
return {
|
|
name: min(x[0] for x in get_style_contrasts(style))
|
|
for name, style in builtin_styles()
|
|
}
|
|
|
|
|
|
def update_json():
|
|
with open(JSON_FILENAME, "w", encoding="utf-8") as f:
|
|
json.dump(
|
|
min_contrasts(),
|
|
f,
|
|
indent=2,
|
|
)
|
|
|
|
|
|
def test_contrasts(fail_if_improved=True):
|
|
with open(JSON_FILENAME, encoding="utf-8") as f:
|
|
previous_contrasts = json.load(f)
|
|
|
|
for style_name in pygments.styles.STYLE_MAP:
|
|
style = pygments.styles.get_style_by_name(style_name)
|
|
contrasts = get_style_contrasts(style)
|
|
min_contrast = min([x[0] for x in contrasts])
|
|
|
|
bar = previous_contrasts.get(style_name, WCAG_AA_CONTRAST)
|
|
|
|
assert not min_contrast < bar, (
|
|
"contrast degradation for style '{}'\n"
|
|
"The following rules have a contrast lower than the required {}:\n\n"
|
|
"{}\n"
|
|
).format(
|
|
style_name,
|
|
bar,
|
|
"\n".join(
|
|
[
|
|
"* {:.2f} {}".format(contrast, ttype)
|
|
for contrast, ttype in contrasts
|
|
if contrast < bar
|
|
]
|
|
),
|
|
)
|
|
|
|
if fail_if_improved:
|
|
assert (
|
|
not min_contrast > bar
|
|
), "congrats, you improved a contrast! please run ./scripts/update_contrasts.py"
|