Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MAINT: add (optional) pre-commit hook #17812

Merged
merged 19 commits into from Jan 29, 2023
Merged

Conversation

stefanv
Copy link
Member

@stefanv stefanv commented Jan 18, 2023

  • Flake8 no longer handles patches, so instead apply to entire files (see I'm temporarily disabling the issue tracker PyCQA/flake8#1760)
  • Rename tools/lint_diff.py to tools/lint.py
  • Files can be obtained either via diffing against a branch, or by specifying them manually
  • tools/pre-commit-hook.sh is a pre-commit hook that utilizes tools/lint.py but only lints staged changes
  • Update contributor guidelines to point to pre-commit hook

See also the discussion at #15489

@j-bowhay j-bowhay added the DX Everything related to making the experience of working on SciPy more pleasant label Jan 18, 2023
- Flake8 no longer handles patches, so instead apply to entire
  files (see PyCQA/flake8#1760)
- Rename `tools/lint_diff.py` to `tools/lint.py`
- Files can be obtained either via diffing against a branch,
  or by specifying them manually
- `tools/pre-commit-hook.sh` is a pre-commit hook that utilizes
  `tools/lint.py` but only lints staged changes
- Update contributor guidelines to point to pre-commit hook

See also the discussion at scipy#15489
- Removed the `output-file` option, which was not applied consistently
  across linting tools.
- Hard-coded main branch as `main`: perhaps some logic to ensure that
  the branch is the latest master and up to date is warranted in a
  future PR?
@stefanv
Copy link
Member Author

stefanv commented Jan 18, 2023

Also updated dev.py to keep on working.

@stefanv
Copy link
Member Author

stefanv commented Jan 18, 2023

Failures seem unrelated.

@j-bowhay
Copy link
Member

j-bowhay commented Jan 18, 2023

I am not sure about this change specific part. I tried your changes locally and it got a bunch of unrelated lint errors:

Details
python tools/lint.py --diff-against main                                                                                                                                                                          ✔  scipy-dev  
scipy/integrate/_quadrature.py:289:5: E741 ambiguous variable name 'l'
scipy/integrate/_quadrature.py:835:80: E501 line too long (82 > 79 characters)
scipy/integrate/_quadrature.py:890:80: E501 line too long (103 > 79 characters)
scipy/integrate/_quadrature.py:906:10: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:906:12: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:906:15: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:906:18: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:906:21: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:907:10: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:907:12: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:907:15: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:907:17: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:907:20: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:907:23: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:908:10: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:908:12: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:908:15: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:908:17: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:908:19: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:908:22: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:908:25: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:909:10: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:909:13: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:909:16: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:909:19: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:909:22: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:909:25: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:909:28: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:909:31: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:910:10: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:910:14: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:910:18: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:910:21: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:910:24: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:910:27: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:910:30: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:910:34: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:910:39: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:911:10: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:911:14: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:911:18: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:911:22: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:911:25: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:911:29: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:911:32: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:911:36: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:911:40: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:911:43: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:912:10: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:912:16: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:912:21: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:912:26: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:912:31: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:912:36: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:912:41: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:912:46: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:912:51: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:912:56: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:912:62: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:913:10: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:913:16: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:913:21: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:913:26: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:913:31: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:913:37: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:913:43: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:913:49: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:913:54: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:913:59: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:914:14: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:915:10: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:915:16: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:915:22: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:915:28: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:915:33: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:915:39: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:915:44: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:915:49: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:915:55: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:916:23: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:917:11: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:917:18: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:917:25: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:917:32: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:917:39: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:917:46: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:917:54: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:918:27: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:918:34: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:918:41: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:918:48: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:920:12: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:920:21: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:920:30: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:920:39: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:920:58: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:921:31: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:921:40: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:921:49: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:921:58: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:922:31: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:923:30: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:923:38: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:923:47: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:923:56: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:924:31: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:924:41: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:924:50: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:924:60: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:925:31: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:925:39: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:926:26: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:927:40: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:927:54: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:928:40: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:928:53: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:929:41: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:929:54: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:930:39: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:932:34: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:932:44: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:932:55: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:933:37: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:933:49: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:933:62: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:934:38: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:934:50: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:934:62: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:935:36: E231 missing whitespace after ','
scipy/integrate/_quadrature.py:935:46: E231 missing whitespace after ','
scipy/stats/_continuous_distns.py:224:80: E501 line too long (84 > 79 characters)
scipy/stats/_continuous_distns.py:282:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:528:80: E501 line too long (82 > 79 characters)
scipy/stats/_continuous_distns.py:1010:80: E501 line too long (80 > 79 characters)
scipy/stats/_continuous_distns.py:1011:80: E501 line too long (83 > 79 characters)
scipy/stats/_continuous_distns.py:1021:20: E128 continuation line under-indented for visual indent
scipy/stats/_continuous_distns.py:1022:20: E128 continuation line under-indented for visual indent
scipy/stats/_continuous_distns.py:1022:22: E251 unexpected spaces around keyword / parameter equals
scipy/stats/_continuous_distns.py:1022:24: E251 unexpected spaces around keyword / parameter equals
scipy/stats/_continuous_distns.py:1031:80: E501 line too long (81 > 79 characters)
scipy/stats/_continuous_distns.py:1033:15: E251 unexpected spaces around keyword / parameter equals
scipy/stats/_continuous_distns.py:1033:17: E251 unexpected spaces around keyword / parameter equals
scipy/stats/_continuous_distns.py:1056:39: E231 missing whitespace after ','
scipy/stats/_continuous_distns.py:1057:9: E265 block comment should start with '# '
scipy/stats/_continuous_distns.py:1065:80: E501 line too long (94 > 79 characters)
scipy/stats/_continuous_distns.py:1127:80: E501 line too long (88 > 79 characters)
scipy/stats/_continuous_distns.py:1145:80: E501 line too long (82 > 79 characters)
scipy/stats/_continuous_distns.py:1349:9: E741 ambiguous variable name 'l'
scipy/stats/_continuous_distns.py:1425:80: E501 line too long (82 > 79 characters)
scipy/stats/_continuous_distns.py:1502:80: E501 line too long (89 > 79 characters)
scipy/stats/_continuous_distns.py:2176:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:2177:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:2179:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:2180:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:2182:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:2735:39: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:2742:39: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:2926:9: E731 do not assign a lambda expression, use a def
scipy/stats/_continuous_distns.py:2933:80: E501 line too long (86 > 79 characters)
scipy/stats/_continuous_distns.py:2999:5: E731 do not assign a lambda expression, use a def
scipy/stats/_continuous_distns.py:3021:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:3023:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:3024:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:3025:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:3057:80: E501 line too long (94 > 79 characters)
scipy/stats/_continuous_distns.py:3170:17: E731 do not assign a lambda expression, use a def
scipy/stats/_continuous_distns.py:4533:17: E731 do not assign a lambda expression, use a def
scipy/stats/_continuous_distns.py:4544:17: E731 do not assign a lambda expression, use a def
scipy/stats/_continuous_distns.py:5307:80: E501 line too long (81 > 79 characters)
scipy/stats/_continuous_distns.py:5641:80: E501 line too long (91 > 79 characters)
scipy/stats/_continuous_distns.py:6096:30: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:6098:30: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:6109:30: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:6111:30: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:6475:80: E501 line too long (97 > 79 characters)
scipy/stats/_continuous_distns.py:6667:80: E501 line too long (82 > 79 characters)
scipy/stats/_continuous_distns.py:6926:80: E501 line too long (82 > 79 characters)
scipy/stats/_continuous_distns.py:8433:29: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:8443:29: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:8562:80: E501 line too long (80 > 79 characters)
scipy/stats/_continuous_distns.py:9779:80: E501 line too long (84 > 79 characters)
scipy/stats/_continuous_distns.py:10052:80: E501 line too long (84 > 79 characters)
scipy/stats/tests/test_distributions.py:465:1: E302 expected 2 blank lines, found 1
scipy/stats/tests/test_distributions.py:3595:1: E302 expected 2 blank lines, found 1
scipy/stats/tests/test_distributions.py:7643:80: E501 line too long (80 > 79 characters)
scipy/stats/tests/test_distributions.py:7644:80: E501 line too long (81 > 79 characters)
scipy/stats/tests/test_distributions.py:7646:80: E501 line too long (80 > 79 characters)
scipy/stats/tests/test_distributions.py:7648:80: E501 line too long (80 > 79 characters)

Currently we just have the flake8 version pinned at flake8 < 6.0 so to avoid the removal of --diff

@ilayn
Copy link
Member

ilayn commented Jan 19, 2023

Not to influence this PR but many are already switching to https://github.com/charliermarsh/ruff. Just saying in case flake8 starts to lag behind.

@stefanv
Copy link
Member Author

stefanv commented Jan 19, 2023

I am not sure about this change specific part. I tried your changes locally and it got a bunch of unrelated lint errors:

Where is your main branch at? If it's not up to date, you'll see this. That was one of the question I had in the commit message.

@j-bowhay
Copy link
Member

j-bowhay commented Jan 19, 2023

I am not sure about this change specific part. I tried your changes locally and it got a bunch of unrelated lint errors:

Where is your main branch at? If it's not up to date, you'll see this. That was one of the question I had in the commit message.

Apologies yes you are right about this specific part.

However in the case that I make an edit to a file, I then see all the failures for that whole file which obscure any failures that I introduce. For example, if I make a bad edit in scipy/stats/_continuous_distns.py. On main I get just those failures relating to my bad edit:

python tools/lint_diff.py                                                                                                                                                                                                
scipy/stats/_continuous_distns.py:4421:56: E261 at least two spaces before inline comment
scipy/stats/_continuous_distns.py:4421:56: E262 inline comment should start with '# '

Whereas on this branch I get all the failures in that file which obscures my bad change:

python tools/lint.py --diff-against main                                                                                                                                                                      
scipy/stats/_continuous_distns.py:224:80: E501 line too long (84 > 79 characters)
scipy/stats/_continuous_distns.py:282:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:528:80: E501 line too long (82 > 79 characters)
scipy/stats/_continuous_distns.py:1010:80: E501 line too long (80 > 79 characters)
scipy/stats/_continuous_distns.py:1011:80: E501 line too long (83 > 79 characters)
scipy/stats/_continuous_distns.py:1021:20: E128 continuation line under-indented for visual indent
scipy/stats/_continuous_distns.py:1022:20: E128 continuation line under-indented for visual indent
scipy/stats/_continuous_distns.py:1022:22: E251 unexpected spaces around keyword / parameter equals
scipy/stats/_continuous_distns.py:1022:24: E251 unexpected spaces around keyword / parameter equals
scipy/stats/_continuous_distns.py:1031:80: E501 line too long (81 > 79 characters)
scipy/stats/_continuous_distns.py:1033:15: E251 unexpected spaces around keyword / parameter equals
scipy/stats/_continuous_distns.py:1033:17: E251 unexpected spaces around keyword / parameter equals
scipy/stats/_continuous_distns.py:1056:39: E231 missing whitespace after ','
scipy/stats/_continuous_distns.py:1057:9: E265 block comment should start with '# '
scipy/stats/_continuous_distns.py:1065:80: E501 line too long (94 > 79 characters)
scipy/stats/_continuous_distns.py:1127:80: E501 line too long (88 > 79 characters)
scipy/stats/_continuous_distns.py:1145:80: E501 line too long (82 > 79 characters)
scipy/stats/_continuous_distns.py:1349:9: E741 ambiguous variable name 'l'
scipy/stats/_continuous_distns.py:1425:80: E501 line too long (82 > 79 characters)
scipy/stats/_continuous_distns.py:1502:80: E501 line too long (89 > 79 characters)
scipy/stats/_continuous_distns.py:2176:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:2177:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:2179:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:2180:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:2182:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:2735:39: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:2742:39: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:2926:9: E731 do not assign a lambda expression, use a def
scipy/stats/_continuous_distns.py:2933:80: E501 line too long (86 > 79 characters)
scipy/stats/_continuous_distns.py:2999:5: E731 do not assign a lambda expression, use a def
scipy/stats/_continuous_distns.py:3021:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:3023:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:3024:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:3025:1: E266 too many leading '#' for block comment
scipy/stats/_continuous_distns.py:3057:80: E501 line too long (94 > 79 characters)
scipy/stats/_continuous_distns.py:3170:17: E731 do not assign a lambda expression, use a def
scipy/stats/_continuous_distns.py:4421:56: E261 at least two spaces before inline comment
scipy/stats/_continuous_distns.py:4421:56: E262 inline comment should start with '# '
scipy/stats/_continuous_distns.py:4533:17: E731 do not assign a lambda expression, use a def
scipy/stats/_continuous_distns.py:4544:17: E731 do not assign a lambda expression, use a def
scipy/stats/_continuous_distns.py:5307:80: E501 line too long (81 > 79 characters)
scipy/stats/_continuous_distns.py:5641:80: E501 line too long (91 > 79 characters)
scipy/stats/_continuous_distns.py:6096:30: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:6098:30: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:6109:30: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:6111:30: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:6475:80: E501 line too long (97 > 79 characters)
scipy/stats/_continuous_distns.py:6667:80: E501 line too long (82 > 79 characters)
scipy/stats/_continuous_distns.py:6926:80: E501 line too long (82 > 79 characters)
scipy/stats/_continuous_distns.py:8433:29: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:8443:29: E127 continuation line over-indented for visual indent
scipy/stats/_continuous_distns.py:8562:80: E501 line too long (80 > 79 characters)
scipy/stats/_continuous_distns.py:9779:80: E501 line too long (84 > 79 characters)
scipy/stats/_continuous_distns.py:10052:80: E501 line too long (84 > 79 characters)

@rgommers
Copy link
Member

Not to influence this PR but many are already switching to https://github.com/charliermarsh/ruff. Just saying in case flake8 starts to lag behind.

Also, to lint Cython files you now need https://github.com/kmaehashi/flake8-force.

@stefanv
Copy link
Member Author

stefanv commented Jan 19, 2023

However in the case that I make an edit to a file, I then see all the failures for that whole file which obscure any failures that I introduce.

This is intended; please see the commit messages: flake8 no longer supports diffs.

@j-bowhay
Copy link
Member

j-bowhay commented Jan 19, 2023

However in the case that I make an edit to a file, I then see all the failures for that whole file which obscure any failures that I introduce.

This is intended; please see the commit messages: flake8 no longer supports diffs.

Sorry I am not articulating my point well. I understand that as of 6.0.0 diffs are no longer supported. I actually raised an issue about it a while ago #17503. At the time it was decided to pin the version to < 6.0.0 in the CI job and in the environment.yml as a good enough solution (for now at least). What I am asking is has anything changed enough that this is no longer an acceptable solution? In my mind there is a trade off, either:

  1. we can have newer versions of flake8 but accept we cant have diffs so will see lots of unrelated failures
  2. we are stuck with older versions of flake8 but we can use diff to have cleaner outputs.

At least in my mind we haven't reached the point where 1 is the better option but interested to hear your thoughts.

@stefanv
Copy link
Member Author

stefanv commented Jan 19, 2023

Where is your main branch at? If it's not up to date, you'll see this. That was one of the question I had in the commit message.

FWIW, this is not a problem with the pre-commit hook, because it just scans whatever files were added to the index.

@stefanv
Copy link
Member Author

stefanv commented Jan 19, 2023

  1. we can have newer versions of flake8 but accept we cant have diffs so will see lots of unrelated failures

    1. we are stuck with older versions of flake8 but we can use diff to have cleaner outputs.

At least in my mind we haven't reached the point where 1 is the better option but interest to hear your thoughts.

I know people are irate with the flake8 developer, and I understand why. That said, it is worth reading what he wrote on the topic: operating on diffs are unreliable, so on a fairly fundamental level flake8 cannot guarantee accurate results when doing so.

A second argument in favor for operating on whole files is that we will much sooner get to a place where the entire SciPy is cleaned up. This will come at some initial annoyance to reviewers, but it won't last long. If helpful, I am also happy to make a single linting commit that we can place in .git-blame-ignore-revs that sorts this out once and for all.

In my opinion, it's not worth clinging on to diff linting, since no tool is likely to support it well.

@stefanv
Copy link
Member Author

stefanv commented Jan 19, 2023

Also, to lint Cython files you now need https://github.com/kmaehashi/flake8-force.

Implemented. I'd appreciate if someone could check whether I got the conda environments.yml file right.

@rgommers
Copy link
Member

Implemented. I'd appreciate if someone could check whether I got the conda environments.yml file right.

It works, but shows this message:

$ mamba env create -f environment.yml 
Warning: you have pip-installed dependencies in your environment file, but you do not list pip itself as one of your conda dependencies.  Conda may not use the correct pip to install your packages, and they may end up in the wrong place.  Please add an explicit pip dependency.  I'm adding one for you, but still nagging you.

If you add pip as a dependency right above where it's used in environment.yml, that will fix it.

HACKING.rst.txt Outdated
for proper style. Install it (once) by running the following from
the root of the SciPy repository::

ln -s tools/pre-commit-hook.sh .git/hooks/pre-commit
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work on Windows, because there is no support for symlinks on Windows.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can use copy instead, thanks.

Copy link
Member

@rgommers rgommers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If helpful, I am also happy to make a single linting commit that we can place in .git-blame-ignore-revs that sorts this out once and for all.

This seems fine, or continuing to pin is fine too. I'm going to stay out of this one. This PR LGTM (modulo my comments) from a security and it being optional point of view. So I'll approve based on that. I didn't look at flake8 behavior.

@stefanv
Copy link
Member Author

stefanv commented Jan 20, 2023

@j-bowhay I thought about the behavior you saw some more: it doesn't seem right that the files affected should depend on whether the main branch had been updated, but I don't know how to identify the branching point in that case either. Any ideas? E.g., we cannot know the name of the remote (origin or upstream is typical), and not everyone sets up feature branches to track main.

With the pre-commit hook its easy, because we just look at what has been staged for commit.

@tupui
Copy link
Member

tupui commented Jan 24, 2023

@stefanv anything else you wanted to check? Happy to get this in and see how it goes.

(Ruff is really getting a lot of traction, tons of big projects are starting to use it right now.)

@j-bowhay
Copy link
Member

@j-bowhay I thought about the behavior you saw some more: it doesn't seem right that the files affected should depend on whether the main branch had been updated, but I don't know how to identify the branching point in that case either. Any ideas? E.g., we cannot know the name of the remote (origin or upstream is typical), and not everyone sets up feature branches to track main.

With the pre-commit hook its easy, because we just look at what has been staged for commit.

Unfortunately not sure I have any clever suggestions on how to overcome this. Maybe just a documentation or a warning saying to check it's up to date?

PS. I wasn't sure if this was intentional or not but flake8 is pinned to an old version in the CI job where as in other places you have unpinned it.

@stefanv
Copy link
Member Author

stefanv commented Jan 24, 2023

PS. I wasn't sure if this was intentional or not but flake8 is pinned to an old version in the CI job where as in other places you have unpinned it.

Thanks for letting me know. I looked now, but I don't see it; which CI job do you see it on?

@j-bowhay
Copy link
Member

PS. I wasn't sure if this was intentional or not but flake8 is pinned to an old version in the CI job where as in other places you have unpinned it.

Thanks for letting me know. I looked now, but I don't see it; which CI job do you see it on?

"flake8<6"

@tupui
Copy link
Member

tupui commented Jan 26, 2023

The issue I think is that Black does not look at docstrings and won't help for the line too longs there, which I think represent the majority due to urls and other things we cannot break.

@MarcoGorelli
Copy link
Contributor

Maybe we should get the pre-commit hook where we want it to be, and then take one module at a time.

In case it's useful, for reference, this is how we usually do it in pandas if applying something to the whole codebase in one go is too hard:

  1. make a list of excluded files / folders
  2. start running it immediately in CI
  3. as folders / files are fixed up, they're removed from the exclusions list (e.g. STYLE #49656: fixed redefined-outer-name linting issue to format.py  pandas-dev/pandas#49937)

I think it's important to get it running in CI as soon as possible, otherwise it hangs around forever. And if there's a list of exclusions, then it kind of gamifies it and contributors enjoy seeing the list reduce as they make PRs

@charliermarsh
Copy link
Contributor

So, if I calculated correctly, we are missing the following rules...

On first glance, that list looks right to me. If any are particularly impactful for SciPy, I'd consider adding support for them! Although the tradeoff is that it could lead to significantly more existing errors, if I'm understanding the discussion correctly.

If helpful... please let me know if there's anything I can do from the Ruff side to help you here. We don't support diffs either (there's been some discussion of various error suppressions systems here, but it's not being actively worked on right now), but it'd obviously be my pleasure to help support SciPy.

(You should also feel free to ignore me completely and, of course, to make whatever decision is best for the project.)

@stefanv
Copy link
Member Author

stefanv commented Jan 26, 2023

On first glance, that list looks right to me. If any are particularly impactful for SciPy, I'd consider adding support for them! Although the tradeoff is that it could lead to significantly more existing errors, if I'm understanding the discussion correctly.

Thanks for chiming in here, @charliermarsh, it's good to have you in the discussion! I think the question we need to answer, broadly, is "how does ruff relate to projects that do not use black as an auto-formatter".

And, FWIW, I think the diff approach is not very necessary. It's complex, and ultimately does not add much once you've linted the whole codebase.

@charliermarsh
Copy link
Contributor

I think the question we need to answer, broadly, is "how does ruff relate to projects that do not use black as an auto-formatter".

Although Ruff doesn't support plugins, it is meant to be configured and tailored to each individual projects. We implement rules that are derived from a bunch of different flake8 plugins, but it's not the intention of Ruff that any given project should enable all rules; rather, that they enable the rules that make sense for that project. (Not all projects want to lint docstrings, not all projects have type annotations enabled, etc.)

This is all to say: just because Ruff is designed to work with Black, doesn't mean the rule set has to assume you use Black. And it would be fair game to add rules to Ruff that are made redundant by Black. The omission of those rules is more related to prioritization than anything else.

More succinctly: I'd like Ruff to be useful to projects that don't use Black, even if it means adding rules that aren't useful to the projects that do. (But, again, it's all a matter of prioritization :))

@stefanv
Copy link
Member Author

stefanv commented Jan 26, 2023

Thanks @charliermarsh, that makes sense. I am aware of the config porting tool, but it may be worth extending that tool to help projects that migrate so they know which rules they use and which they leave behind. I did that calculation yesterday here, and it wasn't too hard to implement, but the result is highly informative.

@tylerjereddy
Copy link
Contributor

@stefanv are you leaning toward a ruff.toml exclusion directive at this point, so that we can uncouple formatting changes from "regular" PRs? (given that the full-reformatting PR is now impractical?) I recall some old rule about not making individual formatting-only PRs, but perhaps if they are scoped to 1 per submodule people may not get too annoyed with the volume relative to coupling small contributions to reformats over a longer period of time.

We could run black on all offending files, but not sure everyone will be happy with that level of intervention.

I'm pretty sure that particular one is a no-go with SciPy team, it causes too much friction for now I think re: some of the changes black makes. I heard there was some debate on the broader community discourse about black changes/plugin for scientific code, but don't know where that stands. Could we avoid the black changes completely per the original plan or is that more problematic now somehow?

@WarrenWeckesser is usually opinionated on formatting decisions too, wonder if he missed this one.

@stefanv
Copy link
Member Author

stefanv commented Jan 27, 2023

@tylerjereddy I think I figured out a path forward. If we disable E501 (long lines) and run autopep8 on the code base, only 307 errors remain. That feels doable to me.

While we're at it, we may want to consider also running pyupgrade --py38-plus.

@tupui
Copy link
Member

tupui commented Jan 27, 2023

+1 although pyupgrade being from the same maintainer of flake8, I am not sure about having it in the CI. But nothing wrong with running it once.

@charliermarsh
Copy link
Contributor

If helpful, Ruff implements a good chunk of pyupgrade under the UP error codes (but not the entire thing -- happy to enumerate the rules we're missing, it's mostly 2-to-3 compatibility stuff).

@tupui
Copy link
Member

tupui commented Jan 27, 2023

Oh that's good to know, thank you for the precision. Then I personally would prefer that we just stick with Ruff. And if we think we are missing something, we raise this on Ruff's side.

@stefanv
Copy link
Member Author

stefanv commented Jan 27, 2023

Thanks @charliermarsh! Great, if I add the UP rules, then after auto-fixing we are left with 338 errors. Again, that is doable to fix by hand.

As an experiment, I enabled the "D" rules, but I get a lot of:

warning: Failed to converge after 100 iterations.

Adding isort ("I"), all errors are auto-fixed, so still 338 to do by hand. I quite like isort, but I think most devs just find that annoying.

pep-naming ("N") increases errors a lot; we have a lot of matrices like X and Y, and it wants those to be lowercase. That's a no.

Blind exceptions ("BLE") (by itself) takes us up to 395.

Bugbear ("B") (by itself) takes us up to 489.

flake8-builtins ("A") (by itself) takes us up to 853.

flake8-commas ("COM") (by itself) takes us up to 346.

flake8-comprehension ("C4") (by itself) takes us up to 352.

Anyway, you get the gist. We can browse the list of rules and see which ones we'd like to include. Avoid pedantic ones and include helpful ones, preferably.

@stefanv
Copy link
Member Author

stefanv commented Jan 27, 2023

For now, I added UP and suppressed E501.

@charliermarsh
Copy link
Contributor

As an experiment, I enabled the "D" rules, but I get a lot of...

(Without trying it myself, I'm guessing that it's because the D rules contain some pairs of rules that conflict based on convention. E.g., there's a rule that requires your one blank line before a class docstring, and a rule that requires zero blank lines before a class docstring, so if you enable all of D, they try to fix each other until we hit the limit. There should be a warning when you enable those two codes, but if you have a lot of violations, I'm guessing it'd be really easy to miss it at the top of the output. I recommend setting:

[tool.ruff.pydocstyle]
convention = "google"  # Or "numpy", or "pep-257"

...which will automatically give you a consistent set of rules.)

@stefanv
Copy link
Member Author

stefanv commented Jan 28, 2023

Thanks to @charliermarsh's advice, I could activate the D rules in numpy mode. Our docstrings are in "bad" shape, linting wise: 1544 violations!

This is for after the clean-up commit
scipy#17878 is merged.
@stefanv stefanv mentioned this pull request Jan 28, 2023
@tupui tupui added this to the 1.11.0 milestone Jan 29, 2023
@tupui tupui changed the title Add (optional) pre-commit hook MAINT: add (optional) pre-commit hook Jan 29, 2023
@tupui tupui merged commit 146bf0e into scipy:main Jan 29, 2023
@tupui
Copy link
Member

tupui commented Jan 29, 2023

I am merging this now so we don't lose all the great work of everyone here. Time is of essence here. Thank you all!

@WarrenWeckesser
Copy link
Member

@tylerjereddy wrote

@WarrenWeckesser is usually opinionated on formatting decisions too, wonder if he missed this one.

Sorry, I haven't been following all PRs in real time. But if the end result is stricter automated compliance checks for code formatting, then I'm all for it.

@tupui
Copy link
Member

tupui commented Jan 31, 2023

@MarcoGorelli @charliermarsh would you be willing to put your packages on conda-forge? That would allow us to stay consistent with our stack (either pip or conda).

I can do it and ping you if wanted.

@MarcoGorelli
Copy link
Contributor

looks like someone's put it on https://anaconda.org/conda-forge/cython-lint

ruff's there too https://anaconda.org/conda-forge/ruff

@tupui
Copy link
Member

tupui commented Jan 31, 2023

oh thanks! That was fast 😅

@charliermarsh
Copy link
Contributor

Yup we're on conda-forge! The last few builds have been failing for reasons that we're still trying to debug, so it's a bit behind, but that's known.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DX Everything related to making the experience of working on SciPy more pleasant
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants