diff --git a/.circleci/config.yml b/.circleci/config.yml index 1e5832b37a7f6..e0ec9a85978f2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2.1 jobs: lint: docker: - - image: cimg/python:3.9.18 + - image: cimg/python:3.10.16 steps: - checkout - run: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e8730b679a5d6..c40c686751020 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,8 +6,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - # Ruff version. - rev: v0.5.1 + rev: v0.11.0 hooks: - id: ruff args: ["--fix", "--output-format=full"] diff --git a/build_tools/get_comment.py b/build_tools/get_comment.py index b357c68f23e3e..21b8b9483306d 100644 --- a/build_tools/get_comment.py +++ b/build_tools/get_comment.py @@ -117,7 +117,7 @@ def get_message(log_file, repo, pr_number, sha, run_id, details, versions): title="`ruff`", message=( "`ruff` detected issues. Please run " - "`ruff check --fix --output-format=full .` locally, fix the remaining " + "`ruff check --fix --output-format=full` locally, fix the remaining " "issues, and push the changes. Here you can see the detected issues. Note " f"that the installed `ruff` version is `ruff={versions['ruff']}`." ), diff --git a/build_tools/linting.sh b/build_tools/linting.sh index aefabfae7b3f5..5af5709652225 100755 --- a/build_tools/linting.sh +++ b/build_tools/linting.sh @@ -23,7 +23,7 @@ else fi echo -e "### Running ruff ###\n" -ruff check --output-format=full . +ruff check --output-format=full status=$? if [[ $status -eq 0 ]] then diff --git a/pyproject.toml b/pyproject.toml index a96c517cf840e..ace8cab9cb89e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,7 +83,7 @@ tests = [ "pandas>=1.4.0", "pytest>=7.1.2", "pytest-cov>=2.9.0", - "ruff>=0.5.1", + "ruff>=0.11.0", "black>=24.3.0", "mypy>=1.9", "pyamg>=5.0.0", @@ -126,7 +126,6 @@ exclude = ''' [tool.ruff] # max line length for black line-length = 88 -target-version = "py38" exclude=[ ".git", "__pycache__", @@ -146,7 +145,7 @@ exclude=[ preview = true # This enables us to use the explicit preview rules that we want only explicit-preview-rules = true -# all rules can be found here: https://beta.ruff.rs/docs/rules/ +# all rules can be found here: https://docs.astral.sh/ruff/rules/ select = ["E", "F", "W", "I", "CPY001", "RUF"] ignore=[ # space before : (needed for how black formats slicing) @@ -155,11 +154,11 @@ ignore=[ "E731", # do not use variables named 'l', 'O', or 'I' "E741", - # E721 is in preview (july 2024) and gives many false positives. + # E721 gives many false positives. # Use `is` and `is not` for type comparisons, or `isinstance()` for # isinstance checks "E721", - # F841 is in preview (july 2024), and we don't care much about it. + # We don't care much about F841. # Local variable ... is assigned to but never used "F841", # some RUF rules trigger too many changes diff --git a/sklearn/_min_dependencies.py b/sklearn/_min_dependencies.py index 8e0592abddd74..8ef448ab0c48c 100644 --- a/sklearn/_min_dependencies.py +++ b/sklearn/_min_dependencies.py @@ -32,7 +32,7 @@ "memory_profiler": ("0.57.0", "benchmark, docs"), "pytest": (PYTEST_MIN_VERSION, "tests"), "pytest-cov": ("2.9.0", "tests"), - "ruff": ("0.5.1", "tests"), + "ruff": ("0.11.0", "tests"), "black": ("24.3.0", "tests"), "mypy": ("1.9", "tests"), "pyamg": ("5.0.0", "tests"), diff --git a/sklearn/datasets/tests/test_base.py b/sklearn/datasets/tests/test_base.py index 8b5231f68abdd..0bf63a7c3483d 100644 --- a/sklearn/datasets/tests/test_base.py +++ b/sklearn/datasets/tests/test_base.py @@ -255,7 +255,7 @@ def test_load_diabetes_raw(): get an unscaled version when setting `scaled=False`.""" diabetes_raw = load_diabetes(scaled=False) assert diabetes_raw.data.shape == (442, 10) - assert diabetes_raw.target.size, 442 + assert diabetes_raw.target.size == 442 assert len(diabetes_raw.feature_names) == 10 assert diabetes_raw.DESCR diff --git a/sklearn/datasets/tests/test_samples_generator.py b/sklearn/datasets/tests/test_samples_generator.py index 5611f8d2d02ac..ad2021dd770ea 100644 --- a/sklearn/datasets/tests/test_samples_generator.py +++ b/sklearn/datasets/tests/test_samples_generator.py @@ -112,7 +112,7 @@ def test_make_classification_informative_features(): (2, [1 / 2] * 2, 2), (2, [3 / 4, 1 / 4], 2), (10, [1 / 3] * 3, 10), - (int(64), [1], 1), + (64, [1], 1), ]: n_classes = len(weights) n_clusters = n_classes * n_clusters_per_class diff --git a/sklearn/decomposition/tests/test_incremental_pca.py b/sklearn/decomposition/tests/test_incremental_pca.py index e12be7337cbb3..6bca13d0ad627 100644 --- a/sklearn/decomposition/tests/test_incremental_pca.py +++ b/sklearn/decomposition/tests/test_incremental_pca.py @@ -1,5 +1,6 @@ """Tests for Incremental PCA.""" +import itertools import warnings import numpy as np @@ -228,7 +229,7 @@ def test_incremental_pca_batch_signs(): ipca = IncrementalPCA(n_components=None, batch_size=batch_size).fit(X) all_components.append(ipca.components_) - for i, j in zip(all_components[:-1], all_components[1:]): + for i, j in itertools.pairwise(all_components): assert_almost_equal(np.sign(i), np.sign(j), decimal=6) @@ -265,7 +266,7 @@ def test_incremental_pca_batch_values(): ipca = IncrementalPCA(n_components=None, batch_size=batch_size).fit(X) all_components.append(ipca.components_) - for i, j in zip(all_components[:-1], all_components[1:]): + for i, j in itertools.pairwise(all_components): assert_almost_equal(i, j, decimal=1) @@ -281,7 +282,7 @@ def test_incremental_pca_batch_rank(): ipca = IncrementalPCA(n_components=20, batch_size=batch_size).fit(X) all_components.append(ipca.components_) - for components_i, components_j in zip(all_components[:-1], all_components[1:]): + for components_i, components_j in itertools.pairwise(all_components): assert_allclose_dense_sparse(components_i, components_j) @@ -300,7 +301,7 @@ def test_incremental_pca_partial_fit(): pipca = IncrementalPCA(n_components=2, batch_size=batch_size) # Add one to make sure endpoint is included batch_itr = np.arange(0, n + 1, batch_size) - for i, j in zip(batch_itr[:-1], batch_itr[1:]): + for i, j in itertools.pairwise(batch_itr): pipca.partial_fit(X[i:j, :]) assert_almost_equal(ipca.components_, pipca.components_, decimal=3) diff --git a/sklearn/ensemble/tests/test_forest.py b/sklearn/ensemble/tests/test_forest.py index aadf230fd751e..fcefa31db097c 100644 --- a/sklearn/ensemble/tests/test_forest.py +++ b/sklearn/ensemble/tests/test_forest.py @@ -926,7 +926,7 @@ def test_parallel_train(): X_test = rng.randn(n_samples, n_features) probas = [clf.predict_proba(X_test) for clf in clfs] - for proba1, proba2 in zip(probas, probas[1:]): + for proba1, proba2 in itertools.pairwise(probas): assert_array_almost_equal(proba1, proba2) diff --git a/sklearn/manifold/tests/test_spectral_embedding.py b/sklearn/manifold/tests/test_spectral_embedding.py index d63f6bd33fc96..7826fe64eede2 100644 --- a/sklearn/manifold/tests/test_spectral_embedding.py +++ b/sklearn/manifold/tests/test_spectral_embedding.py @@ -1,3 +1,4 @@ +import itertools from unittest.mock import Mock import numpy as np @@ -71,7 +72,7 @@ def test_sparse_graph_connected_component(coo_container): p = rng.permutation(n_samples) connections = [] - for start, stop in zip(boundaries[:-1], boundaries[1:]): + for start, stop in itertools.pairwise(boundaries): group = p[start:stop] # Connect all elements within the group at least once via an # arbitrary path that spans the group. @@ -91,7 +92,7 @@ def test_sparse_graph_connected_component(coo_container): affinity = coo_container((data, (row_idx, column_idx))) affinity = 0.5 * (affinity + affinity.T) - for start, stop in zip(boundaries[:-1], boundaries[1:]): + for start, stop in itertools.pairwise(boundaries): component_1 = _graph_connected_component(affinity, p[start]) component_size = stop - start assert component_1.sum() == component_size diff --git a/sklearn/model_selection/tests/test_split.py b/sklearn/model_selection/tests/test_split.py index f26c9bd2b34ff..c7af88ad2666b 100644 --- a/sklearn/model_selection/tests/test_split.py +++ b/sklearn/model_selection/tests/test_split.py @@ -756,7 +756,7 @@ def test_shuffle_split(): ss1 = ShuffleSplit(test_size=0.2, random_state=0).split(X) ss2 = ShuffleSplit(test_size=2, random_state=0).split(X) ss3 = ShuffleSplit(test_size=np.int32(2), random_state=0).split(X) - ss4 = ShuffleSplit(test_size=int(2), random_state=0).split(X) + ss4 = ShuffleSplit(test_size=2, random_state=0).split(X) for t1, t2, t3, t4 in zip(ss1, ss2, ss3, ss4): assert_array_equal(t1[0], t2[0]) assert_array_equal(t2[0], t3[0]) diff --git a/sklearn/neural_network/_multilayer_perceptron.py b/sklearn/neural_network/_multilayer_perceptron.py index 6c09ca4f804e4..b223a4173120d 100644 --- a/sklearn/neural_network/_multilayer_perceptron.py +++ b/sklearn/neural_network/_multilayer_perceptron.py @@ -5,7 +5,7 @@ import warnings from abc import ABC, abstractmethod -from itertools import chain +from itertools import chain, pairwise from numbers import Integral, Real import numpy as np @@ -491,7 +491,7 @@ def _fit(self, X, y, sample_weight=None, incremental=False): coef_grads = [ np.empty((n_fan_in_, n_fan_out_), dtype=X.dtype) - for n_fan_in_, n_fan_out_ in zip(layer_units[:-1], layer_units[1:]) + for n_fan_in_, n_fan_out_ in pairwise(layer_units) ] intercept_grads = [ diff --git a/sklearn/tree/_classes.py b/sklearn/tree/_classes.py index 53a1187ec5a50..870d7313b4fb9 100644 --- a/sklearn/tree/_classes.py +++ b/sklearn/tree/_classes.py @@ -322,12 +322,12 @@ def _fit( if isinstance(self.min_samples_leaf, numbers.Integral): min_samples_leaf = self.min_samples_leaf else: # float - min_samples_leaf = int(ceil(self.min_samples_leaf * n_samples)) + min_samples_leaf = ceil(self.min_samples_leaf * n_samples) if isinstance(self.min_samples_split, numbers.Integral): min_samples_split = self.min_samples_split else: # float - min_samples_split = int(ceil(self.min_samples_split * n_samples)) + min_samples_split = ceil(self.min_samples_split * n_samples) min_samples_split = max(2, min_samples_split) min_samples_split = max(min_samples_split, 2 * min_samples_leaf) diff --git a/sklearn/tree/tests/test_tree.py b/sklearn/tree/tests/test_tree.py index ade052cbeebcc..8348cd29e1c8e 100644 --- a/sklearn/tree/tests/test_tree.py +++ b/sklearn/tree/tests/test_tree.py @@ -8,7 +8,7 @@ import pickle import re import struct -from itertools import chain, product +from itertools import chain, pairwise, product import joblib import numpy as np @@ -1865,7 +1865,7 @@ def assert_pruning_creates_subtree(estimator_cls, X, y, pruning_path): # A pruned tree must be a subtree of the previous tree (which had a # smaller ccp_alpha) - for prev_est, next_est in zip(estimators, estimators[1:]): + for prev_est, next_est in pairwise(estimators): assert_is_subtree(prev_est.tree_, next_est.tree_) diff --git a/sklearn/utils/_tags.py b/sklearn/utils/_tags.py index c8b1623682a0c..4843a7b0035c5 100644 --- a/sklearn/utils/_tags.py +++ b/sklearn/utils/_tags.py @@ -3,7 +3,7 @@ import warnings from collections import OrderedDict from dataclasses import dataclass, field -from itertools import chain +from itertools import chain, pairwise from .fixes import _dataclass_args @@ -437,7 +437,7 @@ def get_tags(estimator) -> Tags: # inheritance sklearn_tags_diff = {} items = list(sklearn_tags_provider.items()) - for current_item, next_item in zip(items[:-1], items[1:]): + for current_item, next_item in pairwise(items): current_name, current_tags = current_item next_name, next_tags = next_item current_tags = _to_old_tags(current_tags) diff --git a/sklearn/utils/sparsefuncs.py b/sklearn/utils/sparsefuncs.py index fb29de8ad7c6e..a9f2c14035b80 100644 --- a/sklearn/utils/sparsefuncs.py +++ b/sklearn/utils/sparsefuncs.py @@ -3,6 +3,8 @@ # Authors: The scikit-learn developers # SPDX-License-Identifier: BSD-3-Clause +import itertools + import numpy as np import scipy.sparse as sp from scipy.sparse.linalg import LinearOperator @@ -704,7 +706,7 @@ def csc_median_axis_0(X): n_samples, n_features = X.shape median = np.zeros(n_features) - for f_ind, (start, end) in enumerate(zip(indptr[:-1], indptr[1:])): + for f_ind, (start, end) in enumerate(itertools.pairwise(indptr)): # Prevent modifying X in place data = np.copy(X.data[start:end]) nz = n_samples - data.size diff --git a/sklearn/utils/tests/test_extmath.py b/sklearn/utils/tests/test_extmath.py index 67851b62ea0ba..74cb47388692f 100644 --- a/sklearn/utils/tests/test_extmath.py +++ b/sklearn/utils/tests/test_extmath.py @@ -1,6 +1,8 @@ # Authors: The scikit-learn developers # SPDX-License-Identifier: BSD-3-Clause +import itertools + import numpy as np import pytest from scipy import linalg, sparse @@ -905,7 +907,7 @@ def test_incremental_variance_ddof(): if steps[-1] != X.shape[0]: steps = np.hstack([steps, n_samples]) - for i, j in zip(steps[:-1], steps[1:]): + for i, j in itertools.pairwise(steps): batch = X[i:j, :] if i == 0: incremental_means = batch.mean(axis=0)