Skip to content

Commit

Permalink
Fix type hinting incompatibility in Python 3.9
Browse files Browse the repository at this point in the history
Refactor to use Pathlike type alias for path representation

Unified path type handling across the codebase by introducing the `Pathlike` type alias (`Union[str, Path]`). This improves readability and consistency in path-related functions and methods, reducing redundancy. Updated corresponding type annotations, imports, and tests accordingly.
  • Loading branch information
coordt committed Feb 2, 2025
1 parent d8ecca8 commit 96b29f5
Show file tree
Hide file tree
Showing 7 changed files with 29 additions and 29 deletions.
8 changes: 4 additions & 4 deletions bumpversion/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Union
from typing import TYPE_CHECKING, Any, Optional

from bumpversion.config.files import read_config_file
from bumpversion.config.models import Config
from bumpversion.exceptions import ConfigurationError
from bumpversion.ui import get_indented_logger

if TYPE_CHECKING: # pragma: no-coverage
from pathlib import Path
if TYPE_CHECKING:
from bumpversion.utils import Pathlike

Check warning on line 13 in bumpversion/config/__init__.py

View check run for this annotation

Codecov / codecov/patch

bumpversion/config/__init__.py#L13

Added line #L13 was not covered by tests

logger = get_indented_logger(__name__)

Expand Down Expand Up @@ -54,7 +54,7 @@ def set_config_defaults(parsed_config: dict[str, Any], **overrides: Any) -> dict
return config_dict


def get_configuration(config_file: Union[str, Path, None] = None, **overrides: Any) -> Config:
def get_configuration(config_file: Optional[Pathlike] = None, **overrides: Any) -> Config:
"""
Return the configuration based on any configuration files and overrides.
Expand Down
7 changes: 4 additions & 3 deletions bumpversion/config/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING, Any, Dict, MutableMapping, Union
from typing import TYPE_CHECKING, Any, Dict, MutableMapping, Optional, Union

from bumpversion.config.files_legacy import read_ini_file
from bumpversion.ui import get_indented_logger, print_warning

if TYPE_CHECKING: # pragma: no-coverage
from bumpversion.config.models import Config
from bumpversion.utils import Pathlike
from bumpversion.versioning.models import Version

logger = get_indented_logger(__name__)
Expand All @@ -22,7 +23,7 @@
)


def find_config_file(explicit_file: Union[str, Path, None] = None) -> Union[Path, None]:
def find_config_file(explicit_file: Optional[Pathlike] = None) -> Union[Path, None]:
"""
Find the configuration file, if it exists.
Expand All @@ -48,7 +49,7 @@ def find_config_file(explicit_file: Union[str, Path, None] = None) -> Union[Path
)


def read_config_file(config_file: Union[str, Path, None] = None) -> Dict[str, Any]:
def read_config_file(config_file: Optional[Pathlike] = None) -> Dict[str, Any]:
"""
Read the configuration file, if it exists.
Expand Down
6 changes: 3 additions & 3 deletions bumpversion/scm/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from bumpversion.exceptions import DirtyWorkingDirectoryError
from bumpversion.scm.models import LatestTagInfo, SCMConfig
from bumpversion.ui import get_indented_logger
from bumpversion.utils import format_and_raise_error, is_subpath, run_command
from bumpversion.utils import Pathlike, format_and_raise_error, is_subpath, run_command

logger = get_indented_logger(__name__)

Expand Down Expand Up @@ -60,7 +60,7 @@ def latest_tag_info(self) -> LatestTagInfo:
self._latest_tag_info = LatestTagInfo(**info)
return self._latest_tag_info

def add_path(self, path: str | Path) -> None:
def add_path(self, path: Pathlike) -> None:
"""Add a path to the VCS."""
repository_root = self.latest_tag_info().repository_root
if not (repository_root and is_subpath(repository_root, path)):
Expand All @@ -86,7 +86,7 @@ def get_all_tags(self) -> list[str]:
):
return []

def commit_and_tag(self, files: list[Path | str], context: MutableMapping, dry_run: bool = False) -> None:
def commit_and_tag(self, files: list[Pathlike], context: MutableMapping, dry_run: bool = False) -> None:
"""Commit and tag files to the repository using the configuration."""
if dry_run:
return
Expand Down
7 changes: 3 additions & 4 deletions bumpversion/scm/hg.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
"""Mercurial source control management."""

import subprocess
from pathlib import Path
from typing import ClassVar, MutableMapping, Optional

from bumpversion.exceptions import DirtyWorkingDirectoryError, SignedTagsError
from bumpversion.scm.models import LatestTagInfo, SCMConfig
from bumpversion.ui import get_indented_logger
from bumpversion.utils import run_command
from bumpversion.utils import Pathlike, run_command

logger = get_indented_logger(__name__)

Expand Down Expand Up @@ -51,11 +50,11 @@ def latest_tag_info(self) -> LatestTagInfo:
self._latest_tag_info = LatestTagInfo(**info)
return self._latest_tag_info

def add_path(self, path: str | Path) -> None:
def add_path(self, path: Pathlike) -> None:
"""Add a path to the Source Control Management repository."""
pass

def commit_and_tag(self, files: list[Path | str], context: MutableMapping, dry_run: bool = False) -> None:
def commit_and_tag(self, files: list[Pathlike], context: MutableMapping, dry_run: bool = False) -> None:
"""Commit and tag files to the repository using the configuration."""

def tag(self, name: str, sign: bool = False, message: Optional[str] = None) -> None:
Expand Down
21 changes: 8 additions & 13 deletions bumpversion/scm/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from bumpversion.config import Config
from bumpversion.ui import get_indented_logger
from bumpversion.utils import extract_regex_flags
from bumpversion.utils import Pathlike, extract_regex_flags

logger = get_indented_logger(__name__)

Expand Down Expand Up @@ -82,15 +82,15 @@ def latest_tag_info(self) -> LatestTagInfo:
"""Return the latest tag information."""
...

def add_path(self, path: str | Path) -> None:
def add_path(self, path: Pathlike) -> None:
"""Add a path to the pending commit."""
...

def get_all_tags(self) -> list[str]:
"""Return all tags in the SCM."""
...

def commit_and_tag(self, files: list[Path | str], context: MutableMapping, dry_run: bool = False) -> None:
def commit_and_tag(self, files: list[Pathlike], context: MutableMapping, dry_run: bool = False) -> None:
"""Commit and tag files to the repository using the configuration."""
...

Expand Down Expand Up @@ -126,15 +126,15 @@ def latest_tag_info(self) -> LatestTagInfo:
"""Return the latest tag information."""
return LatestTagInfo()

def add_path(self, path: str | Path) -> None:
def add_path(self, path: Pathlike) -> None:
"""Add a path to the pending commit."""
logger.debug("No source code management system configured. Skipping adding path '%s'.", path)

def get_all_tags(self) -> list[str]:
"""Return all tags in the SCM."""
return []

def commit_and_tag(self, files: list[Path | str], context: MutableMapping, dry_run: bool = False) -> None:
def commit_and_tag(self, files: list[Pathlike], context: MutableMapping, dry_run: bool = False) -> None:
"""Pretend to commit and tag files to the repository using the configuration."""
logger.debug("No source code management system configured. Skipping committing and tagging.")

Expand Down Expand Up @@ -200,20 +200,15 @@ def _update_from_latest_tag_info(self, latest_tag_info: LatestTagInfo):
for attr_name, value in asdict(latest_tag_info).items():
setattr(self, attr_name, value)

def path_in_repo(self, path: Path | str) -> bool:
def path_in_repo(self, path: Pathlike) -> bool:
"""Return whether a path is inside this repository."""
if self.repository_root is None:
return True
elif not Path(path).is_absolute():
return True
return str(path).startswith(str(self.repository_root))

def commit_and_tag(
self,
files: list[str | Path],
context: MutableMapping,
dry_run: bool = False,
) -> None:
def commit_and_tag(self, files: list[Pathlike], context: MutableMapping, dry_run: bool = False) -> None:
"""Commit the files to the source code management system."""
logger.indent()

Expand All @@ -230,7 +225,7 @@ def commit_and_tag(
self.tool.commit_and_tag(files=files, context=context, dry_run=dry_run)
logger.dedent()

def _commit(self, files: list[Path | str], context: MutableMapping, dry_run: bool = False) -> None:
def _commit(self, files: list[Pathlike], context: MutableMapping, dry_run: bool = False) -> None:
"""Commit the files to the source code management system."""
do_commit = not dry_run
logger.info("%s the commit", "Preparing" if do_commit else "Would prepare")
Expand Down
6 changes: 5 additions & 1 deletion bumpversion/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
logger = get_indented_logger(__name__)


Pathlike = Union[str, Path]
"""A type alias for a path-like object."""


def extract_regex_flags(regex_pattern: str) -> Tuple[str, str]:
"""
Extract the regex flags from the regex pattern.
Expand Down Expand Up @@ -133,7 +137,7 @@ def run_command(command: list, env: Optional[dict] = None) -> CompletedProcess:
return result


def is_subpath(parent: Union[Path, str], path: Union[Path, str]) -> bool:
def is_subpath(parent: Pathlike, path: Pathlike) -> bool:
"""Return whether a path is inside the parent."""
normalized_parent = Path(parent)
normalized_path = Path(path)
Expand Down
3 changes: 2 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from pytest import param

from bumpversion import utils
from bumpversion.utils import Pathlike

DRIVE_PREFIX = "c:" if sys.platform == "win32" else ""

Expand Down Expand Up @@ -183,7 +184,7 @@ def test_returns_empty_string_when_no_flags(self):
param(Path(f"{DRIVE_PREFIX}/parent"), "relative/path", True, id="parent_as_path_path_as_string_4"),
],
)
def test_is_subpath(parent: Path | str, path: Path | str, expected: bool) -> None:
def test_is_subpath(parent: Pathlike, path: Pathlike, expected: bool) -> None:
"""Test the is_subpath function."""
# Act
result = utils.is_subpath(parent, path)
Expand Down

0 comments on commit 96b29f5

Please sign in to comment.