From: Vladimír Vondruš Date: Wed, 25 Sep 2024 16:18:36 +0000 (+0200) Subject: documentation/python: defer HTML escaping until the template is rendered. X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~cjwatson/git?a=commitdiff_plain;h=b586540ce4f6eb39c3024edf6bc68200db88cc82;p=blog.git documentation/python: defer HTML escaping until the template is rendered. Except for stuff that may itself contain actual HTML, such as type / value links or the summary / details prose rendered from reST. Needed to be done this way in order to be able to generate Python stubs without having to nastily unescape for them again. --- diff --git a/documentation/python.py b/documentation/python.py index 2e10ca7d..f560c4ce 100755 --- a/documentation/python.py +++ b/documentation/python.py @@ -1093,12 +1093,14 @@ def format_value(state: State, referrer_path: List[str], value) -> Optional[Tupl # TODO Python 3.8+ supports `a, *b`, switch to that once 3.7 is dropped return (value.name, ) + make_name_relative_link(state, referrer_path, '{}.{}.{}'.format(value.__class__.__module__, value.__class__.__qualname__, value.name)) elif inspect.isfunction(value): - return (html.escape(''.format(value.__name__)), )*3 + out = ''.format(value.__name__) + return out, out, html.escape(out) elif '__repr__' in type(value).__dict__: rendered = repr(value) # TODO: tuples of non-representable values will still be ugly # If the value is too large, return just an ellipsis - return (html.escape(rendered) if len(rendered) < 128 else '…', )*3 + out = rendered if len(rendered) < 128 else '…' + return out, out, html.escape(out) else: return None @@ -1440,7 +1442,7 @@ def extract_enum_doc(state: State, entry: Empty): value = Empty() value.name = i.name value.id = state.config['ID_FORMATTER'](EntryType.ENUM_VALUE, entry.path[-1:] + [i.name]) - value.value = html.escape(repr(i.value)) + value.value = repr(i.value) # Value doc gets by default inherited from the enum, that's useless # This gets further processed below. @@ -1638,8 +1640,8 @@ def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]: param.default = '.'.join(state.name_map[arg_type].path[:-1] + [arg_default]) param.default_relative, param.default_link = make_name_relative_link(state, entry.path, param.default) else: - param.default = html.escape(arg_default) - param.default_relative, param.default_link = param.default, param.default + param.default, param.default_relative = arg_default, arg_default + param.default_link = html.escape(arg_default) else: param.default, param.default_relative, param.default_link = None, None, None if arg_type or arg_default: out.has_complex_params = True @@ -2137,6 +2139,25 @@ def render_module(state: State, path, module, env): result.name = path[-1] state.search += [result] + # Perform HTML escaping for everything going into the template. Done here + # instead of inside extract_*_doc() to avoid having to unescape in certain + # cases. + for enum in page.enums: + for value in enum.values: + value.value = html.escape(value.value) + for function in page.functions: + for param in function.params: + if param.default: + param.default = html.escape(param.default) + param.default_relative = html.escape(param.default_relative) + # param.default_link may contain HTML and thus had to be + # escaped early + for data in page.data: + if data.value: + data.value = html.escape(data.value) + data.value_relative = html.escape(data.value_relative) + # data.value_link may contain HTML and thus had to be escaped early + render(config=state.config, template='module.html', filename=os.path.join(state.config['OUTPUT'], filename), @@ -2242,6 +2263,25 @@ def render_class(state: State, path, class_, env): result.name = path[-1] state.search += [result] + # Perform HTML escaping for everything going into the template. Done here + # instead of inside extract_*_doc() to avoid having to unescape in certain + # cases. + for enum in page.enums: + for value in enum.values: + value.value = html.escape(value.value) + for function in page.classmethods + page.staticmethods + page.dunder_methods + page.methods: + for param in function.params: + if param.default: + param.default = html.escape(param.default) + param.default_relative = html.escape(param.default_relative) + # param.default_link may contain HTML and thus had to be + # escaped early + for data in page.data: + if data.value: + data.value = html.escape(data.value) + data.value_relative = html.escape(data.value_relative) + # data.value_link may contain HTML and thus had to be escaped early + render(config=state.config, template='class.html', filename=os.path.join(state.config['OUTPUT'], filename),