Set up ruff so that test files have at least some linting (#10399)

Alternative implementation for #7908.

In this PR:
- set up ruff in CI (add to `tox.ini`, mark dep in `certbot/setup.py`)
- add a `ruff.toml` that ignores particularly annoying errors. I think
line length isn't actually necessary to set with this workflow since
we're not checking it but putting it there for future usage.
- either fix or ignore the rest of the errors that come with the default
linting configuration. fixed errors are mostly unused variables. ignored
are usually where we're doing weird import things for a specific reason.
This commit is contained in:
ohemorange
2025-08-08 08:48:43 -07:00
committed by GitHub
parent 5859e50e44
commit dea3e5f1c4
26 changed files with 102 additions and 81 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ import warnings
# #
# It is based on # It is based on
# https://github.com/requests/requests/blob/1278ecdf71a312dc2268f3bfc0aabfab3c006dcf/requests/packages.py # https://github.com/requests/requests/blob/1278ecdf71a312dc2268f3bfc0aabfab3c006dcf/requests/packages.py
import josepy as jose import josepy as jose # noqa: F401
for mod in list(sys.modules): for mod in list(sys.modules):
# This traversal is apparently necessary such that the identities are # This traversal is apparently necessary such that the identities are
+3 -3
View File
@@ -293,7 +293,7 @@ class ClientV2Test(unittest.TestCase):
updated_order_valid = self.order.update( updated_order_valid = self.order.update(
certificate='https://www.letsencrypt-demo.org/acme/cert/', certificate='https://www.letsencrypt-demo.org/acme/cert/',
status=messages.STATUS_VALID) status=messages.STATUS_VALID)
updated_orderr = self.orderr.update(body=updated_order_valid, fullchain_pem=CERT_SAN_PEM) self.orderr.update(body=updated_order_valid, fullchain_pem=CERT_SAN_PEM)
self.response.text = CERT_SAN_PEM self.response.text = CERT_SAN_PEM
@@ -489,14 +489,14 @@ class ClientV2Test(unittest.TestCase):
not_after=datetime.datetime(2025, 3, 20, 00, 00, 00), not_after=datetime.datetime(2025, 3, 20, 00, 00, 00),
) )
t, _ = self.client.renewal_time(cert_pem) t, _ = self.client.renewal_time(cert_pem)
assert t == None assert t is None
cert_pem = make_cert_for_renewal( cert_pem = make_cert_for_renewal(
not_before=datetime.datetime(2025, 3, 12, 00, 00, 00), not_before=datetime.datetime(2025, 3, 12, 00, 00, 00),
not_after=datetime.datetime(2025, 3, 30, 00, 00, 00), not_after=datetime.datetime(2025, 3, 30, 00, 00, 00),
) )
t, _ = self.client.renewal_time(cert_pem) t, _ = self.client.renewal_time(cert_pem)
assert t == None assert t is None
@mock.patch('acme.client.datetime') @mock.patch('acme.client.datetime')
def test_renewal_time_with_renewal_info(self, dt_mock): def test_renewal_time_with_renewal_info(self, dt_mock):
@@ -123,7 +123,7 @@ class GenMakeSelfSignedCertTest(unittest.TestCase):
def test_no_ips(self): def test_no_ips(self):
from acme.crypto_util import make_self_signed_cert from acme.crypto_util import make_self_signed_cert
cert = make_self_signed_cert(self.privkey, ['dummy']) make_self_signed_cert(self.privkey, ['dummy'])
@mock.patch("acme.crypto_util._now") @mock.patch("acme.crypto_util._now")
def test_expiry_times(self, mock_now): def test_expiry_times(self, mock_now):
+2 -2
View File
@@ -20,9 +20,9 @@ def _test_it(submodule, attribute):
# We use the imports below with eval, but pylint doesn't # We use the imports below with eval, but pylint doesn't
# understand that. # understand that.
import josepy # pylint: disable=unused-import import josepy # pylint: disable=unused-import # noqa: F401
import acme # pylint: disable=unused-import import acme # pylint: disable=unused-import # noqa: F401
acme_jose_mod = eval(acme_jose_path) # pylint: disable=eval-used acme_jose_mod = eval(acme_jose_path) # pylint: disable=eval-used
josepy_mod = eval(josepy_path) # pylint: disable=eval-used josepy_mod = eval(josepy_path) # pylint: disable=eval-used
assert acme_jose_mod is josepy_mod assert acme_jose_mod is josepy_mod
+2 -2
View File
@@ -419,8 +419,8 @@ class ClientV2:
if 'Link' not in response.headers: if 'Link' not in response.headers:
return [] return []
links = parse_header_links(response.headers['Link']) links = parse_header_links(response.headers['Link'])
return [l['url'] for l in links return [link['url'] for link in links
if 'rel' in l and 'url' in l and l['rel'] == relation_type] if 'rel' in link and 'url' in link and link['rel'] == relation_type]
@classmethod @classmethod
def get_directory(cls, url: str, net: 'ClientNetwork') -> messages.Directory: def get_directory(cls, url: str, net: 'ClientNetwork') -> messages.Directory:
@@ -1245,7 +1245,7 @@ class MultipleVhostsTest(util.ApacheTest):
def test_deploy_cert_no_mod_ssl(self): def test_deploy_cert_no_mod_ssl(self):
# Create # Create
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0]) self.config.make_vhost_ssl(self.vh_truth[0])
self.config.parser.modules["socache_shmcb_module"] = None self.config.parser.modules["socache_shmcb_module"] = None
self.config.prepare_server_https = mock.Mock() self.config.prepare_server_https = mock.Mock()
@@ -1,4 +1,5 @@
"""Tests for ApacheConfigurator for AugeasParserNode classes""" """Tests for ApacheConfigurator for AugeasParserNode classes"""
import importlib
import sys import sys
import unittest import unittest
from unittest import mock from unittest import mock
@@ -7,10 +8,10 @@ import pytest
from certbot_apache._internal.tests import util from certbot_apache._internal.tests import util
try:
import apacheconfig if importlib.util.find_spec('apacheconfig'):
HAS_APACHECONFIG = True HAS_APACHECONFIG = True
except ImportError: # pragma: no cover else: # pragma: no cover
HAS_APACHECONFIG = False HAS_APACHECONFIG = False
@@ -112,12 +112,12 @@ def test_dummy():
dirty=False, dirty=False,
filepath="/some/random/path" filepath="/some/random/path"
) )
dummydirective = DummyDirectiveNode( dummydirective = DummyDirectiveNode( # noqa: F841
name="Name", name="Name",
ancestor=None, ancestor=None,
filepath="/another/path" filepath="/another/path"
) )
dummycomment = DummyCommentNode( dummycomment = DummyCommentNode( # noqa: F841
comment="Comment", comment="Comment",
ancestor=dummyblock, ancestor=dummyblock,
filepath="/some/file" filepath="/some/file"
@@ -24,7 +24,12 @@ from certbot_integration_tests.utils import misc
from certbot_integration_tests.utils import pebble_artifacts from certbot_integration_tests.utils import pebble_artifacts
from certbot_integration_tests.utils import pebble_ocsp_server from certbot_integration_tests.utils import pebble_ocsp_server
from certbot_integration_tests.utils import proxy from certbot_integration_tests.utils import proxy
from certbot_integration_tests.utils.constants import * from certbot_integration_tests.utils.constants import DEFAULT_HTTP_01_PORT
from certbot_integration_tests.utils.constants import CHALLTESTSRV_PORT
from certbot_integration_tests.utils.constants import PEBBLE_DIRECTORY_URL
from certbot_integration_tests.utils.constants import PEBBLE_CHALLTESTSRV_URL
from certbot_integration_tests.utils.constants import PEBBLE_ALTERNATE_ROOTS
from certbot_integration_tests.utils.constants import MAX_SUBPROCESS_WAIT
class ACMEServer: class ACMEServer:
@@ -10,8 +10,9 @@ from typing import Optional
from typing import Tuple from typing import Tuple
import certbot_integration_tests import certbot_integration_tests
# pylint: disable=wildcard-import,unused-wildcard-import from certbot_integration_tests.utils.constants import DEFAULT_HTTP_01_PORT
from certbot_integration_tests.utils.constants import * from certbot_integration_tests.utils.constants import HTTPS_PORT
from certbot_integration_tests.utils.constants import PEBBLE_DIRECTORY_URL
def certbot_test(certbot_args: List[str], directory_url: Optional[str], http_01_port: int, def certbot_test(certbot_args: List[str], directory_url: Optional[str], http_01_port: int,
@@ -17,7 +17,6 @@ from certbot.plugins.dns_test_common import DOMAIN
from certbot.tests import acme_util from certbot.tests import acme_util
from certbot.tests import util as test_util from certbot.tests import util as test_util
DOMAIN = 'example.com'
KEY = jose.jwk.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) KEY = jose.jwk.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
+1
View File
@@ -71,6 +71,7 @@ test_extras = [
'pytest', 'pytest',
'pytest-cov', 'pytest-cov',
'pytest-xdist', 'pytest-xdist',
'ruff',
'setuptools', 'setuptools',
'tox', 'tox',
'types-httplib2', 'types-httplib2',
@@ -1,5 +1,6 @@
"""Certbot command line argument & config processing.""" """Certbot command line argument & config processing."""
# pylint: disable=too-many-lines # pylint: disable=too-many-lines
# ruff: noqa: F401
import argparse import argparse
import logging import logging
import logging.handlers import logging.handlers
@@ -51,7 +51,7 @@ class ChallengeFactoryTest(unittest.TestCase):
[messages.STATUS_PENDING]) [messages.STATUS_PENDING])
achalls = self.handler._challenge_factory(authzr, [0]) achalls = self.handler._challenge_factory(authzr, [0])
assert type(achalls[0]) == achallenges.Other assert type(achalls[0]) is achallenges.Other
class HandleAuthorizationsTest(unittest.TestCase): class HandleAuthorizationsTest(unittest.TestCase):
@@ -226,7 +226,8 @@ class CertificatesTest(BaseCertManagerTest):
# pylint: disable=protected-access # pylint: disable=protected-access
# pylint: disable=protected-access # pylint: disable=protected-access
get_report = lambda: cert_manager._report_human_readable(mock_config, parsed_certs) def get_report():
return cert_manager._report_human_readable(mock_config, parsed_certs)
out = get_report() out = get_report()
assert "INVALID: EXPIRED" in out assert "INVALID: EXPIRED" in out
@@ -117,11 +117,10 @@ class ParseTest(unittest.TestCase):
with tempfile.NamedTemporaryFile() as tmp_config: with tempfile.NamedTemporaryFile() as tmp_config:
tmp_config.close() # close now because of compatibility issues on Windows tmp_config.close() # close now because of compatibility issues on Windows
# use a shim to get ConfigArgParse to pick up tmp_config # use a shim to get ConfigArgParse to pick up tmp_config
shim = ( def shim(v):
lambda v: copy.deepcopy(constants.CLI_DEFAULTS[v]) return (copy.deepcopy(constants.CLI_DEFAULTS[v])
if v != "config_files" if v != "config_files"
else [tmp_config.name] else [tmp_config.name])
)
mock_flag_default.side_effect = shim mock_flag_default.side_effect = shim
namespace = self.parse(["certonly"]) namespace = self.parse(["certonly"])
@@ -1061,7 +1061,7 @@ class EnhanceConfigTest(ClientTestCommon):
def _test_error(self, enhance_error=False, restart_error=False): def _test_error(self, enhance_error=False, restart_error=False):
self.config.redirect = True self.config.redirect = True
with mock.patch('certbot._internal.client.logger') as mock_logger, \ with mock.patch('certbot._internal.client.logger') as mock_logger, \
test_util.patch_display_util() as mock_gu: test_util.patch_display_util():
with pytest.raises(errors.PluginError): with pytest.raises(errors.PluginError):
self._test_with_all_supported() self._test_with_all_supported()
@@ -565,7 +565,7 @@ class RealpathTest(test_util.TempDirTestCase):
os.symlink(link2_path, link3_path) os.symlink(link2_path, link3_path)
os.symlink(link3_path, link1_path) os.symlink(link3_path, link1_path)
with pytest.raises(RuntimeError, match='link1 is a loop!') as error: with pytest.raises(RuntimeError, match='link1 is a loop!'):
filesystem.realpath(link1_path) filesystem.realpath(link1_path)
@@ -1,5 +1,6 @@
"""Tests for certbot._internal.lock.""" """Tests for certbot._internal.lock."""
import functools import functools
import importlib
import multiprocessing import multiprocessing
import sys import sys
from unittest import mock from unittest import mock
@@ -10,13 +11,10 @@ from certbot import errors
from certbot.compat import os from certbot.compat import os
from certbot.tests import util as test_util from certbot.tests import util as test_util
try: if importlib.util.find_spec('fcntl'):
import fcntl # pylint: disable=import-error,unused-import
except ImportError:
POSIX_MODE = False
else:
POSIX_MODE = True POSIX_MODE = True
else:
POSIX_MODE = False
@@ -198,7 +198,7 @@ class RenewalTest(test_util.ConfigTestCase):
from certbot._internal import renewal from certbot._internal import renewal
lineage_config = copy.deepcopy(self.config) lineage_config = copy.deepcopy(self.config)
renewal_candidate = renewal.reconstitute(lineage_config, rc_path) renewal.reconstitute(lineage_config, rc_path)
# This means that manual_public_ip_logging_ok was not modified in the config based on its # This means that manual_public_ip_logging_ok was not modified in the config based on its
# value in the renewal conf file # value in the renewal conf file
assert isinstance(lineage_config.manual_public_ip_logging_ok, mock.MagicMock) assert isinstance(lineage_config.manual_public_ip_logging_ok, mock.MagicMock)
@@ -233,7 +233,7 @@ class RenewalTest(test_util.ConfigTestCase):
test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf', ec=False) test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf', ec=False)
config = configuration.NamespaceConfig(self.config) config = configuration.NamespaceConfig(self.config)
with mock.patch('time.sleep') as sleep: with mock.patch('time.sleep'):
renewal.handle_renewal_request(config) renewal.handle_renewal_request(config)
mock_renew_cert.assert_called_once() mock_renew_cert.assert_called_once()
@@ -254,7 +254,7 @@ class RenewalTest(test_util.ConfigTestCase):
test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf', ec=False) test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf', ec=False)
with mock.patch('time.sleep') as sleep: with mock.patch('time.sleep'):
renewal.handle_renewal_request(self.config) renewal.handle_renewal_request(self.config)
assert mock_client_network_get.call_count == 0 assert mock_client_network_get.call_count == 0
@@ -265,7 +265,7 @@ class RenewalTest(test_util.ConfigTestCase):
self.config.dry_run = True self.config.dry_run = True
ari_client_pool = mock.MagicMock() ari_client_pool = mock.MagicMock()
ari_client_pool.get.return_value = mock_acme ari_client_pool.get.return_value = mock_acme
with mock.patch('time.sleep') as sleep: with mock.patch('time.sleep'):
renewal.should_renew(self.config, mock.Mock(), ari_client_pool) renewal.should_renew(self.config, mock.Mock(), ari_client_pool)
assert mock_acme.renewal_time.call_count == 0 assert mock_acme.renewal_time.call_count == 0
+1 -1
View File
@@ -13,7 +13,7 @@ from __future__ import absolute_import
# First round of wrapping: we import statically all public attributes exposed by the os.path # First round of wrapping: we import statically all public attributes exposed by the os.path
# module. This allows in particular to have pylint, mypy, IDEs be aware that most of os.path # module. This allows in particular to have pylint, mypy, IDEs be aware that most of os.path
# members are available in certbot.compat.path. # members are available in certbot.compat.path.
from os.path import * # pylint: disable=wildcard-import,unused-wildcard-import,os-module-forbidden from os.path import * # pylint: disable=wildcard-import,unused-wildcard-import,os-module-forbidden # noqa: F403
# Second round of wrapping: we import dynamically all attributes from the os.path module that have # Second round of wrapping: we import dynamically all attributes from the os.path module that have
# not yet been imported by the first round (static star import). # not yet been imported by the first round (static star import).
+2 -2
View File
@@ -21,7 +21,7 @@ from __future__ import absolute_import
# First round of wrapping: we import statically all public attributes exposed by the os module # First round of wrapping: we import statically all public attributes exposed by the os module
# This allows in particular to have pylint, mypy, IDEs be aware that most of os members are # This allows in particular to have pylint, mypy, IDEs be aware that most of os members are
# available in certbot.compat.os. # available in certbot.compat.os.
from os import * # pylint: disable=wildcard-import,unused-wildcard-import,redefined-builtin,os-module-forbidden from os import * # pylint: disable=wildcard-import,unused-wildcard-import,redefined-builtin,os-module-forbidden # noqa: F403
# Second round of wrapping: we import dynamically all attributes from the os module that have not # Second round of wrapping: we import dynamically all attributes from the os module that have not
# yet been imported by the first round (static import). This covers in particular the case of # yet been imported by the first round (static import). This covers in particular the case of
@@ -43,7 +43,7 @@ if not std_os.environ.get("CERTBOT_DOCS") == "1":
# Import our internal path module, then allow certbot.compat.os.path # Import our internal path module, then allow certbot.compat.os.path
# to behave as a module (similarly to os.path). # to behave as a module (similarly to os.path).
from certbot.compat import _path as path # type: ignore # pylint: disable=wrong-import-position from certbot.compat import _path as path # type: ignore # pylint: disable=wrong-import-position # noqa: E402
std_sys.modules[__name__ + '.path'] = path std_sys.modules[__name__ + '.path'] = path
# Clean all remaining importables that are not from the core os module. # Clean all remaining importables that are not from the core os module.
+8
View File
@@ -0,0 +1,8 @@
line-length = 100
extend-exclude = ['tools', 'letstest']
[lint]
# Skip bare `except` rules (`E722`).
# Skip ambiguous variable name (`E741`).
ignore = ["E722", "E741",]
+28 -24
View File
@@ -2,28 +2,28 @@
# that script. # that script.
apacheconfig==0.3.2 ; python_full_version >= "3.9.2" and python_version < "3.10" apacheconfig==0.3.2 ; python_full_version >= "3.9.2" and python_version < "3.10"
asn1crypto==0.24.0 ; python_full_version >= "3.9.2" and python_version < "3.10" asn1crypto==0.24.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
astroid==3.3.9 ; python_full_version >= "3.9.2" and python_version < "3.10" astroid==3.3.11 ; python_full_version >= "3.9.2" and python_version < "3.10"
attrs==25.3.0 ; python_full_version >= "3.9.2" and python_version < "3.10" attrs==25.3.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
beautifulsoup4==4.13.4 ; python_full_version >= "3.9.2" and python_version < "3.10" beautifulsoup4==4.13.4 ; python_full_version >= "3.9.2" and python_version < "3.10"
boto3==1.15.15 ; python_full_version >= "3.9.2" and python_version < "3.10" boto3==1.15.15 ; python_full_version >= "3.9.2" and python_version < "3.10"
botocore==1.18.15 ; python_full_version >= "3.9.2" and python_version < "3.10" botocore==1.18.15 ; python_full_version >= "3.9.2" and python_version < "3.10"
cachetools==5.5.2 ; python_full_version >= "3.9.2" and python_version < "3.10" cachetools==5.5.2 ; python_full_version >= "3.9.2" and python_version < "3.10"
certifi==2025.4.26 ; python_full_version >= "3.9.2" and python_version < "3.10" certifi==2025.8.3 ; python_full_version >= "3.9.2" and python_version < "3.10"
cffi==1.12.3 ; python_full_version >= "3.9.2" and python_version < "3.10" cffi==1.12.3 ; python_full_version >= "3.9.2" and python_version < "3.10"
chardet==3.0.4 ; python_full_version >= "3.9.2" and python_version < "3.10" chardet==3.0.4 ; python_full_version >= "3.9.2" and python_version < "3.10"
cloudflare==2.19.0 ; python_full_version >= "3.9.2" and python_version < "3.10" cloudflare==2.19.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
colorama==0.4.6 ; python_full_version >= "3.9.2" and python_version < "3.10" and sys_platform == "win32" colorama==0.4.6 ; python_full_version >= "3.9.2" and python_version < "3.10" and sys_platform == "win32"
configargparse==1.5.3 ; python_full_version >= "3.9.2" and python_version < "3.10" configargparse==1.5.3 ; python_full_version >= "3.9.2" and python_version < "3.10"
configobj==5.0.6 ; python_full_version >= "3.9.2" and python_version < "3.10" configobj==5.0.6 ; python_full_version >= "3.9.2" and python_version < "3.10"
coverage==7.8.0 ; python_full_version >= "3.9.2" and python_version < "3.10" coverage==7.10.2 ; python_full_version >= "3.9.2" and python_version < "3.10"
cryptography==43.0.0 ; python_full_version >= "3.9.2" and python_version < "3.10" cryptography==43.0.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
cython==0.29.37 ; python_full_version >= "3.9.2" and python_version < "3.10" cython==0.29.37 ; python_full_version >= "3.9.2" and python_version < "3.10"
dill==0.4.0 ; python_full_version >= "3.9.2" and python_version < "3.10" dill==0.4.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
distlib==0.3.9 ; python_full_version >= "3.9.2" and python_version < "3.10" distlib==0.4.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
distro==1.0.1 ; python_full_version >= "3.9.2" and python_version < "3.10" distro==1.0.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
dns-lexicon==3.15.1 ; python_full_version >= "3.9.2" and python_version < "3.10" dns-lexicon==3.15.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
dnspython==2.6.1 ; python_full_version >= "3.9.2" and python_version < "3.10" dnspython==2.6.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
exceptiongroup==1.2.2 ; python_full_version >= "3.9.2" and python_version < "3.10" exceptiongroup==1.3.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
execnet==2.1.1 ; python_full_version >= "3.9.2" and python_version < "3.10" execnet==2.1.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
filelock==3.18.0 ; python_full_version >= "3.9.2" and python_version < "3.10" filelock==3.18.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
funcsigs==0.4 ; python_full_version >= "3.9.2" and python_version < "3.10" funcsigs==0.4 ; python_full_version >= "3.9.2" and python_version < "3.10"
@@ -37,58 +37,62 @@ iniconfig==2.1.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
ipaddress==1.0.16 ; python_full_version >= "3.9.2" and python_version < "3.10" ipaddress==1.0.16 ; python_full_version >= "3.9.2" and python_version < "3.10"
isort==6.0.1 ; python_full_version >= "3.9.2" and python_version < "3.10" isort==6.0.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
jmespath==0.10.0 ; python_full_version >= "3.9.2" and python_version < "3.10" jmespath==0.10.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
josepy==2.0.0 ; python_full_version >= "3.9.2" and python_version < "3.10" josepy==2.1.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
jsonlines==4.0.0 ; python_full_version >= "3.9.2" and python_version < "3.10" jsonlines==4.0.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
mccabe==0.7.0 ; python_full_version >= "3.9.2" and python_version < "3.10" mccabe==0.7.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
mypy-extensions==1.1.0 ; python_full_version >= "3.9.2" and python_version < "3.10" mypy-extensions==1.1.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
mypy==1.15.0 ; python_full_version >= "3.9.2" and python_version < "3.10" mypy==1.17.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
ndg-httpsclient==0.3.2 ; python_full_version >= "3.9.2" and python_version < "3.10" ndg-httpsclient==0.3.2 ; python_full_version >= "3.9.2" and python_version < "3.10"
oauth2client==4.1.3 ; python_full_version >= "3.9.2" and python_version < "3.10" oauth2client==4.1.3 ; python_full_version >= "3.9.2" and python_version < "3.10"
packaging==25.0 ; python_full_version >= "3.9.2" and python_version < "3.10" packaging==25.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
parsedatetime==2.4 ; python_full_version >= "3.9.2" and python_version < "3.10" parsedatetime==2.4 ; python_full_version >= "3.9.2" and python_version < "3.10"
pathspec==0.12.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
pbr==1.8.0 ; python_full_version >= "3.9.2" and python_version < "3.10" pbr==1.8.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
pip==25.1 ; python_full_version >= "3.9.2" and python_version < "3.10" pip==25.2 ; python_full_version >= "3.9.2" and python_version < "3.10"
platformdirs==4.3.7 ; python_full_version >= "3.9.2" and python_version < "3.10" platformdirs==4.3.8 ; python_full_version >= "3.9.2" and python_version < "3.10"
pluggy==1.5.0 ; python_full_version >= "3.9.2" and python_version < "3.10" pluggy==1.6.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
ply==3.4 ; python_full_version >= "3.9.2" and python_version < "3.10" ply==3.4 ; python_full_version >= "3.9.2" and python_version < "3.10"
py==1.11.0 ; python_full_version >= "3.9.2" and python_version < "3.10" py==1.11.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
pyasn1-modules==0.4.1 ; python_full_version >= "3.9.2" and python_version < "3.10" pyasn1-modules==0.4.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
pyasn1==0.4.8 ; python_full_version >= "3.9.2" and python_version < "3.10" pyasn1==0.4.8 ; python_full_version >= "3.9.2" and python_version < "3.10"
pycparser==2.14 ; python_full_version >= "3.9.2" and python_version < "3.10" pycparser==2.14 ; python_full_version >= "3.9.2" and python_version < "3.10"
pylint==3.3.6 ; python_full_version >= "3.9.2" and python_version < "3.10" pygments==2.19.2 ; python_full_version >= "3.9.2" and python_version < "3.10"
pylint==3.3.7 ; python_full_version >= "3.9.2" and python_version < "3.10"
pyopenssl==25.0.0 ; python_full_version >= "3.9.2" and python_version < "3.10" pyopenssl==25.0.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
pyotp==2.9.0 ; python_full_version >= "3.9.2" and python_version < "3.10" pyotp==2.9.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
pyparsing==2.4.7 ; python_full_version >= "3.9.2" and python_version < "3.10" pyparsing==2.4.7 ; python_full_version >= "3.9.2" and python_version < "3.10"
pyrfc3339==1.0 ; python_full_version >= "3.9.2" and python_version < "3.10" pyrfc3339==1.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
pytest-cov==6.1.1 ; python_full_version >= "3.9.2" and python_version < "3.10" pytest-cov==6.2.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
pytest-xdist==3.6.1 ; python_full_version >= "3.9.2" and python_version < "3.10" pytest-xdist==3.8.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
pytest==8.3.5 ; python_full_version >= "3.9.2" and python_version < "3.10" pytest==8.4.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
python-augeas==0.5.0 ; python_full_version >= "3.9.2" and python_version < "3.10" python-augeas==0.5.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
python-dateutil==2.9.0.post0 ; python_full_version >= "3.9.2" and python_version < "3.10" python-dateutil==2.9.0.post0 ; python_full_version >= "3.9.2" and python_version < "3.10"
python-digitalocean==1.11 ; python_full_version >= "3.9.2" and python_version < "3.10" python-digitalocean==1.11 ; python_full_version >= "3.9.2" and python_version < "3.10"
pywin32==310 ; python_full_version >= "3.9.2" and python_version < "3.10" and sys_platform == "win32" pytz==2025.2 ; python_full_version >= "3.9.2" and python_version < "3.10"
pywin32==311 ; python_full_version >= "3.9.2" and python_version < "3.10" and sys_platform == "win32"
pyyaml==6.0.2 ; python_full_version >= "3.9.2" and python_version < "3.10" pyyaml==6.0.2 ; python_full_version >= "3.9.2" and python_version < "3.10"
requests-file==2.1.0 ; python_full_version >= "3.9.2" and python_version < "3.10" requests-file==2.1.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
requests==2.20.0 ; python_full_version >= "3.9.2" and python_version < "3.10" requests==2.20.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
rsa==4.9.1 ; python_full_version >= "3.9.2" and python_version < "3.10" rsa==4.9.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
ruff==0.12.8 ; python_full_version >= "3.9.2" and python_version < "3.10"
s3transfer==0.3.7 ; python_full_version >= "3.9.2" and python_version < "3.10" s3transfer==0.3.7 ; python_full_version >= "3.9.2" and python_version < "3.10"
setuptools==80.0.1 ; python_full_version >= "3.9.2" and python_version < "3.10" setuptools==80.9.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
six==1.11.0 ; python_full_version >= "3.9.2" and python_version < "3.10" six==1.11.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
soupsieve==2.7 ; python_full_version >= "3.9.2" and python_version < "3.10" soupsieve==2.7 ; python_full_version >= "3.9.2" and python_version < "3.10"
tldextract==5.3.0 ; python_full_version >= "3.9.2" and python_version < "3.10" tldextract==5.3.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
tomli==2.2.1 ; python_full_version >= "3.9.2" and python_version < "3.10" tomli==2.2.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
tomlkit==0.13.2 ; python_full_version >= "3.9.2" and python_version < "3.10" tomlkit==0.13.3 ; python_full_version >= "3.9.2" and python_version < "3.10"
tox==1.9.2 ; python_full_version >= "3.9.2" and python_version < "3.10" tox==1.9.2 ; python_full_version >= "3.9.2" and python_version < "3.10"
types-httplib2==0.22.0.20250401 ; python_full_version >= "3.9.2" and python_version < "3.10" types-httplib2==0.22.0.20250622 ; python_full_version >= "3.9.2" and python_version < "3.10"
types-pyrfc3339==2.0.1.20241107 ; python_full_version >= "3.9.2" and python_version < "3.10" types-pyrfc3339==2.0.1.20241107 ; python_full_version >= "3.9.2" and python_version < "3.10"
types-python-dateutil==2.9.0.20241206 ; python_full_version >= "3.9.2" and python_version < "3.10" types-python-dateutil==2.9.0.20250708 ; python_full_version >= "3.9.2" and python_version < "3.10"
types-pywin32==310.0.0.20250429 ; python_full_version >= "3.9.2" and python_version < "3.10" types-pywin32==311.0.0.20250801 ; python_full_version >= "3.9.2" and python_version < "3.10"
types-requests==2.31.0.6 ; python_full_version >= "3.9.2" and python_version < "3.10" types-requests==2.31.0.6 ; python_full_version >= "3.9.2" and python_version < "3.10"
types-setuptools==80.0.0.20250429 ; python_full_version >= "3.9.2" and python_version < "3.10" types-setuptools==80.9.0.20250801 ; python_full_version >= "3.9.2" and python_version < "3.10"
types-urllib3==1.26.25.14 ; python_full_version >= "3.9.2" and python_version < "3.10" types-urllib3==1.26.25.14 ; python_full_version >= "3.9.2" and python_version < "3.10"
typing-extensions==4.13.2 ; python_full_version >= "3.9.2" and python_version < "3.10" typing-extensions==4.14.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
uritemplate==3.0.1 ; python_full_version >= "3.9.2" and python_version < "3.10" uritemplate==3.0.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
urllib3==1.24.2 ; python_full_version >= "3.9.2" and python_version < "3.10" urllib3==1.24.2 ; python_full_version >= "3.9.2" and python_version < "3.10"
virtualenv==20.30.0 ; python_full_version >= "3.9.2" and python_version < "3.10" virtualenv==20.33.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
wheel==0.45.1 ; python_full_version >= "3.9.2" and python_version < "3.10" wheel==0.45.1 ; python_full_version >= "3.9.2" and python_version < "3.10"
zipp==3.21.0 ; python_full_version >= "3.9.2" and python_version < "3.10" zipp==3.23.0 ; python_full_version >= "3.9.2" and python_version < "3.10"
+16 -15
View File
@@ -7,7 +7,7 @@
# for more info. # for more info.
alabaster==0.7.16 ; python_full_version >= "3.9.2" and python_version < "3.10" alabaster==0.7.16 ; python_full_version >= "3.9.2" and python_version < "3.10"
alabaster==1.0.0 ; python_version >= "3.10" and python_version < "4.0" alabaster==1.0.0 ; python_version >= "3.10" and python_version < "4.0"
anyio==4.9.0 ; python_full_version >= "3.9.2" and python_version < "4.0" anyio==4.10.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
apacheconfig==0.3.2 ; python_full_version >= "3.9.2" and python_version < "4.0" apacheconfig==0.3.2 ; python_full_version >= "3.9.2" and python_version < "4.0"
astroid==3.3.11 ; python_full_version >= "3.9.2" and python_version < "4.0" astroid==3.3.11 ; python_full_version >= "3.9.2" and python_version < "4.0"
asttokens==3.0.0 ; python_full_version >= "3.9.2" and python_version < "4.0" asttokens==3.0.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
@@ -18,12 +18,12 @@ babel==2.17.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
backports-tarfile==1.2.0 ; python_full_version >= "3.9.2" and python_version < "3.12" backports-tarfile==1.2.0 ; python_full_version >= "3.9.2" and python_version < "3.12"
bcrypt==4.3.0 ; python_full_version >= "3.9.2" and python_version < "4.0" bcrypt==4.3.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
beautifulsoup4==4.13.4 ; python_full_version >= "3.9.2" and python_version < "4.0" beautifulsoup4==4.13.4 ; python_full_version >= "3.9.2" and python_version < "4.0"
boto3==1.39.16 ; python_full_version >= "3.9.2" and python_version < "4.0" boto3==1.40.5 ; python_full_version >= "3.9.2" and python_version < "4.0"
botocore==1.39.16 ; python_full_version >= "3.9.2" and python_version < "4.0" botocore==1.40.5 ; python_full_version >= "3.9.2" and python_version < "4.0"
build==1.2.2.post1 ; python_full_version >= "3.9.2" and python_version < "4.0" build==1.3.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
cachecontrol==0.14.3 ; python_full_version >= "3.9.2" and python_version < "4.0" cachecontrol==0.14.3 ; python_full_version >= "3.9.2" and python_version < "4.0"
cachetools==5.5.2 ; python_full_version >= "3.9.2" and python_version < "4.0" cachetools==5.5.2 ; python_full_version >= "3.9.2" and python_version < "4.0"
certifi==2025.7.14 ; python_full_version >= "3.9.2" and python_version < "4.0" certifi==2025.8.3 ; python_full_version >= "3.9.2" and python_version < "4.0"
cffi==1.17.1 ; python_full_version >= "3.9.2" and python_version < "4.0" cffi==1.17.1 ; python_full_version >= "3.9.2" and python_version < "4.0"
chardet==5.2.0 ; python_full_version >= "3.9.2" and python_version < "4.0" chardet==5.2.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
charset-normalizer==3.4.2 ; python_full_version >= "3.9.2" and python_version < "4.0" charset-normalizer==3.4.2 ; python_full_version >= "3.9.2" and python_version < "4.0"
@@ -34,9 +34,9 @@ cloudflare==2.19.4 ; python_full_version >= "3.9.2" and python_version < "4.0"
colorama==0.4.6 ; python_full_version >= "3.9.2" and python_version < "4.0" colorama==0.4.6 ; python_full_version >= "3.9.2" and python_version < "4.0"
configargparse==1.7.1 ; python_full_version >= "3.9.2" and python_version < "4.0" configargparse==1.7.1 ; python_full_version >= "3.9.2" and python_version < "4.0"
configobj==5.0.9 ; python_full_version >= "3.9.2" and python_version < "4.0" configobj==5.0.9 ; python_full_version >= "3.9.2" and python_version < "4.0"
coverage==7.10.1 ; python_full_version >= "3.9.2" and python_version < "4.0" coverage==7.10.2 ; python_full_version >= "3.9.2" and python_version < "4.0"
crashtest==0.4.1 ; python_full_version >= "3.9.2" and python_version < "4.0" crashtest==0.4.1 ; python_full_version >= "3.9.2" and python_version < "4.0"
cryptography==45.0.5 ; python_full_version >= "3.9.2" and python_version < "4.0" cryptography==45.0.6 ; python_full_version >= "3.9.2" and python_version < "4.0"
cython==0.29.37 ; python_full_version >= "3.9.2" and python_version < "4.0" cython==0.29.37 ; python_full_version >= "3.9.2" and python_version < "4.0"
decorator==5.2.1 ; python_full_version >= "3.9.2" and python_version < "4.0" decorator==5.2.1 ; python_full_version >= "3.9.2" and python_version < "4.0"
deprecated==1.2.18 ; python_full_version >= "3.9.2" and python_version < "4.0" deprecated==1.2.18 ; python_full_version >= "3.9.2" and python_version < "4.0"
@@ -55,7 +55,7 @@ fastjsonschema==2.21.1 ; python_full_version >= "3.9.2" and python_version < "4.
filelock==3.18.0 ; python_full_version >= "3.9.2" and python_version < "4.0" filelock==3.18.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
findpython==0.6.3 ; python_full_version >= "3.9.2" and python_version < "4.0" findpython==0.6.3 ; python_full_version >= "3.9.2" and python_version < "4.0"
google-api-core==2.25.1 ; python_full_version >= "3.9.2" and python_version < "4.0" google-api-core==2.25.1 ; python_full_version >= "3.9.2" and python_version < "4.0"
google-api-python-client==2.177.0 ; python_full_version >= "3.9.2" and python_version < "4.0" google-api-python-client==2.178.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
google-auth-httplib2==0.2.0 ; python_full_version >= "3.9.2" and python_version < "4.0" google-auth-httplib2==0.2.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
google-auth==2.40.3 ; python_full_version >= "3.9.2" and python_version < "4.0" google-auth==2.40.3 ; python_full_version >= "3.9.2" and python_version < "4.0"
googleapis-common-protos==1.70.0 ; python_full_version >= "3.9.2" and python_version < "4.0" googleapis-common-protos==1.70.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
@@ -103,19 +103,19 @@ mypy==1.9.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
nh3==0.3.0 ; python_full_version >= "3.9.2" and python_version < "4.0" nh3==0.3.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
oauthlib==3.3.1 ; python_full_version >= "3.9.2" and python_version < "4.0" oauthlib==3.3.1 ; python_full_version >= "3.9.2" and python_version < "4.0"
packaging==25.0 ; python_full_version >= "3.9.2" and python_version < "4.0" packaging==25.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
paramiko==3.5.1 ; python_full_version >= "3.9.2" and python_version < "4.0" paramiko==4.0.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
parsedatetime==2.6 ; python_full_version >= "3.9.2" and python_version < "4.0" parsedatetime==2.6 ; python_full_version >= "3.9.2" and python_version < "4.0"
parso==0.8.4 ; python_full_version >= "3.9.2" and python_version < "4.0" parso==0.8.4 ; python_full_version >= "3.9.2" and python_version < "4.0"
pbs-installer==2025.7.23 ; python_full_version >= "3.9.2" and python_version < "4.0" pbs-installer==2025.8.7 ; python_full_version >= "3.9.2" and python_version < "4.0"
pexpect==4.9.0 ; python_full_version >= "3.9.2" and python_version < "4.0" and (sys_platform != "win32" and sys_platform != "emscripten" or python_version < "3.10") and sys_platform != "win32" pexpect==4.9.0 ; python_full_version >= "3.9.2" and python_version < "4.0" and (sys_platform != "win32" and sys_platform != "emscripten" or python_version < "3.10") and sys_platform != "win32"
pip==25.1.1 ; python_full_version >= "3.9.2" and python_version < "4.0" pip==25.2 ; python_full_version >= "3.9.2" and python_version < "4.0"
pkginfo==1.12.1.2 ; python_full_version >= "3.9.2" and python_version < "4.0" pkginfo==1.12.1.2 ; python_full_version >= "3.9.2" and python_version < "4.0"
platformdirs==4.3.8 ; python_full_version >= "3.9.2" and python_version < "4.0" platformdirs==4.3.8 ; python_full_version >= "3.9.2" and python_version < "4.0"
pluggy==1.6.0 ; python_full_version >= "3.9.2" and python_version < "4.0" pluggy==1.6.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
ply==3.11 ; python_full_version >= "3.9.2" and python_version < "4.0" ply==3.11 ; python_full_version >= "3.9.2" and python_version < "4.0"
poetry-core==2.1.3 ; python_full_version >= "3.9.2" and python_version < "4.0" poetry-core==2.1.3 ; python_full_version >= "3.9.2" and python_version < "4.0"
poetry-plugin-export==1.9.0 ; python_full_version >= "3.9.2" and python_version < "4.0" poetry-plugin-export==1.9.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
poetry==2.1.3 ; python_full_version >= "3.9.2" and python_version < "4.0" poetry==2.1.4 ; python_full_version >= "3.9.2" and python_version < "4.0"
prompt-toolkit==3.0.51 ; python_full_version >= "3.9.2" and python_version < "4.0" prompt-toolkit==3.0.51 ; python_full_version >= "3.9.2" and python_version < "4.0"
proto-plus==1.26.1 ; python_full_version >= "3.9.2" and python_version < "4.0" proto-plus==1.26.1 ; python_full_version >= "3.9.2" and python_version < "4.0"
protobuf==6.31.1 ; python_full_version >= "3.9.2" and python_version < "4.0" protobuf==6.31.1 ; python_full_version >= "3.9.2" and python_version < "4.0"
@@ -152,6 +152,7 @@ rfc3986==2.0.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
rich==14.1.0 ; python_full_version >= "3.9.2" and python_version < "4.0" rich==14.1.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
roman-numerals-py==3.1.0 ; python_version >= "3.11" and python_version < "4.0" roman-numerals-py==3.1.0 ; python_version >= "3.11" and python_version < "4.0"
rsa==4.9.1 ; python_full_version >= "3.9.2" and python_version < "4.0" rsa==4.9.1 ; python_full_version >= "3.9.2" and python_version < "4.0"
ruff==0.12.8 ; python_full_version >= "3.9.2" and python_version < "4.0"
s3transfer==0.13.1 ; python_full_version >= "3.9.2" and python_version < "4.0" s3transfer==0.13.1 ; python_full_version >= "3.9.2" and python_version < "4.0"
secretstorage==3.3.3 ; python_full_version >= "3.9.2" and python_version < "4.0" and sys_platform == "linux" secretstorage==3.3.3 ; python_full_version >= "3.9.2" and python_version < "4.0" and sys_platform == "linux"
semantic-version==2.10.0 ; python_full_version >= "3.9.2" and python_version < "4.0" semantic-version==2.10.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
@@ -180,15 +181,15 @@ tomlkit==0.13.3 ; python_full_version >= "3.9.2" and python_version < "4.0"
towncrier==24.8.0 ; python_full_version >= "3.9.2" and python_version < "4.0" towncrier==24.8.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
tox==4.27.0 ; python_full_version >= "3.9.2" and python_version < "4.0" tox==4.27.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
traitlets==5.14.3 ; python_full_version >= "3.9.2" and python_version < "4.0" traitlets==5.14.3 ; python_full_version >= "3.9.2" and python_version < "4.0"
trove-classifiers==2025.5.9.12 ; python_full_version >= "3.9.2" and python_version < "4.0" trove-classifiers==2025.8.6.13 ; python_full_version >= "3.9.2" and python_version < "4.0"
twine==6.1.0 ; python_full_version >= "3.9.2" and python_version < "4.0" twine==6.1.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
types-httplib2==0.22.0.20250622 ; python_full_version >= "3.9.2" and python_version < "4.0" types-httplib2==0.22.0.20250622 ; python_full_version >= "3.9.2" and python_version < "4.0"
types-pyrfc3339==2.0.1.20241107 ; python_full_version >= "3.9.2" and python_version < "4.0" types-pyrfc3339==2.0.1.20241107 ; python_full_version >= "3.9.2" and python_version < "4.0"
types-python-dateutil==2.9.0.20250708 ; python_full_version >= "3.9.2" and python_version < "4.0" types-python-dateutil==2.9.0.20250708 ; python_full_version >= "3.9.2" and python_version < "4.0"
types-pywin32==311.0.0.20250728 ; python_full_version >= "3.9.2" and python_version < "4.0" types-pywin32==311.0.0.20250801 ; python_full_version >= "3.9.2" and python_version < "4.0"
types-requests==2.31.0.6 ; python_full_version >= "3.9.2" and python_version < "3.10" types-requests==2.31.0.6 ; python_full_version >= "3.9.2" and python_version < "3.10"
types-requests==2.32.4.20250611 ; python_version >= "3.10" and python_version < "4.0" types-requests==2.32.4.20250611 ; python_version >= "3.10" and python_version < "4.0"
types-setuptools==80.9.0.20250529 ; python_full_version >= "3.9.2" and python_version < "4.0" types-setuptools==80.9.0.20250801 ; python_full_version >= "3.9.2" and python_version < "4.0"
types-urllib3==1.26.25.14 ; python_full_version >= "3.9.2" and python_version < "3.10" types-urllib3==1.26.25.14 ; python_full_version >= "3.9.2" and python_version < "3.10"
typing-extensions==4.14.1 ; python_full_version >= "3.9.2" and python_version < "4.0" typing-extensions==4.14.1 ; python_full_version >= "3.9.2" and python_version < "4.0"
uritemplate==4.2.0 ; python_full_version >= "3.9.2" and python_version < "4.0" uritemplate==4.2.0 ; python_full_version >= "3.9.2" and python_version < "4.0"
+3 -1
View File
@@ -98,7 +98,9 @@ commands =
commands = {[testenv:cover]commands} commands = {[testenv:cover]commands}
[testenv:lint{,-posix}] [testenv:lint{,-posix}]
commands = python -m pylint --reports=n --rcfile=.pylintrc {[base]source_paths} commands =
python -m pylint --reports=n --rcfile=.pylintrc {[base]source_paths}
ruff check
[testenv:mypy] [testenv:mypy]
deps = deps =