From 10d381eb246b79266bad6e16d9d004bd608359cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Wed, 29 Apr 2026 17:17:42 +0200 Subject: [PATCH] enh: Move to lxml --- nbsite/scripts/_fix_links.py | 27 ++++++++++++--------------- pixi.toml | 1 + pyproject.toml | 1 + 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/nbsite/scripts/_fix_links.py b/nbsite/scripts/_fix_links.py index bf2b6c36..bfdcd038 100644 --- a/nbsite/scripts/_fix_links.py +++ b/nbsite/scripts/_fix_links.py @@ -11,7 +11,7 @@ from functools import partial from pathlib import Path -from bs4 import BeautifulSoup +from lxml import html as lhtml # TODO: holoviews specific links e.g. to reference manual...doc & generalize @@ -95,43 +95,40 @@ def cleanup_links(path, inspect_links=False): # text = text.replace(k, v) text = component_links(text, path) - soup = BeautifulSoup(text, features="html.parser") - for a in soup.find_all('a'): + tree = lhtml.document_fromstring(text) + for a in tree.iter('a'): href = a.get('href', '') if '.ipynb' in href and 'http' not in href: - # for k, v in LINK_REPLACEMENTS.items(): - # href = href.replace(k, v) - a['href'] = href.replace('.ipynb', '.html') + a.set('href', href.replace('.ipynb', '.html')) - # check to make sure that path exists, if not, try un-numbered version - try_path = os.path.join(os.path.dirname(path), a['href']) + try_path = os.path.join(os.path.dirname(path), a.get('href')) if not os.path.exists(try_path): num_name = os.path.basename(try_path) name = re.split(r"^#?\d+( |-|_)", num_name)[-1] new_path = try_path.replace(num_name, name) if os.path.exists(new_path): - a['href'] = os.path.relpath(new_path, os.path.dirname(path)) + a.set('href', os.path.relpath(new_path, os.path.dirname(path))) else: also_tried = 'Also tried: {}'.format(name) if name != num_name else '' - msg = 'Found missing link {} in: {}. {}'.format(a['href'], path, also_tried) + msg = 'Found missing link {} in: {}. {}'.format(a.get('href'), path, also_tried) warnings.warn(msg) - if inspect_links and 'http' in a['href']: - print(a['href']) + if inspect_links and 'http' in href: + print(href) - for img in soup.find_all('img'): + for img in tree.iter('img'): src = img.get('src', '') if 'http' not in src and 'assets' in src: try_path = os.path.join(os.path.dirname(path), src) if not os.path.exists(try_path): also_tried = os.path.join('..', src) if os.path.exists(os.path.join(os.path.dirname(path), also_tried)): - img['src'] = also_tried + img.set('src', also_tried) else: msg = f'Found reference to missing image {src} in: {path}. Also tried: {also_tried}' warnings.warn(msg) with open(path, 'w', encoding='utf-8') as f: - f.write(str(soup)) + f.write(lhtml.tostring(tree, doctype='', encoding='unicode')) def fix_links(build_dir, inspect_links=False): files = map(os.fspath, Path(build_dir).rglob("*.html")) diff --git a/pixi.toml b/pixi.toml index f26c0c04..91e5a5ec 100644 --- a/pixi.toml +++ b/pixi.toml @@ -46,6 +46,7 @@ beautifulsoup4 = "*" ipykernel = "*" jinja2 = "*" jupyter_client = "*" +lxml = "*" myst-nb = ">=1.1" myst-parser = '>=3' nbconvert = "*" diff --git a/pyproject.toml b/pyproject.toml index 33f674c6..931bf7e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,7 @@ dependencies = [ 'sphinxext-rediraffe', 'packaging', 'requests', + 'lxml', ] [project.scripts]