Support placeholders for years in copyright (#12910)

This commit is contained in:
Adam Turner
2024-10-04 13:52:29 +01:00
committed by GitHub
parent 3677dd1bca
commit be52db2bb3
5 changed files with 96 additions and 3 deletions

View File

@@ -53,6 +53,12 @@ Features added
GMT (universal time) instead of local time for the date-time
supplied to :confval:`html_last_updated_fmt`.
Patch by Adam Turner.
* #12910: Copyright entries now support the ``'%Y'`` placeholder
to substitute the current year.
This is helpful for reducing the reliance on Python modules
such as :py:mod:`time` or :py:mod:`datetime` in :file:`conf.py`.
See :ref:`the docs <config-copyright>` for further detail.
Patch by Adam Turner.
Bugs fixed
----------

View File

@@ -3,7 +3,6 @@ from __future__ import annotations
import os
import re
import time
from typing import TYPE_CHECKING
from sphinx import __display_version__
@@ -27,7 +26,7 @@ templates_path = ['_templates']
exclude_patterns = ['_build']
project = 'Sphinx'
copyright = f'2007-{time.strftime("%Y")}, the Sphinx developers'
copyright = '2007-%Y, the Sphinx developers'
release = version = __display_version__
show_authors = True
nitpicky = True

View File

@@ -114,6 +114,8 @@ Project information
author = 'Joe Bloggs'
.. _config-copyright:
.. confval:: copyright
project_copyright
:type: :code-py:`str | Sequence[str]`
@@ -128,6 +130,14 @@ Project information
* :code-py:`copyright = 'YYYY-YYYY, Author Name'`
* :code-py:`copyright = 'YYYY-YYYY Author Name'`
If the string :code-py:`'%Y'` appears in a copyright line,
it will be replaced with the current four-digit year.
For example:
* :code-py:`copyright = '%Y'`
* :code-py:`copyright = '%Y, Author Name'`
* :code-py:`copyright = 'YYYY-%Y, Author Name'`
.. versionadded:: 3.5
The :code-py:`project_copyright` alias.
@@ -135,6 +145,9 @@ Project information
The value may now be a sequence of copyright statements in the above form,
which will be displayed each to their own line.
.. versionchanged:: 8.1
Copyright statements support the :code-py:`'%Y'` placeholder.
.. confval:: version
:type: :code-py:`str`
:default: :code-py:`''`
@@ -2528,6 +2541,7 @@ so the HTML options also apply where appropriate.
:default: The value of **copyright**
The copyright of the document.
See :confval:`copyright` for permitted formats.
.. confval:: epub_identifier
:type: :code-py:`str`

View File

@@ -619,6 +619,21 @@ def init_numfig_format(app: Sphinx, config: Config) -> None:
config.numfig_format = numfig_format
def evaluate_copyright_placeholders(_app: Sphinx, config: Config) -> None:
"""Replace copyright year placeholders (%Y) with the current year."""
replace_yr = str(time.localtime().tm_year)
for k in ('copyright', 'epub_copyright'):
if k in config:
value: str | Sequence[str] = config[k]
if isinstance(value, str):
if '%Y' in value:
config[k] = value.replace('%Y', replace_yr)
else:
if any('%Y' in line for line in value):
items = (line.replace('%Y', replace_yr) for line in value)
config[k] = type(value)(items) # type: ignore[call-arg]
def correct_copyright_year(_app: Sphinx, config: Config) -> None:
"""Correct values of copyright year that are not coherent with
the SOURCE_DATE_EPOCH environment variable (if set)
@@ -775,6 +790,7 @@ def setup(app: Sphinx) -> ExtensionMetadata:
app.connect('config-inited', convert_source_suffix, priority=800)
app.connect('config-inited', convert_highlight_options, priority=800)
app.connect('config-inited', init_numfig_format, priority=800)
app.connect('config-inited', evaluate_copyright_placeholders, priority=795)
app.connect('config-inited', correct_copyright_year, priority=800)
app.connect('config-inited', check_confval_types, priority=800)
app.connect('config-inited', check_primary_domain, priority=800)

View File

@@ -4,7 +4,11 @@ import time
import pytest
from sphinx.config import Config, correct_copyright_year
from sphinx.config import (
Config,
correct_copyright_year,
evaluate_copyright_placeholders,
)
LT = time.localtime()
LT_NEW = (2009, *LT[1:], LT.tm_zone, LT.tm_gmtoff)
@@ -106,6 +110,19 @@ def test_correct_year_single_no_author(expect_date):
assert cfg.copyright == copyright_date
def test_correct_year_placeholder(expect_date):
# test that copyright is substituted
copyright_date = '2006-%Y, Alice'
cfg = Config({'copyright': copyright_date}, {})
assert cfg.copyright == copyright_date
evaluate_copyright_placeholders(None, cfg) # type: ignore[arg-type]
correct_copyright_year(None, cfg) # type: ignore[arg-type]
if expect_date and expect_date <= LOCALTIME_2009.tm_year:
assert cfg.copyright == f'2006-{expect_date}, Alice'
else:
assert cfg.copyright == '2006-2009, Alice'
def test_correct_year_multi_line(expect_date):
# test that copyright is substituted
copyright_dates = (
@@ -159,6 +176,47 @@ def test_correct_year_multi_line_all_formats(expect_date):
assert cfg.copyright == copyright_dates
def test_correct_year_multi_line_all_formats_placeholder(expect_date):
# test that copyright is substituted
copyright_dates = (
'%Y',
'%Y Alice',
'%Y, Bob',
'2006-%Y',
'2006-%Y Charlie',
'2006-%Y, David',
# other format codes are left as-is
'2006-%y, Eve',
'%Y-%m-%d %H:%M:S %z, Francis',
)
cfg = Config({'copyright': copyright_dates}, {})
assert cfg.copyright == copyright_dates
evaluate_copyright_placeholders(None, cfg) # type: ignore[arg-type]
correct_copyright_year(None, cfg) # type: ignore[arg-type]
if expect_date and expect_date <= LOCALTIME_2009.tm_year:
assert cfg.copyright == (
f'{expect_date}',
f'{expect_date} Alice',
f'{expect_date}, Bob',
f'2006-{expect_date}',
f'2006-{expect_date} Charlie',
f'2006-{expect_date}, David',
'2006-%y, Eve',
'2009-%m-%d %H:%M:S %z, Francis',
)
else:
assert cfg.copyright == (
'2009',
'2009 Alice',
'2009, Bob',
'2006-2009',
'2006-2009 Charlie',
'2006-2009, David',
'2006-%y, Eve',
'2009-%m-%d %H:%M:S %z, Francis',
)
def test_correct_year_app(expect_date, tmp_path, make_app):
# integration test
copyright_date = '2006-2009, Alice'