Repository: N-Wouda/ALNS (language: python)

Issue:
Adding pre-commit to the library
[pre-commit](https://pre-commit.com/) is a library for managing pre-commit git hooks. Since we already have `black`, `mypy`, `flake8`, etc., it's just a matter of installing and configuring pre-commit to make sure all linters/type checkers are run before committing. We can also add many other useful hooks, e.g. any of the [flake8 extensions](https://github.com/DmytroLitvinov/awesome-flake8-extensions), so that we have more consistent style and minimize the unimportant discussions in code reviews.

I use pre-commit in my local environment, not the least because I sometimes forget to run one of the hooks manually. What do you think of adding this to the library?


Discussion / hints:
Sounds like a useful dev-dependency to me. Would you care to setup a PR with the required config file?
Sure thing. I'll submit a PR next week.
@leonlan is this still something you want to do?
Yea, I'll submit a PR this weekend.

Fix patch:
diff --git a/.github/workflows/alns.yml b/.github/workflows/alns.yml
index b219f85..9181193 100644
--- a/.github/workflows/alns.yml
+++ b/.github/workflows/alns.yml
@@ -27,14 +27,11 @@ jobs:
           python -m pip install --upgrade pip
           pip install poetry
           poetry install
+          poetry run pre-commit install
       - name: Run pytest
         run: poetry run pytest
-      - name: Run black
-        uses: psf/black@stable
-      - name: Run flake8
-        uses: py-actions/flake8@v2
-      - name: Run mypy
-        run: poetry run mypy alns
+      - name: Run pre-commit
+        run: poetry run pre-commit run --all-files
       - uses: codecov/codecov-action@v3
   deploy:
     needs: build
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..afed64f
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,31 @@
+fail_fast: true
+
+repos:
+-   repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v3.2.0
+    hooks:
+    -   id: debug-statements
+
+-   repo: https://github.com/psf/black
+    rev: 22.3.0
+    hooks:
+    -   id: black
+        args: [--line-length=79]
+
+-   repo: https://github.com/pycqa/flake8
+    rev: 4.0.1
+    hooks:
+    -   id: flake8
+        args: [--per-file-ignores=__init__.py:F401, --extend-ignore="E203"]
+
+-   repo: https://github.com/pycqa/isort
+    rev: 5.10.1
+    hooks:
+    -   id: isort
+        args: [--case-sensitive, --profile=black, --line-length=79]
+
+-   repo: https://github.com/pre-commit/mirrors-mypy
+    rev: v0.982
+    hooks:
+    -   id: mypy
+        args: [--ignore-missing-imports]
diff --git a/alns/select/RouletteWheel.py b/alns/select/RouletteWheel.py
index 9e00fe8..a336180 100644
--- a/alns/select/RouletteWheel.py
+++ b/alns/select/RouletteWheel.py
@@ -3,8 +3,8 @@
 import numpy as np
 from numpy.random import RandomState
 
-from alns.select.OperatorSelectionScheme import OperatorSelectionScheme
 from alns.State import State
+from alns.select.OperatorSelectionScheme import OperatorSelectionScheme
 
 
 class RouletteWheel(OperatorSelectionScheme):
diff --git a/alns/select/SegmentedRouletteWheel.py b/alns/select/SegmentedRouletteWheel.py
index fabe1f0..eb61762 100644
--- a/alns/select/SegmentedRouletteWheel.py
+++ b/alns/select/SegmentedRouletteWheel.py
@@ -3,8 +3,8 @@
 
 import numpy as np
 
-from alns.select.RouletteWheel import RouletteWheel
 from alns.State import State
+from alns.select.RouletteWheel import RouletteWheel
 
 logger = logging.getLogger(__name__)
 
diff --git a/alns/stop/MaxRuntime.py b/alns/stop/MaxRuntime.py
index 76af625..6c39fd6 100644
--- a/alns/stop/MaxRuntime.py
+++ b/alns/stop/MaxRuntime.py
@@ -1,6 +1,6 @@
 import time
-
 from typing import Optional
+
 from numpy.random import RandomState
 
 from alns.State import State
diff --git a/alns/stop/NoImprovement.py b/alns/stop/NoImprovement.py
index 2c5fdb3..7562162 100644
--- a/alns/stop/NoImprovement.py
+++ b/alns/stop/NoImprovement.py
@@ -1,6 +1,7 @@
-from numpy.random import RandomState
 from typing import Optional
 
+from numpy.random import RandomState
+
 from alns.State import State
 from alns.stop.StoppingCriterion import StoppingCriterion
 
diff --git a/pyproject.toml b/pyproject.toml
index 6cc7603..73e8129 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -35,12 +35,10 @@ numpy = ">=1.15.2"
 matplotlib = ">=2.2.0"
 
 [tool.poetry.dev-dependencies]
+pre-commit = "^2.20.0"
 pytest = ">=6.0.0"
 pytest-cov = ">=2.6.1"
-mypy = ">=0.670"
 codecov = "*"
-black = "^22.3.0"
-flake8 = "^4.0.1"
 
 # These are solely for work on the example notebooks, and not required for
 # the package itself.
@@ -48,15 +46,6 @@ cvrplib = "^0.1.1"
 networkx = ">=2.4.0"
 tsplib95 = ">=0.7.0"
 
-
-
-[tool.mypy]
-# These are generally already settled anyway, so there's no real
-# need to worry about them here
-ignore_missing_imports = true
-
-
-
 [tool.pytest.ini_options]
 markers = [
     "matplotlib: test related to matplotlib functionality.",
@@ -64,8 +53,6 @@ markers = [
 
 addopts = "--cov=. --cov-report=xml"
 
-
-
 [tool.coverage.run]
 omit = [
     "*/tests/*",
@@ -81,13 +68,6 @@ exclude_lines = [
     "@abstract"
 ]
 
-
-
-[tool.black]
-line-length = 79
-
-
-
 [build-system]
 requires = ["poetry-core>=1.0.0"]
 build-backend = "poetry.core.masonry.api"
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index ed33b6c..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-[flake8]
-per-file-ignores = __init__.py:F401
-extend-ignore =
-    # See https://github.com/PyCQA/pycodestyle/issues/373
-    "E203",


Test patch:
diff --git a/alns/accept/tests/test_record_to_record_travel.py b/alns/accept/tests/test_record_to_record_travel.py
index 2b0102d..9259c0e 100644
--- a/alns/accept/tests/test_record_to_record_travel.py
+++ b/alns/accept/tests/test_record_to_record_travel.py
@@ -1,10 +1,5 @@
 import numpy.random as rnd
-from numpy.testing import (
-    assert_,
-    assert_allclose,
-    assert_equal,
-    assert_raises,
-)
+from numpy.testing import assert_, assert_allclose, assert_equal, assert_raises
 from pytest import mark
 
 from alns.accept import RecordToRecordTravel
diff --git a/alns/tests/test_alns.py b/alns/tests/test_alns.py
index 9bc8901..701152b 100644
--- a/alns/tests/test_alns.py
+++ b/alns/tests/test_alns.py
@@ -11,8 +11,8 @@
 from alns.accept import HillClimbing, SimulatedAnnealing
 from alns.select import RouletteWheel
 from alns.stop import MaxIterations, MaxRuntime
-from .states import One, VarObj, Zero
 
+from .states import One, VarObj, Zero
 
 # HELPERS ---------------------------------------------------------------------
 
diff --git a/alns/tests/test_result.py b/alns/tests/test_result.py
index 4ef448f..8e33150 100644
--- a/alns/tests/test_result.py
+++ b/alns/tests/test_result.py
@@ -5,6 +5,7 @@
 
 from alns.Result import Result
 from alns.Statistics import Statistics
+
 from .states import Sentinel
 
 try:
diff --git a/alns/tests/test_statistics.py b/alns/tests/test_statistics.py
index ea83160..a4cd5bb 100644
--- a/alns/tests/test_statistics.py
+++ b/alns/tests/test_statistics.py
@@ -1,4 +1,4 @@
-from numpy.testing import assert_equal, assert_almost_equal, assert_allclose
+from numpy.testing import assert_allclose, assert_almost_equal, assert_equal
 
 from alns.Statistics import Statistics