Skip to content

Commit

Permalink
Support placeholders for years in copyright (#12910)
Browse files Browse the repository at this point in the history
  • Loading branch information
AA-Turner authored Oct 4, 2024
1 parent 3677dd1 commit be52db2
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 3 deletions.
6 changes: 6 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------
Expand Down
3 changes: 1 addition & 2 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import os
import re
import time
from typing import TYPE_CHECKING

from sphinx import __display_version__
Expand All @@ -27,7 +26,7 @@
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
Expand Down
14 changes: 14 additions & 0 deletions doc/usage/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ Project information
author = 'Joe Bloggs'
.. _config-copyright:

.. confval:: copyright
project_copyright
:type: :code-py:`str | Sequence[str]`
Expand All @@ -128,13 +130,24 @@ 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.

.. versionchanged:: 7.1
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:`''`
Expand Down Expand Up @@ -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`
Expand Down
16 changes: 16 additions & 0 deletions sphinx/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

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)
Expand Down Expand Up @@ -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 = (
Expand Down Expand Up @@ -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'
Expand Down

0 comments on commit be52db2

Please sign in to comment.