Compare commits

...

264 Commits

Author SHA1 Message Date
Charlie Marsh
48e3c046b0 Fix integration tests 2022-12-21 21:25:37 -05:00
Charlie Marsh
03e4f5be8a Bump version to 0.0.191 2022-12-21 21:16:21 -05:00
Charlie Marsh
99657b7d92 Implement E401 ("multiple imports on one line") (#1326) 2022-12-21 21:15:57 -05:00
Charlie Marsh
40377aa1fc Move number of errors to the bottom of the output summary (#1325) 2022-12-21 21:04:26 -05:00
Charlie Marsh
2a37017e8c Add src to Settings hash 2022-12-21 21:01:20 -05:00
Charlie Marsh
ff66d08cef Run generate-options 2022-12-21 20:58:14 -05:00
Charlie Marsh
dad8035eef Support shell expansion in src field (#1324) 2022-12-21 20:57:20 -05:00
Charlie Marsh
bf5fec342c Support shell expansion in extend paths (#1323) 2022-12-21 20:46:38 -05:00
Charlie Marsh
66a6c81ebf Infer package roots when running via stdin (#1321) 2022-12-21 20:30:10 -05:00
Charlie Marsh
5c70f5044b Improve debug logging in flake8-to-ruff (#1320) 2022-12-21 20:05:48 -05:00
Charlie Marsh
953d141ab2 Support code redirects in flake8-to-ruff (#1318) 2022-12-21 19:31:20 -05:00
Charlie Marsh
07dba46039 Extract line length from pyproject.toml Black section (#1317) 2022-12-21 19:05:18 -05:00
Ran Benita
3b02da9d7b Fix false positive DTZ001 on datetime(2000, 1, 1, 0, 0, 0, 0, utc) (#1308) 2022-12-21 19:03:36 -05:00
Charlie Marsh
20234c6156 Bump version to 0.0.190 2022-12-21 16:01:48 -05:00
Charlie Marsh
de767cc026 Avoid used-prior-global-declaration false-positives in f-strings (#1314) 2022-12-21 14:34:09 -05:00
Charlie Marsh
ce1663d302 Allow overriding cache location via RUFF_CACHE_DIR (#1312) 2022-12-21 14:24:10 -05:00
Charlie Marsh
f40e4bcd14 Avoid flagging RUF100 as a RUF100 violation (#1305) 2022-12-20 17:40:10 -05:00
Charlie Marsh
e7d40d435f Avoid F821 false positives for Mypy extensions (#1304) 2022-12-20 16:29:33 -05:00
Charlie Marsh
ef8fe31c0c Bump version to 0.0.189 2022-12-20 13:26:17 -05:00
Charlie Marsh
226f682c99 Avoid DTZ007 false-positives for non-string arguments (#1300) 2022-12-20 13:20:53 -05:00
Hannes Käufler
468ffd29fb [Stylistic/non-functional] Use an r# format string to make json easier to read (#1299) 2022-12-20 12:55:21 -05:00
Charlie Marsh
a61126ab23 Run generate-options 2022-12-19 23:19:05 -05:00
Charlie Marsh
54c7c25861 Revert test changes to setup.py 2022-12-19 20:09:47 -05:00
Charlie Marsh
eff7700d92 Add --force-exclude setting to force exclusions with pre-commit (#1295) 2022-12-19 20:08:59 -05:00
Charlie Marsh
8934f6938d Avoid RET504 errors for intermediary function calls (#1294) 2022-12-19 19:48:09 -05:00
Charlie Marsh
8f0fc3033a Update Arg section checking to match latest pydocstyle (#1293) 2022-12-19 16:39:42 -05:00
Charlie Marsh
4107bc828d Bump version to 0.0.188 2022-12-19 12:18:06 -05:00
Charlie Marsh
706d28cabc Rename PDV checks to PD (#1288) 2022-12-19 00:20:28 -05:00
Charlie Marsh
4da2264722 Avoid T201 errors for print(..., file=fp)-like calls (#1287) 2022-12-19 00:10:07 -05:00
Charlie Marsh
bf88c815aa Move flake8-debugger tests into flake8-debugger subdirectory (#1286) 2022-12-18 22:06:42 -05:00
Yasu_umi
8a4831dd5b Implement flake8-datetimez (#1270) 2022-12-18 22:06:06 -05:00
Charlie Marsh
b5ab492a70 Bump version to 0.0.187 2022-12-18 20:09:02 -05:00
Charlie Marsh
1fc09ebd5c Fix inverted E501 condition (#1285) 2022-12-18 20:08:30 -05:00
Charlie Marsh
6cf047976c Bump pygrep-hooks tally in README 2022-12-18 18:05:32 -05:00
Reiner Gerecke
87465daacc pygrep-hooks - deprecated use of logging.warn & no blanket type ignore (#1275) 2022-12-18 18:04:21 -05:00
Chris Brendel
a52bed7101 Use --stdin-filename when resolving configuration files (#1281) 2022-12-18 17:51:55 -05:00
Anders Kaseorg
20ac823778 generate-check-code-prefix: Run rustfmt automatically; only write if changed (#1282) 2022-12-18 17:46:23 -05:00
Charlie Marsh
1028ed3565 Bump version to 0.0.186 2022-12-18 14:30:30 -05:00
Anders Kaseorg
98897db6ac Add packaging status badge from repology (#1276) 2022-12-18 14:27:29 -05:00
Honkertonken
5ce4262112 Readme : Fix incorrect exmaple. (#1277) 2022-12-18 12:04:48 -05:00
Charlie Marsh
6b2359384d Print redirect warnings exactly once per code (#1280) 2022-12-18 12:03:49 -05:00
Harutaka Kawamura
d3443d7c19 Update RustPython to use correct Tuple location (#1278) 2022-12-18 08:53:57 -05:00
Anders Kaseorg
04b1e1de6f README: Add missing backtick (#1274) 2022-12-18 00:29:33 -05:00
Anders Kaseorg
c93c85300f Repair corrupted PDV007, PDV009 messages (#1273) 2022-12-18 00:29:12 -05:00
Charlie Marsh
73ed6f8654 Touch-up README 2022-12-17 21:38:45 -05:00
Charlie Marsh
eb183645f3 Add ruff-lsp to README (#1272)
Add ruff-lsp to README
2022-12-17 21:37:11 -05:00
Charlie Marsh
ef17aa93da Add ruff-lsp to README (#1272) 2022-12-17 21:37:00 -05:00
Charlie Marsh
f366b0147f Add ruff-lsp to README (#1272) 2022-12-17 21:35:44 -05:00
Charlie Marsh
fc88fa35ff Add instructions for Sublime Text installation (#1271) 2022-12-17 16:22:50 -05:00
Charlie Marsh
a2806eb8ef Bump version to 0.0.185 2022-12-16 23:47:56 -05:00
Charlie Marsh
89d919eac5 Re-remove W605_1.py from Black compatibility test 2022-12-16 23:17:27 -05:00
Charlie Marsh
5ad77fbc8d Move checkers into their own module (#1268) 2022-12-16 22:55:47 -05:00
Charlie Marsh
9cb18a481b Separate line-based checker from noqa enforcement (#1267) 2022-12-16 22:49:27 -05:00
Charlie Marsh
2393e270ed Change a few more methods to take AsRef<Path> 2022-12-16 21:38:52 -05:00
Charlie Marsh
f36e6035c8 Change a few methods to take AsRef<Path> 2022-12-16 21:28:19 -05:00
Charlie Marsh
ecf0dd05d6 Auto-detect same-package imports (#1266) 2022-12-16 21:19:11 -05:00
Charlie Marsh
5f67ee93f7 Replace cache bool with an enum 2022-12-16 15:45:30 -05:00
Charlie Marsh
1e19142d0e Bump version to 0.0.184 2022-12-16 14:36:25 -05:00
Charlie Marsh
6a95dade6d Actually check-in snapshots for #1265 2022-12-16 14:36:00 -05:00
Charlie Marsh
d6e765877e Enable autofix for __init__ method with missing None-return (#1265) 2022-12-16 14:28:56 -05:00
Charlie Marsh
e4d36bae57 Replace ignore_noqa and autofix booleans with enums (#1264) 2022-12-16 14:01:25 -05:00
Harutaka Kawamura
e3531276a7 Fix F501 (line-too-long) start location (#1262) 2022-12-16 11:29:47 -05:00
Charlie Marsh
634553f188 Add ignore-variadic-names options to flake8-unused-arguments (#1261) 2022-12-16 00:22:38 -05:00
Edgar R. M
4ff0b75045 test: Fix flake8-errmsg snapshots (#1260) 2022-12-15 23:53:15 -05:00
Charlie Marsh
481d668511 Add flake8-errmsg to README 2022-12-15 23:16:38 -05:00
Charlie Marsh
a9f56ee76e Bump version to 0.0.183 2022-12-15 23:15:12 -05:00
Charlie Marsh
b4bfa87104 Avoid removing partially-unused imports (#1259) 2022-12-15 23:13:58 -05:00
Charlie Marsh
b9f42bf5e5 Remove extraneous test file 2022-12-15 23:12:19 -05:00
Edgar R. M
8281d414ca Implement flake8-errmsg (#1258) 2022-12-15 23:10:59 -05:00
Charlie Marsh
7e45a9f2e2 Avoid generating invalid statements when deleting from multi-statement lines (#1253) 2022-12-15 22:17:31 -05:00
Reiner Gerecke
a000cd4a09 Test to prevent continious reformatting when used together with black (#1206) 2022-12-15 15:26:41 -05:00
Martin Lehoux
d8b4b92733 Implement U016: Remove six compatibility code (#1013) 2022-12-15 14:16:58 -05:00
Edgar R. M
27de342e75 Implement pandas-vet (#1235) 2022-12-15 14:01:01 -05:00
Charlie Marsh
d805067683 Avoid fixing E711 and E712 issues that would cause F632 (#1248) 2022-12-15 12:08:31 -05:00
Charlie Marsh
1ea2e93f8e Bump version to 0.0.182 2022-12-14 22:57:22 -05:00
Charlie Marsh
dc180dc277 Negate ignore_names condition 2022-12-14 22:50:26 -05:00
Charlie Marsh
6be910ae07 Use more precise ranges for function and class checks (#1247) 2022-12-14 22:40:00 -05:00
Charlie Marsh
ba85eb846c Run cargo fmt 2022-12-14 21:52:44 -05:00
Charlie Marsh
d067efe265 Treat extend-* configuration options as "always extended" (#1245) 2022-12-14 20:22:40 -05:00
Charlie Marsh
549ea2f85f Ignore any pyproject.toml without a [tool.ruff] section (#1243) 2022-12-14 19:35:52 -05:00
Charlie Marsh
d814ebd21f Bump version to 0.0.181 2022-12-14 17:35:36 -05:00
Charlie Marsh
3f272b6cf8 Enable opt-out of .gitignore checks via respect-gitignore flag (#1242) 2022-12-14 16:54:23 -05:00
Charlie Marsh
76891a8c07 Always check zero-depth CLI paths (#1241) 2022-12-14 16:32:02 -05:00
Charlie Marsh
e389201b5f Add new .gitignore behavior to BREAKING_CHANGES.md (#1240) 2022-12-14 16:04:06 -05:00
Charlie Marsh
4b2020d03a Automatically ignore files specified in .gitignore (#1234) 2022-12-14 15:58:40 -05:00
Charlie Marsh
0aa356c96c Avoid converting expression to statement in invald contexts (#1239) 2022-12-14 13:57:25 -05:00
Charlie Marsh
630b4b627d Apply fix to all errors in E711 and E712 autofix (#1238) 2022-12-14 13:29:56 -05:00
Charlie Marsh
854cd14842 Bump version to 0.0.180 2022-12-14 13:21:10 -05:00
Chris Brendel
6b93c8403f Apply CLI options even when no pyproject.toml is found (#1232) 2022-12-13 22:55:04 -05:00
Charlie Marsh
765d21c7b0 Bump version to 0.0.179 2022-12-13 10:17:16 -05:00
Charlie Marsh
a58b9b5063 Upgrade RustPython to support parenthesized context managers (#1228) 2022-12-13 10:16:43 -05:00
Charlie Marsh
f3e11a30cb Bump version to 0.0.178 2022-12-12 22:06:04 -05:00
Charlie Marsh
2f3b5367ff Add a note on extends to README 2022-12-12 21:36:39 -05:00
Charlie Marsh
92bc417e4e Add support for glob patterns in src (#1225) 2022-12-12 21:35:03 -05:00
Charlie Marsh
9853b0728b Enable configuration files to "extend" other configuration files (#1219) 2022-12-12 20:28:22 -05:00
Charlie Marsh
77709dcc41 Remove underscore from extend_exclude 2022-12-12 16:34:16 -05:00
Charlie Marsh
b0cb5fc7ef Document current behavior around pyproject.toml discovery (#1213) 2022-12-12 11:49:21 -05:00
Charlie Marsh
d6f51e55dd Remove extraneous test_project 2022-12-12 10:53:12 -05:00
Charlie Marsh
4bb6b4851a Rename p to path 2022-12-12 10:51:24 -05:00
Charlie Marsh
54c5ded938 Move settings path discovery into its own function 2022-12-12 10:50:08 -05:00
Charlie Marsh
0157fedab5 Move Python file resolution into resolver.rs (#1211) 2022-12-12 10:43:50 -05:00
Charlie Marsh
cd69610741 Use --config everywhere if provided (#1210) 2022-12-12 10:28:00 -05:00
Charlie Marsh
a3d06d0005 Move more commands into commands.rs (#1209) 2022-12-12 10:22:47 -05:00
Charlie Marsh
ac6fa1dc88 Simplify some logic around configuration detection (#1197) 2022-12-12 10:15:05 -05:00
Charlie Marsh
73794fc299 Resolve hierarchical settings and Python files in a single filesystem pass (#1205) 2022-12-12 10:13:52 -05:00
Charlie Marsh
0adc9ed259 Support hierarchical settings for nested directories (#1190) 2022-12-12 10:12:23 -05:00
Charlie Marsh
19e9eb1af8 Bump version to 0.0.177 2022-12-11 22:38:52 -05:00
Anders Kaseorg
e57044800c Fix quotes in SIM118 error message (#1204) 2022-12-11 22:30:39 -05:00
Charlie Marsh
ae8ff7cb7f Add notes around python-lsp-ruff (#1202) 2022-12-11 17:36:20 -05:00
Charlie Marsh
c05914f222 Avoid inserting extra newlines for comment-delimited import blocks (#1201) 2022-12-11 17:13:09 -05:00
Charlie Marsh
24179655b8 Fix 'a test' reference 2022-12-11 13:31:14 -05:00
Charlie Marsh
d27b419e68 Run cargo dev generate-options 2022-12-11 10:34:52 -05:00
Charlie Marsh
9fc7a32a24 Sort list in README 2022-12-11 10:25:27 -05:00
Charlie Marsh
9161b866b5 Bump version to 0.0.176 2022-12-11 10:19:50 -05:00
Reiner Gerecke
38141a6f14 Check for outdated auto-generated files in CI (#1192) 2022-12-11 10:18:57 -05:00
Charlie Marsh
aa5402fc0e Add flake8-simplify to flake8-to-ruff 2022-12-11 10:10:46 -05:00
Charlie Marsh
99f077aa4e Add missing hash in README comment 2022-12-11 10:05:32 -05:00
Reiner Gerecke
7f25d1ec70 Implement SIM118 (key in dict) of flake8-simplify (#1195) 2022-12-11 10:05:11 -05:00
Charlie Marsh
360b033e04 Avoid F821 false positive on annotated global (#1196) 2022-12-11 10:04:06 -05:00
Reiner Gerecke
247dcc9f9c Mark C413 as fixable (#1191) 2022-12-11 09:07:51 -05:00
Charlie Marsh
c86e52193c Bump version to 0.0.175 2022-12-10 21:23:19 -05:00
Harutaka Kawamura
efdc4e801d Upgrade RustPython to fix end location of implicitly concatenated strings (#1187) 2022-12-10 19:16:01 -05:00
Charlie Marsh
f8f2eeed35 Enable --no-show-source for consistency (#1189) 2022-12-10 19:09:49 -05:00
Charlie Marsh
8fa414b67e Move configuration-CLI resolution into dedicated methods (#1188) 2022-12-10 19:07:38 -05:00
Charlie Marsh
484d7a30bd Add TODO around nested globals 2022-12-10 17:44:08 -05:00
Charlie Marsh
fb681c614a Move string formatting checks to plugins (#1185) 2022-12-10 16:43:21 -05:00
Reiner Gerecke
06ed125771 Add autofix for F504 and F522 (#1184) 2022-12-10 16:33:09 -05:00
Charlie Marsh
74668915b0 Remove serialization format from Settings struct (#1183) 2022-12-10 13:38:59 -05:00
Charlie Marsh
6da3de25ba Add jupyter_server to README (#1182) 2022-12-10 12:10:27 -05:00
Charlie Marsh
63b3e00c97 Bump version to 0.0.174 2022-12-10 12:08:48 -05:00
Charlie Marsh
39440aa274 Create function and lambda scopes eagerly (#1181) 2022-12-10 12:08:33 -05:00
Charlie Marsh
add96d3dc5 Implement E0117 (nonlocal-without-binding) (#1180) 2022-12-10 11:41:57 -05:00
Charlie Marsh
6f8e0224d0 Implement W0602 (global-variable-not-assigned) (#1179) 2022-12-10 11:33:24 -05:00
Charlie Marsh
b8bbafd85b Flag global usages prior to global declarations (#1178) 2022-12-10 11:19:24 -05:00
Charlie Marsh
40b54d3e8c Ignore imports in class scopes (#1176) 2022-12-10 10:23:33 -05:00
Charlie Marsh
2b44941d63 Add pacman instructions to README (#1175) 2022-12-10 10:00:01 -05:00
Charlie Marsh
257bd7f1d7 Bump version to 0.0.173 2022-12-09 23:23:12 -05:00
Charlie Marsh
5728dceef0 Add note around redefinitions 2022-12-09 23:18:51 -05:00
Charlie Marsh
6739602806 Mark redefined-but-unused imports as unused regardless of scope (#1173) 2022-12-09 23:17:33 -05:00
Charlie Marsh
305326f7d7 Remove some string clones from docstring helpers (#1172) 2022-12-09 22:30:34 -05:00
Charlie Marsh
69866f5461 Extract docstring exactly once (#1171) 2022-12-09 22:21:16 -05:00
Charlie Marsh
41ca29c4f4 Add TODO in redefined_by_function 2022-12-09 21:19:57 -05:00
Charlie Marsh
b35a804f9d Bump version to 0.0.172 2022-12-09 17:47:34 -05:00
Charlie Marsh
e594ed6528 Implement D301 (backslash checks) (#1169) 2022-12-09 17:44:18 -05:00
Charlie Marsh
197645d90d Always use raw docstrings for pydocstyle rules (#1167) 2022-12-09 17:31:04 -05:00
Charlie Marsh
26d3ff5a3a Add pyflakes test suite for annotations (#1166) 2022-12-09 16:28:07 -05:00
Charlie Marsh
0dacf61153 Implement F842 (UnusedAnnotation) (#1165) 2022-12-09 12:42:03 -05:00
Charlie Marsh
a6251360b7 Avoid RET false-positives for usages in f-strings (#1163) 2022-12-09 12:28:09 -05:00
Charlie Marsh
2965e2561d Clarify combination of combine-as-imports and force-wrap-aliases (#1162) 2022-12-09 12:20:15 -05:00
Charlie Marsh
a19050b8a4 Update README.md 2022-12-08 23:39:01 -05:00
Charlie Marsh
dfd6225d85 Bump version to 0.0.171 2022-12-08 23:18:48 -05:00
Charlie Marsh
a0a6327fae Only allowlist noqa et al at the start of a comment (#1157) 2022-12-08 23:10:36 -05:00
Charlie Marsh
db815a565f Run release job on release: published event (#1156) 2022-12-08 23:05:28 -05:00
Charlie Marsh
3bacdafd1c Improve some __all__ handling cases (#1155) 2022-12-08 23:03:23 -05:00
Charlie Marsh
6403e3630d Fix flaky unused import test 2022-12-08 22:51:13 -05:00
Charlie Marsh
229eab6f42 Improve some behavior around global handling (#1154) 2022-12-08 22:47:19 -05:00
Charlie Marsh
e33582fb0e Add pyflakes import test suite (#1151) 2022-12-08 22:23:37 -05:00
Charlie Marsh
aaeab0ecf1 Implement F811 (RedefinedWhileUnused) (#1137) 2022-12-08 21:31:08 -05:00
Charlie Marsh
f9a16d9c44 Fix GitHub link 2022-12-08 20:54:54 -05:00
Charlie Marsh
2aa884eb9b Re-implement the entire test_undefined_names.py test suite (#1150) 2022-12-08 20:53:01 -05:00
Charlie Marsh
84fa64d98c Move bindings to an arena (#1147) 2022-12-08 19:48:00 -05:00
Charlie Marsh
c1b1ac069e Include else block in break detection (#1143) 2022-12-08 11:53:31 -05:00
Charlie Marsh
a710e35ebc Bump version to 0.0.170 2022-12-08 11:36:24 -05:00
Charlie Marsh
49df43bb78 Use single newlines in .pyi import sorting (#1142) 2022-12-08 11:34:41 -05:00
Charlie Marsh
e338d9acbe Remove 'consider' language from check messages (#1135) 2022-12-07 20:10:36 -05:00
Charlie Marsh
5c8655f479 Bump ruff_macros to 0.0.169 2022-12-07 19:10:16 -05:00
Charlie Marsh
60987888a2 Re-increase max iterations to 100 2022-12-07 19:10:03 -05:00
Charlie Marsh
a81581c781 Bump ruff_macros to 0.0.168 2022-12-07 19:08:18 -05:00
Charlie Marsh
3152dd7a8e Don't prompt users to --fix if they ran with --fix (#1133) 2022-12-07 19:07:51 -05:00
Charlie Marsh
528416f07a Rename I252 to TID252; add redirects for all renamed codes (#1129) 2022-12-07 15:12:22 -05:00
Charlie Marsh
4405a6a903 Bump version to 0.0.168 2022-12-07 13:18:40 -05:00
Charlie Marsh
35fa2a3c32 Convert more BTree usages to Fx (#1112) 2022-12-07 12:21:12 -05:00
Charlie Marsh
bb67fbb73a Implement unused argument detection (ARG) (#1126)
Detect unused arguments
2022-12-07 12:15:41 -05:00
Charlie Marsh
d698c6123e Bump version to 0.0.167 2022-12-07 10:37:31 -05:00
Charlie Marsh
9579faffa8 Avoid flagging bare exception issues when exception is re-raised (#1124) 2022-12-07 10:37:08 -05:00
Phillip Verheyden
9c6e8c7644 Auto-generate the rules table of contents (#1121) 2022-12-07 10:03:42 -05:00
Charlie Marsh
7abecd4f0e Implement B905 (#1122) 2022-12-07 10:01:24 -05:00
Phillip Verheyden
b8ff209af8 Encode prefixes in README headings not just in TOC (#1109) 2022-12-07 09:24:49 -05:00
Jeong YunWon
c5451cd8ad Reduce indents (#1116) 2022-12-07 09:20:33 -05:00
Jonathan Plasse
92b9ab3010 Add aiter() and anext() to BUILTINS (#1118) 2022-12-07 09:20:06 -05:00
Edgar R. M
f2ac8c4ec2 Add flake8-import-conventions to TOC in readme (#1114) 2022-12-06 21:27:04 -05:00
Charlie Marsh
80e2f0c92e Bump version to 0.0.166 2022-12-06 16:06:19 -05:00
Edgar R. M
ea550abd3c Implement flake8-import-conventions (#1098) 2022-12-06 16:01:17 -05:00
Charlie Marsh
5c26777e4c Avoid flagging ANN errors in @overload implementations (#1110) 2022-12-06 12:46:38 -05:00
Laurent Baillet
6eb6b6eede Update readme in order to match pylint prefixes (#1105) 2022-12-06 08:59:45 -05:00
Charlie Marsh
f1d3e3698a Bump version to 0.0.165 2022-12-06 00:03:30 -05:00
Charlie Marsh
080411bc89 Re-create ruff snapshots 2022-12-06 00:03:14 -05:00
Charlie Marsh
f2ad915224 Bump version to 0.0.164 2022-12-05 23:37:22 -05:00
Charlie Marsh
71543eeabc Rename rules mod to ruff (#1104) 2022-12-05 23:35:36 -05:00
Charlie Marsh
44025f1c92 Improve F841's Flake8 parity for unpacking assignments (#1103) 2022-12-05 23:34:40 -05:00
Charlie Marsh
971bf6d232 Run cargo fmt 2022-12-05 23:01:47 -05:00
Charlie Marsh
0acc47386a Use pyproject.toml parent as project root when explicitly provided (#1101) 2022-12-05 23:00:59 -05:00
Reiner Gerecke
982ac6b0ad Auto-generate options in README from field attributes (#1015) 2022-12-05 22:34:40 -05:00
Charlie Marsh
541440f7a8 Re-support F841 detection for single context managers (#1099) 2022-12-05 22:09:45 -05:00
Charlie Marsh
66dde46e03 Track nested imports without column number detection (#1097) 2022-12-05 21:16:44 -05:00
Charlie Marsh
1339e2a002 Bump version to 0.0.163 2022-12-05 20:45:24 -05:00
Charlie Marsh
38ad10f60d Treat nested classes and functions as "standard" siblings (#1095) 2022-12-05 20:45:12 -05:00
Charlie Marsh
88e78c5cde Add missing D415 fixture 2022-12-05 20:42:21 -05:00
Charlie Marsh
436aeed20a Implement autofix for D400 and D415 (#1094) 2022-12-05 20:24:56 -05:00
Charlie Marsh
b94169a8bb Don't autofix D210 by introducing a syntax error (#1093) 2022-12-05 19:13:22 -05:00
Charlie Marsh
995994be3e Bump version to 0.0.162 2022-12-05 19:07:44 -05:00
Charlie Marsh
f001305b2e Only autofix D205 by deleting blank lines (#1091) 2022-12-05 19:01:32 -05:00
Charlie Marsh
e88093541f Avoid wrapping import-star statements (#1089) 2022-12-05 18:39:16 -05:00
Charlie Marsh
da41a495f1 Remove extraneous plugin creation script 2022-12-05 18:31:19 -05:00
Charlie Marsh
55b7ec8f85 Ignore newline enforcement when imports break indentation boundaries (#1085) 2022-12-05 18:02:41 -05:00
Charlie Marsh
4b41ae3f53 Bump version to 0.0.161 2022-12-05 17:02:05 -05:00
Charlie Marsh
f944e1e1cf Add action comments to README.md (#1082) 2022-12-05 16:56:28 -05:00
Charlie Marsh
4fbc1082de Support isort: split directive (#1081) 2022-12-05 16:48:10 -05:00
Charlie Marsh
cf2e887e38 Tweak summary message to include total error counts (#1067) 2022-12-05 16:12:12 -05:00
Charlie Marsh
ee994e8c07 Import compatibility with isort newline-insertion behavior (#1078) 2022-12-05 16:07:07 -05:00
Charlie Marsh
c69c4fd655 Support isort: skip_file directive (#1075) 2022-12-05 15:02:01 -05:00
Charlie Marsh
e01e45ca35 Remove extraneous test file 2022-12-05 14:58:54 -05:00
Charlie Marsh
4be74785fe Support unterminated isort: off directives (#1074) 2022-12-05 14:54:47 -05:00
Charlie Marsh
40b7c64f7d Bump version to 0.0.160 2022-12-05 12:56:38 -05:00
Jonathan Plasse
a76c5d1226 Add allowed-confusable settings (#1059) 2022-12-05 12:53:55 -05:00
Charlie Marsh
5aeddeb825 Include pyproject.toml path in error message (#1068) 2022-12-05 12:04:50 -05:00
Charlie Marsh
5f8294aea4 Preserve star imports when re-formatting import blocks (#1066) 2022-12-05 11:48:38 -05:00
Charlie Marsh
e07d3f6313 Fix clippy 2022-12-05 11:47:42 -05:00
Charlie Marsh
1d1662cb9c Bump version to 0.0.159 2022-12-05 11:22:02 -05:00
Charlie Marsh
55ce7bd0df Migrate invalid_literal_comparisons fix to token-based logic (#1065) 2022-12-05 11:16:59 -05:00
Charlie Marsh
e695f6eb25 Avoid false-positive on PLR1701 for multi-type isinstance calls (#1063) 2022-12-05 10:07:05 -05:00
messense
fb2c457a9b Upgrade to notify 5.0.0 (#1048) 2022-12-05 09:58:42 -05:00
Jeong YunWon
523cf62eda Style fixes (#1049) 2022-12-05 09:57:48 -05:00
Charlie Marsh
7024ad7cc7 Bump version to 0.0.158 2022-12-04 21:22:24 -05:00
Charlie Marsh
871ac511ae Add an option to force one-member-per-line for aliased import-froms (#1047) 2022-12-04 21:22:00 -05:00
Charlie Marsh
0685af8a4f Update RustPython (#1045) 2022-12-04 20:09:28 -05:00
Charlie Marsh
5e9a8fcf53 Bump version to 0.0.157 2022-12-04 14:46:46 -05:00
Charlie Marsh
76439235af Remove unused imports in __init__.py files by default (#1042) 2022-12-04 14:45:54 -05:00
Charlie Marsh
0c9c6a1c1c Remove extraneous test file 2022-12-04 13:59:55 -05:00
Charlie Marsh
46a99243cd Fix D205 autofix by detecting summary line (#1041) 2022-12-04 13:56:51 -05:00
Charlie Marsh
d06dc4c72d Bump version to 0.0.156 2022-12-04 10:22:09 -05:00
Jonathan Plasse
8f6b2fb32b Extend and rename RUF004 to PLR1722 (#1036) 2022-12-04 10:20:12 -05:00
Charlie Marsh
1d61db5b62 Allow import builtins under T100 (#1037) 2022-12-04 10:13:17 -05:00
Charlie Marsh
e15c0c68cb Fix PLW0120 snapshot 2022-12-04 10:10:00 -05:00
Harutaka Kawamura
abb2adc4d8 Implement useless-import-alias (#1025) 2022-12-04 09:48:56 -05:00
Harutaka Kawamura
e070166409 Implement useless-else-on-loop (#1031) 2022-12-04 09:22:04 -05:00
Jonathan Plasse
0ae6890094 Fix Table of Contents (#1030) 2022-12-04 09:14:09 -05:00
Harutaka Kawamura
21cace0973 Fix PLR0402 (#1024) 2022-12-04 09:13:47 -05:00
Jakub Kuczys
4994b72ba2 Fix README header links in isort config section (#1033) 2022-12-04 09:12:25 -05:00
Anders Kaseorg
dbb1a6e44b Remove sloppy match_name_or_attr helper (#1027) 2022-12-04 09:12:03 -05:00
Charlie Marsh
4b0c3e3bc9 Bump version to 0.0.155 2022-12-04 00:12:00 -05:00
Charlie Marsh
ddae3586d5 Add support for combine-as-imports import formatting (#1022) 2022-12-04 00:11:48 -05:00
Charlie Marsh
97684b7215 Mark MisplacedComparisonConstant as fixable 2022-12-04 00:11:19 -05:00
Harutaka Kawamura
20e6ff112a Implement misplaced-comparison-constant (#1023) 2022-12-04 00:10:07 -05:00
Charlie Marsh
6cb84d29f0 Rename M001 to RUF100 (#1021) 2022-12-03 22:51:05 -05:00
Charlie Marsh
d069054792 Rename RUF101 fixtures 2022-12-03 22:47:16 -05:00
Charlie Marsh
c659cb86d9 Rename RUF101 to RUF004 (#1020) 2022-12-03 22:46:13 -05:00
Charlie Marsh
8a3f29497b Add backwards compatible redirect map for U-to-UP rename (#1019) 2022-12-03 22:43:00 -05:00
Harutaka Kawamura
58e7baedd0 Implement consider-using-from-import (#1018) 2022-12-03 22:36:43 -05:00
Charlie Marsh
00eff2b09a Rename pyupgrade rules from UXXX to UPXXX (#957) 2022-12-03 22:35:42 -05:00
Charlie Marsh
b6b509811d Bump version to 0.0.154 2022-12-03 20:14:38 -05:00
Charlie Marsh
87bdda3cfa Make some flake8-return rules auto-fixable (#1017) 2022-12-03 20:13:17 -05:00
Charlie Marsh
291727a27d Implement flake8-return plugin (#1016) 2022-12-03 20:12:11 -05:00
Charlie Marsh
e66b786229 Bump version to 0.0.153 2022-12-03 17:05:39 -05:00
Harutaka Kawamura
e05e1cdf76 Implement consider-merging-isinstance (#1009) 2022-12-03 16:51:53 -05:00
Harutaka Kawamura
f92cc7a159 Fix clippy errors on main (#1010) 2022-12-03 10:02:36 -05:00
Harutaka Kawamura
ebd2181946 Implement unnecessary-direct-lambda-call (#1008) 2022-12-03 09:59:04 -05:00
Harutaka Kawamura
3efa1a03f2 Fix match_like_matches_macro in src/pylint/plugins.rs (#1007) 2022-12-03 00:21:32 -05:00
Harutaka Kawamura
115e85b47d Rename PLE0206 to PLR0206 (#1006) 2022-12-03 00:14:46 -05:00
Harutaka Kawamura
31a3314ebd Implement PLE0206 (#1005) 2022-12-03 00:04:43 -05:00
Charlie Marsh
bf33025ea9 Support whole-file noqa exclusions (#1001) 2022-12-02 23:56:56 -05:00
Charlie Marsh
1a33ee3fc4 Bump version to 0.0.152 2022-12-02 13:23:00 -05:00
Charlie Marsh
4722885910 Avoid recursing on nested deferred annotations (#1000)
Parse nested deferred annotations
2022-12-02 13:22:39 -05:00
Jonathan Plasse
117fcb6936 Add no-eval rule from pygrep-hooks (#994) 2022-12-02 12:59:06 -05:00
580 changed files with 30014 additions and 6894 deletions

View File

@@ -20,6 +20,9 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-11-01
override: true
components: rustfmt
- uses: actions/cache@v3
env:
cache-name: cache-cargo
@@ -33,6 +36,12 @@ jobs:
${{ runner.os }}-build-
${{ runner.os }}-
- run: cargo build --all --release
- run: ./target/release/ruff_dev generate-rules-table
- run: ./target/release/ruff_dev generate-options
- run: git diff --quiet README.md || echo "::error file=README.md::This file is outdated. You may have to rerun 'cargo dev generate-options' and/or 'cargo dev generate-rules-table'."
- run: ./target/release/ruff_dev generate-check-code-prefix
- run: git diff --quiet src/checks_gen.rs || echo "::error file=src/checks_gen.rs::This file is outdated. You may have to rerun 'cargo dev generate-check-code-prefix'."
- run: git diff --exit-code -- README.md src/checks_gen.rs
cargo_fmt:
name: "cargo fmt"
@@ -106,7 +115,9 @@ jobs:
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- run: pip install black[d]==22.12.0
- run: cargo test --all
- run: cargo test --package ruff --test black_compatibility_test -- --ignored
maturin_build:
name: "maturin build"

View File

@@ -1,9 +1,8 @@
name: "[ruff] Release"
on:
create:
tags:
- v*
release:
types: [published]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}

View File

@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.151
rev: v0.0.191
hooks:
- id: ruff

23
BREAKING_CHANGES.md Normal file
View File

@@ -0,0 +1,23 @@
# Breaking Changes
## 0.0.181
### Files excluded by `.gitignore` are now ignored ([#1234](https://github.com/charliermarsh/ruff/pull/1234))
Ruff will now avoid checking files that are excluded by `.ignore`, `.gitignore`,
`.git/info/exclude`, and global `gitignore` files. This behavior is powered by the [`ignore`](https://docs.rs/ignore/latest/ignore/struct.WalkBuilder.html#ignore-rules)
crate, and is applied in addition to Ruff's built-in `exclude` system.
To disable this behavior, set `respect-gitignore = false` in your `pyproject.toml` file.
Note that hidden files (i.e., files and directories prefixed with a `.`) are _not_ ignored by
default.
## 0.0.178
### Configuration files are now resolved hierarchically ([#1190](https://github.com/charliermarsh/ruff/pull/1190))
`pyproject.toml` files are now resolved hierarchically, such that for each Python file, we find
the first `pyproject.toml` file in its path, and use that to determine its lint settings.
See the [README](https://github.com/charliermarsh/ruff#pyprojecttoml-discovery) for more.

View File

@@ -105,6 +105,8 @@ You may also want to add the new configuration option to the `flake8-to-ruff` to
responsible for converting `flake8` configuration files to Ruff's TOML format. This logic
lives in `flake8_to_ruff/src/converter.rs`.
To update the documentation for supported configuration options, run `cargo dev generate-options`.
## Release process
As of now, Ruff has an ad hoc release process: releases are cut with high frequency via GitHub

461
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ members = [
[package]
name = "ruff"
version = "0.0.151"
version = "0.0.191"
edition = "2021"
rust-version = "1.65.0"
@@ -28,12 +28,14 @@ common-path = { version = "1.0.0" }
dirs = { version = "4.0.0" }
fern = { version = "0.6.1" }
filetime = { version = "0.2.17" }
glob = { version = "0.3.0" }
globset = { version = "0.4.9" }
ignore = { version = "0.4.18" }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
log = { version = "0.4.17" }
nohash-hasher = { version = "0.2.0" }
notify = { version = "4.0.17" }
notify = { version = "5.0.0" }
num-bigint = { version = "0.4.3" }
once_cell = { version = "1.16.0" }
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
@@ -41,12 +43,14 @@ quick-junit = { version = "0.3.2" }
rayon = { version = "1.5.3" }
regex = { version = "1.6.0" }
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
ruff_macros = { version = "0.0.191", path = "ruff_macros" }
rustc-hash = { version = "1.1.0" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
shellexpand = { version = "3.0.0" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }
textwrap = { version = "0.16.0" }
@@ -68,6 +72,7 @@ assert_cmd = { version = "2.0.4" }
criterion = { version = "0.4.0" }
insta = { version = "1.19.1", features = ["yaml"] }
test-case = { version = "2.2.2" }
ureq = { version = "2.5.0", features = [] }
[features]
default = ["update-informer"]

123
LICENSE
View File

@@ -363,6 +363,106 @@ are:
THE SOFTWARE.
"""
- flake8-return, licensed as follows:
"""
MIT License
Copyright (c) 2019 Afonasev Evgeniy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-import-conventions, licensed as follows:
"""
MIT License
Copyright (c) 2021 João Palmeiro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-unused-arguments, licensed as follows:
"""
MIT License
Copyright (c) 2019 Nathan Hoad
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-simplify, licensed as follows:
"""
MIT License
Copyright (c) 2020 Martin Thoma
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- isort, licensed as follows:
"""
The MIT License (MIT)
@@ -471,6 +571,29 @@ are:
SOFTWARE.
"""
- pygrep-hooks, licensed as follows:
"""
Copyright (c) 2018 Anthony Sottile
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
- pyupgrade, licensed as follows:
"""
Copyright (c) 2017 Anthony Sottile

1317
README.md

File diff suppressed because it is too large Load Diff

View File

@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8_to_ruff"
version = "0.0.151"
version = "0.0.191"
dependencies = [
"anyhow",
"clap",
@@ -1975,7 +1975,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.151"
version = "0.0.191"
dependencies = [
"anyhow",
"bincode",
@@ -2028,7 +2028,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.1.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
dependencies = [
"num-bigint",
"rustpython-common",
@@ -2038,7 +2038,7 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.0.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
dependencies = [
"ascii",
"cfg-if 1.0.0",
@@ -2061,7 +2061,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
dependencies = [
"bincode",
"bitflags",
@@ -2078,7 +2078,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
dependencies = [
"ahash",
"anyhow",

View File

@@ -1,6 +1,6 @@
[package]
name = "flake8-to-ruff"
version = "0.0.151-dev.0"
version = "0.0.191-dev.0"
edition = "2021"
[lib]
@@ -16,6 +16,8 @@ ruff = { path = "..", default-features = false }
rustc-hash = { version = "1.1.0" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }
toml = { version = "0.5.9" }
[dev-dependencies]

View File

@@ -0,0 +1,65 @@
[build-system]
requires = [
# The minimum setuptools version is specific to the PEP 517 backend,
# and may be stricter than the version required in `setup.cfg`
"setuptools>=40.6.0,!=60.9.0",
"wheel",
# Must be kept in sync with the `install_requirements` in `setup.cfg`
"cffi>=1.12; platform_python_implementation != 'PyPy'",
"setuptools-rust>=0.11.4",
]
build-backend = "setuptools.build_meta"
[tool.black]
line-length = 79
target-version = ["py36"]
[tool.pytest.ini_options]
addopts = "-r s --capture=no --strict-markers --benchmark-disable"
markers = [
"skip_fips: this test is not executed in FIPS mode",
"supported: parametrized test requiring only_if and skip_message",
]
[tool.mypy]
show_error_codes = true
check_untyped_defs = true
no_implicit_reexport = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_unused_configs = true
strict_equality = true
[[tool.mypy.overrides]]
module = [
"pretend"
]
ignore_missing_imports = true
[tool.coverage.run]
branch = true
relative_files = true
source = [
"cryptography",
"tests/",
]
[tool.coverage.paths]
source = [
"src/cryptography",
"*.tox/*/lib*/python*/site-packages/cryptography",
"*.tox\\*\\Lib\\site-packages\\cryptography",
"*.tox/pypy/site-packages/cryptography",
]
tests =[
"tests/",
"*tests\\",
]
[tool.coverage.report]
exclude_lines = [
"@abc.abstractmethod",
"@abc.abstractproperty",
"@typing.overload",
"if typing.TYPE_CHECKING",
]

View File

@@ -0,0 +1,91 @@
[metadata]
name = cryptography
version = attr: cryptography.__version__
description = cryptography is a package which provides cryptographic recipes and primitives to Python developers.
long_description = file: README.rst
long_description_content_type = text/x-rst
license = BSD-3-Clause OR Apache-2.0
url = https://github.com/pyca/cryptography
author = The Python Cryptographic Authority and individual contributors
author_email = cryptography-dev@python.org
project_urls =
Documentation=https://cryptography.io/
Source=https://github.com/pyca/cryptography/
Issues=https://github.com/pyca/cryptography/issues
Changelog=https://cryptography.io/en/latest/changelog/
classifiers =
Development Status :: 5 - Production/Stable
Intended Audience :: Developers
License :: OSI Approved :: Apache Software License
License :: OSI Approved :: BSD License
Natural Language :: English
Operating System :: MacOS :: MacOS X
Operating System :: POSIX
Operating System :: POSIX :: BSD
Operating System :: POSIX :: Linux
Operating System :: Microsoft :: Windows
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: PyPy
Topic :: Security :: Cryptography
[options]
python_requires = >=3.6
include_package_data = True
zip_safe = False
package_dir =
=src
packages = find:
# `install_requires` must be kept in sync with `pyproject.toml`
install_requires =
cffi >=1.12
[options.packages.find]
where = src
exclude =
_cffi_src
_cffi_src.*
[options.extras_require]
test =
pytest>=6.2.0
pytest-benchmark
pytest-cov
pytest-subtests
pytest-xdist
pretend
iso8601
pytz
hypothesis>=1.11.4,!=3.79.2
docs =
sphinx >= 1.6.5,!=1.8.0,!=3.1.0,!=3.1.1,!=5.2.0,!=5.2.0.post0
sphinx_rtd_theme
docstest =
pyenchant >= 1.6.11
twine >= 1.12.0
sphinxcontrib-spelling >= 4.0.1
sdist =
setuptools_rust >= 0.11.4
pep8test =
black
flake8
flake8-import-order
pep8-naming
# This extra is for OpenSSH private keys that use bcrypt KDF
# Versions: v3.1.3 - ignore_few_rounds, v3.1.5 - abi3
ssh =
bcrypt >= 3.1.5
[flake8]
ignore = E203,E211,W503,W504,N818
exclude = .tox,*.egg,.git,_build,.hypothesis
select = E,W,F,N,I
application-import-names = cryptography,cryptography_vectors,tests

View File

@@ -0,0 +1,32 @@
//! Extract Black configuration settings from a pyproject.toml.
use std::path::Path;
use anyhow::Result;
use ruff::settings::types::PythonVersion;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct Black {
#[serde(alias = "line-length", alias = "line_length")]
pub line_length: Option<usize>,
#[serde(alias = "target-version", alias = "target_version")]
pub target_version: Option<Vec<PythonVersion>>,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
struct Tools {
black: Option<Black>,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
struct Pyproject {
tool: Option<Tools>,
}
pub fn parse_black_options<P: AsRef<Path>>(path: P) -> Result<Option<Black>> {
let contents = std::fs::read_to_string(path)?;
Ok(toml::from_str::<Pyproject>(&contents)?
.tool
.and_then(|tool| tool.black))
}

View File

@@ -7,16 +7,24 @@ use ruff::flake8_tidy_imports::settings::Strictness;
use ruff::settings::options::Options;
use ruff::settings::pyproject::Pyproject;
use ruff::{
flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, mccabe, pep8_naming,
flake8_annotations, flake8_bugbear, flake8_errmsg, flake8_quotes, flake8_tidy_imports, mccabe,
pep8_naming,
};
use crate::black::Black;
use crate::plugin::Plugin;
use crate::{parser, plugin};
pub fn convert(
flake8: &HashMap<String, Option<String>>,
config: &HashMap<String, HashMap<String, Option<String>>>,
black: Option<&Black>,
plugins: Option<Vec<Plugin>>,
) -> Result<Pyproject> {
// Extract the Flake8 section.
let flake8 = config
.get("flake8")
.expect("Unable to find flake8 section in INI file");
// Extract all referenced check code prefixes, to power plugin inference.
let mut referenced_codes: BTreeSet<CheckCodePrefix> = BTreeSet::default();
for (key, value) in flake8 {
@@ -53,10 +61,18 @@ pub fn convert(
plugin::resolve_select(
flake8,
&plugins.unwrap_or_else(|| {
plugin::infer_plugins_from_options(flake8)
.into_iter()
.chain(plugin::infer_plugins_from_codes(&referenced_codes))
.collect()
let from_options = plugin::infer_plugins_from_options(flake8);
if !from_options.is_empty() {
eprintln!("Inferred plugins from settings: {from_options:#?}");
}
let from_codes = plugin::infer_plugins_from_codes(&referenced_codes);
if !from_codes.is_empty() {
eprintln!(
"Inferred plugins from referenced check codes: {:#?}",
from_codes
);
}
from_options.into_iter().chain(from_codes).collect()
}),
)
});
@@ -73,6 +89,7 @@ pub fn convert(
let mut options = Options::default();
let mut flake8_annotations = flake8_annotations::settings::Options::default();
let mut flake8_bugbear = flake8_bugbear::settings::Options::default();
let mut flake8_errmsg = flake8_errmsg::settings::Options::default();
let mut flake8_quotes = flake8_quotes::settings::Options::default();
let mut flake8_tidy_imports = flake8_tidy_imports::settings::Options::default();
let mut mccabe = mccabe::settings::Options::default();
@@ -194,6 +211,15 @@ pub fn convert(
Ok(max_complexity) => mccabe.max_complexity = Some(max_complexity),
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
},
// flake8-errmsg
"errmsg-max-string-length" | "errmsg_max_string_length" => {
match value.clone().parse::<usize>() {
Ok(max_string_length) => {
flake8_errmsg.max_string_length = Some(max_string_length);
}
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
}
}
// Unknown
_ => eprintln!("Skipping unsupported property: {key}"),
}
@@ -209,6 +235,9 @@ pub fn convert(
if flake8_bugbear != flake8_bugbear::settings::Options::default() {
options.flake8_bugbear = Some(flake8_bugbear);
}
if flake8_errmsg != flake8_errmsg::settings::Options::default() {
options.flake8_errmsg = Some(flake8_errmsg);
}
if flake8_quotes != flake8_quotes::settings::Options::default() {
options.flake8_quotes = Some(flake8_quotes);
}
@@ -222,6 +251,19 @@ pub fn convert(
options.pep8_naming = Some(pep8_naming);
}
// Extract any settings from the existing `pyproject.toml`.
if let Some(black) = black {
if let Some(line_length) = &black.line_length {
options.line_length = Some(*line_length);
}
if let Some(target_version) = &black.target_version {
if let Some(target_version) = target_version.iter().min() {
options.target_version = Some(*target_version);
}
}
}
// Create the pyproject.toml.
Ok(Pyproject::new(options))
}
@@ -241,10 +283,16 @@ mod tests {
#[test]
fn it_converts_empty() -> Result<()> {
let actual = convert(&HashMap::from([]), None)?;
let actual = convert(
&HashMap::from([("flake8".to_string(), HashMap::default())]),
None,
None,
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
dummy_variable_rgx: None,
exclude: None,
extend: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
@@ -252,9 +300,12 @@ mod tests {
fix: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
@@ -266,8 +317,11 @@ mod tests {
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
@@ -281,12 +335,18 @@ mod tests {
#[test]
fn it_converts_dashes() -> Result<()> {
let actual = convert(
&HashMap::from([("max-line-length".to_string(), Some("100".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("max-line-length".to_string(), Some("100".to_string()))]),
)]),
None,
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
dummy_variable_rgx: None,
exclude: None,
extend: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
@@ -294,9 +354,12 @@ mod tests {
fix: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: Some(100),
per_file_ignores: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
@@ -308,8 +371,11 @@ mod tests {
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
@@ -323,12 +389,18 @@ mod tests {
#[test]
fn it_converts_underscores() -> Result<()> {
let actual = convert(
&HashMap::from([("max_line_length".to_string(), Some("100".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("max_line_length".to_string(), Some("100".to_string()))]),
)]),
None,
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
dummy_variable_rgx: None,
exclude: None,
extend: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
@@ -336,9 +408,12 @@ mod tests {
fix: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: Some(100),
per_file_ignores: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
@@ -350,8 +425,11 @@ mod tests {
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
@@ -365,12 +443,18 @@ mod tests {
#[test]
fn it_ignores_parse_errors() -> Result<()> {
let actual = convert(
&HashMap::from([("max_line_length".to_string(), Some("abc".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("max_line_length".to_string(), Some("abc".to_string()))]),
)]),
None,
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
dummy_variable_rgx: None,
exclude: None,
extend: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
@@ -378,9 +462,12 @@ mod tests {
fix: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
@@ -392,8 +479,11 @@ mod tests {
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
@@ -407,12 +497,18 @@ mod tests {
#[test]
fn it_converts_plugin_options() -> Result<()> {
let actual = convert(
&HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]),
)]),
None,
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
dummy_variable_rgx: None,
exclude: None,
extend: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
@@ -420,9 +516,12 @@ mod tests {
fix: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
@@ -434,6 +533,7 @@ mod tests {
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: Some(flake8_quotes::settings::Options {
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
multiline_quotes: None,
@@ -441,6 +541,8 @@ mod tests {
avoid_escape: None,
}),
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
@@ -455,14 +557,20 @@ mod tests {
fn it_converts_docstring_conventions() -> Result<()> {
let actual = convert(
&HashMap::from([(
"docstring-convention".to_string(),
Some("numpy".to_string()),
"flake8".to_string(),
HashMap::from([(
"docstring-convention".to_string(),
Some("numpy".to_string()),
)]),
)]),
None,
Some(vec![Plugin::Flake8Docstrings]),
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
dummy_variable_rgx: None,
exclude: None,
extend: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
@@ -470,9 +578,12 @@ mod tests {
fix: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::D100,
CheckCodePrefix::D101,
@@ -495,6 +606,7 @@ mod tests {
CheckCodePrefix::D214,
CheckCodePrefix::D215,
CheckCodePrefix::D300,
CheckCodePrefix::D301,
CheckCodePrefix::D400,
CheckCodePrefix::D403,
CheckCodePrefix::D404,
@@ -519,8 +631,11 @@ mod tests {
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
@@ -534,12 +649,18 @@ mod tests {
#[test]
fn it_infers_plugins_if_omitted() -> Result<()> {
let actual = convert(
&HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]),
)]),
None,
None,
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
dummy_variable_rgx: None,
exclude: None,
extend: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
@@ -547,9 +668,12 @@ mod tests {
fix: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
@@ -562,6 +686,7 @@ mod tests {
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: Some(flake8_quotes::settings::Options {
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
multiline_quotes: None,
@@ -569,6 +694,8 @@ mod tests {
avoid_escape: None,
}),
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,

View File

@@ -11,6 +11,7 @@
clippy::too_many_lines
)]
pub mod black;
pub mod converter;
mod parser;
pub mod plugin;

View File

@@ -17,6 +17,7 @@ use std::path::PathBuf;
use anyhow::Result;
use clap::Parser;
use configparser::ini::Ini;
use flake8_to_ruff::black::parse_black_options;
use flake8_to_ruff::converter;
use flake8_to_ruff::plugin::Plugin;
@@ -26,10 +27,14 @@ use flake8_to_ruff::plugin::Plugin;
long_about = None
)]
struct Cli {
/// Path to the Flake8 configuration file (e.g., 'setup.cfg', 'tox.ini', or
/// '.flake8').
/// Path to the Flake8 configuration file (e.g., `setup.cfg`, `tox.ini`, or
/// `.flake8`).
#[arg(required = true)]
file: PathBuf,
/// Optional path to a `pyproject.toml` file, used to ensure compatibility
/// with Black.
#[arg(long)]
pyproject: Option<PathBuf>,
/// List of plugins to enable.
#[arg(long, value_delimiter = ',')]
plugin: Option<Vec<Plugin>>,
@@ -43,13 +48,15 @@ fn main() -> Result<()> {
ini.set_multiline(true);
let config = ini.load(cli.file).map_err(|msg| anyhow::anyhow!(msg))?;
// Extract the Flake8 section.
let flake8 = config
.get("flake8")
.expect("Unable to find flake8 section in INI file");
// Read the pyproject.toml file.
let black = cli
.pyproject
.map(parse_black_options)
.transpose()?
.flatten();
// Create the pyproject.toml.
let pyproject = converter::convert(flake8, cli.plugin)?;
// Create Ruff's pyproject.toml section.
let pyproject = converter::convert(&config, black.as_ref(), cli.plugin)?;
println!("{}", toml::to_string_pretty(&pyproject)?);
Ok(())

View File

@@ -3,6 +3,7 @@ use std::str::FromStr;
use anyhow::{bail, Result};
use once_cell::sync::Lazy;
use regex::Regex;
use ruff::checks::PREFIX_REDIRECTS;
use ruff::checks_gen::CheckCodePrefix;
use ruff::settings::types::PatternPrefixPair;
use rustc_hash::FxHashMap;
@@ -18,7 +19,9 @@ pub fn parse_prefix_codes(value: &str) -> Vec<CheckCodePrefix> {
if code.is_empty() {
continue;
}
if let Ok(code) = CheckCodePrefix::from_str(code) {
if let Some(code) = PREFIX_REDIRECTS.get(code) {
codes.push(code.clone());
} else if let Ok(code) = CheckCodePrefix::from_str(code) {
codes.push(code);
} else {
eprintln!("Unsupported prefix code: {code}");
@@ -83,16 +86,22 @@ impl State {
fn parse(&self) -> Vec<PatternPrefixPair> {
let mut codes: Vec<PatternPrefixPair> = vec![];
for code in &self.codes {
match CheckCodePrefix::from_str(code) {
Ok(code) => {
for filename in &self.filenames {
codes.push(PatternPrefixPair {
pattern: filename.clone(),
prefix: code.clone(),
});
}
if let Some(code) = PREFIX_REDIRECTS.get(code.as_str()) {
for filename in &self.filenames {
codes.push(PatternPrefixPair {
pattern: filename.clone(),
prefix: code.clone(),
});
}
Err(_) => eprintln!("Skipping unrecognized prefix: {code}"),
} else if let Ok(code) = CheckCodePrefix::from_str(code) {
for filename in &self.filenames {
codes.push(PatternPrefixPair {
pattern: filename.clone(),
prefix: code.clone(),
});
}
} else {
eprintln!("Unsupported prefix code: {code}");
}
}
codes
@@ -135,7 +144,6 @@ fn tokenize_files_to_codes_mapping(value: &str) -> Vec<Token> {
}
/// Parse a 'files-to-codes' mapping, mimicking Flake8's internal logic.
///
/// See: <https://github.com/PyCQA/flake8/blob/7dfe99616fc2f07c0017df2ba5fa884158f3ea8a/src/flake8/utils.py#L45>
pub fn parse_files_to_codes_mapping(value: &str) -> Result<Vec<PatternPrefixPair>> {
if value.trim().is_empty() {

View File

@@ -1,10 +1,11 @@
use std::collections::{BTreeSet, HashMap};
use std::fmt;
use std::str::FromStr;
use anyhow::anyhow;
use ruff::checks_gen::CheckCodePrefix;
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Plugin {
Flake8Annotations,
Flake8Bandit,
@@ -12,13 +13,18 @@ pub enum Plugin {
Flake8Bugbear,
Flake8Builtins,
Flake8Comprehensions,
Flake8Datetimez,
Flake8Debugger,
Flake8Docstrings,
Flake8ErrMsg,
Flake8Eradicate,
Flake8Print,
Flake8Quotes,
Flake8Return,
Flake8Simplify,
Flake8TidyImports,
McCabe,
PandasVet,
PEP8Naming,
Pyupgrade,
}
@@ -34,13 +40,18 @@ impl FromStr for Plugin {
"flake8-bugbear" => Ok(Plugin::Flake8Bugbear),
"flake8-builtins" => Ok(Plugin::Flake8Builtins),
"flake8-comprehensions" => Ok(Plugin::Flake8Comprehensions),
"flake8-datetimez" => Ok(Plugin::Flake8Datetimez),
"flake8-debugger" => Ok(Plugin::Flake8Debugger),
"flake8-docstrings" => Ok(Plugin::Flake8Docstrings),
"flake8-eradicate" => Ok(Plugin::Flake8BlindExcept),
"flake8-eradicate" => Ok(Plugin::Flake8Eradicate),
"flake8-errmsg" => Ok(Plugin::Flake8ErrMsg),
"flake8-print" => Ok(Plugin::Flake8Print),
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
"flake8-return" => Ok(Plugin::Flake8Return),
"flake8-simplify" => Ok(Plugin::Flake8Simplify),
"flake8-tidy-imports" => Ok(Plugin::Flake8TidyImports),
"mccabe" => Ok(Plugin::McCabe),
"pandas-vet" => Ok(Plugin::PandasVet),
"pep8-naming" => Ok(Plugin::PEP8Naming),
"pyupgrade" => Ok(Plugin::Pyupgrade),
_ => Err(anyhow!("Unknown plugin: {string}")),
@@ -48,24 +59,62 @@ impl FromStr for Plugin {
}
}
impl fmt::Debug for Plugin {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Plugin::Flake8Annotations => "flake8-annotations",
Plugin::Flake8Bandit => "flake8-bandit",
Plugin::Flake8BlindExcept => "flake8-blind-except",
Plugin::Flake8Bugbear => "flake8-bugbear",
Plugin::Flake8Builtins => "flake8-builtins",
Plugin::Flake8Comprehensions => "flake8-comprehensions",
Plugin::Flake8Datetimez => "flake8-datetimez",
Plugin::Flake8Debugger => "flake8-debugger",
Plugin::Flake8Docstrings => "flake8-docstrings",
Plugin::Flake8Eradicate => "flake8-eradicate",
Plugin::Flake8ErrMsg => "flake8-errmsg",
Plugin::Flake8Print => "flake8-print",
Plugin::Flake8Quotes => "flake8-quotes",
Plugin::Flake8Return => "flake8-return",
Plugin::Flake8Simplify => "flake8-simplify",
Plugin::Flake8TidyImports => "flake8-tidy-imports",
Plugin::McCabe => "mccabe",
Plugin::PandasVet => "pandas-vet",
Plugin::PEP8Naming => "pep8-naming",
Plugin::Pyupgrade => "pyupgrade",
}
)
}
}
impl Plugin {
pub fn default(&self) -> CheckCodePrefix {
match self {
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
Plugin::Flake8Bandit => CheckCodePrefix::S,
// TODO(charlie): Handle rename of `B` to `BLE`.
Plugin::Flake8BlindExcept => CheckCodePrefix::BLE,
Plugin::Flake8Bugbear => CheckCodePrefix::B,
Plugin::Flake8Builtins => CheckCodePrefix::A,
Plugin::Flake8Comprehensions => CheckCodePrefix::C4,
Plugin::Flake8Datetimez => CheckCodePrefix::DTZ,
Plugin::Flake8Debugger => CheckCodePrefix::T1,
Plugin::Flake8Docstrings => CheckCodePrefix::D,
// TODO(charlie): Handle rename of `E` to `ERA`.
Plugin::Flake8Eradicate => CheckCodePrefix::ERA,
Plugin::Flake8ErrMsg => CheckCodePrefix::EM,
Plugin::Flake8Print => CheckCodePrefix::T2,
Plugin::Flake8Quotes => CheckCodePrefix::Q,
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
Plugin::Flake8Return => CheckCodePrefix::RET,
Plugin::Flake8Simplify => CheckCodePrefix::SIM,
Plugin::Flake8TidyImports => CheckCodePrefix::TID25,
Plugin::McCabe => CheckCodePrefix::C9,
Plugin::PandasVet => CheckCodePrefix::PD,
Plugin::PEP8Naming => CheckCodePrefix::N,
Plugin::Pyupgrade => CheckCodePrefix::U,
Plugin::Pyupgrade => CheckCodePrefix::UP,
}
}
@@ -77,6 +126,7 @@ impl Plugin {
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
Plugin::Flake8Builtins => vec![CheckCodePrefix::A],
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C4],
Plugin::Flake8Datetimez => vec![CheckCodePrefix::DTZ],
Plugin::Flake8Debugger => vec![CheckCodePrefix::T1],
Plugin::Flake8Docstrings => {
// Use the user-provided docstring.
@@ -95,12 +145,16 @@ impl Plugin {
DocstringConvention::PEP8.select()
}
Plugin::Flake8Eradicate => vec![CheckCodePrefix::ERA],
Plugin::Flake8ErrMsg => vec![CheckCodePrefix::EM],
Plugin::Flake8Print => vec![CheckCodePrefix::T2],
Plugin::Flake8Quotes => vec![CheckCodePrefix::Q],
Plugin::Flake8TidyImports => vec![CheckCodePrefix::I25],
Plugin::Flake8Return => vec![CheckCodePrefix::RET],
Plugin::Flake8Simplify => vec![CheckCodePrefix::SIM],
Plugin::Flake8TidyImports => vec![CheckCodePrefix::TID],
Plugin::McCabe => vec![CheckCodePrefix::C9],
Plugin::PandasVet => vec![CheckCodePrefix::PD],
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
Plugin::Pyupgrade => vec![CheckCodePrefix::U],
Plugin::Pyupgrade => vec![CheckCodePrefix::UP],
}
}
}
@@ -158,6 +212,7 @@ impl DocstringConvention {
// CheckCodePrefix::D214,
// CheckCodePrefix::D215,
CheckCodePrefix::D300,
CheckCodePrefix::D301,
CheckCodePrefix::D400,
CheckCodePrefix::D402,
CheckCodePrefix::D403,
@@ -205,6 +260,7 @@ impl DocstringConvention {
CheckCodePrefix::D214,
CheckCodePrefix::D215,
CheckCodePrefix::D300,
CheckCodePrefix::D301,
CheckCodePrefix::D400,
// CheckCodePrefix::D402,
CheckCodePrefix::D403,
@@ -253,6 +309,7 @@ impl DocstringConvention {
CheckCodePrefix::D214,
// CheckCodePrefix::D215,
CheckCodePrefix::D300,
CheckCodePrefix::D301,
// CheckCodePrefix::D400,
CheckCodePrefix::D402,
CheckCodePrefix::D403,
@@ -366,6 +423,9 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
"staticmethod-decorators" | "staticmethod_decorators" => {
plugins.insert(Plugin::PEP8Naming);
}
"max-string-length" | "max_string_length" => {
plugins.insert(Plugin::Flake8ErrMsg);
}
_ => {}
}
}
@@ -384,14 +444,18 @@ pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin
Plugin::Flake8Bugbear,
Plugin::Flake8Builtins,
Plugin::Flake8Comprehensions,
Plugin::Flake8Datetimez,
Plugin::Flake8Debugger,
Plugin::Flake8Docstrings,
Plugin::Flake8Eradicate,
Plugin::Flake8ErrMsg,
Plugin::Flake8Print,
Plugin::Flake8Quotes,
Plugin::Flake8Return,
Plugin::Flake8Simplify,
Plugin::Flake8TidyImports,
Plugin::PandasVet,
Plugin::PEP8Naming,
Plugin::Pyupgrade,
]
.into_iter()
.filter(|plugin| {

View File

@@ -31,3 +31,9 @@ build-backend = "maturin"
[tool.maturin]
bindings = "bin"
strip = true
[tool.ruff]
[tool.ruff.isort]
force-wrap-aliases = true
combine-as-imports = true

3
resources/test/fixtures/README.md vendored Normal file
View File

@@ -0,0 +1,3 @@
# fixtures
Fixture files used for snapshot testing.

View File

@@ -5,9 +5,11 @@ a = 4
#foo(1, 2, 3)
def foo(x, y, z):
contentet = 1 # print('hello')
content = 1 # print('hello')
print(x, y, z)
# This is a real comment.
#return True
return False
#import os # noqa: ERA001

View File

@@ -0,0 +1,49 @@
from typing import overload
@overload
def foo(i: int) -> "int":
...
@overload
def foo(i: "str") -> "str":
...
def foo(i):
return i
@overload
def bar(i: int) -> "int":
...
@overload
def bar(i: "str") -> "str":
...
class X:
def bar(i):
return i
# TODO(charlie): This third case should raise an error (as in Mypy), because we have a
# statement between the interfaces and implementation.
@overload
def baz(i: int) -> "int":
...
@overload
def baz(i: "str") -> "str":
...
x = 1
def baz(i):
return i

View File

@@ -53,3 +53,11 @@ try:
raise e
except Exception:
pass
try:
pass
except Exception as e:
raise bad
except BaseException:
pass

View File

@@ -20,6 +20,8 @@ getattr(foo, "_123abc")
getattr(foo, "abc123")
getattr(foo, r"abc123")
_ = lambda x: getattr(x, "bar")
if getattr(x, "bar"):
pass
# Valid setattr usage
setattr(foo, bar, None)
@@ -28,6 +30,8 @@ setattr(foo, "123abc", None)
setattr(foo, r"123\abc", None)
setattr(foo, "except", None)
_ = lambda x: setattr(x, "bar", 1)
if setattr(x, "bar", 1):
pass
# Invalid usage
setattr(foo, "bar", None)

View File

@@ -0,0 +1,10 @@
zip()
zip(range(3))
zip("a", "b")
zip("a", "b", *zip("c"))
zip(zip("a"), strict=False)
zip(zip("a", strict=True))
zip(range(3), strict=True)
zip("a", "b", strict=False)
zip("a", "b", "c", strict=True)

View File

@@ -0,0 +1,21 @@
import datetime
# no args
datetime.datetime(2000, 1, 1, 0, 0, 0)
# none args
datetime.datetime(2000, 1, 1, 0, 0, 0, 0, None)
# not none arg
datetime.datetime(2000, 1, 1, 0, 0, 0, 0, datetime.timezone.utc)
# no kwargs
datetime.datetime(2000, 1, 1, fold=1)
# none kwargs
datetime.datetime(2000, 1, 1, tzinfo=None)
from datetime import datetime
# no args unqualified
datetime(2000, 1, 1, 0, 0, 0)

View File

@@ -0,0 +1,9 @@
import datetime
# qualified
datetime.datetime.today()
from datetime import datetime
# unqualified
datetime.today()

View File

@@ -0,0 +1,9 @@
import datetime
# qualified
datetime.datetime.utcnow()
from datetime import datetime
# unqualified
datetime.utcnow()

View File

@@ -0,0 +1,9 @@
import datetime
# qualified
datetime.datetime.utcfromtimestamp(1234)
from datetime import datetime
# unqualified
datetime.utcfromtimestamp(1234)

View File

@@ -0,0 +1,18 @@
import datetime
# no args
datetime.datetime.now()
# wrong keywords
datetime.datetime.now(bad=datetime.timezone.utc)
# none args
datetime.datetime.now(None)
# none keywords
datetime.datetime.now(tz=None)
from datetime import datetime
# no args unqualified
datetime.now()

View File

@@ -0,0 +1,18 @@
import datetime
# no args
datetime.datetime.fromtimestamp(1234)
# wrong keywords
datetime.datetime.fromtimestamp(1234, bad=datetime.timezone.utc)
# none args
datetime.datetime.fromtimestamp(1234, None)
# none keywords
datetime.datetime.fromtimestamp(1234, tz=None)
from datetime import datetime
# no args unqualified
datetime.fromtimestamp(1234)

View File

@@ -0,0 +1,35 @@
import datetime
# bad format
datetime.datetime.strptime("something", "%H:%M:%S%Z")
# no replace or astimezone
datetime.datetime.strptime("something", "something")
# wrong replace
datetime.datetime.strptime("something", "something").replace(hour=1)
# none replace
datetime.datetime.strptime("something", "something").replace(tzinfo=None)
# OK
datetime.datetime.strptime("something", "something").replace(
tzinfo=datetime.timezone.utc
)
# OK
datetime.datetime.strptime("something", "something").astimezone()
# OK
datetime.datetime.strptime("something", "%H:%M:%S%z")
# OK
datetime.datetime.strptime("something", something).astimezone()
# OK
datetime.datetime.strptime("something", something).replace(tzinfo=datetime.timezone.utc)
from datetime import datetime
# no replace orastimezone unqualified
datetime.strptime("something", "something")

View File

@@ -0,0 +1,9 @@
import datetime
# qualified
datetime.date.today()
from datetime import date
# unqualified
date.today()

View File

@@ -0,0 +1,9 @@
import datetime
# qualified
datetime.date.fromtimestamp(1234)
from datetime import date
# unqualified
date.fromtimestamp(1234)

View File

@@ -1,14 +1,13 @@
breakpoint()
import pdb
import builtins
from builtins import breakpoint
from pdb import set_trace as st
from celery.contrib.rdb import set_trace
from celery.contrib import rdb
import celery.contrib.rdb
breakpoint()
st()
set_trace()

View File

@@ -0,0 +1,23 @@
from __future__ import annotations
def f_a():
raise RuntimeError("This is an example exception")
def f_a_short():
raise RuntimeError("Error")
def f_b():
example = "example"
raise RuntimeError(f"This is an {example} exception")
def f_c():
raise RuntimeError("This is an {example} exception".format(example="example"))
def f_ok():
msg = "hello"
raise RuntimeError(msg)

View File

@@ -0,0 +1,25 @@
import math # not checked
import altair # unconventional
import dask.array # unconventional
import dask.dataframe # unconventional
import matplotlib.pyplot # unconventional
import numpy # unconventional
import pandas # unconventional
import seaborn # unconventional
import altair as altr # unconventional
import matplotlib.pyplot as plot # unconventional
import dask.array as darray # unconventional
import dask.dataframe as ddf # unconventional
import numpy as nmp # unconventional
import pandas as pdas # unconventional
import seaborn as sbrn # unconventional
import altair as alt # conventional
import dask.array as da # conventional
import dask.dataframe as dd # conventional
import matplotlib.pyplot as plt # conventional
import numpy as np # conventional
import pandas as pd # conventional
import seaborn as sns # conventional

View File

@@ -0,0 +1,19 @@
import math # not checked
import altair # unconventional
import matplotlib.pyplot # unconventional
import numpy # unconventional
import pandas # unconventional
import seaborn # unconventional
import altair as altr # unconventional
import matplotlib.pyplot as plot # unconventional
import numpy as nmp # unconventional
import pandas as pdas # unconventional
import seaborn as sbrn # unconventional
import altair as alt # conventional
import matplotlib.pyplot as plt # conventional
import numpy as np # conventional
import pandas as pd # conventional
import seaborn as sns # conventional

View File

@@ -0,0 +1,19 @@
import math # not checked
import altair # unconventional
import matplotlib.pyplot # unconventional
import numpy # unconventional
import pandas # unconventional
import seaborn # unconventional
import altair as altr # unconventional
import matplotlib.pyplot as plot # unconventional
import numpy as np # unconventional
import pandas as pdas # unconventional
import seaborn as sbrn # unconventional
import altair as alt # conventional
import matplotlib.pyplot as plt # conventional
import numpy as nmp # conventional
import pandas as pd # conventional
import seaborn as sns # conventional

View File

@@ -0,0 +1,19 @@
import math # not checked
import altair # unconventional
import matplotlib.pyplot # unconventional
import numpy # not checked
import pandas # unconventional
import seaborn # unconventional
import altair as altr # unconventional
import matplotlib.pyplot as plot # unconventional
import numpy as nmp # not checked
import pandas as pdas # unconventional
import seaborn as sbrn # unconventional
import altair as alt # conventional
import matplotlib.pyplot as plt # conventional
import numpy as np # not checked
import pandas as pd # conventional
import seaborn as sns # conventional

View File

@@ -1 +1,10 @@
import sys
import tempfile
print("Hello, world!") # T201
print("Hello, world!", file=None) # T201
print("Hello, world!", file=sys.stdout) # T201
print("Hello, world!", file=sys.stderr) # T201
with tempfile.NamedTemporaryFile() as fp:
print("Hello, world!", file=fp) # OK

View File

@@ -2,7 +2,6 @@ from pprint import pprint
pprint("Hello, world!") # T203
import pprint
pprint.pprint("Hello, world!") # T203

View File

@@ -0,0 +1,4 @@
def x(y):
if not y:
return
return None # error

View File

@@ -0,0 +1,65 @@
def x(y):
if not y:
return # error
return 1
# without return value
def x(y):
if not y:
return
return
def x(y):
if y:
return
print()
# with return value
def x(y):
if not y:
return 1
return 2
def x(y):
for i in range(10):
if i > 100:
return i
else:
return 1
def x(y):
try:
return 1
except:
return 2
def x(y):
try:
return 1
finally:
return 2
# inner function
def x(y):
if not y:
return 1
def inner():
return
return 2
def x(y):
if not y:
return
def inner():
return 1

View File

@@ -0,0 +1,116 @@
###
# Errors
###
# if/elif/else
def x(y):
if not y:
return 1
# error
def x(y):
if not y:
print() # error
else:
return 2
def x(y):
if not y:
return 1
print() # error
# for
def x(y):
for i in range(10):
if i > 10:
return i
# error
def x(y):
for i in range(10):
if i > 10:
return i
else:
print() # error
###
# Non-errors
###
# raise as last return
def x(y):
if not y:
return 1
raise Exception
# last line in while loop
def x(y):
while True:
if y > 0:
return 1
y += 1
# exclude empty functions
def x(y):
return None
# return inner with statement
def x(y):
with y:
return 1
async def function():
async with thing as foo:
return foo.bar
# assert as last return
def x(y):
if not y:
return 1
assert False, "Sanity check"
# return value within loop
def bar1(x, y, z):
for i in x:
if i > y:
break
return z
def bar3(x, y, z):
for i in x:
if i > y:
if z:
break
else:
return z
return None
def bar1(x, y, z):
for i in x:
if i < y:
continue
return z
def bar3(x, y, z):
for i in x:
if i < y:
if z:
continue
else:
return z
return None

View File

@@ -0,0 +1,239 @@
###
# Errors
###
def x():
a = 1
return a # error
def x():
a = 1
print(a)
a = 2
return a # error
def x():
a = 1
if True:
return a # error
a = 2
print(a)
return a
# Can be refactored false positives
# https://github.com/afonasev/flake8-return/issues/47#issuecomment-1122571066
def get_bar_if_exists(obj):
result = ""
if hasattr(obj, "bar"):
result = str(obj.bar)
return result
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
def x():
formatted = _USER_AGENT_FORMATTER.format(format_string, **values)
# clean up after any blank components
formatted = formatted.replace("()", "").replace(" ", " ").strip()
return formatted
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
def user_agent_username(username=None):
if not username:
return ""
username = username.replace(" ", "_") # Avoid spaces or %20.
try:
username.encode("ascii") # just test, but not actually use it
except UnicodeEncodeError:
username = quote(username.encode("utf-8"))
else:
# % is legal in the default $wgLegalTitleChars
# This is so that ops know the real pywikibot will not
# allow a useragent in the username to allow through a hand-coded
# percent-encoded value.
if "%" in username:
username = quote(username)
return username
###
# Non-errors
###
def x(y):
a = 1
print(a)
return a
def x():
a = 1
if y:
return a
a = a + 2
print(a)
return a
def x():
a = {}
a["b"] = 2
return a
def x():
a = []
a.append(2)
return a
def x():
a = lambda x: x
a()
return a
# ignore unpacking
def x():
b, a = [1, 2]
return a
def x():
val = ""
for i in range(5):
val = val + str(i)
return val
def x():
val = ""
i = 5
while i:
val = val + str(i)
i = i - x
return val
def x():
a = 1
print(f"a={a}")
return a
# Considered OK, since functions can have side effects.
def x():
b, a = 1, 2
print(b)
return a
# Considered OK, since functions can have side effects.
def x():
a = 1
print()
return a
# Test cases for using value for assignment then returning it
# See:https://github.com/afonasev/flake8-return/issues/47
def resolve_from_url(self, url: str) -> dict:
local_match = self.local_scope_re.match(url)
if local_match:
schema = get_schema(name=local_match.group(1))
self.store[url] = schema
return schema
raise NotImplementedError(...)
my_dict = {}
def my_func():
foo = calculate_foo()
my_dict["foo_result"] = foo
return foo
# Refactored incorrect false positives
# See test cases above marked: "Can be refactored false positives"
# https://github.com/afonasev/flake8-return/issues/47#issuecomment-1122571066
def get_bar_if_exists(obj):
if hasattr(obj, "bar"):
return str(obj.bar)
return ""
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
def x():
formatted = _USER_AGENT_FORMATTER.format(format_string, **values)
# clean up after any blank components
return formatted.replace("()", "").replace(" ", " ").strip()
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
def user_agent_username(username=None):
if not username:
return ""
username = username.replace(" ", "_") # Avoid spaces or %20.
try:
username.encode("ascii") # just test,
# but not actually use it
except UnicodeEncodeError:
username = quote(username.encode("utf-8"))
else:
# % is legal in the default $wgLegalTitleChars
# This is so that ops know the real pywikibot will not
# allow a useragent in the username to allow through a
# hand-coded percent-encoded value.
if "%" in username:
username = quote(username)
return username
# https://github.com/afonasev/flake8-return/issues/116#issue-1367575481
def no_exception_loop():
success = False
for _ in range(10):
try:
success = True
except Exception:
print("exception")
return success
# https://github.com/afonasev/flake8-return/issues/116#issue-1367575481
def no_exception():
success = False
try:
success = True
except Exception:
print("exception")
return success
# https://github.com/afonasev/flake8-return/issues/116#issue-1367575481
def exception():
success = True
try:
print("raising")
raise Exception
except Exception:
success = False
return success
# https://github.com/afonasev/flake8-return/issues/66
def close(self):
any_failed = False
for task in self.tasks:
try:
task()
except BaseException:
any_failed = True
report(traceback.format_exc())
return any_failed

View File

@@ -0,0 +1,141 @@
###
# Error
###
def foo2(x, y, w, z):
if x: # [no-else-return]
a = 1
return y
elif z:
b = 2
return w
else:
c = 3
return z
def foo5(x, y, z):
if x: # [no-else-return]
if y:
a = 4
else:
b = 2
return
elif z:
c = 2
else:
c = 3
return
def foo2(x, y, w, z):
if x:
a = 1
elif z:
b = 2
else:
c = 3
if x: # [no-else-return]
a = 1
return y
elif z:
b = 2
return w
else:
c = 3
return z
def foo1(x, y, z):
if x: # [no-else-return]
a = 1
return y
else:
b = 2
return z
def foo3(x, y, z):
if x:
a = 1
if y: # [no-else-return]
b = 2
return y
else:
c = 3
return x
else:
d = 4
return z
def foo4(x, y):
if x: # [no-else-return]
if y:
a = 4
else:
b = 2
return
else:
c = 3
return
def foo6(x, y):
if x:
if y: # [no-else-return]
a = 4
return
else:
b = 2
else:
c = 3
return
def bar4(x):
if x: # [no-else-return]
return True
else:
try:
return False
except ValueError:
return None
###
# Non-error
###
def bar1(x, y, z):
if x:
return y
return z
def bar2(w, x, y, z):
if x:
return y
if z:
a = 1
else:
return w
return None
def bar3(x, y, z):
if x:
if z:
return y
else:
return z
return None
x = 0
if x == 1:
y = "a"
elif x == 2:
y = "b"
else:
y = "c"

View File

@@ -0,0 +1,112 @@
###
# Errors
###
def foo2(x, y, w, z):
if x: # [no-else-raise]
a = 1
raise Exception(y)
elif z:
b = 2
raise Exception(w)
else:
c = 3
raise Exception(z)
def foo5(x, y, z):
if x: # [no-else-raise]
if y:
a = 4
else:
b = 2
raise Exception(x)
elif z:
raise Exception(y)
else:
c = 3
raise Exception(z)
def foo1(x, y, z):
if x: # [no-else-raise]
a = 1
raise Exception(y)
else:
b = 2
raise Exception(z)
def foo3(x, y, z):
if x:
a = 1
if y: # [no-else-raise]
b = 2
raise Exception(y)
else:
c = 3
raise Exception(x)
else:
d = 4
raise Exception(z)
def foo4(x, y):
if x: # [no-else-raise]
if y:
a = 4
else:
b = 2
raise Exception(x)
else:
c = 3
raise Exception(y)
def foo6(x, y):
if x:
if y: # [no-else-raise]
a = 4
raise Exception(x)
else:
b = 2
else:
c = 3
raise Exception(y)
def bar4(x):
if x: # [no-else-raise]
raise Exception(True)
else:
try:
raise Exception(False)
except ValueError:
raise Exception(None)
###
# Non-errors
###
def bar1(x, y, z):
if x:
raise Exception(y)
raise Exception(z)
def bar2(w, x, y, z):
if x:
raise Exception(y)
if z:
a = 1
else:
raise Exception(w)
raise Exception(None)
def bar3(x, y, z):
if x:
if z:
raise Exception(y)
else:
raise Exception(z)
raise Exception(None)

View File

@@ -0,0 +1,106 @@
###
# Errors
###
def foo2(x, y, w, z):
for i in x:
if i < y: # [no-else-continue]
continue
elif i < w:
continue
else:
a = z
def foo5(x, y, z):
for i in x:
if i < y: # [no-else-continue]
if y:
a = 4
else:
b = 2
continue
elif z:
c = 2
else:
c = 3
continue
###
# Non-errors
###
def foo1(x, y, z):
for i in x:
if i < y: # [no-else-continue]
continue
else:
a = z
def foo3(x, y, z):
for i in x:
if i < y:
a = 1
if z: # [no-else-continue]
b = 2
continue
else:
c = 3
continue
else:
d = 4
continue
def foo4(x, y):
for i in x:
if i < x: # [no-else-continue]
if y:
a = 4
else:
b = 2
continue
else:
c = 3
continue
def foo6(x, y):
for i in x:
if i < x:
if y: # [no-else-continue]
a = 4
continue
else:
b = 2
else:
c = 3
continue
def bar4(x):
for i in range(10):
if x: # [no-else-continue]
continue
else:
try:
return
except ValueError:
continue
def bar1(x, y, z):
for i in x:
if i < y:
continue
return z
def bar3(x, y, z):
for i in x:
if i < y:
if z:
continue
else:
return z
return None

View File

@@ -0,0 +1,138 @@
###
# Errors
###
def foo2(x, y, w, z):
for i in x:
if i > y: # [no-else-break]
break
elif i > w:
break
else:
a = z
def foo5(x, y, z):
for i in x:
if i > y: # [no-else-break]
if y:
a = 4
else:
b = 2
break
elif z:
c = 2
else:
c = 3
break
def foo1(x, y, z):
for i in x:
if i > y: # [no-else-break]
break
else:
a = z
def foo3(x, y, z):
for i in x:
if i > y:
a = 1
if z: # [no-else-break]
b = 2
break
else:
c = 3
break
else:
d = 4
break
def foo4(x, y):
for i in x:
if i > x: # [no-else-break]
if y:
a = 4
else:
b = 2
break
else:
c = 3
break
def foo6(x, y):
for i in x:
if i > x:
if y: # [no-else-break]
a = 4
break
else:
b = 2
else:
c = 3
break
def bar4(x):
for i in range(10):
if x: # [no-else-break]
break
else:
try:
return
except ValueError:
break
###
# Non-errors
###
def bar1(x, y, z):
for i in x:
if i > y:
break
return z
def bar2(w, x, y, z):
for i in w:
if i > x:
break
if z < i:
a = y
else:
break
return
def bar3(x, y, z):
for i in x:
if i > y:
if z:
break
else:
return z
return None
def nested1(x, y, z):
for i in x:
if i > x:
for j in y:
if j < z:
break
else:
a = z
def nested2(x, y, z):
for i in x:
if i > x:
for j in y:
if j > z:
break
break
else:
a = z

View File

@@ -0,0 +1,12 @@
key in dict.keys() # SIM118
foo["bar"] in dict.keys() # SIM118
foo() in dict.keys() # SIM118
for key in dict.keys(): # SIM118
pass
for key in list(dict.keys()):
if some_property(key):
del dict[key]

View File

@@ -0,0 +1,137 @@
from abc import abstractmethod
from typing_extensions import override
###
# Unused arguments on functions.
###
def f(self, x):
print("Hello, world!")
def f(cls, x):
print("Hello, world!")
def f(self, x):
...
def f(cls, x):
...
###
# Unused arguments on lambdas.
###
lambda x: print("Hello, world!")
class C:
###
# Unused arguments.
###
def f(self, x):
print("Hello, world!")
def f(self, /, x):
print("Hello, world!")
def f(cls, x):
print("Hello, world!")
@classmethod
def f(cls, x):
print("Hello, world!")
@staticmethod
def f(cls, x):
print("Hello, world!")
@staticmethod
def f(x):
print("Hello, world!")
###
# Unused arguments attached to empty functions (OK).
###
def f(self, x):
...
def f(self, /, x):
...
def f(cls, x):
...
@classmethod
def f(cls, x):
...
@staticmethod
def f(cls, x):
...
@staticmethod
def f(x):
...
###
# Unused functions attached to abstract methods (OK).
###
@abstractmethod
def f(self, x):
print("Hello, world!")
@abstractmethod
def f(self, /, x):
print("Hello, world!")
@abstractmethod
def f(cls, x):
print("Hello, world!")
@classmethod
@abstractmethod
def f(cls, x):
print("Hello, world!")
@staticmethod
@abstractmethod
def f(cls, x):
print("Hello, world!")
@staticmethod
@abstractmethod
def f(x):
print("Hello, world!")
###
# Unused functions attached to overrides (OK).
###
@override
def f(self, x):
print("Hello, world!")
@override
def f(self, /, x):
print("Hello, world!")
@override
def f(cls, x):
print("Hello, world!")
@classmethod
@override
def f(cls, x):
print("Hello, world!")
@staticmethod
@override
def f(cls, x):
print("Hello, world!")
@staticmethod
@override
def f(x):
print("Hello, world!")

View File

@@ -0,0 +1,14 @@
def f(a, b):
print("Hello, world!")
def f(a, b, *args, **kwargs):
print("Hello, world!")
class C:
def f(self, a, b):
print("Hello, world!")
def f(self, a, b, *args, **kwargs):
print("Hello, world!")

View File

@@ -0,0 +1,4 @@
from module import Class as C
from module import CONSTANT
from module import function
from module import function as f

View File

@@ -0,0 +1,3 @@
from .a import a1 as a1, a2 as a2
from .b import b1 as b1
from .c import c1

View File

@@ -0,0 +1,65 @@
import a
import b
x = 1
import os
import sys
def f():
pass
if True:
x = 1
import collections
import typing
class X: pass
y = 1
import os
import sys
"""Docstring"""
if True:
import os
def f():
pass
if True:
import os
def f():
pass
if True:
x = 1
import collections
import typing
class X: pass
if True:
x = 1
import collections
import typing
def f(): pass
import os
# Comment goes here.
def f():
pass
import os
# Comment goes here.
def f():
pass
import os
# Comment goes here.
# And another.
def f():
pass

View File

@@ -0,0 +1,65 @@
import a
import b
x = 1
import os
import sys
def f():
pass
if True:
x = 1
import collections
import typing
class X: pass
y = 1
import os
import sys
"""Docstring"""
if True:
import os
def f():
pass
if True:
import os
def f():
pass
if True:
x = 1
import collections
import typing
class X: pass
if True:
x = 1
import collections
import typing
def f(): pass
import os
# Comment goes here.
def f():
pass
import os
# Comment goes here.
def f():
pass
import os
# Comment goes here.
# And another.
def f():
pass

View File

@@ -4,3 +4,10 @@ import os
if True:
x = 1; import sys
import os
if True:
x = 1; \
import os
x = 1; \
import os

View File

@@ -0,0 +1 @@
from .subscription import * # type: ignore # some very long comment explaining why this needs a type ignore

View File

@@ -0,0 +1,6 @@
from some_other_module import some_class
from some_other_module import *
# Above
from some_module import some_class # Aside
# Above
from some_module import * # Aside

View File

@@ -0,0 +1,10 @@
# isort: skip_file
import e
import f
# isort: split
import a
import b
import c
import d

View File

@@ -0,0 +1,9 @@
import e
import f
# isort: split
import a
import b
import c
import d

View File

@@ -0,0 +1,61 @@
#: E401
import os, sys
#: Okay
import os
import sys
from subprocess import Popen, PIPE
from myclass import MyClass
from foo.bar.yourclass import YourClass
import myclass
import foo.bar.yourclass
#: Okay
__all__ = ['abc']
import foo
#: Okay
__version__ = "42"
import foo
#: Okay
__author__ = "Simon Gomizelj"
import foo
#: Okay
try:
import foo
except ImportError:
pass
else:
print('imported foo')
finally:
print('made attempt to import foo')
import bar
#: Okay
with warnings.catch_warnings():
warnings.filterwarnings("ignore", DeprecationWarning)
import foo
import bar
#: Okay
if False:
import foo
elif not True:
import bar
else:
import mwahaha
import bar
#: E402
VERSION = '1.2.3'
import foo
#: E402
import foo
a = 1
import bar

View File

@@ -55,3 +55,8 @@ sit amet consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labor
# OK
# https://loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong.url.com
# Not OK
_ = """
Source: https://github.com/PyCQA/pycodestyle/pull/258/files#diff-841c622497a8033d10152bfdfb15b20b92437ecdea21a260944ea86b77b51533
"""

View File

@@ -0,0 +1,39 @@
###
# Errors
###
if "abc" is "def": # F632 (fix)
pass
if "abc" is None: # F632 (fix, but leaves behind unfixable E711)
pass
if None is "abc": # F632 (fix, but leaves behind unfixable E711)
pass
if "abc" is False: # F632 (fix, but leaves behind unfixable E712)
pass
if False is "abc": # F632 (fix, but leaves behind unfixable E712)
pass
if False == None: # E711, E712 (fix)
pass
if None == False: # E711, E712 (fix)
pass
###
# Unfixable errors
###
if "abc" == None: # E711
pass
if None == "abc": # E711
pass
if "abc" == False: # E712
pass
if False == "abc": # E712
pass
###
# Non-errors
###
if "def" == "abc":
pass
if False is None:
pass
if None is False:
pass

View File

@@ -571,3 +571,11 @@ def multiline_trailing_and_leading_space():
"""
pass
@expect('D210: No whitespaces allowed surrounding docstring text')
@expect("D400: First line should end with a period (not '\"')")
@expect("D415: First line should end with a period, question mark, "
"or exclamation point (not '\"')")
def endswith_quote():
"""Whitespace at the end, but also a quote" """

View File

@@ -0,0 +1,73 @@
def f():
"Here's a line without a period"
...
def f():
"""Here's a line without a period"""
...
def f():
"""
Here's a line without a period,
but here's the next line
"""
...
def f():
"""Here's a line without a period"""
...
def f():
"""
Here's a line without a period,
but here's the next line"""
...
def f():
"""
Here's a line without a period,
but here's the next line with trailing space """
...
def f():
r"Here's a line without a period"
...
def f():
r"""Here's a line without a period"""
...
def f():
r"""
Here's a line without a period,
but here's the next line
"""
...
def f():
r"""Here's a line without a period"""
...
def f():
r"""
Here's a line without a period,
but here's the next line"""
...
def f():
r"""
Here's a line without a period,
but here's the next line with trailing space """
...

View File

@@ -0,0 +1,16 @@
"""Test: noqa directives."""
from module import (
A, # noqa: F401
B,
)
from module import (
A, # noqa: F401
B, # noqa: F401
)
from module import (
A,
B,
)

View File

@@ -4,3 +4,6 @@ a = "wrong"
hidden = {"a": "!"}
"%(a)s %(c)s" % {"x": 1, **hidden} # Ok (cannot see through splat)
"%(a)s" % {"a": 1, r"b": "!"} # F504 ("b" not used)
"%(a)s" % {'a': 1, u"b": "!"} # F504 ("b" not used)

View File

@@ -9,3 +9,4 @@ e = (
)
g = f"ghi{123:{45}}"
h = "x" "y" f"z"

View File

@@ -4,5 +4,18 @@ if x is "abc":
if 123 is not y:
pass
if 123 is \
not y:
pass
if "123" is x < 3:
pass
if "123" != x is 3:
pass
if ("123" != x) is 3:
pass
if "123" != (x is 3):
pass

View File

@@ -0,0 +1,11 @@
def foo(x):
return x
@foo
def bar():
pass
def bar():
pass

View File

@@ -0,0 +1 @@
import fu as FU, bar as FU

View File

@@ -0,0 +1,8 @@
"""Test that importing a module twice using a nested does not issue a warning."""
try:
if True:
if True:
import os
except:
import os
os.path

View File

@@ -0,0 +1,9 @@
try:
from aa import mixer
except AttributeError:
from bb import mixer
except RuntimeError:
from cc import mixer
except:
from dd import mixer
mixer(123)

View File

@@ -0,0 +1,7 @@
try:
from aa import mixer
except ImportError:
pass
else:
from bb import mixer
mixer(123)

View File

@@ -0,0 +1,8 @@
try:
import funca
except ImportError:
from bb import funca
from bb import funcb
else:
from bbb import funcb
print(funca, funcb)

View File

@@ -0,0 +1,10 @@
try:
import b
except ImportError:
b = Ellipsis
from bb import a
else:
from aa import a
finally:
a = 42
print(a, b)

View File

@@ -0,0 +1,5 @@
import fu
def fu():
pass

View File

@@ -0,0 +1,9 @@
"""Test that shadowing a global with a nested function generates a warning."""
import fu
def bar():
def baz():
def fu():
pass

View File

@@ -0,0 +1,10 @@
"""Test that shadowing a global name with a nested function generates a warning."""
import fu
def bar():
import fu
def baz():
def fu():
pass

View File

@@ -0,0 +1,14 @@
"""Test that a global import which is redefined locally, but used later in another scope does not generate a warning."""
import unittest, transport
class GetTransportTestCase(unittest.TestCase):
def test_get_transport(self):
transport = 'transport'
self.assertIsNotNone(transport)
class TestTransportMethodArgs(unittest.TestCase):
def test_send_defaults(self):
transport.Transport()

View File

@@ -0,0 +1,7 @@
"""If an imported name is redefined by a class statement which also uses that name in the bases list, no warning is emitted."""
from fu import bar
class bar(bar):
pass

View File

@@ -0,0 +1 @@
from moo import fu as FU, bar as FU

View File

@@ -0,0 +1,13 @@
"""
Test that shadowing a global with a class attribute does not produce a
warning.
"""
import fu
class bar:
fu = 1
print(fu)

View File

@@ -0,0 +1 @@
import fu; fu = 3

View File

@@ -0,0 +1 @@
import fu; fu, bar = 3

View File

@@ -0,0 +1 @@
import fu; [fu, bar] = 3

View File

@@ -0,0 +1,7 @@
"""Test that importing a module twice within an if block does raise a warning."""
i = 2
if i == 1:
import os
import os
os.path

View File

@@ -0,0 +1,8 @@
"""Test that importing a module twice in if-else blocks does not raise a warning."""
i = 2
if i == 1:
import os
else:
import os
os.path

View File

@@ -0,0 +1,8 @@
"""Test that importing a module twice in a try block does raise a warning."""
try:
import os
import os
except:
pass
os.path

View File

@@ -0,0 +1,7 @@
"""Test that importing a module twice in a try block does not raise a warning."""
try:
import os
except:
import os
os.path

View File

@@ -0,0 +1,17 @@
"""Test: annotated global."""
n: int
def f():
print(n)
def g():
global n
n = 1
g()
f()

View File

@@ -0,0 +1,13 @@
"""Test: Mypy extensions."""
from mypy_extensions import DefaultNamedArg
# OK
_ = DefaultNamedArg(bool | None, name="some_prop_name")
_ = DefaultNamedArg(type=bool | None, name="some_prop_name")
_ = DefaultNamedArg(bool | None, "some_prop_name")
# Not OK
_ = DefaultNamedArg("Undefined", name="some_prop_name")
_ = DefaultNamedArg(type="Undefined", name="some_prop_name")
_ = DefaultNamedArg("Undefined", "some_prop_name")

View File

@@ -10,13 +10,13 @@ except ValueError as e:
print(e)
def f1():
def f():
x = 1
y = 2
z = x + y
def f2():
def f():
foo = (1, 2)
(a, b) = (1, 2)
@@ -26,12 +26,12 @@ def f2():
(x, y) = baz = bar
def f3():
def f():
locals()
x = 1
def f4():
def f():
_ = 1
__ = 1
_discarded = 1
@@ -40,28 +40,49 @@ def f4():
a = 1
def f5():
def f():
global a
# Used in `f7` via `nonlocal`.
# Used in `c` via `nonlocal`.
b = 1
def f6():
def c():
# F841
b = 1
def f7():
def d():
nonlocal b
def f6():
def f():
annotations = []
assert len([annotations for annotations in annotations])
def f7():
def f():
def connect():
return None, None
with connect() as (connection, cursor):
cursor.execute("SELECT * FROM users")
def f():
def connect():
return None, None
with (connect() as (connection, cursor)):
cursor.execute("SELECT * FROM users")
def f():
with open("file") as my_file, open("") as ((this, that)):
print("hello")
def f():
with (
open("file") as my_file,
open("") as ((this, that)),
):
print("hello")

View File

@@ -0,0 +1,24 @@
def f(tup):
x, y = tup # this does NOT trigger F841
def f():
x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed
def f():
(x, y) = coords = 1, 2 # this does NOT trigger F841
if x > 1:
print(coords)
def f():
(x, y) = coords = 1, 2 # this triggers F841 on coords
def f():
coords = (x, y) = 1, 2 # this triggers F841 on coords
def f():
(a, b) = (x, y) = 1, 2 # this triggers F841 on everything

View File

@@ -0,0 +1,13 @@
def f():
name: str
age: int
class A:
name: str
age: int
class B:
name: str = "Bob"
age: int = 18

View File

@@ -1,7 +1,7 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import List, Optional
from typing import Callable, List, Tuple, Optional, Sequence
from models import (
Fruit,
@@ -37,4 +37,12 @@ def f(x: int) -> List[int]:
return y
x: Optional[int] = None
x: Tuple[int, ...] = (1, 2)
def f(param: "Optional[Callable]" = None) -> "None":
pass
def f(param: Optional["Sequence"] = None) -> "None":
pass

Some files were not shown because too many files have changed in this diff Show More