Skip to content

Commit

Permalink
fix: make compatible with towncrier>=24.7
Browse files Browse the repository at this point in the history
towncrier 24.7 changed the way that its find_fragments() function works
to accept a Config dataclass instead of specific components of the
config. This commit adds a new version of lookup_towncrier_fragments()
to use the new API and chooses which one to use based on the version
of towncrier obtained from importlib.metadata. It also slightly
changes the way that towncrier config can be obtained: the previous
get_towncrier_config() function (which returned a dict) is now renamed
as get_towncrier_config_as_dict() and get_towncrier_config() returns
the new Config dataclass object for towncrier versions above 22.12.0rc1
and raises NotImplementedError for older versions. This API change
should be ok as get_towncrier_config() was only called in one place
before.
  • Loading branch information
Ben Rowland authored and webknjaz committed Dec 22, 2024
1 parent e032be3 commit 0c1de45
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 2 deletions.
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ classifiers =
[options]
include_package_data = True
install_requires =
packaging
sphinx
towncrier >= 23
package_dir =
Expand Down
52 changes: 50 additions & 2 deletions src/sphinxcontrib/towncrier/_fragment_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@


from functools import lru_cache
from importlib.metadata import version
from pathlib import Path
from typing import Optional, Set

from sphinx.util import logging

from packaging.version import Version


try:
# pylint: disable=no-name-in-module
Expand All @@ -17,7 +20,9 @@
find_fragments,
)

from ._towncrier import get_towncrier_config # noqa: WPS436
from ._towncrier import ( # noqa: WPS436
get_towncrier_config, get_towncrier_config_as_dict,
)


logger = logging.getLogger(__name__)
Expand All @@ -42,11 +47,21 @@ def _find_config_file(base: Path) -> Path:
# pylint: disable=fixme
# FIXME: refactor `lookup_towncrier_fragments` to drop noqas
@lru_cache(maxsize=1, typed=True) # noqa: WPS210
def lookup_towncrier_fragments( # noqa: WPS210
def lookup_towncrier_fragments(
working_dir: Optional[str] = None,
config_path: Optional[str] = None,
) -> Set[Path]:
"""Emit RST-formatted Towncrier changelog fragment paths."""
tc_version = Version(version('towncrier'))

Check warning on line 55 in src/sphinxcontrib/towncrier/_fragment_discovery.py

View check run for this annotation

Codecov / codecov/patch

src/sphinxcontrib/towncrier/_fragment_discovery.py#L55

Added line #L55 was not covered by tests
if tc_version >= Version('24.7.0'):
return _lookup_towncrier_fragments_post24_7(working_dir, config_path)
return _lookup_towncrier_fragments_pre24_7(working_dir, config_path)

Check warning on line 58 in src/sphinxcontrib/towncrier/_fragment_discovery.py

View check run for this annotation

Codecov / codecov/patch

src/sphinxcontrib/towncrier/_fragment_discovery.py#L57-L58

Added lines #L57 - L58 were not covered by tests


# used for towncrier version 24.7 and above
def _lookup_towncrier_fragments_post24_7( # noqa: WPS210
working_dir: Optional[str] = None, config_path: Optional[str] = None,
) -> Set[Path]:
project_path = Path.cwd() if working_dir is None else Path(working_dir)

final_config_path = (
Expand All @@ -66,6 +81,39 @@ def lookup_towncrier_fragments( # noqa: WPS210
)
return set()

fragment_dir = (towncrier_config.directory or 'newsfragments')
fragment_base_directory = project_path / fragment_dir

Check warning on line 85 in src/sphinxcontrib/towncrier/_fragment_discovery.py

View check run for this annotation

Codecov / codecov/patch

src/sphinxcontrib/towncrier/_fragment_discovery.py#L84-L85

Added lines #L84 - L85 were not covered by tests

_fragments, fragment_filenames = find_fragments(

Check warning on line 87 in src/sphinxcontrib/towncrier/_fragment_discovery.py

View check run for this annotation

Codecov / codecov/patch

src/sphinxcontrib/towncrier/_fragment_discovery.py#L87

Added line #L87 was not covered by tests
str(fragment_base_directory), towncrier_config, strict=False,
)

return {Path(fname[0]) for fname in fragment_filenames}

Check warning on line 91 in src/sphinxcontrib/towncrier/_fragment_discovery.py

View check run for this annotation

Codecov / codecov/patch

src/sphinxcontrib/towncrier/_fragment_discovery.py#L91

Added line #L91 was not covered by tests


# used for versions of towncrier before 24.7
def _lookup_towncrier_fragments_pre24_7( # noqa: WPS210
working_dir: Optional[str] = None, config_path: Optional[str] = None,
) -> Set[Path]:
project_path = Path.cwd() if working_dir is None else Path(working_dir)

Check warning on line 98 in src/sphinxcontrib/towncrier/_fragment_discovery.py

View check run for this annotation

Codecov / codecov/patch

src/sphinxcontrib/towncrier/_fragment_discovery.py#L98

Added line #L98 was not covered by tests

final_config_path = (

Check warning on line 100 in src/sphinxcontrib/towncrier/_fragment_discovery.py

View check run for this annotation

Codecov / codecov/patch

src/sphinxcontrib/towncrier/_fragment_discovery.py#L100

Added line #L100 was not covered by tests
_resolve_spec_config(project_path, config_path)
or _find_config_file(project_path)
)

try:
towncrier_config = get_towncrier_config_as_dict(

Check warning on line 106 in src/sphinxcontrib/towncrier/_fragment_discovery.py

View check run for this annotation

Codecov / codecov/patch

src/sphinxcontrib/towncrier/_fragment_discovery.py#L105-L106

Added lines #L105 - L106 were not covered by tests
project_path,
final_config_path,
)
except KeyError as key_err:

Check warning on line 110 in src/sphinxcontrib/towncrier/_fragment_discovery.py

View check run for this annotation

Codecov / codecov/patch

src/sphinxcontrib/towncrier/_fragment_discovery.py#L110

Added line #L110 was not covered by tests
# NOTE: The error is missing key 'towncrier' or similar
logger.warning(

Check warning on line 112 in src/sphinxcontrib/towncrier/_fragment_discovery.py

View check run for this annotation

Codecov / codecov/patch

src/sphinxcontrib/towncrier/_fragment_discovery.py#L112

Added line #L112 was not covered by tests
f'Missing key {key_err!s} in file {final_config_path!s}',
)
return set()

Check warning on line 115 in src/sphinxcontrib/towncrier/_fragment_discovery.py

View check run for this annotation

Codecov / codecov/patch

src/sphinxcontrib/towncrier/_fragment_discovery.py#L115

Added line #L115 was not covered by tests

fragment_directory: Optional[str] = 'newsfragments'
try:
fragment_base_directory = project_path / towncrier_config['directory']
Expand Down
17 changes: 17 additions & 0 deletions src/sphinxcontrib/towncrier/_towncrier.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
"""Towncrier related shims."""

from dataclasses import asdict as _dataclass_to_dict
from importlib.metadata import version
from pathlib import Path
from typing import Any, Dict, Union

from towncrier._settings.load import load_config_from_file # noqa: WPS436

from packaging.version import Version


def get_towncrier_config(
project_path: Path,
final_config_path: Union[Path, None],
) -> Any:
"""Return the towncrier config in native format."""
tc_version = Version(version('towncrier'))
if tc_version >= Version('22.12.0rc1'):
return load_config_from_file(str(project_path), str(final_config_path))
raise NotImplementedError(
'Towncrier Config is not available before version 22.12.0rc1, consider '
'using "get_towncrier_config_as_dict()" instead',
)


def get_towncrier_config_as_dict(
project_path: Path,
final_config_path: Union[Path, None],
) -> Dict[str, Any]: # FIXME: add a better type # pylint: disable=fixme
Expand Down

0 comments on commit 0c1de45

Please sign in to comment.