From 9abd62d7c3e5cba4705a63cff7adb987f130b00b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 25 Jul 2023 11:43:16 +0200 Subject: [PATCH] Fix ``SOURCE_DATE_EPOCH`` for multi-line copyright values --- CHANGES | 3 +++ sphinx/config.py | 10 +++++++++- tests/test_config.py | 45 +++++++++++++++++++++++++++++--------------- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/CHANGES b/CHANGES index 3a2f997ff47..e54a2b42bbf 100644 --- a/CHANGES +++ b/CHANGES @@ -21,6 +21,9 @@ Features added Bugs fixed ---------- +* #11514: Fix ``SOURCE_DATE_EPOCH`` in multi-line copyright footer. + Patch by Bénédikt Tran. + Testing ------- diff --git a/sphinx/config.py b/sphinx/config.py index 3aa62651ca8..2d87ef8f615 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -429,7 +429,15 @@ def correct_copyright_year(app: Sphinx, config: Config) -> None: for k in ('copyright', 'epub_copyright'): if k in config: replace = r'\g<1>%s' % format_date('%Y', language='en') - config[k] = copyright_year_re.sub(replace, config[k]) + value: str | list[str] | tuple[str, ...] = config[k] + if isinstance(value, str): + config[k] = copyright_year_re.sub(replace, value) + else: + items = (copyright_year_re.sub(replace, x) for x in value) + if isinstance(value, list): + config[k] = list(items) + else: + config[k] = tuple(items) def check_confval_types(app: Sphinx | None, config: Config) -> None: diff --git a/tests/test_config.py b/tests/test_config.py index f01ea0c32d2..7ca276d8c9c 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,5 +1,7 @@ """Test the sphinx.config.Config class.""" +import os +import textwrap from unittest import mock import pytest @@ -8,6 +10,7 @@ from sphinx.config import ENUM, Config, check_confval_types from sphinx.errors import ConfigError, ExtensionError, VersionRequirementError from sphinx.testing.path import path +from sphinx.util.i18n import format_date @pytest.mark.sphinx(testroot='config', confoverrides={ @@ -445,22 +448,34 @@ def test_conf_py_nitpick_ignore_list(tempdir): @pytest.mark.sphinx(testroot='copyright-multiline') -def test_multi_line_copyright(app, status, warning): +def test_multi_line_copyright(app): app.builder.build_all() content = (app.outdir / 'index.html').read_text(encoding='utf-8') - assert ' © Copyright 2006-2009, Alice.
' in content - assert ' © Copyright 2010-2013, Bob.
' in content - assert ' © Copyright 2014-2017, Charlie.
' in content - assert ' © Copyright 2018-2021, David.
' in content - assert ' © Copyright 2022-2025, Eve.' in content - - lines = ( - ' © Copyright 2006-2009, Alice.
\n \n' - ' © Copyright 2010-2013, Bob.
\n \n' - ' © Copyright 2014-2017, Charlie.
\n \n' - ' © Copyright 2018-2021, David.
\n \n' - ' © Copyright 2022-2025, Eve.\n \n' - ) - assert lines in content + if os.getenv('SOURCE_DATE_EPOCH') is None: + copyright_footer = ( + ' © Copyright 2006-2009, Alice.
\n', + ' © Copyright 2010-2013, Bob.
\n', + ' © Copyright 2014-2017, Charlie.
\n', + ' © Copyright 2018-2021, David.
\n', + ' © Copyright 2022-2025, Eve.', + ) + else: + source_date_year = format_date('%Y', language='en') + copyright_footer = ( + f' © Copyright 2006-{source_date_year}, Alice.
\n', + f' © Copyright 2010-{source_date_year}, Bob.
\n', + f' © Copyright 2014-{source_date_year}, Charlie.
\n', + f' © Copyright 2018-{source_date_year}, David.
\n', + f' © Copyright 2022-{source_date_year}, Eve.', + ) + + # check the copyright footer line by line (empty lines ignored) + for line in copyright_footer: + assert line in content + + # check the raw copyright footer block (empty lines included) + expect = '\n'.join(copyright_footer) + expect = textwrap.indent(expect, ' ', lambda _: True) + assert expect in content