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
10 changes: 5 additions & 5 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.9'
python-version: '3.10'
- uses: pre-commit/action@v3.0.1

tests-core:
Expand All @@ -23,12 +23,12 @@ jobs:
fail-fast: false # Set on "false" to get the results of ALL builds
matrix:
os: ["ubuntu-latest"]
python-version: ["3.9", "3.12", "3.13"]
python-version: ["3.10", "3.12", "3.13"]
sphinx-version: ["7.4", "8.2"]
include:
# corner cases for Windows
- os: "windows-latest"
python-version: "3.9"
python-version: "3.10"
sphinx-version: "7.4"
- os: "windows-latest"
python-version: "3.12"
Expand All @@ -39,7 +39,7 @@ jobs:
exclude:
# Sphinx 8.2 only supports py3.11+
- os: "ubuntu-latest"
python-version: "3.9"
python-version: "3.10"
sphinx-version: "8.2"

steps:
Expand Down Expand Up @@ -81,7 +81,7 @@ jobs:
matrix:
include:
- os: "ubuntu-latest"
python-version: "3.9"
python-version: "3.10"
sphinx-version: "7.4"
- os: "ubuntu-latest"
python-version: "3.13"
Expand Down
2 changes: 1 addition & 1 deletion docs/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Or use tox (recommended):

.. code-block:: bash

tox -e py39
tox -e py310

Note some tests use `syrupy <https://github.com/tophat/syrupy>`__ to perform snapshot testing.
These snapshots can be updated by running:
Expand Down
9 changes: 4 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ classifiers = [
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
Expand All @@ -26,7 +25,7 @@ classifiers = [
'Topic :: Utilities',
'Framework :: Sphinx :: Extension',
]
requires-python = ">=3.9,<4"
requires-python = ">=3.10,<4"
dependencies = [
"sphinx>=7.4,<9",
"requests-file~=2.1", # external links
Expand Down Expand Up @@ -145,19 +144,19 @@ disable_error_code = ["no-redef"]

legacy_tox_ini = """
[tox]
envlist = py39
envlist = py10

[testenv]
usedevelop = true

[testenv:py{39,310,311,312,313}]
[testenv:py{310,311,312,313}]
extras =
test
test-parallel
commands =
pytest --ignore tests/benchmarks {posargs:tests}

[testenv:py{39,310,311,312,313}-benchmark]
[testenv:py{310,311,312,313}-benchmark]
extras =
test
benchmark
Expand Down
2 changes: 1 addition & 1 deletion sphinx_needs/api/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from __future__ import annotations

from typing import Callable
from collections.abc import Callable

from sphinx.application import Sphinx
from sphinx.util.logging import SphinxLoggerAdapter
Expand Down
4 changes: 2 additions & 2 deletions sphinx_needs/config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from __future__ import annotations

from collections.abc import Mapping
from collections.abc import Callable, Mapping
from dataclasses import MISSING, dataclass, field, fields
from typing import TYPE_CHECKING, Any, Callable, Literal, TypedDict
from typing import TYPE_CHECKING, Any, Literal, TypedDict

from docutils.parsers.rst import directives
from sphinx.application import Sphinx
Expand Down
3 changes: 2 additions & 1 deletion sphinx_needs/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
import inspect
import json
import os.path
from collections.abc import Callable
from datetime import datetime
from functools import wraps
from pathlib import Path
from timeit import default_timer as timer # Used for timing measurements
from typing import Any, Callable, TypeVar
from typing import Any, TypeVar

from jinja2 import Environment, PackageLoader, select_autoescape
from sphinx.application import Sphinx
Expand Down
4 changes: 2 additions & 2 deletions sphinx_needs/directives/need.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from __future__ import annotations

import re
from collections.abc import Sequence
from typing import Any, Callable
from collections.abc import Callable, Sequence
from typing import Any

from docutils import nodes
from sphinx.addnodes import desc_name, desc_signature
Expand Down
7 changes: 5 additions & 2 deletions sphinx_needs/directives/needbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,9 @@

if current_needbar["stacked"]:
# handle stacked bar
y_offset = [i + j for i, j in zip(y_offset, local_data_number[x])]
y_offset = [

Check warning on line 393 in sphinx_needs/directives/needbar.py

View check run for this annotation

Codecov / codecov/patch

sphinx_needs/directives/needbar.py#L393

Added line #L393 was not covered by tests
i + j for i, j in zip(y_offset, local_data_number[x], strict=False)
]

if current_needbar["show_sum"]:
try:
Expand Down Expand Up @@ -427,7 +429,8 @@
matplotlib.pyplot.setp(bar_labels, rotation=int(sum_rotation))

centers = [
(i + j) / 2.0 for i, j in zip(index[0], index[len(local_data_number) - 1])
(i + j) / 2.0
for i, j in zip(index[0], index[len(local_data_number) - 1], strict=False)
]
if not current_needbar["horizontal"]:
# We want to support even older version of matplotlib, which do not support axes.set_xticks(labels)
Expand Down
4 changes: 2 additions & 2 deletions sphinx_needs/directives/needextend.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from collections.abc import Sequence
from typing import Any, Callable
from collections.abc import Callable, Sequence
from typing import Any

from docutils import nodes
from docutils.parsers.rst import directives
Expand Down
3 changes: 2 additions & 1 deletion sphinx_needs/directives/needflow/_graphviz.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import html
import textwrap
from collections.abc import Callable
from functools import cache
from typing import Callable, Literal, TypedDict
from typing import Literal, TypedDict
from urllib.parse import urlparse

from docutils import nodes
Expand Down
4 changes: 2 additions & 2 deletions sphinx_needs/directives/needservice.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from collections.abc import Sequence
from typing import Any, Callable
from collections.abc import Callable, Sequence
from typing import Any

from docutils import nodes
from docutils.parsers.rst import directives
Expand Down
4 changes: 2 additions & 2 deletions sphinx_needs/directives/needtable.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from __future__ import annotations

import re
from collections.abc import Sequence
from typing import Any, Callable
from collections.abc import Callable, Sequence
from typing import Any

from docutils import nodes
from docutils.parsers.rst import directives
Expand Down
18 changes: 9 additions & 9 deletions sphinx_needs/filter_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
import ast
import json
import re
from collections.abc import Iterable
from collections.abc import Callable, Iterable
from pathlib import Path
from timeit import default_timer as timer
from types import CodeType
from typing import Any, Callable, TypedDict, overload
from typing import Any, TypedDict, overload

from docutils import nodes
from docutils.parsers.rst import directives
Expand Down Expand Up @@ -318,8 +318,8 @@ def _analyze_and_apply_expr(
:returns: the needs (potentially filtered),
and a boolean denoting if it still requires python eval filtering
"""
if isinstance(expr, (ast.Str, ast.Constant)):
if isinstance(expr.s, (str, bool)):
if isinstance(expr, ast.Str | ast.Constant):
if isinstance(expr.s, str | bool):
# "value" / True / False
return needs if expr.s else needs.filter_ids([]), False

Expand All @@ -335,13 +335,13 @@ def _analyze_and_apply_expr(
if (
isinstance(expr.left, ast.Name)
and len(expr.comparators) == 1
and isinstance(expr.comparators[0], (ast.Str, ast.Constant))
and isinstance(expr.comparators[0], ast.Str | ast.Constant)
):
# x == "value"
field = expr.left.id
value = expr.comparators[0].s
elif (
isinstance(expr.left, (ast.Str, ast.Constant))
isinstance(expr.left, ast.Str | ast.Constant)
and len(expr.comparators) == 1
and isinstance(expr.comparators[0], ast.Name)
):
Expand Down Expand Up @@ -369,9 +369,9 @@ def _analyze_and_apply_expr(
if (
isinstance(expr.left, ast.Name)
and len(expr.comparators) == 1
and isinstance(expr.comparators[0], (ast.List, ast.Tuple, ast.Set))
and isinstance(expr.comparators[0], ast.List | ast.Tuple | ast.Set)
and all(
isinstance(elt, (ast.Str, ast.Constant))
isinstance(elt, ast.Str | ast.Constant)
for elt in expr.comparators[0].elts
)
):
Expand All @@ -386,7 +386,7 @@ def _analyze_and_apply_expr(
# type in ["a", "b", ...]
return needs.filter_types(values), False
elif (
isinstance(expr.left, (ast.Str, ast.Constant))
isinstance(expr.left, ast.Str | ast.Constant)
and len(expr.comparators) == 1
and isinstance(expr.comparators[0], ast.Name)
and expr.comparators[0].id == "tags"
Expand Down
16 changes: 9 additions & 7 deletions sphinx_needs/functions/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ def execute_func(
)
return "??"

if func_return is not None and not isinstance(func_return, (str, int, float, list)):
if func_return is not None and not isinstance(
func_return, str | int | float | list
):
log_warning(
logger,
f"Return value of function {func_name!r} is of type {type(func_return)}. Allowed are str, int, float, list",
Expand All @@ -122,7 +124,7 @@ def execute_func(
return "??"
if isinstance(func_return, list):
for i, element in enumerate(func_return):
if not isinstance(element, (str, int, float)):
if not isinstance(element, str | int | float):
log_warning(
logger,
f"Return value item {i} of function {func_name!r} is of type {type(element)}. Allowed are str, int, float",
Expand Down Expand Up @@ -204,7 +206,7 @@ def find_and_replace_node_content(
return node
else:
for child in node.children:
if isinstance(child, (nodes.literal_block, nodes.literal, Need)):
if isinstance(child, nodes.literal_block | nodes.literal | Need):
# Do not parse literal blocks or nested needs
new_children.append(child)
continue
Expand Down Expand Up @@ -253,7 +255,7 @@ def resolve_dynamic_values(needs: NeedsMutable, app: Sphinx) -> None:
for need_option in need:
if need_option not in allowed_fields:
continue
if not isinstance(need[need_option], (list, set)):
if not isinstance(need[need_option], list | set):
func_call: str | None = "init"
while func_call:
try:
Expand Down Expand Up @@ -314,7 +316,7 @@ def resolve_dynamic_values(needs: NeedsMutable, app: Sphinx) -> None:
)
if func_call is None:
new_values.append(element)
elif isinstance(func_return, (list, set)):
elif isinstance(func_return, list | set):
new_values += func_return
else:
new_values += [func_return]
Expand Down Expand Up @@ -370,7 +372,7 @@ def resolve_variants_options(
for var_option in variants_options:
if (
var_option in need
and isinstance(need[var_option], (str, list, tuple, set))
and isinstance(need[var_option], str | list | tuple | set)
and (
result := match_variants(
need[var_option],
Expand Down Expand Up @@ -476,7 +478,7 @@ def _analyze_func_string(
for arg in func_call.args:
if isinstance(arg, ast.Num):
func_args.append(arg.n)
elif isinstance(arg, (ast.Str, ast.BoolOp)):
elif isinstance(arg, ast.Str | ast.BoolOp):
func_args.append(arg.s) # type: ignore
elif isinstance(arg, ast.List):
arg_list: list[Any] = []
Expand Down
2 changes: 1 addition & 1 deletion sphinx_needs/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
import os
import re
import uuid
from collections.abc import Callable
from contextlib import suppress
from functools import lru_cache
from optparse import Values
from pathlib import Path
from typing import Callable
from urllib.parse import urlparse

import requests
Expand Down
13 changes: 7 additions & 6 deletions sphinx_needs/needs.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from __future__ import annotations

import contextlib
from collections.abc import Callable
from pathlib import Path
from timeit import default_timer as timer # Used for timing measurements
from typing import Any, Callable, Literal
from typing import Any, Literal

from docutils import nodes
from docutils.parsers.rst import directives
Expand Down Expand Up @@ -782,9 +783,9 @@ def _gather_field_defaults(
k: v for k, v in value.items() if k in {"predicates", "default"}
}
if "predicates" in single_default and (
not isinstance(single_default["predicates"], (list, tuple))
not isinstance(single_default["predicates"], list | tuple)
or not all(
isinstance(x, (list, tuple))
isinstance(x, list | tuple)
and len(x) == 2
and isinstance(x[0], str)
for x in single_default["predicates"]
Expand All @@ -798,9 +799,9 @@ def _gather_field_defaults(
)
continue
elif (
isinstance(value, (list, tuple))
isinstance(value, list | tuple)
and len(value) > 0
and all(isinstance(x, (list, tuple)) for x in value)
and all(isinstance(x, list | tuple) for x in value)
):
old_format = True
single_default = {"predicates": []}
Expand Down Expand Up @@ -830,7 +831,7 @@ def _gather_field_defaults(
"config",
None,
)
elif isinstance(value, (list, tuple)):
elif isinstance(value, list | tuple):
old_format = True
if len(value) == 2:
# single (value, predicate) pair
Expand Down
2 changes: 1 addition & 1 deletion sphinx_needs/roles/need_ref.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def value_to_string(value: Any) -> str:
return value
elif isinstance(value, dict):
return ";".join([str(i) for i in value.items()])
elif isinstance(value, (Iterable, list, tuple)):
elif isinstance(value, Iterable | list | tuple):
return ";".join([str(i) for i in value])

return str(value)
Expand Down
Loading