Fix upper-constraints and linters for Python 3.14 compatibility

greenlet 3.1.1 and yappi 1.6.10 fail to build with Python 3.14 due
to CPython C API changes. Add version-conditional constraints to
use newer compatible versions on Python >= 3.14 while preserving
the existing pins for older Python versions.

libxslt-devel and libxml2-devel added to bindep.txt, required to
build lxml-5.3.0.

Update mypy from v0.981 to v1.9.0 in pre-commit config, as the old
version depends on typed_ast which cannot be built on Python 3.14.
Add implicit_optional = True to mypy.ini to preserve the previous
behavior. Fix type annotations in tobiko/common/_fixture.py to resolve
new mypy errors caused by unbound TypeVar usage.

Generated-By: claude-opus-4-6
Change-Id: Ibfb9d7ee1033eb06c1939a36071ad26df126f131
Signed-off-by: Eduardo Olivares <eolivare@redhat.com>
This commit is contained in:
Eduardo Olivares committed 2026-09-23 12:58:02 +02:00
1 parent 7c5b4730b6
commit 9b3a636203
6 files changed
+41 -27

No files matched your search

+1 -1
View File
@@ -34,7 +34,7 @@ repos:
files: ^tobiko/
- repo: https://github.com/pre-commit/mirrors-mypy.git
rev: 'v0.981'
rev: 'v1.9.0'
hooks:
- id: mypy
files: ^tobiko/
+4
View File
@@ -9,6 +9,8 @@ iperf3 [platform:redhat]
iproute [platform:redhat]
libffi-devel [platform:redhat]
libguestfs-tools-c [platform:redhat]
libxml2-devel [platform:redhat]
libxslt-devel [platform:redhat]
make [platform:redhat]
openssl-devel [platform:redhat]
podman [platform:redhat]
@@ -33,3 +35,5 @@ libssl-dev [platform:dpkg]
make [platform:dpkg]
python3 [platform:dpkg]
python3-dev [platform:dpkg]
libxml2-dev [platform:dpkg]
libxslt1-dev [platform:dpkg]
+1
View File
@@ -1,3 +1,4 @@
[mypy]
show_error_codes = True
# disable_error_code = type-arg
implicit_optional = True
+30 -23
View File
@@ -34,7 +34,9 @@ LOG = log.getLogger(__name__)
F = typing.TypeVar('F', 'SharedFixture', fixtures.Fixture)
G = typing.TypeVar('G', bound=fixtures.Fixture)
FixtureType = typing.Union[F, typing.Type[F], str]
FixtureType = typing.Union[fixtures.Fixture,
typing.Type[fixtures.Fixture],
str]
def is_fixture(obj: typing.Any) -> bool:
@@ -71,7 +73,7 @@ def get_fixture(obj: str,
def get_fixture(obj: FixtureType,
fixture_id: typing.Any = None,
manager: 'FixtureManager' = None,
**kwargs) -> F:
**kwargs) -> fixtures.Fixture:
"""Returns a fixture identified by given :param obj:
It returns registered fixture for given :param obj:. If none has been
@@ -97,7 +99,7 @@ def get_fixture(obj: FixtureType,
"""
if isinstance(obj, fixtures.Fixture):
return typing.cast(F, obj)
return obj
return fixture_manager(obj, manager).get_fixture(
obj, fixture_id=fixture_id, **kwargs)
@@ -175,7 +177,7 @@ def setup_fixture(obj: FixtureType,
manager: 'FixtureManager' = None,
alternative: FixtureType = None,
**kwargs) \
-> F:
-> fixtures.Fixture:
"""I setups registered fixture
"""
@@ -187,11 +189,11 @@ def setup_fixture(obj: FixtureType,
handle_exception=handle_setup_error):
errors = []
for _obj in objs:
fixture: F = typing.cast(F,
get_fixture(_obj,
fixture_id=fixture_id,
manager=manager,
**kwargs))
fixture = get_fixture(
_obj,
fixture_id=fixture_id,
manager=manager,
**kwargs)
try:
fixture.setUp()
break
@@ -234,9 +236,9 @@ def reset_fixture(obj: F,
def reset_fixture(obj: FixtureType,
fixture_id: typing.Any = None,
manager: 'FixtureManager' = None,
**kwargs) -> F:
**kwargs) -> fixtures.Fixture:
"""It cleanups and setups registered fixture"""
fixture: F = get_fixture(
fixture = get_fixture(
obj, fixture_id=fixture_id, manager=manager, **kwargs)
with _exception.handle_multiple_exceptions():
fixture.reset()
@@ -259,7 +261,7 @@ def cleanup_fixture(obj: F,
def cleanup_fixture(obj: FixtureType,
fixture_id: typing.Any = None,
manager: 'FixtureManager' = None) -> F:
manager: 'FixtureManager' = None) -> fixtures.Fixture:
"""It cleans up registered fixture"""
fixture = get_fixture(obj, fixture_id=fixture_id, manager=manager)
with _exception.handle_multiple_exceptions():
@@ -286,7 +288,7 @@ def use_fixture(obj: F,
def use_fixture(obj: FixtureType,
fixture_id: typing.Any = None,
manager: 'FixtureManager' = None,
**kwargs) -> F:
**kwargs) -> fixtures.Fixture:
"""It setups registered fixture and then register it for cleanup
At the end of the test case execution it will call cleanup_fixture
@@ -445,7 +447,7 @@ def required_fixture(cls: typing.Type[G], setup=True, **params) \
-> 'RequiredFixture[G]':
"""Creates a property that gets fixture identified by given :param cls:
"""
return RequiredFixture[F](cls, setup=setup, **params)
return RequiredFixture[G](cls, setup=setup, **params)
def get_fixture_id(obj: typing.Any) -> typing.Any:
@@ -480,30 +482,34 @@ def get_object_name(obj) -> str:
class FixtureManager:
def __init__(self):
self.fixtures: typing.Dict[str, F] = {}
self.fixtures: typing.Dict[str, fixtures.Fixture] = {}
def get_fixture(self,
obj: FixtureType,
fixture_id: typing.Any = None,
**kwargs) -> F:
**kwargs) -> fixtures.Fixture:
name, obj = get_name_and_object(obj)
if fixture_id:
name += f'-{fixture_id}'
try:
return self.fixtures[name]
except KeyError:
fixture: F = self.init_fixture(obj=obj,
name=name,
fixture_id=fixture_id,
**kwargs)
fixture = self.init_fixture(
obj=obj,
name=name,
fixture_id=fixture_id,
**kwargs)
assert isinstance(fixture, fixtures.Fixture)
self.fixtures[name] = fixture
return fixture
def init_fixture(self, obj: typing.Union[typing.Type[F], F],
def init_fixture(self,
obj: typing.Union[
typing.Type[fixtures.Fixture],
fixtures.Fixture],
name: str,
fixture_id: typing.Any,
**kwargs) -> F:
**kwargs) -> fixtures.Fixture:
return init_fixture(obj=obj,
name=name,
fixture_id=fixture_id,
@@ -511,7 +517,8 @@ class FixtureManager:
def remove_fixture(self,
obj: FixtureType,
fixture_id: typing.Any = None) -> typing.Optional[F]:
fixture_id: typing.Any = None) \
-> typing.Optional[fixtures.Fixture]:
name = get_object_name(obj)
if fixture_id:
name += '-' + str(fixture_id)
+1 -1
View File
@@ -111,7 +111,7 @@ def list_systemd_units(*units: SystemdUnitType,
names.append(match.group(0).lower())
starts.append(match.start())
search_pos = match.end()
ends: typing.List[int | None] = list(starts[1:]) + [None]
ends: typing.List[typing.Optional[int]] = list(starts[1:]) + [None]
for line in table_lines:
message = line.strip()
+4 -2
View File
@@ -116,7 +116,8 @@ google-auth-httplib2===0.1.0
googleapis-common-protos===1.56.4
gossip===2.4.0
graphviz===0.20.1
greenlet===3.1.1
greenlet===3.1.1;python_version<'3.14'
greenlet===3.3.2;python_version>='3.14'
grpcio===1.49.1
gssapi===1.8.1
gunicorn===20.1.0
@@ -591,7 +592,8 @@ XStatic-term.js===0.0.7.0
XStatic-tv4===1.2.7.0
xvfbwrapper===0.2.9
yamlloader===1.1.0
yappi===1.6.10
yappi===1.6.10;python_version<'3.14'
yappi===1.7.6;python_version>='3.14'
yaql===2.0.0
zake===0.2.2
zeroconf===0.38.3;python_version=='3.8'