Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:
strategy:
matrix:
platform: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
runs-on: ${{ matrix.platform }}
permissions:
contents: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
- run: coverage run --source=dfetch --append -m behave features # Run features tests
- run: coverage xml -o coverage.xml # Create XML report
- run: pyroma --directory --min=10 . # Check pyproject
- run: find dfetch -name "*.py" | xargs pyupgrade --py39-plus # Check syntax
- run: find dfetch -name "*.py" | xargs pyupgrade --py310-plus # Check syntax

- name: Run codacy-coverage-reporter
uses: codacy/codacy-coverage-reporter-action@a38818475bb21847788496e9f0fddaa4e84955ba # master
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ repos:
entry: pyupgrade
language: python
files: ^dfetch/
args: [--py39-plus]
args: [--py310-plus]
types: [file, python]

- repo: https://github.com/gitleaks/gitleaks
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Release 0.12.0 (unreleased)
* Group logging under a project name header (#953)
* Introduce new ``update-patch`` command (#614)
* Introduce new ``format-patch`` command (#943)
* Drop python 3.9 support (#988)

Release 0.11.0 (released 2026-01-03)
====================================
Expand Down
3 changes: 1 addition & 2 deletions dfetch/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import argparse
import sys
from collections.abc import Sequence
from typing import Optional

from rich.console import Console

Expand Down Expand Up @@ -65,7 +64,7 @@ def _help(_: argparse.Namespace) -> None:
parser.print_help()


def run(argv: Sequence[str], console: Optional[Console] = None) -> None:
def run(argv: Sequence[str], console: Console | None = None) -> None:
"""Start dfetch."""
args = create_parser().parse_args(argv)

Expand Down
3 changes: 1 addition & 2 deletions dfetch/commands/update_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@

import argparse
import pathlib
from typing import Optional

import dfetch.commands.command
import dfetch.manifest.project
Expand Down Expand Up @@ -154,7 +153,7 @@ def _update_patch(
root: pathlib.Path,
project_name: str,
patch_text: str,
) -> Optional[pathlib.Path]:
) -> pathlib.Path | None:
"""Update the specified patch file with new patch text."""
patch_path = pathlib.Path(patch_to_update).resolve()

Expand Down
12 changes: 6 additions & 6 deletions dfetch/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import os
import sys
from contextlib import nullcontext
from typing import Any, Optional, Union, cast
from typing import Any, cast

from rich.console import Console
from rich.highlighter import NullHighlighter
Expand All @@ -23,7 +23,7 @@ def make_console(no_color: bool = False) -> Console:
)


def configure_root_logger(console: Optional[Console] = None) -> None:
def configure_root_logger(console: Console | None = None) -> None:
"""Configure the root logger with RichHandler using the provided Console."""
console = console or make_console()

Expand Down Expand Up @@ -94,10 +94,10 @@ def error(self, msg: object, *args: Any, **kwargs: Any) -> None:

def status(
self, name: str, message: str, spinner: str = "dots", enabled: bool = True
) -> Union[Status, nullcontext[None]]:
) -> Status | nullcontext[None]:
"""Show status message with spinner if enabled."""
rich_console = None
logger: Optional[logging.Logger] = self
logger: logging.Logger | None = self
while logger:
for handler in getattr(logger, "handlers", []):
if isinstance(handler, RichHandler):
Expand Down Expand Up @@ -143,7 +143,7 @@ def filter(self, record: logging.LogRecord) -> bool:
return True


def setup_root(name: str, console: Optional[Console] = None) -> DLogger:
def setup_root(name: str, console: Console | None = None) -> DLogger:
"""Create and return the root logger."""
logging.setLoggerClass(DLogger)
configure_root_logger(console)
Expand Down Expand Up @@ -173,7 +173,7 @@ def increase_verbosity() -> None:
logger_.setLevel(new_level)


def get_logger(name: str, console: Optional[Console] = None) -> DLogger:
def get_logger(name: str, console: Console | None = None) -> DLogger:
"""Get logger for a module, optionally configuring console colors."""
logging.setLoggerClass(DLogger)
logger = logging.getLogger(name)
Expand Down
28 changes: 13 additions & 15 deletions dfetch/manifest/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import re
from collections.abc import Sequence
from dataclasses import dataclass
from typing import IO, Any, Optional, Union
from typing import IO, Any

import yaml
from typing_extensions import NotRequired, TypedDict
Expand Down Expand Up @@ -95,11 +95,9 @@ def _guess_project(self, names: Sequence[str]) -> Sequence[str]:
class ManifestDict(TypedDict, total=True): # pylint: disable=too-many-ancestors
"""Serialized dict types."""

version: Union[int, str]
remotes: NotRequired[Sequence[Union[RemoteDict, Remote]]]
projects: Sequence[
Union[ProjectEntryDict, ProjectEntry, dict[str, Union[str, list[str]]]]
]
version: int | str
remotes: NotRequired[Sequence[RemoteDict | Remote]]
projects: Sequence[ProjectEntryDict | ProjectEntry | dict[str, str | list[str]]]


class Manifest:
Expand All @@ -113,8 +111,8 @@ class Manifest:
def __init__(
self,
manifest: ManifestDict,
text: Optional[str] = None,
path: Optional[Union[str, os.PathLike[str]]] = None,
text: str | None = None,
path: str | os.PathLike[str] | None = None,
) -> None:
"""Create the manifest."""
self.__version: str = str(manifest.get("version", self.CURRENT_VERSION))
Expand Down Expand Up @@ -142,7 +140,7 @@ def __init__(
def _init_projects(
self,
projects: Sequence[
Union[ProjectEntryDict, ProjectEntry, dict[str, Union[str, list[str]]]]
ProjectEntryDict | ProjectEntry | dict[str, str | list[str]]
],
) -> dict[str, ProjectEntry]:
"""Iterate over projects from manifest and initialize ProjectEntries from it.
Expand Down Expand Up @@ -189,7 +187,7 @@ def _init_projects(

@staticmethod
def _determine_remotes(
remotes_from_manifest: Sequence[Union[RemoteDict, Remote]],
remotes_from_manifest: Sequence[RemoteDict | Remote],
) -> tuple[dict[str, Remote], list[Remote]]:
default_remotes: list[Remote] = []
remotes: dict[str, Remote] = {}
Expand All @@ -208,8 +206,8 @@ def _determine_remotes(

@staticmethod
def from_yaml(
text: Union[io.TextIOWrapper, str, IO[str]],
path: Optional[Union[str, os.PathLike[str]]] = None,
text: io.TextIOWrapper | str | IO[str],
path: str | os.PathLike[str] | None = None,
) -> "Manifest":
"""Create a manifest from a file like object."""
if isinstance(text, (io.TextIOWrapper, IO)):
Expand All @@ -228,7 +226,7 @@ def from_yaml(
return Manifest(manifest, text=text, path=path)

@staticmethod
def _load_yaml(text: Union[io.TextIOWrapper, str, IO[str]]) -> Any:
def _load_yaml(text: io.TextIOWrapper | str | IO[str]) -> Any:
try:
return yaml.safe_load(text)
except yaml.YAMLError as exc:
Expand Down Expand Up @@ -306,9 +304,9 @@ def _as_dict(self) -> dict[str, ManifestDict]:
if len(remotes) == 1:
remotes[0].pop("default", None)

projects: list[dict[str, Union[str, list[str]]]] = []
projects: list[dict[str, str | list[str]]] = []
for project in self.projects:
project_yaml: dict[str, Union[str, list[str]]] = project.as_yaml()
project_yaml: dict[str, str | list[str]] = project.as_yaml()
if len(remotes) == 1:
project_yaml.pop("remote", None)
projects.append(project_yaml)
Expand Down
4 changes: 2 additions & 2 deletions dfetch/manifest/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import os
import pathlib
from typing import Any, Optional, cast
from typing import Any, cast

from strictyaml import StrictYAMLError, YAMLValidationError, load

Expand Down Expand Up @@ -82,7 +82,7 @@ def find_manifest() -> str:
return os.path.realpath(paths[0])


def get_childmanifests(skip: Optional[list[str]] = None) -> list[Manifest]:
def get_childmanifests(skip: list[str] | None = None) -> list[Manifest]:
"""Parse & validate any manifest file in cwd and return a list of all valid manifests."""
skip = skip or []
logger.debug("Looking for sub-manifests")
Expand Down
9 changes: 4 additions & 5 deletions dfetch/manifest/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,6 @@

import copy
from collections.abc import Sequence
from typing import Optional, Union

from typing_extensions import Required, TypedDict

Expand All @@ -291,7 +290,7 @@
"src": str,
"dst": str,
"url": str,
"patch": Union[str, list[str]],
"patch": str | list[str],
"repo": str,
"branch": str,
"tag": str,
Expand All @@ -315,7 +314,7 @@ def __init__(self, kwargs: ProjectEntryDict) -> None:
self._revision: str = kwargs.get("revision", "")

self._remote: str = kwargs.get("remote", "")
self._remote_obj: Optional[Remote] = None
self._remote_obj: Remote | None = None
self._src: str = kwargs.get("src", "") # noqa
self._dst: str = kwargs.get("dst", self._name)
self._url: str = kwargs.get("url", "")
Expand All @@ -332,7 +331,7 @@ def __init__(self, kwargs: ProjectEntryDict) -> None:
@classmethod
def from_yaml(
cls,
yamldata: Union[dict[str, Union[str, list[str]]], ProjectEntryDict],
yamldata: dict[str, str | list[str]] | ProjectEntryDict,
default_remote: str = "",
) -> "ProjectEntry":
"""Create a Project Entry from yaml data.
Expand Down Expand Up @@ -461,7 +460,7 @@ def as_recommendation(self) -> "ProjectEntry":
recommendation._repo_path = "" # pylint: disable=protected-access
return recommendation

def as_yaml(self) -> dict[str, Union[str, list[str]]]:
def as_yaml(self) -> dict[str, str | list[str]]:
"""Get this project as yaml dictionary."""
yamldata = {
"name": self._name,
Expand Down
6 changes: 2 additions & 4 deletions dfetch/manifest/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
url-base: https://github.com/
"""

from typing import Optional, Union

from typing_extensions import TypedDict

_MandatoryRemoteDict = TypedDict("_MandatoryRemoteDict", {"name": str, "url-base": str})
Expand All @@ -28,7 +26,7 @@
class RemoteDict(_MandatoryRemoteDict, total=False):
"""Class representing data types of Remote class construction."""

default: Optional[bool]
default: bool | None


class Remote:
Expand All @@ -41,7 +39,7 @@ def __init__(self, kwargs: RemoteDict) -> None:
self._default: bool = bool(kwargs.get("default", False))

@classmethod
def from_yaml(cls, yamldata: Union[dict[str, str], RemoteDict]) -> "Remote":
def from_yaml(cls, yamldata: dict[str, str] | RemoteDict) -> "Remote":
"""Create a remote entry in the manifest from yaml data.

Returns:
Expand Down
3 changes: 1 addition & 2 deletions dfetch/project/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import os
import pathlib
from typing import Union

import dfetch.manifest.project
from dfetch.log import get_logger
Expand Down Expand Up @@ -48,7 +47,7 @@ def create_super_project() -> SuperProject:
return determine_superproject_vcs(root_directory)(manifest, root_directory)


def determine_superproject_vcs(path: Union[str, pathlib.Path]) -> type[SuperProject]:
def determine_superproject_vcs(path: str | pathlib.Path) -> type[SuperProject]:
"""Determine correct VCS type of the superproject in the given path."""
for project_type in SUPPORTED_SUPERPROJECT_TYPES:
if project_type.check(path):
Expand Down
5 changes: 2 additions & 3 deletions dfetch/project/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import datetime
import os
from typing import Optional, Union

import yaml
from typing_extensions import TypedDict
Expand All @@ -27,7 +26,7 @@ class Options(TypedDict): # pylint: disable=too-many-ancestors
remote_url: str
destination: str
hash: str
patch: Union[str, list[str]]
patch: str | list[str]


class Metadata:
Expand Down Expand Up @@ -78,7 +77,7 @@ def from_file(cls, path: str) -> "Metadata":
return cls(data)

def fetched(
self, version: Version, hash_: str = "", patch_: Optional[list[str]] = None
self, version: Version, hash_: str = "", patch_: list[str] | None = None
) -> None:
"""Update metadata."""
self._last_fetch = datetime.datetime.now()
Expand Down
Loading
Loading