Skip to content
Draft
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/step-pre-commit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
# Python 3.9 is for mypy testing the lowest python version
# Python 3.13 is for ansible-lint hard-coding the python requirement
python-version: |
3.9
3.12
3.13
3.x
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
14 changes: 2 additions & 12 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,14 @@ repos:
hooks:
- id: mypy
language: python # Required for Renovate additional_dependencies
language_version: "3.9"
language_version: "3.12"
# TODO: find out how to amend mypy, pyright, pre-commit and package requirements.
# Apparently, there is no easy way to avoid some level of duplication of
# information when the package is *not* installed, which is the case of tmt
# & pre-commit in our setup.
#
# For now, we simply copy & paste from pyproject.toml :(
additional_dependencies:
- "importlib-metadata>=3.6.0; python_version < '3.10'"
- "importlib-resources; python_version < '3.12'"
- "click>=8.0.3"
- "docutils>=0.16"
- "fmf>=1.7.0"
Expand All @@ -57,9 +55,6 @@ repos:
# which is the version a developer encounters given the requirements are not
# frozen.
- "types-Markdown"
# prevent conflict between types-requests and urllib3
- "types-requests<2.33.0.20260519; python_version < '3.10'"
- "types-requests; python_version >= '3.10'"
- "types-setuptools"
- "types-jsonschema"
- "types-urllib3"
Expand All @@ -75,16 +70,14 @@ repos:
hooks:
- id: pyright
language: python # Required for Renovate additional_dependencies
language_version: "3.9"
language_version: "3.12"
# TODO: find out how to amend mypy, pyright, pre-commit and package requirements.
# Apparently, there is no easy way to avoid some level of duplication of
# information when the package is *not* installed, which is the case of tmt
# & pre-commit in our setup.
#
# For now, we simply copy & paste from pyproject.toml :(
additional_dependencies:
- "importlib-metadata>=3.6.0; python_version < '3.10'"
- "importlib-resources; python_version < '3.12'"
- "click>=8.0.3"
- "docutils>=0.16"
- "fmf>=1.7.0"
Expand All @@ -110,9 +103,6 @@ repos:
# which is the version a developer encounters given the requirements are not
# frozen.
- "types-Markdown"
# prevent conflict between types-requests and urllib3
- "types-requests<2.33.0.20260519; python_version < '3.10'"
- "types-requests; python_version >= '3.10'"
- "types-setuptools"
- "types-jsonschema"
- "types-urllib3"
Expand Down
5 changes: 2 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@
import os
import sys
from pathlib import Path
from typing import Optional

import tmt.utils

_POSSIBLE_THEMES: list[tuple[Optional[str], str]] = [
_POSSIBLE_THEMES: list[tuple[str | None, str]] = [
# Use renku as the default theme
('renku_sphinx_theme', 'renku'),
# Fall back to sphinx_rtd_theme if available
Expand Down Expand Up @@ -58,7 +57,7 @@ def _load_theme(theme_package_name: str, theme_name: str) -> bool:


if 'TMT_DOCS_THEME' in os.environ:
theme_package_name: Optional[str]
theme_package_name: str | None
theme_name: str

theme_specs = os.environ['TMT_DOCS_THEME']
Expand Down
4 changes: 2 additions & 2 deletions docs/ext/linkcheck_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import datetime
import functools
import json
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from sphinx.application import Sphinx
Expand All @@ -23,7 +23,7 @@ def get_web_git_url(app: "Sphinx") -> str:
return web_git_url(gitinfo.url, gitinfo.ref)


def linkcheck_cache(app: "Sphinx", uri: str) -> Optional[str]:
def linkcheck_cache(app: "Sphinx", uri: str) -> str | None:
# Get the cache result files
cache_file = app.outdir / "linkcheck_cache.json"
now = datetime.datetime.now(datetime.UTC)
Expand Down
10 changes: 5 additions & 5 deletions docs/ext/tmt_domain/autodoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import itertools
import typing
from contextlib import contextmanager
from typing import Optional, overload
from typing import overload

from docutils.statemachine import StringList
from sphinx.util.docutils import SphinxDirective, switch_source_input
Expand Down Expand Up @@ -131,8 +131,8 @@ def directive(
name: str,
*directive_args: str,
source: str,
offset_count: Optional[itertools.count] = None,
**directive_kwargs: Optional[str],
offset_count: itertools.count | None = None,
**directive_kwargs: str | None,
) -> "Generator[Self]":
"""
Add the directive header and start appending its content.
Expand Down Expand Up @@ -203,7 +203,7 @@ def new_list(
self,
*,
source: str,
offset_count: Optional[itertools.count] = None,
offset_count: itertools.count | None = None,
) -> "Generator[Self]":
"""
Start a new list context.
Expand Down Expand Up @@ -327,7 +327,7 @@ def directive(
self,
name: str,
*directive_args: str,
**directive_kwargs: Optional[str],
**directive_kwargs: str | None,
) -> "Generator[Content]":
"""
Wrapper around :py:meth:`Content.directive`.
Expand Down
8 changes: 4 additions & 4 deletions docs/ext/tmt_domain/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import abc
import typing
from functools import cached_property
from typing import Generic, Optional
from typing import Generic

from docutils.parsers.rst import directives
from sphinx import addnodes
Expand Down Expand Up @@ -150,10 +150,10 @@ def __init__(
self,
fix_parens: bool = False,
lowercase: bool = False,
nodeclass: Optional[type["Element"]] = None,
innernodeclass: Optional[type["TextElement"]] = None,
nodeclass: type["Element"] | None = None,
innernodeclass: type["TextElement"] | None = None,
warn_dangling: bool = False,
use_obj_name: Optional[bool] = None,
use_obj_name: bool | None = None,
) -> None:
if use_obj_name is not None:
self.use_obj_name = use_obj_name
Expand Down
7 changes: 3 additions & 4 deletions docs/ext/tmt_domain/story.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import re
import typing
from collections.abc import Callable, Iterable
from typing import Optional

import fmf.utils
from docutils import nodes
Expand Down Expand Up @@ -98,7 +97,7 @@ def _get_tmt_object(self) -> None:
for source in self.story.node.sources:
self.env.note_dependency(source)

def _has_story_attr(self, attr: str, story: Optional[Story] = None) -> bool:
def _has_story_attr(self, attr: str, story: Story | None = None) -> bool:
story = story or self.story
value = getattr(story, attr)
if not value:
Expand All @@ -121,7 +120,7 @@ def _add_story_content(
section: str,
*,
source_suffix: str = "",
content: typing.Optional[str] = None,
content: str | None = None,
transform_line: Callable[[str], str] = lambda x: x,
new_line: bool = True,
) -> None:
Expand Down Expand Up @@ -250,7 +249,7 @@ class StoryIndex(Index):
domain: "TmtDomain"

def generate(
self, docnames: Optional[Iterable[str]] = None
self, docnames: Iterable[str] | None = None
) -> tuple[list[tuple[str, list[IndexEntry]]], bool]:
content = {}
# TODO: What to actually use for key-value in the index?
Expand Down
31 changes: 7 additions & 24 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ description = "Test Management Tool"
readme = "README.rst"
license = "MIT"
license-file = "LICENSE"
requires-python = ">=3.9"
requires-python = ">=3.12"
authors = [
{ name = "Petr Splichal", email = "psplicha@redhat.com" },
]
Expand All @@ -23,19 +23,15 @@ keywords = [
classifiers = [
"License :: OSI Approved :: MIT License",
"Natural Language :: English",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Topic :: Utilities",
"Topic :: Software Development :: Testing",
"Operating System :: POSIX :: Linux",
]
dependencies = [
# Minimal versions based on c9s rpms
"importlib-metadata>=3.6.0; python_version < '3.10'", # Backporting selectable entry_points
"importlib-resources; python_version < '3.12'", # MultiplexedPath is broken on earlier versions
"click>=8.0.3",
"docutils>=0.16",
"fmf>=1.7.0",
Expand Down Expand Up @@ -147,9 +143,6 @@ dependencies = [
# which is the version a developer encounters given the requirements are not
# frozen.
"types-Markdown",
# prevent conflict between types-requests and urllib3
"types-requests<2.31.0.7; python_version < '3.10'",
"types-requests; python_version >= '3.10'",
"types-setuptools",
"types-jsonschema",
"types-urllib3",
Expand Down Expand Up @@ -229,7 +222,7 @@ show_error_codes = true
# annotations across module boundaries.
follow_imports = "normal"

python_version = "3.9"
python_version = "3.12"
files = [
"tmt/",
"tests/core/about/",
Expand Down Expand Up @@ -285,7 +278,7 @@ ignore = [
"tmt/utils/structured_field.py",
]

pythonVersion = "3.9"
pythonVersion = "3.12"
pythonPlatform = "Linux"

# Be vewy, vewy stwict, we'we hunting wabbits^Wbugs.
Expand All @@ -301,7 +294,7 @@ reportUnnecessaryCast = false # Unnecessary "cast" call; type is already...
[tool.ruff]
line-length = 99
src = ["tmt", "tests"]
target-version = "py39"
target-version = "py312"

[tool.ruff.format]
quote-style = "preserve" # TODO: Convert to a specific quote style
Expand Down Expand Up @@ -442,15 +435,7 @@ convention = "pep257"
builtins-ignorelist = ["help", "format", "input", "filter", "copyright", "max"]

[tool.ruff.lint.flake8-tidy-imports.banned-api]
"typing.TypeAlias".msg = "Use tmt._compat.typing.TypeAlias instead."
"typing_extensions.TypeAlias".msg = "Use tmt._compat.typing.TypeAlias instead."
"typing.Self".msg = "Use tmt._compat.typing.Self instead."
"typing.Concatenate".msg = "Use tmt._compat.typing.Concatenate instead."
"typing.ParamSpec".msg = "Use tmt._compat.typing.ParamSpec instead."
"typing.TypeGuard".msg = "Use tmt._compat.typing.TypeGuard instead."
"typing.TypeIs".msg = "Not present in centos-stream-10"
"typing_extensions.Self".msg = "Use tmt._compat.typing.Self instead."
"typing.override".msg = "Use tmt._compat.typing.override instead."
"pathlib.Path".msg = "Use tmt._compat.pathlib.Path instead."
"pathlib.PosixPath".msg = "Use tmt._compat.pathlib.Path instead."
"pydantic".msg = "Use tmt._compat.pydantic instead."
Expand All @@ -462,9 +447,7 @@ builtins-ignorelist = ["help", "format", "input", "filter", "copyright", "max"]
"dataclasses.field".msg = "Use tmt.container.field instead."
"click.style".msg = "Use tmt.log.style instead."
"annotationlib".msg = "Use tmt._compat.annotationlib instead"
"importlib.metadata.entry_points".msg = "Use tmt._compat.importlib.metadata.entry_points instead."
"importlib.readers".msg = "Use tmt._compat.importlib.readers instead."
"importlib.resources".msg = "Use tmt._compat.importlib.resources instead."
"importlib.readers.MultiplexedPath".msg = "Use tmt._compat.importlib.readers.MultiplexedPath instead."

[tool.ruff.lint.flake8-type-checking]
runtime-evaluated-base-classes = ["tmt.config.models.BaseConfig"]
Expand Down
3 changes: 1 addition & 2 deletions scripts/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"""

import dataclasses
from typing import Optional


@dataclasses.dataclass
Expand All @@ -16,7 +15,7 @@ class Item:
type: str
repo: str
status: str
size: Optional[int]
size: int | None
url: str
title: str

Expand Down
8 changes: 4 additions & 4 deletions scripts/sprint-overview
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import re
import sys
from collections import defaultdict
from collections.abc import Iterator
from typing import Any, Optional
from typing import Any

import click
import jinja2
Expand Down Expand Up @@ -121,7 +121,7 @@ def load_items(filepath: str) -> list[Item]:

def github_api_get(
path: str,
params: Optional[dict[str, str]] = None,
params: dict[str, str] | None = None,
) -> Iterator[list[dict[str, Any]]]:
"""
Make a GET request to the GitHub REST API, following pagination.
Expand Down Expand Up @@ -375,8 +375,8 @@ def display_comparison(sprint_name: str, base: list[Item], current: list[Item])
def main(
sprint: str,
output_yaml: bool,
base_path: Optional[str] = None,
current_path: Optional[str] = None,
base_path: str | None = None,
current_path: str | None = None,
) -> None:
"""
List all issues and pull requests in a GitHub Project sprint with story points.
Expand Down
8 changes: 4 additions & 4 deletions scripts/sprint-sync
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Sync a Jira sprint with GitHub sprint items.
import os
import re
import sys
from typing import IO, Any, Optional, cast
from typing import IO, Any, cast

import click
import jinja2
Expand Down Expand Up @@ -58,8 +58,8 @@ class SyncItem:
key: str,
decision: str,
summary: str,
url: Optional[str] = None,
duplicates: Optional[list[str]] = None,
url: str | None = None,
duplicates: list[str] | None = None,
) -> None:
self.key = key
self.decision = decision
Expand Down Expand Up @@ -139,7 +139,7 @@ def load_github_items(source: IO[str]) -> list[Item]:
return [Item(**item) for item in cast(list[dict[str, Any]], data)]


def get_upstream_url(jira: JIRA, issue: Issue) -> Optional[str]:
def get_upstream_url(jira: JIRA, issue: Issue) -> str | None:
"""
Get the "Upstream issue" remote link URL for a Jira issue.

Expand Down
12 changes: 6 additions & 6 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ class RunTmt(Protocol):
def __call__(
self,
*args: str,
command: Optional[click.BaseCommand] = None,
input: Optional[Union[str, bytes, IO[Any]]] = None,
env: Optional[Mapping[str, Optional[str]]] = None,
command: click.BaseCommand | None = None,
input: str | bytes | IO[Any] | None = None,
env: Mapping[str, str | None] | None = None,
catch_exceptions: bool = True,
color: bool = False,
**kwargs: Any,
Expand All @@ -59,9 +59,9 @@ def __init__(self) -> None:
def invoke( # type: ignore[override]
self,
*args: str,
command: Optional[click.BaseCommand] = None,
input: Optional[Union[str, bytes, IO[Any]]] = None,
env: Optional[Mapping[str, Optional[str]]] = None,
command: click.BaseCommand | None = None,
input: str | bytes | IO[Any] | None = None,
env: Mapping[str, str | None] | None = None,
catch_exceptions: bool = True,
color: bool = False,
**kwargs: Any,
Expand Down
Loading
Loading