Skip to content

Commit 146bf0e

Browse files
authored
MAINT: add (optional) pre-commit hook (scipy#17812)
1 parent 03c337a commit 146bf0e

14 files changed

+292
-202
lines changed

HACKING.rst.txt

+16-9
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,22 @@ tests, benchmarks, and correct code style.
8282
as it will appear online.
8383

8484
4. Code style
85-
Uniformity of style in which code is written is important to others trying
86-
to understand the code. SciPy follows the standard Python guidelines for
87-
code style, `PEP8`_. In order to check that your code conforms to PEP8,
88-
you can use the `pep8 package`_ style checker. Most IDEs and text editors
89-
have settings that can help you follow PEP8, for example by translating
90-
tabs by four spaces. Using `pyflakes`_ to check your code is also a good
91-
idea. More information is available in :ref:`pep8-scipy`.
85+
Uniform code style makes it easier for others to read your code.
86+
SciPy follows the standard Python style guideline, `PEP8`_.
87+
88+
We provide a git pre-commit hook that can check each of your commits
89+
for proper style. Install it (once) by running the following from
90+
the root of the SciPy repository::
91+
92+
cp tools/pre-commit-hook.sh .git/hooks/pre-commit
93+
94+
Alternatively, you may run the linter manually::
95+
96+
python dev.py lint
97+
98+
Most IDEs and text editors also have settings that can help you
99+
follow PEP8, for example by translating tabs by four spaces. More
100+
information is available in :ref:`pep8-scipy`.
92101

93102
A :ref:`checklist<pr-checklist>`, including these and other requirements, is
94103
available at the end of the example :ref:`development-workflow`.
@@ -259,8 +268,6 @@ improvements, and submit your first PR!
259268

260269
.. _pep8 package: https://pypi.python.org/pypi/pep8
261270

262-
.. _pyflakes: https://pypi.python.org/pypi/pyflakes
263-
264271
.. _Github help pages: https://help.github.com/articles/set-up-git/
265272

266273
.. _issue list: https://github.com/scipy/scipy/issues

azure-pipelines.yml

+2-4
Original file line numberDiff line numberDiff line change
@@ -144,19 +144,17 @@ stages:
144144
addToPath: true
145145
architecture: 'x64'
146146
- script: >-
147-
python -m pip install
148-
"flake8<6"
147+
python -m pip install ruff cython-lint
149148
displayName: 'Install tools'
150149
failOnStderr: false
151150
- script: |
152151
set -euo pipefail
153-
flake8 scipy benchmarks/benchmarks
154152
# for Travis CI we used to do: git fetch --unshallow
155153
# but apparently Azure clones are not as shallow
156154
# so does not appear to be needed to fetch maintenance
157155
# branches (which Azure logs show being checked out
158156
# in Checkout stage)
159-
python tools/lint_diff.py --branch origin/$(System.PullRequest.TargetBranch)
157+
python tools/lint.py --diff-against origin/$(System.PullRequest.TargetBranch)
160158
tools/unicode-check.py
161159
tools/check_test_name.py
162160
displayName: 'Run Lint Checks'

dev.py

+14-35
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ def task_meta(cls, **kwargs):
123123

124124
import click
125125
from click import Option, Argument
126-
from doit import task_params
127126
from doit.cmd_base import ModuleTaskLoader
128127
from doit.reporter import ZeroReporter
129128
from doit.exceptions import TaskError
@@ -269,7 +268,7 @@ def cli(ctx, **kwargs):
269268
270269
\b**python dev.py --build-dir my-build test -s stats**
271270
272-
"""
271+
""" # noqa: E501
273272
CLI.update_context(ctx, kwargs)
274273

275274

@@ -526,7 +525,7 @@ def install_project(cls, dirs, args):
526525
log_size = os.stat(log_filename).st_size
527526
if log_size > last_log_size:
528527
elapsed = datetime.datetime.now() - start_time
529-
print(" ... installation in progress ({0} "
528+
print(" ... installation in progress ({} "
530529
"elapsed)".format(elapsed))
531530
last_blip = time.time()
532531
last_log_size = log_size
@@ -539,7 +538,7 @@ def install_project(cls, dirs, args):
539538

540539
if ret != 0:
541540
if not args.show_build_log:
542-
with open(log_filename, 'r') as f:
541+
with open(log_filename) as f:
543542
print(f.read())
544543
print(f"Installation failed! ({elapsed} elapsed)")
545544
sys.exit(1)
@@ -558,7 +557,7 @@ def copy_openblas(cls, dirs):
558557
default `_distributor_init.py` file with the one
559558
we use for wheels uploaded to PyPI so that DLL gets loaded.
560559
561-
Assumes pkg-config is installed and aware of OpenBLAS.
560+
Assumes pkg-config is installed and aware of OpenBLAS.
562561
563562
The "dirs" parameter is typically a "Dirs" object with the
564563
structure as the following, say, if dev.py is run from the
@@ -652,7 +651,7 @@ class Test(Task):
652651
$ python dev.py test -s cluster -m full --durations 20
653652
$ python dev.py test -s stats -- --tb=line # `--` passes next args to pytest
654653
```
655-
"""
654+
""" # noqa: E501
656655
ctx = CONTEXT
657656

658657
verbose = Option(
@@ -889,29 +888,14 @@ def emit_cmdstr(cmd):
889888
console.print(f"{EMOJI.cmd} [cmd] {cmd}")
890889

891890

892-
@task_params([{'name': 'output_file', 'long': 'output-file', 'default': None,
893-
'help': 'Redirect report to a file'}])
894-
def task_flake8(output_file):
895-
"""Run flake8 over the code base and benchmarks."""
896-
opts = ''
897-
if output_file:
898-
opts += f'--output-file={output_file}'
899-
900-
cmd = f"flake8 {opts} scipy benchmarks/benchmarks"
901-
emit_cmdstr(f"{cmd}")
902-
return {
903-
'actions': [cmd],
904-
'doc': 'Lint scipy and benchmarks directory',
905-
}
906-
907-
908-
def task_pep8diff():
891+
def task_lint():
909892
# Lint just the diff since branching off of main using a
910893
# stricter configuration.
911-
emit_cmdstr(os.path.join('tools', 'lint_diff.py'))
894+
emit_cmdstr(os.path.join('tools', 'lint.py') + ' --diff-against main')
912895
return {
913-
'basename': 'pep8-diff',
914-
'actions': [str(Dirs().root / 'tools' / 'lint_diff.py')],
896+
'basename': 'lint',
897+
'actions': [str(Dirs().root / 'tools' / 'lint.py') +
898+
' --diff-against=main'],
915899
'doc': 'Lint only files modified since last commit (stricter rules)',
916900
}
917901

@@ -937,16 +921,11 @@ def task_check_test_name():
937921

938922
@cli.cls_cmd('lint')
939923
class Lint():
940-
""":dash: Run flake8, check PEP 8 compliance on branch diff and check for
924+
""":dash: Run linter on modified files and check for
941925
disallowed Unicode characters and possibly-invalid test names."""
942-
output_file = Option(
943-
['--output-file'], default=None, help='Redirect report to a file')
944-
945-
def run(output_file):
946-
opts = {'output_file': output_file}
926+
def run():
947927
run_doit_task({
948-
'flake8': opts,
949-
'pep8-diff': {},
928+
'lint': {},
950929
'unicode-check': {},
951930
'check-testname': {},
952931
})
@@ -1109,7 +1088,7 @@ def run(cls, pythonpath, extra_argv=None, **kwargs):
11091088
# Don't use subprocess, since we don't want to include the
11101089
# current path in PYTHONPATH.
11111090
sys.argv = extra_argv
1112-
with open(extra_argv[0], 'r') as f:
1091+
with open(extra_argv[0]) as f:
11131092
script = f.read()
11141093
sys.modules['__main__'] = new_module('__main__')
11151094
ns = dict(__name__='__main__', __file__=extra_argv[0])

doc/source/dev/contributor/pep8.rst

+18-19
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,32 @@ compliance before pushing your code:
1919
Editor |rarr| Advanced Settings. This can help you fix PEP8 issues as you
2020
write your code.
2121

22-
- SciPy makes use of special configuration files for linting with the
23-
|flake8|_ tool.
22+
- Note, however, that SciPy's linting configuration may not match
23+
that of your IDE exactly. See below on how to run the official
24+
checks.
2425

25-
- It is typically recommended to leave any existing style issues alone
26-
unless they are part of the code you're already modifying.
26+
- It is recommended to leave existing style issues alone
27+
unless they exist in files you are already modifying.
2728
This practice ensures that the codebase is gradually cleaned up
2829
without dedicating precious review time to style-only cleanups.
29-
Before sending a Pull Request, we suggest running the lint tests only
30-
for the changes you've made in your feature branch. This will mimic
31-
the continuous integration linting checks setup on GitHub.
32-
After installing ``flake8``, you can run the following check locally
33-
in the SciPy root directory to ensure your Pull Request doesn't
34-
break the Continuous Integration linting tests.
3530

36-
::
31+
- Before sending a Pull Request, run the linter on changes made in
32+
your feature branch. The checks will also be made during
33+
continuous integration, but it's quicker to catch them early.
3734

38-
python tools/lint_diff.py
35+
The easiest way to do so is to install our pre-commit hook (once)::
3936

40-
If you want to run the diff based lint tests only for specific files
41-
or directories, please consider using the ``--files`` option.
37+
cp tools/pre-commit-hook.sh .git/hooks/pre-commit
4238

43-
::
39+
This will run linting checks before each commit is made.
4440

45-
python tools/lint_diff.py --files scipy/odr/models.py scipy/ndimage
41+
Alternatively, you can run the check manually from the SciPy root directory::
42+
43+
python dev.py lint
44+
45+
You can also run the linter on specific files, using the ``--files`` option::
46+
47+
python tools/lint.py --files scipy/odr/models.py scipy/ndimage
4648

4749
- If you have existing code with a lot of PEP8 issues, consider using
4850
|autopep8|_ to automatically fix them before incorporating the code into
@@ -51,9 +53,6 @@ compliance before pushing your code:
5153
.. _PEP8: https://www.python.org/dev/peps/pep-0008/
5254
.. _enable Real-time code style analysis: https://stackoverflow.com/questions/51463223/how-to-use-pep8-module-using-spyder
5355

54-
.. |flake8| replace:: ``flake8``
55-
.. _flake8: http://flake8.pycqa.org/en/latest/
56-
5756
.. |autopep8| replace:: ``autopep8``
5857
.. _autopep8: https://pypi.org/project/autopep8/0.8/
5958

environment.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ dependencies:
3939
- pydata-sphinx-theme==0.9.0
4040
- sphinx-design
4141
# For linting
42-
- flake8 < 6.0
4342
# Some optional test dependencies
4443
- mpmath
4544
- gmpy2
@@ -49,3 +48,7 @@ dependencies:
4948
- click
5049
- doit>=0.36.0
5150
- pydevtool
51+
- pip
52+
- pip:
53+
- ruff
54+
- cython-lint

pyproject.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ dev = [
121121
"mypy",
122122
"typing_extensions",
123123
"pycodestyle",
124-
"flake8",
124+
"ruff",
125+
"cython-lint",
125126
"rich-click",
126127
"click",
127128
"doit>=0.36.0",

runtests.py

+8-10
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,23 @@
3939
EXTRA_PATH = ['/usr/lib/ccache', '/usr/lib/f90cache',
4040
'/usr/local/lib/ccache', '/usr/local/lib/f90cache']
4141

42-
# ---------------------------------------------------------------------
43-
4442

4543
if __doc__ is None:
4644
__doc__ = "Run without -OO if you want usage info"
4745
else:
4846
__doc__ = __doc__.format(**globals())
4947

5048

49+
# ---------------------------------------------------------------------
50+
# ruff: noqa E402
51+
5152
import sys
5253
import os
5354
import errno
5455
# the following multiprocessing import is necessary to prevent tests that use
5556
# multiprocessing from hanging on >= Python3.8 (macOS) using pytest. Just the
5657
# import is enough...
57-
import multiprocessing
58+
import multiprocessing # noqa: F401
5859

5960

6061
# In case we are run from the source directory, we don't want to import the
@@ -69,6 +70,7 @@
6970
import datetime
7071
from types import ModuleType as new_module # noqa: E402
7172

73+
7274
ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__)))
7375

7476

@@ -128,20 +130,16 @@ def main(argv):
128130
parser.add_argument("args", metavar="ARGS", default=[], nargs=REMAINDER,
129131
help="Arguments to pass to Nose, Python or shell")
130132
parser.add_argument("--pep8", action="store_true", default=False,
131-
help="Perform pep8 check with flake8.")
133+
help="Perform pep8 check.")
132134
parser.add_argument("--mypy", action="store_true", default=False,
133135
help="Run mypy on the codebase")
134136
parser.add_argument("--doc", action="append", nargs="?",
135137
const="html", help="Build documentation")
136138
args = parser.parse_args(argv)
137139

138140
if args.pep8:
139-
# Lint the source using the configuration in tox.ini.
140-
os.system("flake8 scipy benchmarks/benchmarks")
141-
# Lint just the diff since branching off of main using a
142-
# stricter configuration.
143-
lint_diff = os.path.join(ROOT_DIR, 'tools', 'lint_diff.py')
144-
os.system(lint_diff)
141+
linter = os.path.join(ROOT_DIR, 'tools', 'lint.py')
142+
os.system(linter + " --diff-against=main")
145143
sys.exit(0)
146144

147145
if args.mypy:

0 commit comments

Comments
 (0)