Repository: scikit-activeml/scikit-activeml (language: python)

Issue:
Implement automatic formatting via black
The goal is to include automatic code formatting via black, e.g.,
```
black -l 79 python_file.py
```
into the CI process of this repository.


Discussion / hints:

Fix patch:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index b5ff1ef5..00000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,36 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
-title: "[BUG]"
-labels: bug
-assignees: ''
-
----
-
-**Describe the bug**
-> A clear and concise description of what the bug is.
-
-**To Reproduce**
-> Steps to reproduce the behavior
-
-**Expected behavior**
-> A clear and concise description of what you expected to happen.
-
-**Screenshots**
-> If applicable, add screenshots to help explain your problem.
-
-**Desktop (please complete the following information):**
- - OS: [e.g. iOS]
- - Browser [e.g. chrome, safari]
- - Version [e.g. 22]
-
-**Smartphone (please complete the following information):**
- - Device: [e.g. iPhone6]
- - OS: [e.g. iOS8.1]
- - Browser [e.g. stock browser, safari]
- - Version [e.g. 22]
-
-**Additional context**
-> Add any other context about the problem here.
-> OS: [e.g. macOS, Linux]
-> Dependency versions [e.g. numpy@1.21.4]
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 00000000..d8f2f808
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,52 @@
+#### This Template was inspired by [numpy](https://github.com/numpy/numpy/blob/main/.github/ISSUE_TEMPLATE/bug-report.yml) and [scikit-learn](https://github.com/scikit-learn/scikit-learn/blob/main/.github/ISSUE_TEMPLATE/bug_report.yml)
+
+
+name: Bug report
+description: Create a report to help us improve
+title: "BUG: <Please write a comprehensive title after the 'BUG: ' prefix>"
+
+body:
+- type: markdown
+  attributes:
+    value: >
+      Thank you for taking the time to file a bug report. Before creating a new
+      issue, please make sure to take a few minutes to check the issue tracker
+      for existing issues about the bug.
+    
+- type: textarea
+  attributes: 
+    label: "Describe the issue:"
+  validations:
+    required: true
+
+- type: textarea
+  attributes:
+    label: "Reproduce the bug via a code example:"
+    description: >
+      A short code example that reproduces the problem/missing feature. It
+      should be self-contained, i.e., can be copy-pasted into the Python
+      interpreter or run as-is via `python myproblem.py`.
+    placeholder: |
+      << your code here >>
+    render: python
+  validations:
+    required: true
+  
+- type: textarea
+  attributes:
+    label: "Error message:"
+    description: >
+      Please include full error message, if any.
+    placeholder: |
+      << Full traceback starting from `Traceback: ...` >>
+    render: shell
+
+- type: textarea
+  attributes:
+    label: "Context for the issue:"
+    description: |
+      Please explain how this issue affects your work or why it should be prioritized.
+    placeholder: |
+      << your explanation here >>
+  validations:
+    required: false
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 00000000..fccd9365
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: true
+contact_links:
+  - name: Questions
+    url: https://github.com/scikit-activeml/scikit-activeml/discussions
+    about: Ask questions and discuss with other scikit-activeml community members
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml
new file mode 100644
index 00000000..bd351c03
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/documentation.yml
@@ -0,0 +1,20 @@
+#### This Template was inspired by [numpy](https://github.com/numpy/numpy/blob/main/.github/ISSUE_TEMPLATE/documentation.yml) and [scikit-learn](https://github.com/scikit-learn/scikit-learn/blob/main/.github/ISSUE_TEMPLATE/doc_improvement.yml)
+
+name: Documentation improvement
+description: Create a report to help us improve the documentation. Alternatively you can just open a pull request with the suggested change.
+labels: [Documentation, 'Needs Triage']
+title: "DOC: <Please write a comprehensive title after the 'DOC: ' prefix>"
+
+body:
+- type: textarea
+  attributes:
+    label: Describe the issue linked to the documentation
+    description: >
+      Tell us about the confusion introduced in the documentation.
+  validations:
+    required: true
+- type: textarea
+  attributes:
+    label: Suggest a potential alternative/fix
+    description: >
+      Tell us how we could improve the documentation in this regard.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index 11fc491e..00000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for this project
-title: ''
-labels: enhancement
-assignees: ''
-
----
-
-**Is your feature request related to a problem? Please describe.**
-A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-
-**Describe the solution you'd like**
-A clear and concise description of what you want to happen.
-
-**Describe alternatives you've considered**
-A clear and concise description of any alternative solutions or features you've considered.
-
-**Additional context**
-Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 00000000..d34a4a74
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,24 @@
+#### This Template was inspired by [scikit-learn](https://github.com/scikit-learn/scikit-learn/blob/main/.github/ISSUE_TEMPLATE/feature_request.yml)
+
+name: Feature request
+description: Suggest a new algorithm, enhancement to an existing algorithm, etc.
+title: "FR: <Please write a comprehensive title after the 'FR: ' prefix>"
+
+body:
+
+- type: textarea
+  attributes:
+    label: Describe the workflow you want to enable
+  validations:
+    required: true
+- type: textarea
+  attributes:
+    label: Describe your proposed solution
+  validations:
+    required: true
+- type: textarea
+  attributes:
+    label: Describe alternatives you've considered, if relevant
+- type: textarea
+  attributes:
+    label: Additional context
\ No newline at end of file
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000..394aeb7b
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,4 @@
+Closes #ISSUE_NUMBER
+
+**Additional context**
+> Add any other changes not described in the Issue here.
\ No newline at end of file
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index f1f52901..700e4e9d 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -32,6 +32,11 @@ jobs:
     - name: Install latest dependencies
       run: |
         pip install --upgrade -r requirements.txt
+    - name: Black
+      uses: psf/black@stable
+      with:
+        options: "--check --line-length 79"
+        src: "./skactiveml"
     - name: Lint with flake8
       run: |
         # stop the build if there are Python syntax errors or undefined names
diff --git a/skactiveml/base.py b/skactiveml/base.py
index 8522d9ed..fa8041d4 100644
--- a/skactiveml/base.py
+++ b/skactiveml/base.py
@@ -1399,7 +1399,6 @@ def _validate_data(
         check_y_dict=None,
         y_ensure_1d=True,
     ):
-
         if check_X_dict is None:
             check_X_dict = {"ensure_min_samples": 0, "ensure_min_features": 0}
         if check_y_dict is None:
diff --git a/skactiveml/classifier/_wrapper.py b/skactiveml/classifier/_wrapper.py
index af039ff6..213875f8 100644
--- a/skactiveml/classifier/_wrapper.py
+++ b/skactiveml/classifier/_wrapper.py
@@ -529,7 +529,6 @@ def partial_fit(self, X, y, sample_weight=None, **fit_kwargs):
             )
 
     def _add_samples(self, fit_func, X, y, sample_weight=None):
-
         if not hasattr(self, "X_train_"):
             self.X_train_ = deque(maxlen=self.window_size)
         if not hasattr(self, "y_train_"):
@@ -558,7 +557,6 @@ def _add_samples(self, fit_func, X, y, sample_weight=None):
             self.sample_weight_train_ = None
 
     def _fit(self, fit_function, X, y, sample_weight=None, **fit_kwargs):
-
         # Check whether estimator can deal with cost matrix.
         if self.cost_matrix is not None and not hasattr(
             self.estimator, "predict_proba"
diff --git a/skactiveml/pool/__init__.py b/skactiveml/pool/__init__.py
index df82772d..300dda85 100644
--- a/skactiveml/pool/__init__.py
+++ b/skactiveml/pool/__init__.py
@@ -17,9 +17,7 @@
 from ._expected_model_variance import ExpectedModelVarianceReduction
 from ._four_ds import FourDs
 from ._greedy_sampling import GreedySamplingX, GreedySamplingTarget
-from ._information_gain_maximization import (
-    KLDivergenceMaximization,
-)
+from ._information_gain_maximization import KLDivergenceMaximization
 from ._probabilistic_al import ProbabilisticAL, cost_reduction
 from ._query_by_committee import (
     QueryByCommittee,
diff --git a/skactiveml/pool/_expected_error_reduction.py b/skactiveml/pool/_expected_error_reduction.py
index f7d9886d..0e9472bd 100644
--- a/skactiveml/pool/_expected_error_reduction.py
+++ b/skactiveml/pool/_expected_error_reduction.py
@@ -298,7 +298,6 @@ def _validate_data(
         reset=True,
         check_X_dict=None,
     ):
-
         # Validate input parameters.
         (
             X,
@@ -372,7 +371,6 @@ def _estimate_error_for_candidate(
         )
 
     def _validate_cost_matrix(self, n_classes):
-
         cost_matrix = (
             1 - np.eye(n_classes)
             if self.cost_matrix is None
@@ -390,7 +388,6 @@ def _concatenate_samples(
         X_eval,
         sample_weight_eval,
     ):
-
         # Check if candidates are samples if sample_weight_candidates is set
         if (
             candidates is None or candidates.ndim == 1
@@ -895,7 +892,6 @@ def _estimate_current_error(
     def _precompute_and_fit_clf(
         self, id_clf, X_full, y_full, idx_train, idx_cand, idx_eval, fit_clf
     ):
-
         # TODO: replace the following line by more efficient code
         id_clf.precompute(
             idx_train, idx_train, fit_params="all", pred_params="all"
diff --git a/skactiveml/pool/_greedy_sampling.py b/skactiveml/pool/_greedy_sampling.py
index 4a30fe6f..b441451b 100644
--- a/skactiveml/pool/_greedy_sampling.py
+++ b/skactiveml/pool/_greedy_sampling.py
@@ -399,7 +399,6 @@ def _greedy_sampling(
     method=None,
     **kwargs,
 ):
-
     dist_dict = dict(
         X_cand=X_cand, y_cand=y_cand, X=X, y=y, method=method, **kwargs
     )
diff --git a/skactiveml/pool/multiannotator/_wrapper.py b/skactiveml/pool/multiannotator/_wrapper.py
index a856f229..da9d4210 100644
--- a/skactiveml/pool/multiannotator/_wrapper.py
+++ b/skactiveml/pool/multiannotator/_wrapper.py
@@ -376,7 +376,6 @@ def _query_annotators(
         return_utilities,
         pref_n_annotators,
     ):
-
         random_state = check_random_state(self.random_state)
 
         n_annotators = A_cand.shape[1]
@@ -424,7 +423,6 @@ def _query_annotators(
     def _get_order_preserving_s_query(
         A, candidate_utilities, annotator_utilities
     ):
-
         nan_indices = np.argwhere(np.isnan(candidate_utilities))
 
         candidate_utilities[nan_indices[:, 0], nan_indices[:, 1]] = -np.inf
@@ -448,7 +446,6 @@ def _get_order_preserving_s_query(
 
     @staticmethod
     def _n_to_assign_annotators(batch_size, A, s_indices, pref_n_annotators):
-
         n_max_annotators = np.sum(A, axis=1)
         n_max_chosen_annotators = n_max_annotators[s_indices]
         annot_per_sample = np.minimum(
diff --git a/skactiveml/regressor/_nic_kernel_regressor.py b/skactiveml/regressor/_nic_kernel_regressor.py
index 2d698ffb..b26faf89 100644
--- a/skactiveml/regressor/_nic_kernel_regressor.py
+++ b/skactiveml/regressor/_nic_kernel_regressor.py
@@ -87,7 +87,7 @@ def fit(self, X, y, sample_weight=None):
 
         X, y, sample_weight = self._validate_data(X, y, sample_weight)
         is_lbld = is_labeled(y, missing_label=self.missing_label_)
-        for (value, name) in [
+        for value, name in [
             (self.kappa_0, "self.kappa_0"),
             (self.nu_0, "self.nu_0"),
             (self.sigma_sq_0, "self.sigma_sq_0"),
@@ -141,7 +141,6 @@ def _estimate_ml_params(self, X):
         return N, mu_ml, var_ml
 
     def _estimate_update_params(self, X):
-
         if len(self.X_) != 0:
             N, mu_ml, var_ml = self._estimate_ml_params(X)
             update_params = (N, N, mu_ml, var_ml)
diff --git a/skactiveml/regressor/_wrapper.py b/skactiveml/regressor/_wrapper.py
index 69e6173a..8a3f8565 100644
--- a/skactiveml/regressor/_wrapper.py
+++ b/skactiveml/regressor/_wrapper.py
@@ -109,7 +109,6 @@ def partial_fit(self, X, y, sample_weight=None, **fit_kwargs):
         )
 
     def _fit(self, fit_function, X, y, sample_weight, **fit_kwargs):
-
         if not is_regressor(estimator=self.estimator):
             raise TypeError(
                 "'{}' must be a scikit-learn "
diff --git a/skactiveml/stream/budgetmanager/_estimated_budget_zliobaite.py b/skactiveml/stream/budgetmanager/_estimated_budget_zliobaite.py
index ed16fb21..ed0b681c 100644
--- a/skactiveml/stream/budgetmanager/_estimated_budget_zliobaite.py
+++ b/skactiveml/stream/budgetmanager/_estimated_budget_zliobaite.py
@@ -466,7 +466,6 @@ def query_by_utility(self, utilities):
             if not budget_left[-1]:
                 sample = False
             else:
-
                 eta = self.random_state_.normal(1, self.delta)
                 theta_random = tmp_theta * eta
                 sample = u < theta_random
diff --git a/skactiveml/stream/budgetmanager/_threshold_budget.py b/skactiveml/stream/budgetmanager/_threshold_budget.py
index bc813561..381a15a9 100644
--- a/skactiveml/stream/budgetmanager/_threshold_budget.py
+++ b/skactiveml/stream/budgetmanager/_threshold_budget.py
@@ -1,9 +1,7 @@
 import numpy as np
 from copy import deepcopy
 
-from skactiveml.base import (
-    BudgetManager,
-)
+from skactiveml.base import BudgetManager
 
 from skactiveml.utils import check_scalar, check_random_state
 
diff --git a/skactiveml/utils/_validation.py b/skactiveml/utils/_validation.py
index 6995baa3..1ad68ae1 100644
--- a/skactiveml/utils/_validation.py
+++ b/skactiveml/utils/_validation.py
@@ -567,7 +567,7 @@ def check_indices(indices, A, dim="adaptive", unique=True):
                 f"{indices.shape[0]}, but must be {len(dim)}"
             )
         indices = tuple(indices.T)
-        for (i, n) in enumerate(indices):
+        for i, n in enumerate(indices):
             if np.any(indices[i] >= A.shape[dim[i]]):
                 raise ValueError(
                     f"`indices[{i}]` contains index of value "
@@ -622,7 +622,6 @@ def check_type(
     wrong_index = all(not i_func(obj) for i_func in indicator_funcs)
 
     if wrong_type and wrong_value and wrong_index:
-
         error_str = f"`{name}` "
         if len(target_types) == 0 and len(target_vals) == 0:
             error_str += f" must"


Test patch:
diff --git a/skactiveml/pool/multiannotator/tests/test_interval_estimation_threshold.py b/skactiveml/pool/multiannotator/tests/test_interval_estimation_threshold.py
index e6712c99..cb1aa0c6 100644
--- a/skactiveml/pool/multiannotator/tests/test_interval_estimation_threshold.py
+++ b/skactiveml/pool/multiannotator/tests/test_interval_estimation_threshold.py
@@ -475,7 +475,6 @@ def test_query(self):
         self.assertEqual(len(query_indices), 0)
 
     def test_query_with_variant_available_annotators(self):
-
         ie_thresh = IntervalEstimationThreshold(epsilon=1.0)
         A_cand = np.array(
             [
@@ -500,7 +499,6 @@ def test_query_with_variant_available_annotators(self):
         self.assertEqual(len(query_indices), 4)
 
     def test_query_with_mapping(self):
-
         ie_thresh = IntervalEstimationThreshold()
         mapping = np.arange(2)
 
diff --git a/skactiveml/pool/multiannotator/tests/test_wrapper.py b/skactiveml/pool/multiannotator/tests/test_wrapper.py
index eab54651..bb9628af 100644
--- a/skactiveml/pool/multiannotator/tests/test_wrapper.py
+++ b/skactiveml/pool/multiannotator/tests/test_wrapper.py
@@ -123,7 +123,6 @@ def test_query_param_candidates(self):
         )
 
     def test_query_param_query_params_dict(self):
-
         uncertainty = UncertaintySampling(method="entropy")
         wrapper = SingleAnnotatorWrapper(
             uncertainty, random_state=self.random_state
@@ -586,7 +585,6 @@ def test_query_custom_annotator_general_equal_preference(self):
         )
 
     def test_query_indexed_annotator_sample_candidates(self):
-
         random = RandomSampling(self.random_state)
         wrapper = SingleAnnotatorWrapper(
             random, random_state=self.random_state
diff --git a/skactiveml/pool/tests/test_information_gain_maximization.py b/skactiveml/pool/tests/test_information_gain_maximization.py
index 6fea7213..9e7eb52e 100644
--- a/skactiveml/pool/tests/test_information_gain_maximization.py
+++ b/skactiveml/pool/tests/test_information_gain_maximization.py
@@ -4,9 +4,7 @@
 from copy import deepcopy
 from scipy.stats import norm
 from skactiveml.base import ProbabilisticRegressor
-from skactiveml.pool import (
-    KLDivergenceMaximization,
-)
+from skactiveml.pool import KLDivergenceMaximization
 from skactiveml.regressor import (
     NICKernelRegressor,
     SklearnRegressor,
diff --git a/skactiveml/pool/tests/test_utils.py b/skactiveml/pool/tests/test_utils.py
index ab38e328..37d40ac4 100644
--- a/skactiveml/pool/tests/test_utils.py
+++ b/skactiveml/pool/tests/test_utils.py
@@ -478,7 +478,6 @@ def test_partial_fit(self):
         )
 
         for BaseClf, speed_up, sample_weight, pred, enforce_unique in params:
-
             with self.subTest(
                 msg="ParzenWindowClassifier use base data",
                 BaseClf=str(BaseClf()),
@@ -521,7 +520,6 @@ def test_partial_fit(self):
                     )
 
         for BaseClf, speed_up, sample_weight, pred, enforce_unique in params:
-
             with self.subTest(
                 msg="ParzenWindowClassifier use fit data",
                 BaseClf=str(BaseClf()),
@@ -571,7 +569,6 @@ def test_partial_fit(self):
                     )
 
         for BaseClf, speed_up, sample_weight, pred, enforce_unique in params:
-
             with self.subTest(
                 msg="ParzenWindowClassifier use fit data with base clf",
                 BaseClf=str(BaseClf()),
@@ -629,7 +626,6 @@ def test_partial_fit(self):
         )
 
         for BaseClf, speed_up, sample_weight, pred in params:
-
             with self.subTest(
                 msg="NB use fit data",
                 BaseClf=str(BaseClf()),
@@ -716,7 +712,6 @@ def test_conditional_expectation_params(self):
                 )
 
     def test_conditional_expectation(self):
-
         reg = SklearnNormalRegressor(estimator=GaussianProcessRegressor())
         X_train = np.array([[0, 2, 3], [1, 3, 4], [2, 4, 5], [3, 6, 7]])
         y_train = np.array([-1, 2, 1, 4])
@@ -801,7 +796,6 @@ def setUp(self):
         self.y_pot = 5
 
     def test_update_X_y(self):
-
         X_new, y_new = _update_X_y(
             self.X, self.y, self.y_pot, X_update=self.x_pot
         )
diff --git a/skactiveml/stream/tests/test_stream.py b/skactiveml/stream/tests/test_stream.py
index ace82f34..a6e3e452 100644
--- a/skactiveml/stream/tests/test_stream.py
+++ b/skactiveml/stream/tests/test_stream.py
@@ -89,7 +89,6 @@ def _test_query_strategy(
         training_size,
         qs_name,
     ):
-
         rand = check_random_state(rand_seed)
         random_state = rand.randint(2**31 - 1)
         query_strategy = query_strategy_class(random_state=random_state)
diff --git a/skactiveml/tests/test_examples.py b/skactiveml/tests/test_examples.py
index c5dd3255..21c07772 100644
--- a/skactiveml/tests/test_examples.py
+++ b/skactiveml/tests/test_examples.py
@@ -36,7 +36,7 @@ def test_example_files(self):
         os.chdir(self.working_dir)
 
         # Execute the examples.
-        for (root, dirs, files) in os.walk(examples_path, topdown=True):
+        for root, dirs, files in os.walk(examples_path, topdown=True):
             for filename in files:
                 if filename.endswith(".py"):
                     msg = os.path.join(root, filename).replace(
@@ -53,7 +53,7 @@ def test_example_files(self):
     def test_json(self):
         # Collect all strategies for which an example exists
         strats_with_json = []
-        for (root, dirs, files) in os.walk(self.json_path, topdown=True):
+        for root, dirs, files in os.walk(self.json_path, topdown=True):
             for filename in files:
                 if not filename.endswith(".json"):
                     continue
diff --git a/skactiveml/utils/tests/test_selection.py b/skactiveml/utils/tests/test_selection.py
index 36317087..1160a92c 100644
--- a/skactiveml/utils/tests/test_selection.py
+++ b/skactiveml/utils/tests/test_selection.py
@@ -116,7 +116,6 @@ def test_simple_batch(self):
         np.testing.assert_equal((0, 2), indices.shape)
 
     def test_combine_ranking(self):
-
         self.assertRaises(
             ValueError,
             combine_ranking,
diff --git a/skactiveml/utils/tests/test_validation.py b/skactiveml/utils/tests/test_validation.py
index 588e9c15..ac7b0db1 100644
--- a/skactiveml/utils/tests/test_validation.py
+++ b/skactiveml/utils/tests/test_validation.py
@@ -285,7 +285,6 @@ def is_prime(x):
         )
 
     def test_check_callable(self):
-
         self.assertRaises(
             ValueError,
             _check_callable,