Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-sphinx-scrapy for 
openSUSE:Factory checked in at 2026-07-06 12:32:59
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-sphinx-scrapy (Old)
 and      /work/SRC/openSUSE:Factory/.python-sphinx-scrapy.new.1982 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-sphinx-scrapy"

Mon Jul  6 12:32:59 2026 rev:2 rq:1363879 version:0.8.6

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-sphinx-scrapy/python-sphinx-scrapy.changes    
    2026-04-23 17:13:13.029921198 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-sphinx-scrapy.new.1982/python-sphinx-scrapy.changes
      2026-07-06 12:35:37.062260245 +0200
@@ -1,0 +2,18 @@
+Sun Jul  5 15:39:11 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 0.8.6:
+  * Add index templates for extension-defined cross-reference
+    types so related objects appear in generated Sphinx indexes.
+  * Normalize project IDs loaded from :file:`pyproject.toml` and
+    inferred from Sphinx metadata.
+  * Fix links in :file:`llms.txt`.
+  * Rewrite generated Markdown links for supported Intersphinx
+    targets from .html to .md.
+  * Extended easy Intersphinx configuration to aiohttp curl-cffi
+    niquests
+  * Document installation from Git for sphinx-scrapy and sphinx-
+    scrapy[tox].
+  * Upgrade sphinx-markdown-builder.
+
+
+-------------------------------------------------------------------

Old:
----
  sphinx_scrapy-0.8.2.tar.gz

New:
----
  sphinx_scrapy-0.8.6.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-sphinx-scrapy.spec ++++++
--- /var/tmp/diff_new_pack.5YFjAE/_old  2026-07-06 12:35:38.462308892 +0200
+++ /var/tmp/diff_new_pack.5YFjAE/_new  2026-07-06 12:35:38.466309031 +0200
@@ -19,7 +19,7 @@
 # Enable libalternatives by default
 %bcond_without libalternatives
 Name:           python-sphinx-scrapy
-Version:        0.8.2
+Version:        0.8.6
 Release:        0
 Summary:        Sphinx extension for documentation in the Scrapy ecosystem
 License:        BSD-3-Clause

++++++ sphinx_scrapy-0.8.2.tar.gz -> sphinx_scrapy-0.8.6.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sphinx-scrapy-0.8.2/CHANGES.rst 
new/sphinx-scrapy-0.8.6/CHANGES.rst
--- old/sphinx-scrapy-0.8.2/CHANGES.rst 2026-04-10 14:54:33.000000000 +0200
+++ new/sphinx-scrapy-0.8.6/CHANGES.rst 2026-05-12 09:03:58.000000000 +0200
@@ -2,6 +2,39 @@
 Release notes
 =============
 
+0.8.6 (2026-05-12)
+==================
+
+-   Add index templates for extension-defined cross-reference types so related
+    objects appear in generated Sphinx indexes.
+
+0.8.5 (2026-04-29)
+==================
+
+-   Normalize project IDs loaded from :file:`pyproject.toml` and inferred from
+    Sphinx metadata.
+
+0.8.4 (2026-04-28)
+==================
+
+-   Fix links in :file:`llms.txt`.
+
+0.8.3 (2026-04-22)
+==================
+
+-   Rewrite generated Markdown links for supported Intersphinx targets from
+    ``.html`` to ``.md``.
+
+-   | Extended easy Intersphinx configuration to
+    | `aiohttp <https://docs.aiohttp.org/en/stable/>`_
+    | `curl-cffi <https://curl-cffi.readthedocs.io/en/latest/>`_
+    | `niquests <https://niquests.readthedocs.io/en/latest/>`_
+
+-   Document installation from Git for ``sphinx-scrapy`` and
+    ``sphinx-scrapy[tox]``.
+
+-   Upgrade sphinx-markdown-builder.
+
 0.8.2 (2026-04-10)
 ==================
 
@@ -14,7 +47,7 @@
 ==================
 
 -   Addressed warnings affecting sitemap generation due to missing ``/`` at the
-    end of InterSphinx URLs.
+    end of Intersphinx URLs.
 
 0.8.0 (2026-04-09)
 ==================
@@ -53,7 +86,7 @@
 -   Added a "Copy as Markdown" button on HTML pages that copies each page's
     corresponding ``.md`` counterpart.
 
--   | Extended easy intersphinx configuration to
+-   | Extended easy Intersphinx configuration to
     | `coverage <https://coverage.readthedocs.io/en/latest>`_
     | `cryptography <https://cryptography.io/en/latest/>`_
     | `multidict <https://multidict.aio-libs.org/en/latest/>`_
@@ -69,7 +102,7 @@
 0.6.0 (2025-09-16)
 ==================
 
-| Extended easy intersphinx configuration to:
+| Extended easy Intersphinx configuration to:
 | `pydantic <https://docs.pydantic.dev/latest/>`_
 
 
@@ -82,7 +115,7 @@
 0.4.0 (2025-09-16)
 ==================
 
-| Extended easy intersphinx configuration to:
+| Extended easy Intersphinx configuration to:
 | `packaging <https://packaging.pypa.io/en/stable/>`_
 | `pytest <https://docs.pytest.org/en/stable/>`_
 
@@ -90,7 +123,7 @@
 0.3.0 (2025-09-02)
 ==================
 
-| Extended easy intersphinx configuration to:
+| Extended easy Intersphinx configuration to:
 | `jinja <https://jinja.palletsprojects.com/en/latest/>`_
 | `lxml <https://lxml.de/apidoc/>`_
 
@@ -98,7 +131,7 @@
 0.2.0 (2025-06-16)
 ==================
 
-| Extended easy intersphinx configuration to:
+| Extended easy Intersphinx configuration to:
 | `dateparser <https://dateparser.readthedocs.io/en/latest/>`_
 | `form2request <https://form2request.readthedocs.io/en/latest/>`_
 | `formasaurus <https://formasaurus.readthedocs.io/en/latest/>`_
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sphinx-scrapy-0.8.2/README.rst 
new/sphinx-scrapy-0.8.6/README.rst
--- old/sphinx-scrapy-0.8.2/README.rst  2026-04-10 14:54:33.000000000 +0200
+++ new/sphinx-scrapy-0.8.6/README.rst  2026-05-12 09:03:58.000000000 +0200
@@ -56,9 +56,12 @@
 
 #.  Add to ``docs/requirements.txt``:
 
-    .. code-block::
+    Install from Git, because this project currently depends on Git-based
+    packages and cannot be published to PyPI:
+
+    .. code-block:: text
 
-        sphinx-scrapy==0.7.2
+        sphinx-scrapy @ git+https://github.com/scrapy/[email protected]
 
 #.  Add to ``docs/conf.py``:
 
@@ -108,7 +111,7 @@
 
         repos:
         - repo: https://github.com/scrapy/sphinx-scrapy
-            rev: 0.8.2
+            rev: 0.8.6
             hooks:
             - id: sphinx-scrapy
 
@@ -118,7 +121,7 @@
 
         [tox]
         requires =
-            sphinx-scrapy[tox]==0.8.2
+            sphinx-scrapy[tox] @ 
git+https://github.com/scrapy/[email protected]
         envlist = …,docs
 
     .. note:: ``docs`` in ``envlist`` is required.
@@ -156,10 +159,12 @@
 
 ``scrapy_intersphinx_enable`` supports the following packages:
 
+| `aiohttp <https://docs.aiohttp.org/en/stable/>`_
 | `attrs <https://www.attrs.org/en/stable/>`_
 | `coverage <https://coverage.readthedocs.io/en/latest>`_
 | `cryptography <https://cryptography.io/en/latest/>`_
 | `cssselect <https://cssselect.readthedocs.io/en/latest>`_
+| `curl-cffi <https://curl-cffi.readthedocs.io/en/latest/>`_
 | `dateparser <https://dateparser.readthedocs.io/en/latest/>`_
 | `form2request <https://form2request.readthedocs.io/en/latest/>`_
 | `formasaurus <https://formasaurus.readthedocs.io/en/latest/>`_
@@ -167,6 +172,7 @@
 | `jinja <https://jinja.palletsprojects.com/en/latest/>`_
 | `lxml <https://lxml.de/apidoc/>`_
 | `multidict <https://multidict.aio-libs.org/en/latest/>`_
+| `niquests <https://niquests.readthedocs.io/en/latest/>`_
 | `packaging <https://packaging.pypa.io/en/stable/>`_
 | `parsel <https://parsel.readthedocs.io/en/latest/>`_
 | `pydantic <https://docs.pydantic.dev/latest/>`_
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sphinx-scrapy-0.8.2/pyproject.toml 
new/sphinx-scrapy-0.8.6/pyproject.toml
--- old/sphinx-scrapy-0.8.2/pyproject.toml      2026-04-10 14:54:33.000000000 
+0200
+++ new/sphinx-scrapy-0.8.6/pyproject.toml      2026-05-12 09:03:58.000000000 
+0200
@@ -4,14 +4,14 @@
 
 [project]
 name = "sphinx-scrapy"
-version = "0.8.2"
+version = "0.8.6"
 description = "Sphinx extension for documentation in the Scrapy ecosystem"
 dependencies = [
     "packaging",
     "sphinx",
     "sphinx-copybutton",
     "sphinx-llms-txt @ 
git+https://github.com/zytedata/sphinx-llms-txt.git@5e8866cb0cc249aa2017ad9050b3b83a7ca16f69";,
-    "sphinx-markdown-builder @ 
git+https://github.com/zytedata/sphinx-markdown-builder.git@d12c2d188009c5230f18cd6a904c03d1eca066ed";,
+    "sphinx-markdown-builder @ 
git+https://github.com/zytedata/sphinx-markdown-builder.git@cfe4c0bfd7b4542f7e6b65a58cdf9ec765829940";,
     "sphinx-sitemap",
     "tomli; python_version < '3.11'",
 ]
@@ -59,7 +59,7 @@
 strict = true
 
 [tool.bumpversion]
-current_version = "0.8.2"
+current_version = "0.8.6"
 commit = true
 tag = true
 tag_name = "{new_version}"
@@ -74,13 +74,16 @@
 filename = "README.rst"
 search = "rev: {current_version}"
 replace = "rev: {new_version}"
-regex = false
 
 [[tool.bumpversion.files]]
 filename = "README.rst"
-search = "sphinx-scrapy[tox]=={current_version}"
-replace = "sphinx-scrapy[tox]=={new_version}"
-regex = false
+search = "sphinx-scrapy @ 
git+https://github.com/scrapy/sphinx-scrapy.git@{current_version}";
+replace = "sphinx-scrapy @ 
git+https://github.com/scrapy/sphinx-scrapy.git@{new_version}";
+
+[[tool.bumpversion.files]]
+filename = "README.rst"
+search = "sphinx-scrapy[tox] @ 
git+https://github.com/scrapy/sphinx-scrapy.git@{current_version}";
+replace = "sphinx-scrapy[tox] @ 
git+https://github.com/scrapy/sphinx-scrapy.git@{new_version}";
 
 [tool.ruff.lint]
 extend-select = [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sphinx-scrapy-0.8.2/sphinx_scrapy/__init__.py 
new/sphinx-scrapy-0.8.6/sphinx_scrapy/__init__.py
--- old/sphinx-scrapy-0.8.2/sphinx_scrapy/__init__.py   2026-04-10 
14:54:33.000000000 +0200
+++ new/sphinx-scrapy-0.8.6/sphinx_scrapy/__init__.py   2026-05-12 
09:03:58.000000000 +0200
@@ -4,7 +4,7 @@
 from logging import getLogger
 from typing import TYPE_CHECKING
 
-from .config import load_project_config
+from .config import load_project_config, normalize_project_id
 
 if TYPE_CHECKING:
     from collections.abc import Generator
@@ -15,10 +15,12 @@
 logger = getLogger(__name__)
 
 INTERSPHINX_MAPPING = {
+    "aiohttp": ("https://docs.aiohttp.org/en/stable/";, None),
     "attrs": ("https://www.attrs.org/en/stable/";, None),
     "coverage": ("https://coverage.readthedocs.io/en/latest/";, None),
     "cryptography": ("https://cryptography.io/en/latest/";, None),
     "cssselect": ("https://cssselect.readthedocs.io/en/latest/";, None),
+    "curl-cffi": ("https://curl-cffi.readthedocs.io/en/latest/";, None),
     "dateparser": ("https://dateparser.readthedocs.io/en/latest/";, None),
     "form2request": ("https://form2request.readthedocs.io/en/latest/";, None),
     "formasaurus": ("https://formasaurus.readthedocs.io/en/latest/";, None),
@@ -26,6 +28,7 @@
     "jinja": ("https://jinja.palletsprojects.com/en/latest/";, None),
     "lxml": ("https://lxml.de/apidoc/";, None),
     "multidict": ("https://multidict.aio-libs.org/en/latest/";, None),
+    "niquests": ("https://niquests.readthedocs.io/en/latest/";, None),
     "packaging": ("https://packaging.pypa.io/en/stable/";, None),
     "parsel": ("https://parsel.readthedocs.io/en/latest/";, None),
     "pydantic": ("https://pydantic.dev/docs/validation/latest/";, None),
@@ -252,6 +255,7 @@
         app.add_crossref_type(
             directivename=crossref_type,
             rolename=crossref_type,
+            indextemplate=f"pair: %s; {crossref_type}",
         )
 
 
@@ -302,9 +306,9 @@
         package: str | None = None
         project_config = load_project_config()
         if project_config.project_id:
-            package = project_config.project_id
+            package = normalize_project_id(project_config.project_id)
         elif hasattr(config, "project"):
-            package = re.sub(r"[\s_]+", "-", str(config.project)).lower()
+            package = normalize_project_id(re.sub(r"\s+", "-", 
str(config.project)))
         if not package:
             return
         if package in INTERSPHINX_MAPPING:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sphinx-scrapy-0.8.2/sphinx_scrapy/cli.py 
new/sphinx-scrapy-0.8.6/sphinx_scrapy/cli.py
--- old/sphinx-scrapy-0.8.2/sphinx_scrapy/cli.py        2026-04-10 
14:54:33.000000000 +0200
+++ new/sphinx-scrapy-0.8.6/sphinx_scrapy/cli.py        2026-05-12 
09:03:58.000000000 +0200
@@ -1,20 +1,258 @@
 from __future__ import annotations
 
 import argparse
+from collections.abc import Callable
 import concurrent.futures
+import re
 import shutil
 import sys
+from http import HTTPStatus
 from typing import TYPE_CHECKING
+from urllib.error import HTTPError, URLError
+from urllib.parse import urlsplit, urlunsplit
+from urllib.request import Request, urlopen
 
 from packaging.version import InvalidVersion, Version
 from sphinx.cmd.build import main as sphinx_build_main
 
-from .config import LATEST_RTD_PYTHON_VERSION, load_project_config
+from . import INTERSPHINX_MAPPING
+from .config import LATEST_RTD_PYTHON_VERSION, load_project_config, 
normalize_project_id
 
 if TYPE_CHECKING:
     from pathlib import Path
 
 
+URL_PATTERN = re.compile(r"https?://[^\s<>()\[\]{}\"']+")
+MARKDOWN_LINK_PATTERN = re.compile(
+    r"(?P<prefix>!?\[[^\]]*\]\()"
+    r"(?P<target>[^)\s]+)"
+    r"(?P<title>\s+\"[^\"]*\")?"
+    r"(?P<suffix>\))"
+)
+
+
+def _intersphinx_base_urls() -> tuple[str, ...]:
+    bases = {url for url, _inventory in INTERSPHINX_MAPPING.values()}
+    return tuple(sorted(bases, key=len, reverse=True))
+
+
+def _project_docs_base_url(project_id: str | None) -> str | None:
+    if not project_id:
+        return None
+    normalized_project_id = normalize_project_id(project_id)
+    if not normalized_project_id:
+        return None
+    if normalized_project_id in INTERSPHINX_MAPPING:
+        return INTERSPHINX_MAPPING[normalized_project_id][0]
+    return f"https://{normalized_project_id}.readthedocs.io/en/latest/";
+
+
+def _split_wrapped_markdown_target(target: str) -> tuple[str, str, str]:
+    if len(target) >= 2 and target.startswith("<") and target.endswith(">"):
+        return "<", target[1:-1], ">"
+    return "", target, ""
+
+
+def _rewrite_markdown_links(
+    content: str,
+    rewrite_target: Callable[[str], str],
+) -> str:
+    in_fence = False
+    fence_char = ""
+    rewritten_lines: list[str] = []
+
+    def replacement(match: re.Match[str]) -> str:
+        target = match.group("target")
+        rewritten_target = rewrite_target(target)
+        if rewritten_target == target:
+            return match.group(0)
+
+        title = match.group("title") or ""
+        return (
+            f"{match.group('prefix')}"
+            f"{rewritten_target}"
+            f"{title}"
+            f"{match.group('suffix')}"
+        )
+
+    for line in content.splitlines(keepends=True):
+        stripped = line.lstrip()
+        if stripped.startswith("```") or stripped.startswith("~~~"):
+            marker = stripped[0]
+            if not in_fence:
+                in_fence = True
+                fence_char = marker
+            elif marker == fence_char:
+                in_fence = False
+                fence_char = ""
+            rewritten_lines.append(line)
+            continue
+
+        if in_fence:
+            rewritten_lines.append(line)
+            continue
+
+        rewritten_lines.append(MARKDOWN_LINK_PATTERN.sub(replacement, line))
+
+    return "".join(rewritten_lines)
+
+
+def _rewrite_llms_link_target(
+    target: str,
+    docs_host: str,
+    docs_path_prefix: str,
+) -> str:
+    wrapper_prefix, wrapped_target, wrapper_suffix = (
+        _split_wrapped_markdown_target(target)
+    )
+    parts = urlsplit(wrapped_target)
+
+    if parts.scheme or parts.netloc:
+        if parts.scheme not in {"http", "https"}:
+            return target
+        if parts.netloc.lower() != docs_host:
+            return target
+
+    if not parts.path.endswith(".md"):
+        return target
+
+    clean_path = parts.path.lstrip("/")
+    while clean_path.startswith("./"):
+        clean_path = clean_path[2:]
+    if clean_path.startswith("../"):
+        return target
+
+    if docs_path_prefix and not clean_path.startswith(docs_path_prefix):
+        clean_path = f"{docs_path_prefix}{clean_path}"
+
+    rewritten_target = urlunsplit(("", "", clean_path, parts.query, 
parts.fragment))
+    return f"{wrapper_prefix}{rewritten_target}{wrapper_suffix}"
+
+
+def _rewrite_llms_links_to_docs_path(
+    output_dir: Path,
+    docs_base_url: str | None,
+) -> None:
+    llms_path = output_dir / "llms.txt"
+    if docs_base_url is None or not llms_path.is_file():
+        return
+
+    docs_parts = urlsplit(docs_base_url)
+    docs_host = docs_parts.netloc.lower()
+    docs_path = docs_parts.path.strip("/")
+    docs_path_prefix = f"{docs_path}/" if docs_path else ""
+
+    content = llms_path.read_text(encoding="utf-8")
+    rewritten = _rewrite_markdown_links(
+        content,
+        lambda target: _rewrite_llms_link_target(target, docs_host, 
docs_path_prefix),
+    )
+    if rewritten != content:
+        llms_path.write_text(rewritten, encoding="utf-8")
+
+
+def _iter_markdown_outputs(output_dir: Path) -> list[Path]:
+    files = sorted(output_dir.rglob("*.md"))
+    for filename in ("llms.txt", "llms-full.txt"):
+        path = output_dir / filename
+        if path.is_file():
+            files.append(path)
+    return files
+
+
+def _matching_base_url(url: str, base_urls: tuple[str, ...]) -> str | None:
+    for base_url in base_urls:
+        if url.startswith(base_url):
+            return base_url
+    return None
+
+
+def _supports_markdown(base_url: str) -> bool:
+    probe_url = f"{base_url.rstrip('/')}/index.md"
+    headers = {"User-Agent": "sphinx-scrapy"}
+    for method in ("HEAD", "GET"):
+        request = Request(probe_url, headers=headers, method=method)  # noqa: 
S310
+        try:
+            with urlopen(request, timeout=5) as response:  # noqa: S310
+                return HTTPStatus.OK <= response.status < 
HTTPStatus.BAD_REQUEST
+        except HTTPError as error:
+            if method == "HEAD" and error.code in {
+                HTTPStatus.METHOD_NOT_ALLOWED,
+                HTTPStatus.NOT_IMPLEMENTED,
+            }:
+                continue
+            return False
+        except URLError:
+            return False
+    return False
+
+
+def _rewrite_url_to_markdown(url: str, enabled_base_urls: set[str], base_urls: 
tuple[str, ...]) -> str:
+    base_url = _matching_base_url(url, base_urls)
+    if not base_url or base_url not in enabled_base_urls:
+        return url
+
+    parts = urlsplit(url)
+    path = parts.path
+    if not path.endswith(".html"):
+        return url
+    new_path = f"{path[:-5]}.md"
+
+    return urlunsplit((parts.scheme, parts.netloc, new_path, parts.query, 
parts.fragment))
+
+
+def _rewrite_intersphinx_links_to_markdown(output_dir: Path) -> None:
+    target_files = _iter_markdown_outputs(output_dir)
+    if not target_files:
+        return
+
+    base_urls = _intersphinx_base_urls()
+    file_contents: dict[Path, str] = {}
+    candidate_base_urls: set[str] = set()
+
+    for file in target_files:
+        content = file.read_text(encoding="utf-8")
+        file_contents[file] = content
+        for match in URL_PATTERN.finditer(content):
+            url = match.group(0)
+            if ".html" not in url:
+                continue
+            base_url = _matching_base_url(url, base_urls)
+            if base_url is not None:
+                candidate_base_urls.add(base_url)
+
+    if not candidate_base_urls:
+        return
+
+    with concurrent.futures.ThreadPoolExecutor(
+        max_workers=min(8, len(candidate_base_urls))
+    ) as executor:
+        availability = {
+            base_url: executor.submit(_supports_markdown, base_url)
+            for base_url in candidate_base_urls
+        }
+        enabled_base_urls = {
+            base_url
+            for base_url, future in availability.items()
+            if future.result()
+        }
+
+    if not enabled_base_urls:
+        return
+
+    for file, content in file_contents.items():
+        rewritten = URL_PATTERN.sub(
+            lambda match: _rewrite_url_to_markdown(
+                match.group(0),
+                enabled_base_urls,
+                base_urls,
+            ),
+            content,
+        )
+        if rewritten != content:
+            file.write_text(rewritten, encoding="utf-8")
+
+
 def _builder_settings(builder: str) -> list[str]:
     if builder == "html":
         return ["-D", "llms_txt_uri_template={docname}.md"]
@@ -42,6 +280,7 @@
 
 def build_docs() -> int:
     config = load_project_config()
+    docs_base_url = _project_docs_base_url(config.project_id)
     docs_dir = config.root / "docs"
     if not docs_dir.is_dir():
         print("docs directory not found", file=sys.stderr)
@@ -71,6 +310,8 @@
     shutil.copytree(sphinx_build_dir / "html", all_dir, dirs_exist_ok=True)
     shutil.copytree(sphinx_build_dir / "markdown", all_dir, dirs_exist_ok=True)
     shutil.copy2(sphinx_build_dir / "singlemarkdown" / "index.md", all_dir / 
"llms-full.txt")
+    _rewrite_intersphinx_links_to_markdown(all_dir)
+    _rewrite_llms_links_to_docs_path(all_dir, docs_base_url)
 
     print("\nDocumentation generated in docs/_build/all.")
     return 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sphinx-scrapy-0.8.2/sphinx_scrapy/config.py 
new/sphinx-scrapy-0.8.6/sphinx_scrapy/config.py
--- old/sphinx-scrapy-0.8.2/sphinx_scrapy/config.py     2026-04-10 
14:54:33.000000000 +0200
+++ new/sphinx-scrapy-0.8.6/sphinx_scrapy/config.py     2026-05-12 
09:03:58.000000000 +0200
@@ -3,6 +3,8 @@
 from dataclasses import dataclass, field
 from pathlib import Path
 
+from packaging.utils import canonicalize_name
+
 try:
     import tomllib
 except ModuleNotFoundError:
@@ -34,6 +36,10 @@
     return {str(key) for key in optional_dependencies}
 
 
+def normalize_project_id(project_id: str) -> str:
+    return canonicalize_name(project_id.strip())
+
+
 def load_project_config(root: Path | None = None) -> ProjectConfig:
     """Load project configuration from a pyproject.toml if available.
 
@@ -54,10 +60,15 @@
     scrapy_data = tool_data.get("sphinx-scrapy", {})
     python_version = scrapy_data.get("python-version", 
LATEST_RTD_PYTHON_VERSION)
     extras = get_extras(pyproject_data)
-    project_id = pyproject_data.get("project", {}).get("name")
+    raw_project_id = pyproject_data.get("project", {}).get("name")
+    project_id = None
+    if raw_project_id is not None:
+        normalized_project_id = normalize_project_id(str(raw_project_id))
+        if normalized_project_id:
+            project_id = normalized_project_id
     return ProjectConfig(
         root=project_root,
         python_version=str(python_version) if python_version is not None else 
None,
         extras=extras,
-        project_id=str(project_id) if project_id is not None else None,
+        project_id=project_id,
     )

Reply via email to