Compare commits

...

210 Commits

Author SHA1 Message Date
Charlie Marsh
ea9acda732 Bump version to 0.0.143 2022-11-28 15:42:25 -05:00
Anders Kaseorg
6c8021e970 Fix clippy::manual_let_else (pedantic) (#939) 2022-11-28 09:52:59 -05:00
Charlie Marsh
61b6ad46ea Allow @override methods to be undocumented (#941) 2022-11-28 09:52:12 -05:00
Anders Kaseorg
041d8108e6 Don’t require files with --explain or --generate-shell-completion (#937) 2022-11-28 00:40:20 -05:00
Charlie Marsh
e2c4a098de Bump version to 0.0.142 2022-11-28 00:19:27 -05:00
Charlie Marsh
e865f58426 Add all plugin options to README reference (#936) 2022-11-28 00:19:14 -05:00
messense
23b4e16b1d Add shell completions support (#935) 2022-11-27 23:59:36 -05:00
Charlie Marsh
ae2ac905dc Document all top-level configuration options (#934) 2022-11-27 23:50:24 -05:00
Charlie Marsh
55619b321a Run cargo fmt 2022-11-27 22:58:42 -05:00
Harutaka Kawamura
6f31b002f8 Do not enforce line length limit for comments ending with a URL (#920) 2022-11-27 22:36:17 -05:00
Charlie Marsh
1a79965aa0 Allow varargs and kwargs to be prefixed with stars (#933) 2022-11-27 22:08:27 -05:00
Charlie Marsh
16da183f8e Add some user testimonials (#932) 2022-11-27 21:55:01 -05:00
Charlie Marsh
3f689917cb Use alternative TOML format for per-file-ignores in README (#931) 2022-11-27 21:38:43 -05:00
Charlie Marsh
a4a215e8a3 Add Homebrew installation to README (#930) 2022-11-27 21:37:34 -05:00
Charlie Marsh
aa1c884910 Tweak Flake8 parity in README 2022-11-27 21:34:47 -05:00
Oliver Margetts
7fb55c6d99 F50x implementation (#919) 2022-11-27 21:30:55 -05:00
Charlie Marsh
04ea523ad8 Track aliased import-from members (#929) 2022-11-27 17:27:27 -05:00
Charlie Marsh
9897f81cf3 Bump version to 0.0.141 2022-11-26 16:33:08 -05:00
Charlie Marsh
1a2559b001 Avoid flagging redundant open modes when open is rebound (#918) 2022-11-26 16:24:41 -05:00
Denis Gavrilyuk
721a1e9443 Add flake8-debugger (#909) 2022-11-26 16:21:03 -05:00
Charlie Marsh
f38bba18ee Fix clippy warnings 2022-11-26 15:56:33 -05:00
Charlie Marsh
14cf36f922 Bump version to 0.0.140 2022-11-26 15:05:46 -05:00
Charlie Marsh
d28e026525 Preserve existing noqa codes in --add-noqa (#913) 2022-11-26 14:42:19 -05:00
Jonathan Plasse
d19a8aa54d Auto-generate CheckCodePrefix::fixables() (#916) 2022-11-26 14:10:30 -05:00
Charlie Marsh
f299940452 Respect noqa comments in U009 (#917) 2022-11-26 14:03:18 -05:00
Charlie Marsh
e1ab7163ac Respect f-string locations in B023 check (#914) 2022-11-26 10:31:23 -05:00
Jonathan Plasse
9edc479c6c Fix F821 false positive (#911) 2022-11-26 10:12:07 -05:00
Charlie Marsh
560558b814 Bump version to 0.0.139 2022-11-25 18:38:26 -05:00
Andri Bergsson
bef601b994 Add keyword argument handling for redundant open modes. (#906) 2022-11-25 18:38:05 -05:00
Charlie Marsh
7445d00b88 Implement B023 (function uses loop variable) (#907) 2022-11-25 18:29:54 -05:00
Charlie Marsh
7c78d4e103 Use docstring comment for CheckCode 2022-11-25 13:15:43 -05:00
Oliver Margetts
8b14f1b8cc Implement F522-F525 (#899) 2022-11-25 13:14:31 -05:00
Xuan (Sean) Hu
5a6b51e623 Minor changes in README. (#903) 2022-11-25 09:49:16 -05:00
Charlie Marsh
0b60242fb7 Bump version to 0.0.138 2022-11-25 00:05:41 -05:00
Charlie Marsh
65b77feeb8 Bump LibCST version 2022-11-25 00:05:03 -05:00
Charlie Marsh
04b9c0a31d Fix cargo clippy 2022-11-24 23:40:43 -05:00
Harutaka Kawamura
49dc8231be Fix typo (#902) 2022-11-24 23:38:45 -05:00
Charlie Marsh
92ca114882 Move some main.rs subcommands to a new module (#901) 2022-11-24 22:43:43 -05:00
Charlie Marsh
553bc7443a Remove UserConfiguration struct (#900) 2022-11-24 22:39:07 -05:00
CelebrateVC
a3af6c1ea5 Implement GlobSet optimization for file path exclusions (#883) 2022-11-24 22:31:55 -05:00
Charlie Marsh
b50016fe89 Regenerate README.md 2022-11-24 18:10:07 -05:00
Oliver Margetts
2cf2805848 Implement F521 (#898) 2022-11-24 18:09:36 -05:00
Harutaka Kawamura
33fbef7700 Implement B904 (#892) 2022-11-24 09:49:57 -05:00
Charlie Marsh
68668a584b Bump version to 0.0.137 2022-11-23 20:28:45 -05:00
Charlie Marsh
6cd8655d29 Treat withitem variables as bindings (#897) 2022-11-23 20:28:37 -05:00
Charlie Marsh
72a9bd3cfb Revert "Upload wheels back to GitHub Releases (#884)"
This reverts commit bd08fc359d.
2022-11-23 20:27:33 -05:00
Charlie Marsh
58aac21a36 Bump version to 0.0.136 2022-11-23 17:41:17 -05:00
Charlie Marsh
77e0be3464 Visit iter prior to target in comprehensions (#895) 2022-11-23 10:13:21 -05:00
Charlie Marsh
bd08fc359d Upload wheels back to GitHub Releases (#884) 2022-11-23 00:06:36 -05:00
Harutaka Kawamura
19ad6ab4f5 Add --explain (#887) 2022-11-23 00:06:25 -05:00
Charlie Marsh
4b2df99e78 Set rust-version in Cargo.toml (#886) 2022-11-22 23:33:39 -05:00
Charlie Marsh
66975876b2 Bump version to 0.0.135 2022-11-22 19:21:53 -05:00
Charlie Marsh
7316b120ba Log errors in add_noqa and autoformat calls (#881) 2022-11-22 19:21:36 -05:00
Charlie Marsh
fec887e481 Apply a limit to the number of fix iterations (#882) 2022-11-22 19:21:31 -05:00
Charlie Marsh
bdd32c0850 Enforce most pedantic lints on CI (#878) 2022-11-22 18:55:57 -05:00
Charlie Marsh
10dcd5fd0a Remove unused imports 2022-11-22 18:45:24 -05:00
Charlie Marsh
b922e6ecc8 Fix clippy::unnecessary_wraps (pedantic) (#880)
https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
2022-11-22 18:25:30 -05:00
Charlie Marsh
f59799e0c4 Remove lingering empty lines 2022-11-22 17:54:24 -05:00
Charlie Marsh
814ddeb7ea Remove always-inline (#879) 2022-11-22 17:13:25 -05:00
Charlie Marsh
9315b9f459 Remove Mode from various internal checkers (#877) 2022-11-22 16:57:47 -05:00
Charlie Marsh
113b5a10bf Return Vec<Check> from check_tokens (#876) 2022-11-22 16:43:32 -05:00
Charlie Marsh
fe77fb70a1 Apply autofixes iteratively until code is stabilized (#875) 2022-11-22 16:37:52 -05:00
Charlie Marsh
c3f6170503 Update README with list of projects (#874) 2022-11-22 14:28:02 -05:00
Anders Kaseorg
a46160f0e2 Fix clippy::unreadable_literal (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
9a66cf2ffb Fix clippy::uninlined_format_args (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
3205473612 Ignore clippy::struct_excessive_bools (pedantic)
“consider using a state machine or refactoring bools into two-variant
enums”

https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
0bb8b14ae1 Fix clippy::single_match_else (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
9cf4621071 Fix clippy::redundant_else (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
bbc9ed1b21 Fix clippy::redundant_closure_for_method_calls (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
517ca2604a Fix clippy::needless_pass_by_value (pedantic)
“this argument is passed by value, but not consumed in the function
body”

https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
348ff509c0 Fix clippy::mut_mut (pedantic)
“this expression mutably borrows a mutable reference. Consider
reborrowing”

https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
fb545551f8 Fix clippy::match_bool (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#match_bool

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
3b33a431d6 Fix clippy::map_unwrap_or (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
bc95690725 Fix clippy::manual_string_new (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
9dc788d089 Fix clippy::let_underscore_drop (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
15f63494a7 Fix clippy::items_after_statements (pedantic)
“adding items after statements is confusing, since items exist from
the start of the scope”

https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
68668c20e8 Fix clippy::if_not_else (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
9462335371 Fix clippy::from_iter_instead_of_collect (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
3dd6522b4f Fix clippy::explicit_iter_loop (pedantic)
“it is more concise to loop over references to containers instead of
using explicit iteration methods”

https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
6b935121a7 Fix clippy::explicit_deref_methods (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
8d9d9b3204 Fix clippy::documentation_markdown (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
0a506eff34 Fix clippy::cloned_instead_of_copied (pedantic)
“used `cloned` where `copied` could be used instead”

https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
7c7489c1dd Ignore clippy::cast_possible_truncation (pedantic)
“casting `usize` to `u8` may truncate the value”

https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
ae90eccb7f Fix clippy::cast_lossless (pedantic)
“casting `bool` to `u8` is more cleanly stated with `u8::from(_)`”

https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
7a61edbe46 Fix clippy::default-trait-access (pedantic) (#867) 2022-11-21 21:00:38 -05:00
Charlie Marsh
59615486d8 Bump version to 0.0.134 2022-11-21 16:15:23 -05:00
Andri Bergsson
ce116a80ad Automatically remove redundant open modes #640 (#843) 2022-11-21 16:06:41 -05:00
Anders Kaseorg
731fba9006 Ignore clippy::unreadable-literal (pedantic) for CONFUSABLES (#864) 2022-11-21 16:00:05 -05:00
Anders Kaseorg
9bcf194fdc Ignore clippy::match-same-arms (pedantic) in a few places (#863) 2022-11-21 15:59:58 -05:00
Anders Kaseorg
58949b564e Fix clippy::trivially-copy-pass-by-ref (pedantic) (#862) 2022-11-21 15:59:51 -05:00
Anders Kaseorg
6b9e57fb78 Fix clippy::sort-unstable (pedantic) (#861) 2022-11-21 15:59:41 -05:00
Anders Kaseorg
cb119401a7 Fix clippy::inefficient-to-string (pedantic) (#860) 2022-11-21 15:59:35 -05:00
Charlie Marsh
0b9188011b Bump version to 0.0.133 2022-11-21 13:39:37 -05:00
Anders Kaseorg
b657d912d9 Propagate errors from glob::Pattern::new (#858) 2022-11-21 13:39:19 -05:00
Charlie Marsh
1559671093 Target isort code in README.md 2022-11-21 13:38:01 -05:00
Charlie Marsh
70a53bf12b Add unit tests for complexity check (#859) 2022-11-21 13:31:17 -05:00
Charlie Marsh
cd1e07f37c Avoid incrementing McCabe complexity for class methods (#857) 2022-11-21 13:30:36 -05:00
Charlie Marsh
7bd6db62d9 Sort relative imports by parent level descending (#856) 2022-11-21 13:30:24 -05:00
Charlie Marsh
f8b49f308d Upgrade RustPython (#855) 2022-11-21 13:20:03 -05:00
Charlie Marsh
b1f9c7b6bd Update default complexity in README.md 2022-11-21 13:19:55 -05:00
Keming
0867d2ded9 Make it visible under light theme (#854) 2022-11-21 10:18:21 -05:00
messense
3f597a3b30 Upgrade maturin to 0.14 (#846) 2022-11-21 10:00:14 -05:00
Charlie Marsh
6733aad216 Avoid attempting to fix PEP 604 violations with deferred annotations (#845) 2022-11-20 21:41:54 -05:00
Harutaka Kawamura
89980ad651 Implement autofix for B013 (#824) 2022-11-20 18:49:07 -05:00
Charlie Marsh
38f896502a Bump version to 0.0.132 2022-11-20 18:10:13 -05:00
Jonathan Plasse
7cab541343 Add convert exit() to sys.exit() rule (#816) 2022-11-20 18:09:40 -05:00
Charlie Marsh
1a3d2ead41 Support PEP 562 (#841) 2022-11-20 17:55:57 -05:00
Jonathan Plasse
f96c64b40d Fix N804 class method with positional only args (#836) 2022-11-20 15:48:09 -05:00
Charlie Marsh
0791869451 Add RUF to list of fixable defaults (#838) 2022-11-20 15:40:14 -05:00
Charlie Marsh
965918744b Replace FNV with rustc-hash (#837) 2022-11-20 15:38:31 -05:00
Charlie Marsh
6b4aedb366 Bump version to 0.0.131 2022-11-20 13:40:58 -05:00
Charlie Marsh
8123e3e94e Remove extraneous Python file 2022-11-20 13:39:55 -05:00
Charlie Marsh
9f9a545c51 Improve cache performance by removing cacache dependency (#833) 2022-11-20 13:36:33 -05:00
Charlie Marsh
5bf8219db3 Make main.rs robust to cache initialization failures (#831) 2022-11-20 11:05:17 -05:00
Charlie Marsh
529513bf02 Add CACHEDIR.TAG to .ruff_cache (#830) 2022-11-20 10:53:31 -05:00
Charlie Marsh
124782771f Bump version to 0.0.130 2022-11-20 10:37:19 -05:00
Charlie Marsh
98cab5cdba Add class names to NamedTuple and TypedDict rules (#829) 2022-11-20 10:29:47 -05:00
Martin Lehoux
40f38c94a5 Implement U014: Convert NamedTuple function to class (#819) 2022-11-20 10:26:15 -05:00
Harutaka Kawamura
7839204bf7 Implement autofix for B010 (#823) 2022-11-20 10:14:29 -05:00
Jonathan Plasse
e63ea704f0 Adjust U011 start location (#828) 2022-11-20 10:13:29 -05:00
Charlie Marsh
4be09b45ea Bump version to 0.0.129 2022-11-19 19:52:40 -05:00
Harutaka Kawamura
13e8ed0a0a Implement autofix for E731 (#814) 2022-11-19 19:51:41 -05:00
Anders Kaseorg
4161d4ae32 Exempt parameters with immutable annotations from B006 (#821) 2022-11-19 19:46:08 -05:00
Charlie Marsh
99f7854d8c Mark nonlocal variables as used in parent scopes (#822) 2022-11-19 19:21:02 -05:00
Harutaka Kawamura
a580d1a858 Adjust UnusedNOQA start location (#817) 2022-11-19 09:30:02 -05:00
Martin Lehoux
86806a9e39 U013: Also convert typing.TypedDict (#810) 2022-11-19 09:29:05 -05:00
Charlie Marsh
89afc9db74 Bump version to 0.0.128 2022-11-18 18:50:03 -05:00
Charlie Marsh
0f34cdb7a3 Enable customization of autofixable error codes (#811) 2022-11-18 18:49:13 -05:00
Charlie Marsh
437b6f23b9 Remove warn_on checks (#812) 2022-11-18 18:48:24 -05:00
Charlie Marsh
0fe2b15676 Change NotInTest to NotIsTest 2022-11-18 18:23:40 -05:00
Harutaka Kawamura
e81efa5a3d Implement a --show-source setting (#698) 2022-11-18 14:02:29 -05:00
Charlie Marsh
49559da54e Bump version to 0.0.127 2022-11-18 13:31:22 -05:00
Jonathan Plasse
b74fd1fe13 Change error code of flake8-blind-except (#808) 2022-11-18 13:30:36 -05:00
Charlie Marsh
9c4d24a452 Add flake8-boolean-trap to README 2022-11-18 12:36:13 -05:00
pwoolvett
7a4449eacb Add flake8-boolean-trap (#790) 2022-11-18 12:30:07 -05:00
Charlie Marsh
ee31fa6109 Reduce newlines in code gen (#807) 2022-11-18 12:27:56 -05:00
Harutaka Kawamura
6ffe767252 Implement autofix for E713 and E714 (#804) 2022-11-18 12:16:11 -05:00
Jonathan Plasse
2f894e3951 Add flake8-blind-except (#805) 2022-11-18 12:15:10 -05:00
Charlie Marsh
589d923c99 Misc. follow-ups to #716 (#806) 2022-11-18 12:14:41 -05:00
Martin Lehoux
c5722d8a4d Implement U013: Unnecessary TypedDict syntactic form (#716) 2022-11-18 12:10:47 -05:00
Jonathan Plasse
c2d6307e9b Add missing plugins in some sections of README.md (#802) 2022-11-18 09:28:33 -05:00
Edgar R. M
f44fada446 Implement C901 (mccabe) (#765) 2022-11-17 17:40:50 -05:00
Charlie Marsh
6a6f4651aa Bump version to 0.0.126 2022-11-17 17:19:19 -05:00
Charlie Marsh
66ae4db6cd Ignore globals when checking local variable names (#800) 2022-11-17 17:19:01 -05:00
Charlie Marsh
801c76037f Except BaseException from N818 checks (#798) 2022-11-17 15:04:42 -05:00
Charlie Marsh
ab825eb28d Fix D202 to remove line after docstring (#797) 2022-11-17 15:01:58 -05:00
Charlie Marsh
826ef7da67 Trigger N818 when parent ends in Error or Exception (#796) 2022-11-17 14:51:40 -05:00
Charlie Marsh
72f5393d3a Add flake8-tidy-imports to cache key 2022-11-17 14:46:45 -05:00
Charlie Marsh
6602f7f489 Trim dedented sections for arg detection (#793) 2022-11-17 12:55:55 -05:00
Charlie Marsh
aafddae644 Bump version to 0.0.125 2022-11-17 12:07:05 -05:00
Charlie Marsh
749df87de0 Tweak presentation of null-ls and efm docs (#791) 2022-11-17 12:04:11 -05:00
Eddie Bergman
d67db33f22 docs(integrations): neovim null-ls integration (#782) 2022-11-17 11:55:08 -05:00
Charlie Marsh
f0a54716e5 Implement flake8-tidy-imports (#789) 2022-11-17 11:44:06 -05:00
Harutaka Kawamura
c59e1ff0b5 Implement auto-fix for E711 and E712 (#784) 2022-11-17 11:43:44 -05:00
Jonathan Plasse
ecf858cf16 Add the tools identifier in the TOC (#779) 2022-11-17 11:34:32 -05:00
Jonathan Plasse
8063aee006 Remove unnecessary abspath rule (U002) (#781) 2022-11-17 11:29:42 -05:00
Anders Kaseorg
f1fee5d240 Propagate exit code through Python __main__ wrapper (#776) 2022-11-16 16:16:58 -05:00
Anders Kaseorg
d3155560df Fix find_and_parse_pyproject_toml test for #772 (#774) 2022-11-16 13:47:25 -05:00
Charlie Marsh
90bfc4ec4d Bump version to 0.0.124 2022-11-16 12:25:24 -05:00
Charlie Marsh
b04a6a3f7c Support arbitrary expression paths for class and static decorators (#772) 2022-11-16 12:24:46 -05:00
Charlie Marsh
8ec14e7ee2 Bump version to 0.0.123 2022-11-16 12:06:01 -05:00
Charlie Marsh
17c5cd7c42 Fix off-by-one in noqa map detection (#771)
Fix off-by-one in noqa
2022-11-16 12:00:10 -05:00
Charlie Marsh
7d8360a1de Change all &Option<> to Option<&> (#768) 2022-11-16 09:40:01 -05:00
Harutaka Kawamura
910ee523dd Fix E731 (#766) 2022-11-16 09:17:14 -05:00
Charlie Marsh
72e35a535e Run cargo fmt 2022-11-16 09:15:22 -05:00
Charlie Marsh
b4e1563517 Avoid allocations for binding values (#764) 2022-11-16 08:55:35 -05:00
Charlie Marsh
5717cc97d7 Add references to Flake8 licenses 2022-11-15 23:07:41 -05:00
Charlie Marsh
2c89a19f76 Bump Ruff version to 0.0.122 2022-11-15 22:03:46 -05:00
Charlie Marsh
82fea36bb3 Preserve comments when sorting imports (#749) 2022-11-15 22:02:52 -05:00
Charlie Marsh
63d63e8c12 Increase retry counts in GitHub Actions workflows (#763) 2022-11-15 17:21:16 -05:00
Charlie Marsh
9d136de55a Bump version to 0.0.121 2022-11-15 16:18:39 -05:00
Harutaka Kawamura
1821c07367 Implement B020 (#753) 2022-11-15 16:17:03 -05:00
Charlie Marsh
1fe90ef7f4 Only notify once for each app update (#762) 2022-11-15 16:14:10 -05:00
Charlie Marsh
b5cb9485f6 Move updater to its own module 2022-11-15 15:51:24 -05:00
Charlie Marsh
4d798512b1 Only print version checks on tty (#761) 2022-11-15 15:36:06 -05:00
Charlie Marsh
5f9815b103 Disable auto-updates in JSON mode (#760) 2022-11-15 15:29:10 -05:00
Charlie Marsh
0d3fac1bf9 Add --line-length command line argument (#759) 2022-11-15 12:23:38 -05:00
Charlie Marsh
ff0e5f5cb4 Preserve scopes when checking deferred strings (#758) 2022-11-15 12:19:22 -05:00
Charlie Marsh
374d57d822 Limit PEP 604 checks to Python 3.10+ (#757) 2022-11-15 11:52:12 -05:00
Edgar R. M
85b2a9920f docs: Add flake8-bandit to ToC (#750) 2022-11-15 00:11:39 -05:00
Charlie Marsh
3c22913470 Bump version to 0.0.120 2022-11-14 22:53:36 -05:00
Charlie Marsh
ea03a59b72 De-alias Literal checks (#748) 2022-11-14 22:53:23 -05:00
Charlie Marsh
058a5276b0 Bump version to 0.0.119 2022-11-14 21:45:41 -05:00
Charlie Marsh
62d4096be3 Move bindings to FNV map (#747) 2022-11-14 21:42:57 -05:00
Charlie Marsh
8961da7b89 Add support for import alias tracking (#746) 2022-11-14 21:29:30 -05:00
Brett Cannon
58bcffbe2d Add isort to the README's ToC (#745) 2022-11-14 18:51:39 -05:00
Charlie Marsh
f67727b13c Improve performance of import matching code (#744) 2022-11-14 17:14:22 -05:00
Charlie Marsh
fea029ae35 Bump version to 0.0.118 2022-11-14 13:21:27 -05:00
Harutaka Kawamura
3e3c3c7421 Ignore namedtuple assignment in N806, N815, and N816 (#735) 2022-11-14 13:21:04 -05:00
Harutaka Kawamura
9047bf680d Implement B024 and B027 (#738) 2022-11-14 13:12:23 -05:00
Charlie Marsh
d170388b7b Allow second line as 'first line' for punctuation (#741) 2022-11-14 13:07:27 -05:00
Charlie Marsh
502d3316f9 Add flake8-bugbear settings to hash (#739) 2022-11-14 12:29:47 -05:00
Harutaka Kawamura
a8159f9893 Implement B022 (#734) 2022-11-14 09:24:05 -05:00
Charlie Marsh
71f727c380 Use FNV hasher in more places (#732) 2022-11-13 23:44:16 -05:00
Charlie Marsh
ce3c45a361 Make combine-as-imports the default import sorting behavior (#731) 2022-11-13 23:07:13 -05:00
Charlie Marsh
29ae6c159d Add FastAPI to README (#730) 2022-11-13 22:27:35 -05:00
Charlie Marsh
1ae07b4c70 Allow explicit re-export of straight imports (#729) 2022-11-13 22:26:48 -05:00
Charlie Marsh
08ca8788a7 Bump version to 0.0.117 2022-11-13 16:10:29 -05:00
Charlie Marsh
8a97a76038 Make # noqa detection case-insensitive (#728) 2022-11-13 16:09:44 -05:00
Brett Cannon
d2d84cf5bf Fix Markdown in README (#727) 2022-11-13 15:19:25 -05:00
Anders Kaseorg
450970e0e6 Restore clippy on all crates in the workspace (#725) 2022-11-13 15:18:57 -05:00
Charlie Marsh
34ecc69914 Don't mark re-exported symbols as unused (#724) 2022-11-13 14:31:43 -05:00
Charlie Marsh
a310aed128 Bump version to 0.0.116 2022-11-13 13:46:05 -05:00
Harutaka Kawamura
43cc8bc84e Lint test code (#721) 2022-11-13 11:55:57 -05:00
Harutaka Kawamura
84bf36194b Implement B012 (#718) 2022-11-13 11:55:33 -05:00
Harutaka Kawamura
e4d168bb4f Implement B021 (#719) 2022-11-13 11:40:24 -05:00
Charlie Marsh
439642addf Regenerate README 2022-11-13 10:55:14 -05:00
Charlie Marsh
f5b1f957e3 Improve some import tracking code (#715) 2022-11-13 00:10:13 -05:00
Charlie Marsh
8f99705795 Make some error messages more grammatically consistent 2022-11-12 16:57:23 -05:00
Charlie Marsh
9ec7e6bcd6 Add function name to B008 message 2022-11-12 16:53:13 -05:00
407 changed files with 16787 additions and 5186 deletions

View File

@@ -6,6 +6,11 @@ on:
pull_request:
branches: [main]
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
RUSTUP_MAX_RETRIES: 10
jobs:
cargo_build:
name: "cargo build"
@@ -77,7 +82,7 @@ jobs:
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- run: cargo clippy --all -- -D warnings
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic
cargo_test:
name: "cargo test"

View File

@@ -10,6 +10,9 @@ env:
PACKAGE_NAME: flake8-to-ruff
CRATE_NAME: flake8_to_ruff
PYTHON_VERSION: "3.7" # to build abi3 wheels
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
RUSTUP_MAX_RETRIES: 10
jobs:
macos-x86_64:
@@ -31,7 +34,6 @@ jobs:
with:
target: x86_64
args: --release --out dist --sdist -m ./${{ env.CRATE_NAME }}/Cargo.toml
maturin-version: "v0.13.0"
- name: Install built wheel - x86_64
run: |
pip install dist/${{ env.CRATE_NAME }}-*.whl --force-reinstall
@@ -58,7 +60,6 @@ jobs:
uses: messense/maturin-action@v1
with:
args: --release --universal2 --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
maturin-version: "v0.13.0"
- name: Install built wheel - universal2
run: |
pip install dist/${{ env.CRATE_NAME }}-*universal2.whl --force-reinstall
@@ -90,7 +91,6 @@ jobs:
with:
target: ${{ matrix.target }}
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
maturin-version: "v0.13.0"
- name: Install built wheel
shell: bash
run: |
@@ -118,7 +118,6 @@ jobs:
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
maturin-version: "v0.13.0"
- name: Install built wheel
if: matrix.target == 'x86_64'
run: |
@@ -145,7 +144,6 @@ jobs:
target: ${{ matrix.target }}
manylinux: auto
args: --no-default-features --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
maturin-version: "v0.13.0"
- uses: uraimo/run-on-arch-action@v2.0.5
if: matrix.target != 'ppc64'
name: Install built wheel
@@ -184,7 +182,6 @@ jobs:
target: ${{ matrix.target }}
manylinux: musllinux_1_2
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
maturin-version: "v0.13.0"
- name: Install built wheel
if: matrix.target == 'x86_64-unknown-linux-musl'
uses: addnab/docker-run-action@v3
@@ -220,7 +217,6 @@ jobs:
target: ${{ matrix.platform.target }}
manylinux: musllinux_1_2
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
maturin-version: "v0.13.0"
- uses: uraimo/run-on-arch-action@master
name: Install built wheel
with:
@@ -258,7 +254,6 @@ jobs:
- name: Build wheels
uses: messense/maturin-action@v1
with:
maturin-version: "v0.13.0"
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist -i pypy${{ matrix.python-version }} -m ./${{ env.CRATE_NAME }}/Cargo.toml

View File

@@ -12,6 +12,9 @@ concurrency:
env:
PACKAGE_NAME: ruff
PYTHON_VERSION: "3.7" # to build abi3 wheels
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
RUSTUP_MAX_RETRIES: 10
jobs:
macos-x86_64:
@@ -33,7 +36,6 @@ jobs:
with:
target: x86_64
args: --release --out dist --sdist
maturin-version: "v0.13.0"
- name: Install built wheel - x86_64
run: |
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
@@ -60,7 +62,6 @@ jobs:
uses: messense/maturin-action@v1
with:
args: --release --universal2 --out dist
maturin-version: "v0.13.0"
- name: Install built wheel - universal2
run: |
pip install dist/${{ env.PACKAGE_NAME }}-*universal2.whl --force-reinstall
@@ -92,7 +93,6 @@ jobs:
with:
target: ${{ matrix.target }}
args: --release --out dist
maturin-version: "v0.13.0"
- name: Install built wheel
shell: bash
run: |
@@ -120,7 +120,6 @@ jobs:
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist
maturin-version: "v0.13.0"
- name: Install built wheel
if: matrix.target == 'x86_64'
run: |
@@ -147,7 +146,6 @@ jobs:
target: ${{ matrix.target }}
manylinux: auto
args: --no-default-features --release --out dist
maturin-version: "v0.13.0"
- uses: uraimo/run-on-arch-action@v2.0.5
if: matrix.target != 'ppc64'
name: Install built wheel
@@ -186,7 +184,6 @@ jobs:
target: ${{ matrix.target }}
manylinux: musllinux_1_2
args: --release --out dist
maturin-version: "v0.13.0"
- name: Install built wheel
if: matrix.target == 'x86_64-unknown-linux-musl'
uses: addnab/docker-run-action@v3
@@ -222,7 +219,6 @@ jobs:
target: ${{ matrix.platform.target }}
manylinux: musllinux_1_2
args: --release --out dist
maturin-version: "v0.13.0"
- uses: uraimo/run-on-arch-action@master
name: Install built wheel
with:
@@ -260,7 +256,6 @@ jobs:
- name: Build wheels
uses: messense/maturin-action@v1
with:
maturin-version: "v0.13.0"
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist -i pypy${{ matrix.python-version }}

View File

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

View File

@@ -98,6 +98,13 @@ _and_ a `pyproject.toml` parameter to `src/pyproject.rs`. If you want to pattern
existing example, grep for `dummy_variable_rgx`, which defines a regular expression to match against
acceptable unused variables (e.g., `_`).
If the new plugin's configuration should be cached between runs, you'll need to add it to the
`Hash` implementation for `Settings` in `src/settings/mod.rs`.
You may also want to add the new configuration option to the `flake8-to-ruff` tool, which is
responsible for converting `flake8` configuration files to Ruff's TOML format. This logic
lives in `flake8_to_ruff/src/converter.rs`.
## Release process
As of now, Ruff has an ad hoc release process: releases are cut with high frequency via GitHub

720
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,27 +6,31 @@ members = [
[package]
name = "ruff"
version = "0.0.115"
version = "0.0.143"
edition = "2021"
rust-version = "1.65.0"
[lib]
name = "ruff"
[dependencies]
annotate-snippets = { version = "0.9.1", features = ["color"] }
anyhow = { version = "1.0.66" }
atty = { version = "0.2.14" }
bincode = { version = "1.3.3" }
bitflags = { version = "1.3.2" }
cachedir = { version = "0.3.0" }
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
clap = { version = "4.0.1", features = ["derive"] }
clap_complete_command = "0.4.0"
colored = { version = "2.0.0" }
common-path = { version = "1.0.0" }
dirs = { version = "4.0.0" }
fern = { version = "0.6.1" }
filetime = { version = "0.2.17" }
fnv = { version = "1.0.7" }
glob = { version = "0.3.0" }
globset = {version = "0.4.9" }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "a13ec97dd4eb925bde4d426c6e422582793b260c" }
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" }
@@ -36,9 +40,10 @@ path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix
rayon = { version = "1.5.3" }
regex = { version = "1.6.0" }
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
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" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
strum = { version = "0.24.1", features = ["strum_macros"] }
@@ -50,7 +55,6 @@ update-informer = { version = "0.5.0", default-features = false, features = ["py
walkdir = { version = "2.3.2" }
[target.'cfg(not(target_family = "wasm"))'.dependencies]
cacache = { version = "10.0.1" } # uses async-std
clearscreen = { version = "1.0.10" } # uses which
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support

474
LICENSE
View File

@@ -19,3 +19,477 @@ 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.
The externally maintained libraries from which parts of the Software is derived
are:
- Pyflakes, licensed as follows:
"""
Copyright 2005-2011 Divmod, Inc.
Copyright 2013-2014 Florent Xicluna
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.
"""
- autoflake, licensed as follows:
"""
Copyright (C) 2012-2018 Steven Myint
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, licensed as follows:
"""
== Flake8 License (MIT) ==
Copyright (C) 2011-2013 Tarek Ziade <tarek@ziade.org>
Copyright (C) 2012-2016 Ian Cordasco <graffatcolmingov@gmail.com>
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-2020, licensed as follows:
"""
Copyright (c) 2019 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.
"""
- flake8-annotations, licensed as follows:
"""
MIT License
Copyright (c) 2019 - Present S. Co1
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-bandit, licensed as follows:
"""
Copyright (c) 2017 Tyler Wince
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-blind-except, licensed as follows:
"""
The MIT License (MIT)
Copyright (c) 2014 Elijah Andrews
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-bugbear, licensed as follows:
"""
The MIT License (MIT)
Copyright (c) 2016 Łukasz Langa
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-comprehensions, licensed as follows:
"""
MIT License
Copyright (c) 2017 Adam Johnson
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-debugger, licensed as follows:
"""
MIT License
Copyright (c) 2016 Joseph Kahn
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-tidy-imports, licensed as follows:
"""
MIT License
Copyright (c) 2017 Adam Johnson
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-print, licensed as follows:
"""
MIT License
Copyright (c) 2016 Joseph Kahn
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-quotes, licensed as follows:
"""
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)
Copyright (c) 2013 Timothy Edmund Crosley
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.
"""
- pep8-naming, licensed as follows:
"""
Copyright © 2013 Florent Xicluna <florent.xicluna@gmail.com>
Licensed under the terms of the Expat License
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.
"""
- pycodestyle, licensed as follows:
"""
Copyright © 2006-2009 Johann C. Rocholl <johann@rocholl.net>
Copyright © 2009-2014 Florent Xicluna <florent.xicluna@gmail.com>
Copyright © 2014-2020 Ian Lee <IanLee1521@gmail.com>
Licensed under the terms of the Expat License
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.
"""
- pydocstyle, licensed as follows:
"""
Copyright (c) 2012 GreenSteam, <http://greensteam.dk/>
Copyright (c) 2014-2020 Amir Rachum, <http://amir.rachum.com/>
Copyright (c) 2020 Sambhav Kothari, <https://github.com/samj1912>
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
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.
"""
- RustPython, licensed as follows:
"""
MIT License
Copyright (c) 2020 RustPython Team
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.
"""

990
README.md

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,16 @@
use std::fs;
use std::path::Path;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use ropey::Rope;
use ruff::fs;
fn criterion_benchmark(c: &mut Criterion) {
let contents = fs::read_file(Path::new("resources/test/fixtures/D.py")).unwrap();
let contents = fs::read_to_string(Path::new("resources/test/fixtures/D.py")).unwrap();
c.bench_function("rope", |b| {
b.iter(|| {
let rope = Rope::from_str(black_box(&contents));
rope.line_to_char(black_box(4));
})
});
});
}

View File

@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8_to_ruff"
version = "0.0.115"
version = "0.0.143"
dependencies = [
"anyhow",
"clap",
@@ -1265,7 +1265,7 @@ checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
[[package]]
name = "libcst"
version = "0.1.0"
source = "git+https://github.com/charliermarsh/LibCST?rev=a13ec97dd4eb925bde4d426c6e422582793b260c#a13ec97dd4eb925bde4d426c6e422582793b260c"
source = "git+https://github.com/charliermarsh/LibCST?rev=f2f0b7a487a8725d161fe8b3ed73a6758b21e177#f2f0b7a487a8725d161fe8b3ed73a6758b21e177"
dependencies = [
"chic",
"itertools",
@@ -1280,7 +1280,7 @@ dependencies = [
[[package]]
name = "libcst_derive"
version = "0.1.0"
source = "git+https://github.com/charliermarsh/LibCST?rev=a13ec97dd4eb925bde4d426c6e422582793b260c#a13ec97dd4eb925bde4d426c6e422582793b260c"
source = "git+https://github.com/charliermarsh/LibCST?rev=f2f0b7a487a8725d161fe8b3ed73a6758b21e177#f2f0b7a487a8725d161fe8b3ed73a6758b21e177"
dependencies = [
"quote",
"syn",
@@ -1975,7 +1975,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.115"
version = "0.0.143"
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=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
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=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
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=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
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=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
dependencies = [
"ahash",
"anyhow",

View File

@@ -1,6 +1,6 @@
[package]
name = "flake8-to-ruff"
version = "0.0.115-dev.0"
version = "0.0.143-dev.0"
edition = "2021"
[lib]
@@ -13,6 +13,7 @@ configparser = { version = "3.0.2" }
once_cell = { version = "1.16.0" }
regex = { version = "1.6.0" }
ruff = { path = "..", default-features = false }
rustc-hash = { version = "1.1.0" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
toml = { version = "0.5.9" }

View File

@@ -25,7 +25,7 @@ requires-python = ">=3.7"
repository = "https://github.com/charliermarsh/ruff#subdirectory=crates/flake8_to_ruff"
[build-system]
requires = ["maturin>=0.13,<0.14"]
requires = ["maturin>=0.14,<0.15"]
build-backend = "maturin"
[tool.maturin]

View File

@@ -3,9 +3,12 @@ use std::collections::{BTreeSet, HashMap};
use anyhow::Result;
use ruff::checks_gen::CheckCodePrefix;
use ruff::flake8_quotes::settings::Quote;
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, pep8_naming};
use ruff::{
flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, mccabe, pep8_naming,
};
use crate::plugin::Plugin;
use crate::{parser, plugin};
@@ -15,7 +18,7 @@ pub fn convert(
plugins: Option<Vec<Plugin>>,
) -> Result<Pyproject> {
// Extract all referenced check code prefixes, to power plugin inference.
let mut referenced_codes: BTreeSet<CheckCodePrefix> = Default::default();
let mut referenced_codes: BTreeSet<CheckCodePrefix> = BTreeSet::default();
for (key, value) in flake8 {
if let Some(value) = value {
match key.as_str() {
@@ -67,11 +70,13 @@ pub fn convert(
.unwrap_or_default();
// Parse each supported option.
let mut options: Options = Default::default();
let mut flake8_annotations: flake8_annotations::settings::Options = Default::default();
let mut flake8_bugbear: flake8_bugbear::settings::Options = Default::default();
let mut flake8_quotes: flake8_quotes::settings::Options = Default::default();
let mut pep8_naming: pep8_naming::settings::Options = Default::default();
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_quotes = flake8_quotes::settings::Options::default();
let mut flake8_tidy_imports = flake8_tidy_imports::settings::Options::default();
let mut mccabe = mccabe::settings::Options::default();
let mut pep8_naming = pep8_naming::settings::Options::default();
for (key, value) in flake8 {
if let Some(value) = value {
match key.as_str() {
@@ -105,7 +110,7 @@ pub fn convert(
match parser::parse_files_to_codes_mapping(value.as_ref()) {
Ok(per_file_ignores) => {
options.per_file_ignores =
Some(parser::collect_per_file_ignores(per_file_ignores))
Some(parser::collect_per_file_ignores(per_file_ignores));
}
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
}
@@ -172,10 +177,23 @@ pub fn convert(
pep8_naming.staticmethod_decorators =
Some(parser::parse_strings(value.as_ref()));
}
// flake8-tidy-imports
"ban-relative-imports" | "ban_relative_imports" => match value.trim() {
"true" => flake8_tidy_imports.ban_relative_imports = Some(Strictness::All),
"parents" => {
flake8_tidy_imports.ban_relative_imports = Some(Strictness::Parents);
}
_ => eprintln!("Unexpected '{key}' value: {value}"),
},
// flake8-docstrings
"docstring-convention" => {
// No-op (handled above).
}
// mccabe
"max-complexity" | "max_complexity" => match value.clone().parse::<usize>() {
Ok(max_complexity) => mccabe.max_complexity = Some(max_complexity),
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
},
// Unknown
_ => eprintln!("Skipping unsupported property: {key}"),
}
@@ -185,16 +203,22 @@ pub fn convert(
// Deduplicate and sort.
options.select = Some(Vec::from_iter(select));
options.ignore = Some(Vec::from_iter(ignore));
if flake8_annotations != Default::default() {
if flake8_annotations != flake8_annotations::settings::Options::default() {
options.flake8_annotations = Some(flake8_annotations);
}
if flake8_bugbear != Default::default() {
if flake8_bugbear != flake8_bugbear::settings::Options::default() {
options.flake8_bugbear = Some(flake8_bugbear);
}
if flake8_quotes != Default::default() {
if flake8_quotes != flake8_quotes::settings::Options::default() {
options.flake8_quotes = Some(flake8_quotes);
}
if pep8_naming != Default::default() {
if flake8_tidy_imports != flake8_tidy_imports::settings::Options::default() {
options.flake8_tidy_imports = Some(flake8_tidy_imports);
}
if mccabe != mccabe::settings::Options::default() {
options.mccabe = Some(mccabe);
}
if pep8_naming != pep8_naming::settings::Options::default() {
options.pep8_naming = Some(pep8_naming);
}
@@ -219,26 +243,31 @@ mod tests {
fn it_converts_empty() -> Result<()> {
let actual = convert(&HashMap::from([]), None)?;
let expected = Pyproject::new(Options {
line_length: None,
src: None,
fix: None,
dummy_variable_rgx: None,
exclude: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
fix: None,
fixable: None,
ignore: Some(vec![]),
line_length: None,
per_file_ignores: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
CheckCodePrefix::W,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
show_source: None,
src: None,
target_version: None,
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_quotes: None,
flake8_tidy_imports: None,
isort: None,
mccabe: None,
pep8_naming: None,
});
assert_eq!(actual, expected);
@@ -253,26 +282,31 @@ mod tests {
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
line_length: Some(100),
src: None,
fix: None,
dummy_variable_rgx: None,
exclude: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
fix: None,
fixable: None,
ignore: Some(vec![]),
line_length: Some(100),
per_file_ignores: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
CheckCodePrefix::W,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
show_source: None,
src: None,
target_version: None,
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_quotes: None,
flake8_tidy_imports: None,
isort: None,
mccabe: None,
pep8_naming: None,
});
assert_eq!(actual, expected);
@@ -287,26 +321,31 @@ mod tests {
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
line_length: Some(100),
src: None,
fix: None,
dummy_variable_rgx: None,
exclude: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
fix: None,
fixable: None,
ignore: Some(vec![]),
line_length: Some(100),
per_file_ignores: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
CheckCodePrefix::W,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
show_source: None,
src: None,
target_version: None,
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_quotes: None,
flake8_tidy_imports: None,
isort: None,
mccabe: None,
pep8_naming: None,
});
assert_eq!(actual, expected);
@@ -321,26 +360,31 @@ mod tests {
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
line_length: None,
src: None,
fix: None,
dummy_variable_rgx: None,
exclude: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
fix: None,
fixable: None,
ignore: Some(vec![]),
line_length: None,
per_file_ignores: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
CheckCodePrefix::W,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
show_source: None,
src: None,
target_version: None,
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_quotes: None,
flake8_tidy_imports: None,
isort: None,
mccabe: None,
pep8_naming: None,
});
assert_eq!(actual, expected);
@@ -355,22 +399,25 @@ mod tests {
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
line_length: None,
src: None,
fix: None,
dummy_variable_rgx: None,
exclude: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
fix: None,
fixable: None,
ignore: Some(vec![]),
line_length: None,
per_file_ignores: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
CheckCodePrefix::W,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
show_source: None,
src: None,
target_version: None,
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_quotes: Some(flake8_quotes::settings::Options {
@@ -379,7 +426,9 @@ mod tests {
docstring_quotes: None,
avoid_escape: None,
}),
flake8_tidy_imports: None,
isort: None,
mccabe: None,
pep8_naming: None,
});
assert_eq!(actual, expected);
@@ -397,11 +446,16 @@ mod tests {
Some(vec![Plugin::Flake8Docstrings]),
)?;
let expected = Pyproject::new(Options {
line_length: None,
src: None,
fix: None,
dummy_variable_rgx: None,
exclude: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
fix: None,
fixable: None,
ignore: Some(vec![]),
line_length: None,
per_file_ignores: None,
select: Some(vec![
CheckCodePrefix::D100,
CheckCodePrefix::D101,
@@ -442,16 +496,16 @@ mod tests {
CheckCodePrefix::F,
CheckCodePrefix::W,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
show_source: None,
src: None,
target_version: None,
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_quotes: None,
flake8_tidy_imports: None,
isort: None,
mccabe: None,
pep8_naming: None,
});
assert_eq!(actual, expected);
@@ -466,23 +520,26 @@ mod tests {
None,
)?;
let expected = Pyproject::new(Options {
line_length: None,
src: None,
fix: None,
dummy_variable_rgx: None,
exclude: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
fix: None,
fixable: None,
ignore: Some(vec![]),
line_length: None,
per_file_ignores: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
CheckCodePrefix::Q,
CheckCodePrefix::W,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
show_source: None,
src: None,
target_version: None,
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_quotes: Some(flake8_quotes::settings::Options {
@@ -491,7 +548,9 @@ mod tests {
docstring_quotes: None,
avoid_escape: None,
}),
flake8_tidy_imports: None,
isort: None,
mccabe: None,
pep8_naming: None,
});
assert_eq!(actual, expected);

View File

@@ -1,4 +1,15 @@
#![allow(clippy::collapsible_if, clippy::collapsible_else_if)]
#![allow(
clippy::collapsible_else_if,
clippy::collapsible_if,
clippy::implicit_hasher,
clippy::match_same_arms,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::module_name_repetitions,
clippy::must_use_candidate,
clippy::similar_names,
clippy::too_many_lines
)]
pub mod converter;
mod parser;

View File

@@ -1,4 +1,16 @@
//! Utility to generate Ruff's pyproject.toml section from a Flake8 INI file.
#![allow(
clippy::collapsible_else_if,
clippy::collapsible_if,
clippy::implicit_hasher,
clippy::match_same_arms,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::module_name_repetitions,
clippy::must_use_candidate,
clippy::similar_names,
clippy::too_many_lines
)]
use std::path::PathBuf;

View File

@@ -1,4 +1,3 @@
use std::collections::BTreeMap;
use std::str::FromStr;
use anyhow::Result;
@@ -6,6 +5,7 @@ use once_cell::sync::Lazy;
use regex::Regex;
use ruff::checks_gen::CheckCodePrefix;
use ruff::settings::types::PatternPrefixPair;
use rustc_hash::FxHashMap;
static COMMA_SEPARATED_LIST_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"[,\s]").unwrap());
@@ -31,7 +31,7 @@ pub fn parse_prefix_codes(value: &str) -> Vec<CheckCodePrefix> {
pub fn parse_strings(value: &str) -> Vec<String> {
COMMA_SEPARATED_LIST_RE
.split(value)
.map(|part| part.trim())
.map(str::trim)
.filter(|part| !part.is_empty())
.map(String::from)
.collect()
@@ -92,7 +92,7 @@ impl State {
});
}
}
Err(_) => eprintln!("Skipping unrecognized prefix: {}", code),
Err(_) => eprintln!("Skipping unrecognized prefix: {code}"),
}
}
codes
@@ -129,14 +129,14 @@ fn tokenize_files_to_codes_mapping(value: &str) -> Vec<Token> {
}
tokens.push(Token {
token_name: TokenType::Eof,
src: "".to_string(),
src: String::new(),
});
tokens
}
/// Parse a 'files-to-codes' mapping, mimicking Flake8's internal logic.
///
/// See: https://github.com/PyCQA/flake8/blob/7dfe99616fc2f07c0017df2ba5fa884158f3ea8a/src/flake8/utils.py#L45
/// 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() {
return Ok(vec![]);
@@ -179,8 +179,8 @@ pub fn parse_files_to_codes_mapping(value: &str) -> Result<Vec<PatternPrefixPair
/// Collect a list of `PatternPrefixPair` structs as a `BTreeMap`.
pub fn collect_per_file_ignores(
pairs: Vec<PatternPrefixPair>,
) -> BTreeMap<String, Vec<CheckCodePrefix>> {
let mut per_file_ignores: BTreeMap<String, Vec<CheckCodePrefix>> = BTreeMap::new();
) -> FxHashMap<String, Vec<CheckCodePrefix>> {
let mut per_file_ignores: FxHashMap<String, Vec<CheckCodePrefix>> = FxHashMap::default();
for pair in pairs {
per_file_ignores
.entry(pair.pattern)

View File

@@ -10,10 +10,14 @@ pub enum Plugin {
Flake8Bugbear,
Flake8Builtins,
Flake8Comprehensions,
Flake8Debugger,
Flake8Docstrings,
Flake8TidyImports,
Flake8Print,
Flake8Quotes,
Flake8Annotations,
McCabe,
Flake8BlindExcept,
PEP8Naming,
Pyupgrade,
}
@@ -27,10 +31,14 @@ impl FromStr for Plugin {
"flake8-bugbear" => Ok(Plugin::Flake8Bugbear),
"flake8-builtins" => Ok(Plugin::Flake8Builtins),
"flake8-comprehensions" => Ok(Plugin::Flake8Comprehensions),
"flake8-debugger" => Ok(Plugin::Flake8Debugger),
"flake8-docstrings" => Ok(Plugin::Flake8Docstrings),
"flake8-tidy-imports" => Ok(Plugin::Flake8TidyImports),
"flake8-print" => Ok(Plugin::Flake8Print),
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
"flake8-annotations" => Ok(Plugin::Flake8Annotations),
"flake8-blind-except" => Ok(Plugin::Flake8BlindExcept),
"mccabe" => Ok(Plugin::McCabe),
"pep8-naming" => Ok(Plugin::PEP8Naming),
"pyupgrade" => Ok(Plugin::Pyupgrade),
_ => Err(anyhow!("Unknown plugin: {}", string)),
@@ -44,11 +52,15 @@ impl Plugin {
Plugin::Flake8Bandit => CheckCodePrefix::S,
Plugin::Flake8Bugbear => CheckCodePrefix::B,
Plugin::Flake8Builtins => CheckCodePrefix::A,
Plugin::Flake8Comprehensions => CheckCodePrefix::C,
Plugin::Flake8Comprehensions => CheckCodePrefix::C4,
Plugin::Flake8Debugger => CheckCodePrefix::T1,
Plugin::Flake8Docstrings => CheckCodePrefix::D,
Plugin::Flake8Print => CheckCodePrefix::T,
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
Plugin::Flake8Print => CheckCodePrefix::T2,
Plugin::Flake8Quotes => CheckCodePrefix::Q,
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
Plugin::Flake8BlindExcept => CheckCodePrefix::BLE,
Plugin::McCabe => CheckCodePrefix::C9,
Plugin::PEP8Naming => CheckCodePrefix::N,
Plugin::Pyupgrade => CheckCodePrefix::U,
}
@@ -59,7 +71,8 @@ impl Plugin {
Plugin::Flake8Bandit => vec![CheckCodePrefix::S],
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
Plugin::Flake8Builtins => vec![CheckCodePrefix::A],
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C],
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C4],
Plugin::Flake8Debugger => vec![CheckCodePrefix::T1],
Plugin::Flake8Docstrings => {
// Use the user-provided docstring.
for key in ["docstring-convention", "docstring_convention"] {
@@ -76,9 +89,12 @@ impl Plugin {
// Default to PEP8.
DocstringConvention::PEP8.select()
}
Plugin::Flake8Print => vec![CheckCodePrefix::T],
Plugin::Flake8TidyImports => vec![CheckCodePrefix::I25],
Plugin::Flake8Print => vec![CheckCodePrefix::T2],
Plugin::Flake8Quotes => vec![CheckCodePrefix::Q],
Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN],
Plugin::Flake8BlindExcept => vec![CheckCodePrefix::BLE],
Plugin::McCabe => vec![CheckCodePrefix::C9],
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
Plugin::Pyupgrade => vec![CheckCodePrefix::U],
}
@@ -315,6 +331,17 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
"allow-star-arg-any" | "allow_star_arg_any" => {
plugins.insert(Plugin::Flake8Annotations);
}
// flake8-tidy-imports
"ban-relative-imports" | "ban_relative_imports" => {
plugins.insert(Plugin::Flake8TidyImports);
}
"banned-modules" | "banned_modules" => {
plugins.insert(Plugin::Flake8TidyImports);
}
// mccabe
"max-complexity" | "max_complexity" => {
plugins.insert(Plugin::McCabe);
}
// pep8-naming
"ignore-names" | "ignore_names" => {
plugins.insert(Plugin::PEP8Naming);
@@ -341,10 +368,13 @@ pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin
Plugin::Flake8Bugbear,
Plugin::Flake8Builtins,
Plugin::Flake8Comprehensions,
Plugin::Flake8Debugger,
Plugin::Flake8Docstrings,
Plugin::Flake8TidyImports,
Plugin::Flake8Print,
Plugin::Flake8Quotes,
Plugin::Flake8Annotations,
Plugin::Flake8BlindExcept,
Plugin::PEP8Naming,
Plugin::Pyupgrade,
]

View File

@@ -25,12 +25,11 @@ requires-python = ">=3.7"
repository = "https://github.com/charliermarsh/ruff"
[build-system]
requires = ["maturin>=0.13,<0.14"]
requires = ["maturin>=0.14,<0.15"]
build-backend = "maturin"
[tool.maturin]
bindings = "bin"
sdist-include = ["Cargo.lock"]
strip = true
[tool.isort]

View File

@@ -185,3 +185,23 @@ def nested_b008(a=random.randint(0, dt.datetime.now().year)):
# Ignore lambda contents since they are evaluated at call time.
def foo(f=lambda x: print(x)):
f(1)
from collections import abc
from typing import Annotated, Dict, Optional, Sequence, Union, Set
def immutable_annotations(
a: Sequence[int] | None = [],
b: Optional[abc.Mapping[int, int]] = {},
c: Annotated[Union[abc.Set[str], abc.Sized], "annotation"] = set(),
):
pass
def mutable_annotations(
a: list[int] | None = [],
b: Optional[Dict[int, int]] = {},
c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
):
pass

View File

@@ -34,3 +34,4 @@ setattr(foo, "bar", None)
setattr(foo, "_123abc", None)
setattr(foo, "abc123", None)
setattr(foo, r"abc123", None)
setattr(foo.bar, r"baz", None)

107
resources/test/fixtures/B012.py vendored Normal file
View File

@@ -0,0 +1,107 @@
def a():
try:
pass
finally:
return # warning
def b():
try:
pass
finally:
if 1 + 0 == 2 - 1:
return # warning
def c():
try:
pass
finally:
try:
return # warning
except Exception:
pass
def d():
try:
try:
pass
finally:
return # warning
finally:
pass
def e():
if 1 == 2 - 1:
try:
def f():
try:
pass
finally:
return # warning
finally:
pass
def g():
try:
pass
finally:
def h():
return # no warning
e()
def i():
while True:
try:
pass
finally:
break # warning
def j():
while True:
break # no warning
def h():
while True:
try:
pass
finally:
continue # warning
def j():
while True:
continue # no warning
def k():
try:
pass
finally:
while True:
break # no warning
while True:
continue # no warning
while True:
return # warning
while True:
try:
pass
finally:
continue # warning
while True:
try:
pass
finally:
break # warning

40
resources/test/fixtures/B020.py vendored Normal file
View File

@@ -0,0 +1,40 @@
"""
Should emit:
B020 - on lines 8, 21, and 36
"""
items = [1, 2, 3]
for items in items:
print(items)
items = [1, 2, 3]
for item in items:
print(item)
values = {"secret": 123}
for key, value in values.items():
print(f"{key}, {value}")
for key, values in values.items():
print(f"{key}, {values}")
# Variables defined in a comprehension are local in scope
# to that comprehension and are therefore allowed.
for var in [var for var in range(10)]:
print(var)
for var in (var for var in range(10)):
print(var)
for k, v in {k: v for k, v in zip(range(10), range(10, 20))}.items():
print(k, v)
# However we still call out reassigning the iterable in the comprehension.
for vars in [i for i in vars]:
print(vars)
for var in sorted(range(10), key=lambda var: var.real):
print(var)

76
resources/test/fixtures/B021.py vendored Normal file
View File

@@ -0,0 +1,76 @@
f"""
Should emit:
B021 - on lines 14, 22, 30, 38, 46, 54, 62, 70, 73
"""
VARIABLE = "world"
def foo1():
"""hello world!"""
def foo2():
f"""hello {VARIABLE}!"""
class bar1:
"""hello world!"""
class bar2:
f"""hello {VARIABLE}!"""
def foo1():
"""hello world!"""
def foo2():
f"""hello {VARIABLE}!"""
class bar1:
"""hello world!"""
class bar2:
f"""hello {VARIABLE}!"""
def foo1():
"hello world!"
def foo2():
f"hello {VARIABLE}!"
class bar1:
"hello world!"
class bar2:
f"hello {VARIABLE}!"
def foo1():
"hello world!"
def foo2():
f"hello {VARIABLE}!"
class bar1:
"hello world!"
class bar2:
f"hello {VARIABLE}!"
def baz():
f"""I'm probably a docstring: {VARIABLE}!"""
print(f"""I'm a normal string""")
f"""Don't detect me!"""

23
resources/test/fixtures/B022.py vendored Normal file
View File

@@ -0,0 +1,23 @@
"""
Should emit:
B022 - on lines 8
"""
import contextlib
from contextlib import suppress
with contextlib.suppress():
raise ValueError
with suppress():
raise ValueError
with contextlib.suppress(ValueError):
raise ValueError
exceptions_to_suppress = []
if True:
exceptions_to_suppress.append(ValueError)
with contextlib.suppress(*exceptions_to_suppress):
raise

82
resources/test/fixtures/B023.py vendored Normal file
View File

@@ -0,0 +1,82 @@
"""
Should emit:
B023 - on lines 12, 13, 16, 28, 29, 30, 31, 40, 42, 50, 51, 52, 53, 61, 68.
"""
functions = []
z = 0
for x in range(3):
y = x + 1
# Subject to late-binding problems
functions.append(lambda: x)
functions.append(lambda: y) # not just the loop var
def f_bad_1():
return x
# Actually OK
functions.append(lambda x: x * 2)
functions.append(lambda x=x: x)
functions.append(lambda: z) # OK because not assigned in the loop
def f_ok_1(x):
return x * 2
def check_inside_functions_too():
ls = [lambda: x for x in range(2)]
st = {lambda: x for x in range(2)}
gn = (lambda: x for x in range(2))
dt = {x: lambda: x for x in range(2)}
async def pointless_async_iterable():
yield 1
async def container_for_problems():
async for x in pointless_async_iterable():
functions.append(lambda: x)
[lambda: x async for x in pointless_async_iterable()]
a = 10
b = 0
while True:
a = a_ = a - 1
b += 1
functions.append(lambda: a)
functions.append(lambda: a_)
functions.append(lambda: b)
functions.append(lambda: c) # not a name error because of late binding!
c: bool = a > 3
if not c:
break
# Nested loops should not duplicate reports
for j in range(2):
for k in range(3):
lambda: j * k
for j, k, l in [(1, 2, 3)]:
def f():
j = None # OK because it's an assignment
[l for k in range(2)] # error for l, not for k
assert a and functions
a.attribute = 1 # modifying an attribute doesn't make it a loop variable
functions[0] = lambda: None # same for an element
for var in range(2):
def explicit_capture(captured=var):
return captured
for i in range(3):
lambda: f"{i}"

129
resources/test/fixtures/B024.py vendored Normal file
View File

@@ -0,0 +1,129 @@
"""
Should emit:
B024 - on lines 17, 34, 52, 58, 69, 74, 79, 84, 89
"""
import abc
import abc as notabc
from abc import ABC, ABCMeta
from abc import abstractmethod
from abc import abstractmethod as abstract
from abc import abstractmethod as abstractaoeuaoeuaoeu
from abc import abstractmethod as notabstract
import foo
class Base_1(ABC): # error
def method(self):
foo()
class Base_2(ABC):
@abstractmethod
def method(self):
foo()
class Base_3(ABC):
@abc.abstractmethod
def method(self):
foo()
class Base_4(ABC):
@notabc.abstractmethod
def method(self):
foo()
class Base_5(ABC):
@abstract
def method(self):
foo()
class Base_6(ABC):
@abstractaoeuaoeuaoeu
def method(self):
foo()
class Base_7(ABC): # error
@notabstract
def method(self):
foo()
class MetaBase_1(metaclass=ABCMeta): # error
def method(self):
foo()
class MetaBase_2(metaclass=ABCMeta):
@abstractmethod
def method(self):
foo()
class abc_Base_1(abc.ABC): # error
def method(self):
foo()
class abc_Base_2(metaclass=abc.ABCMeta): # error
def method(self):
foo()
class notabc_Base_1(notabc.ABC): # error
def method(self):
foo()
class multi_super_1(notabc.ABC, abc.ABCMeta): # safe
def method(self):
foo()
class multi_super_2(notabc.ABC, metaclass=abc.ABCMeta): # safe
def method(self):
foo()
class non_keyword_abcmeta_1(ABCMeta): # safe
def method(self):
foo()
class non_keyword_abcmeta_2(abc.ABCMeta): # safe
def method(self):
foo()
# very invalid code, but that's up to mypy et al to check
class keyword_abc_1(metaclass=ABC): # safe
def method(self):
foo()
class keyword_abc_2(metaclass=abc.ABC): # safe
def method(self):
foo()
class abc_set_class_variable_1(ABC): # safe
foo: int
class abc_set_class_variable_2(ABC): # safe
foo = 2
class abc_set_class_variable_3(ABC): # safe
foo: int = 2
# this doesn't actually declare a class variable, it's just an expression
class abc_set_class_variable_4(ABC): # error
foo

88
resources/test/fixtures/B027.py vendored Normal file
View File

@@ -0,0 +1,88 @@
"""
Should emit:
B027 - on lines 12, 15, 18, 22, 30
"""
import abc
from abc import ABC
from abc import abstractmethod
from abc import abstractmethod as notabstract
class AbstractClass(ABC):
def empty_1(self): # error
...
def empty_2(self): # error
pass
def empty_3(self): # error
"""docstring"""
...
def empty_4(self): # error
"""multiple ellipsis/pass"""
...
pass
...
pass
@notabstract
def abstract_0(self):
...
@abstractmethod
def abstract_1(self):
...
@abstractmethod
def abstract_2(self):
pass
@abc.abstractmethod
def abstract_3(self):
...
def body_1(self):
print("foo")
...
def body_2(self):
self.body_1()
class NonAbstractClass:
def empty_1(self): # safe
...
def empty_2(self): # safe
pass
# ignore @overload, fixes issue #304
# ignore overload with other imports, fixes #308
import typing
import typing as t
import typing as anything
from typing import Union, overload
class AbstractClass(ABC):
@overload
def empty_1(self, foo: str):
...
@typing.overload
def empty_1(self, foo: int):
...
@t.overload
def empty_1(self, foo: list):
...
@anything.overload
def empty_1(self, foo: float):
...
@abstractmethod
def empty_1(self, foo: Union[str, int, list, float]):
...

55
resources/test/fixtures/B904.py vendored Normal file
View File

@@ -0,0 +1,55 @@
"""
Should emit:
B904 - on lines 10, 11 and 16
"""
try:
raise ValueError
except ValueError:
if "abc":
raise TypeError
raise UserWarning
except AssertionError:
raise # Bare `raise` should not be an error
except Exception as err:
assert err
raise Exception("No cause here...")
except BaseException as base_err:
# Might use this instead of bare raise with the `.with_traceback()` method
raise base_err
finally:
raise Exception("Nothing to chain from, so no warning here")
try:
raise ValueError
except ValueError:
# should not emit, since we are not raising something
def proxy():
raise NameError
try:
from preferred_library import Thing
except ImportError:
try:
from fallback_library import Thing
except ImportError:
class Thing:
def __getattr__(self, name):
# same as the case above, should not emit.
raise AttributeError
try:
from preferred_library import Thing
except ImportError:
try:
from fallback_library import Thing
except ImportError:
def context_switch():
try:
raise ValueError
except ValueError:
raise

55
resources/test/fixtures/BLE.py vendored Normal file
View File

@@ -0,0 +1,55 @@
try:
pass
except ValueError:
pass
except Exception as e:
raise e
finally:
pass
try:
pass
except BaseException as e:
raise e
except TypeError:
pass
else:
pass
try:
pass
except Exception as e:
raise e
except BaseException:
pass
try:
pass
except Exception:
pass
finally:
try:
pass
except BaseException as e:
raise e
try:
pass
except Exception as e:
try:
raise e
except BaseException:
pass
try:
try:
pass
except BaseException as e:
raise e
except Exception:
pass

138
resources/test/fixtures/C901.py vendored Normal file
View File

@@ -0,0 +1,138 @@
# Complexity = 1
def trivial():
pass
# Complexity = 1
def expr_as_statement():
0xF00D
# Complexity = 1
def sequential(n):
k = n + 4
s = k + n
return s
# Complexity = 3
def if_elif_else_dead_path(n):
if n > 3:
return "bigger than three"
elif n > 4:
return "is never executed"
else:
return "smaller than or equal to three"
# Complexity = 3
def nested_ifs():
if n > 3:
if n > 4:
return "bigger than four"
else:
return "bigger than three"
else:
return "smaller than or equal to three"
# Complexity = 2
def for_loop():
for i in range(10):
print(i)
# Complexity = 2
def for_else(mylist):
for i in mylist:
print(i)
else:
print(None)
# Complexity = 2
def recursive(n):
if n > 4:
return f(n - 1)
else:
return n
# Complexity = 3
def nested_functions():
def a():
def b():
pass
b()
a()
# Complexity = 4
def try_else():
try:
print(1)
except TypeA:
print(2)
except TypeB:
print(3)
else:
print(4)
# Complexity = 3
def nested_try_finally():
try:
try:
print(1)
finally:
print(2)
finally:
print(3)
# Complexity = 3
async def foobar(a, b, c):
await whatever(a, b, c)
if await b:
pass
async with c:
pass
async for x in a:
pass
# Complexity = 1
def annotated_assign():
x: Any = None
# Complexity = 9
class Class:
def handle(self, *args, **options):
if args:
return
class ServiceProvider:
def a(self):
pass
def b(self, data):
if not args:
pass
class Logger:
def c(*args, **kwargs):
pass
def error(self, message):
pass
def info(self, message):
pass
def exception(self):
pass
return ServiceProvider(Logger())

View File

@@ -3,6 +3,7 @@ from functools import wraps
import os
from .expected import Expectation
from typing import overload
from typing_extensions import override
expectation = Expectation()
@@ -42,9 +43,13 @@ class class_:
"D418: Function/ Method decorated with @overload"
" shouldn't contain a docstring")
@override
def overridden_method(a):
return str(a)
@property
def foo(self):
"""The foo of the thing, which isn't in imperitive mood."""
"""The foo of the thing, which isn't in imperative mood."""
return "hello"
@expect('D102: Missing docstring in public method')
@@ -532,3 +537,37 @@ class Blah: # noqa: D203,D213
expect(os.path.normcase(__file__ if __file__[-1] != 'c' else __file__[:-1]),
'D100: Missing docstring in public module')
@expect('D201: No blank lines allowed before function docstring (found 1)')
@expect('D213: Multi-line docstring summary should start at the second line')
def multiline_leading_space():
"""Leading space.
More content.
"""
@expect('D202: No blank lines allowed after function docstring (found 1)')
@expect('D213: Multi-line docstring summary should start at the second line')
def multiline_trailing_space():
"""Leading space.
More content.
"""
pass
@expect('D201: No blank lines allowed before function docstring (found 1)')
@expect('D202: No blank lines allowed after function docstring (found 1)')
@expect('D213: Multi-line docstring summary should start at the second line')
def multiline_trailing_and_leading_space():
"""Trailing and leading space.
More content.
"""
pass

View File

@@ -5,7 +5,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
"""
_ = """Lorem ipsum dolor sit amet.
_ = """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
https://github.com/PyCQA/pycodestyle/pull/258/files#diff-841c622497a8033d10152bfdfb15b20b92437ecdea21a260944ea86b77b51533
@@ -49,3 +49,6 @@ sit amet consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labor
sit amet consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
""", # noqa: E501
}
# OK
# A very long URL: https://loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong.url.com

View File

@@ -23,6 +23,9 @@ if None != res[1]:
if None == res[1]:
pass
if x == None != None:
pass
#: Okay
if x not in y:
pass

View File

@@ -22,6 +22,9 @@ var = 1 if cond == True else -1 if cond == False else cond
if (True) == TrueElement or x == TrueElement:
pass
if res == True != False:
pass
#: Okay
if x not in y:
pass

View File

@@ -8,14 +8,14 @@ while False:
f = object()
#: E731
f.method = lambda: "Method"
f = {}
#: E731
f["a"] = lambda x: x ** 2
f = []
f.append(lambda x: x ** 2)
f = g = lambda x: x ** 2
lambda: "no-op"

20
resources/test/fixtures/F401_6.py vendored Normal file
View File

@@ -0,0 +1,20 @@
"""Test: explicit re-export."""
# OK
from .applications import FastAPI as FastAPI
# F401 `background.BackgroundTasks` imported but unused
from .background import BackgroundTasks
# F401 `datastructures.UploadFile` imported but unused
from .datastructures import UploadFile as FileUpload
# OK
import applications as applications
# F401 `background` imported but unused
import background
# F401 `datastructures` imported but unused
import datastructures as structures

13
resources/test/fixtures/F502.py vendored Normal file
View File

@@ -0,0 +1,13 @@
dog = {"bob": "bob"}
"%(bob)s" % dog
"%(bob)s" % {"bob": "bob"}
"%(bob)s" % {**{"bob": "bob"}}
"%(bob)s" % ["bob"] # F202
"%(bob)s" % ("bob",) # F202
"%(bob)s" % {"bob"} # F202
"%(bob)s" % [*["bob"]] # F202
"%(bob)s" % {"bob": "bob" for _ in range(1)}
"%(bob)s" % ["bob" for _ in range(1)] # F202
"%(bob)s" % ("bob" for _ in range(1)) # F202
"%(bob)s" % {"bob" for _ in range(1)} # F202

26
resources/test/fixtures/F503.py vendored Normal file
View File

@@ -0,0 +1,26 @@
dog = {"bob": "bob"}
# Single placeholder always fine
"%s" % dog
"%s" % {"bob": "bob"}
"%s" % {**{"bob": "bob"}}
"%s" % ["bob"]
"%s" % ("bob",)
"%s" % {"bob"}
"%s" % [*["bob"]]
"%s" % {"bob": "bob" for _ in range(1)}
"%s" % ["bob" for _ in range(1)]
"%s" % ("bob" for _ in range(1))
"%s" % {"bob" for _ in range(1)}
# Multiple placeholders
"%s %s" % dog
"%s %s" % {"bob": "bob"} # F503
"%s %s" % {**{"bob": "bob"}} # F503
"%s %s" % ["bob"]
"%s %s" % ("bob",)
"%s %s" % {"bob"}
"%s %s" % [*["bob"]]
"%s %s" % {"bob": "bob" for _ in range(1)} # F503
"%s %s" % ["bob" for _ in range(1)]
"%s %s" % ("bob" for _ in range(1))
"%s %s" % {"bob" for _ in range(1)}

6
resources/test/fixtures/F504.py vendored Normal file
View File

@@ -0,0 +1,6 @@
# Ruff has no way of knowing if the following are F505s
a = "wrong"
"%(a)s %(c)s" % {a: "?", "b": "!"} # F504 ("b" not used)
hidden = {"a": "!"}
"%(a)s %(c)s" % {"x": 1, **hidden} # Ok (cannot see through splat)

25
resources/test/fixtures/F50x.py vendored Normal file
View File

@@ -0,0 +1,25 @@
'%(foo)' % {'foo': 'bar'} # F501
'%s %(foo)s' % {'foo': 'bar'} # F506
'%(foo)s %s' % {'foo': 'bar'} # F506
'%j' % (1,) # F509
'%s %s' % (1,) # F507
'%s %s' % (1, 2, 3) # F507
'%(bar)s' % {} # F505
'%(bar)s' % {'bar': 1, 'baz': 2} # F504
'%(bar)s' % (1, 2, 3) # F502
'%s %s' % {'k': 'v'} # F503
'%(bar)*s' % {'bar': 'baz'} # F506, F508
# ok: single %s with mapping
'%s' % {'foo': 'bar', 'baz': 'womp'}
# ok: %% should not count towards placeholder count
'%% %s %% %s' % (1, 2)
# ok: * consumes one positional argument
'%.*f' % (2, 1.1234)
'%*.*f' % (5, 2, 3.1234)
# ok *args and **kwargs
a = []
'%s %s' % [*a]
'%s %s' % (*a,)
k = {}
'%(k)s' % {**k}

21
resources/test/fixtures/F521.py vendored Normal file
View File

@@ -0,0 +1,21 @@
"{".format(1)
"}".format(1)
"{foo[}".format(foo=1)
# too much string recursion (placeholder-in-placeholder)
"{:{:{}}}".format(1, 2, 3)
# ruff picks these issues up, but flake8 doesn't
"{foo[]}".format(foo={"": 1})
"{foo..}".format(foo=1)
"{foo..bar}".format(foo=1)
# The following are all "good" uses of .format
"{.__class__}".format("")
"{foo[bar]}".format(foo={"bar": "barv"})
"{[bar]}".format({"bar": "barv"})
"{:{}} {}".format(1, 15, 2)
"{:2}".format(1)
"{foo}-{}".format(1, foo=2)
a = ()
"{}".format(*a)
k = {}
"{foo}".format(**k)

4
resources/test/fixtures/F522.py vendored Normal file
View File

@@ -0,0 +1,4 @@
"{}".format(1, bar=2) # F522
"{bar}{}".format(1, bar=2, spam=3) # F522
"{bar:{spam}}".format(bar=2, spam=3) # No issues
"{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522

12
resources/test/fixtures/F523.py vendored Normal file
View File

@@ -0,0 +1,12 @@
# With indexes
"{0}".format(1, 2) # F523
"{1}".format(1, 2, 3) # F523
"{1:{0}}".format(1, 2) # No issues
"{1:{0}}".format(1, 2, 3) # F523
"{0}{2}".format(1, 2) # F523, # F524
# With no indexes
"{}".format(1, 2) # F523
"{}".format(1, 2, 3) # F523
"{:{}}".format(1, 2) # No issues
"{:{}}".format(1, 2, 3) # F523

6
resources/test/fixtures/F524.py vendored Normal file
View File

@@ -0,0 +1,6 @@
"{} {}".format(1) # F524
"{2}".format() # F524
"{bar}".format() # F524
"{0} {bar}".format(1) # F524
"{0} {bar}".format() # F524
"{bar} {0}".format() # F524

2
resources/test/fixtures/F525.py vendored Normal file
View File

@@ -0,0 +1,2 @@
"{} {1}".format(1, 2) # F525
"{0} {}".format(1, 2) # F523, F525

View File

@@ -12,3 +12,13 @@ x: dict["key", "value"]
# OK
x: dict[str, str]
# OK
def unimportant(name):
pass
def dang(dict, set, list):
unimportant(name=dict["name"])
unimportant(name=set["name"])
unimportant(name=list["name"])

24
resources/test/fixtures/F821_4.py vendored Normal file
View File

@@ -0,0 +1,24 @@
"""Test: import alias tracking."""
from typing import List
_ = List["Model"]
from typing import List as IList
_ = IList["Model"]
from collections.abc import ItemsView
_ = ItemsView["Model"]
import collections.abc
_ = collections.abc.ItemsView["Model"]
from collections import abc
_ = abc.ItemsView["Model"]

14
resources/test/fixtures/F821_5.py vendored Normal file
View File

@@ -0,0 +1,14 @@
"""Test: inner class annotation."""
class RandomClass:
def random_func(self) -> "InnerClass":
pass
class OuterClass:
class InnerClass:
pass
def failing_func(self) -> "InnerClass":
return self.InnerClass()

View File

@@ -35,3 +35,33 @@ def f4():
_ = 1
__ = 1
_discarded = 1
a = 1
def f5():
global a
# Used in `f7` via `nonlocal`.
b = 1
def f6():
# F841
b = 1
def f7():
nonlocal b
def f6():
annotations = []
assert len([annotations for annotations in annotations])
def f7():
def connect():
return None, None
with connect() as (connection, cursor):
cursor.execute("SELECT * FROM users")

42
resources/test/fixtures/FBT.py vendored Normal file
View File

@@ -0,0 +1,42 @@
def function(
posonly_nohint,
posonly_nonboolhint: int,
posonly_boolhint: bool,
posonly_boolstrhint: "bool",
/,
offset,
posorkw_nonvalued_nohint,
posorkw_nonvalued_nonboolhint: int,
posorkw_nonvalued_boolhint: bool,
posorkw_nonvalued_boolstrhint: "bool",
posorkw_boolvalued_nohint=True,
posorkw_boolvalued_nonboolhint: int = True,
posorkw_boolvalued_boolhint: bool = True,
posorkw_boolvalued_boolstrhint: "bool" = True,
posorkw_nonboolvalued_nohint=1,
posorkw_nonboolvalued_nonboolhint: int = 2,
posorkw_nonboolvalued_boolhint: bool = 3,
posorkw_nonboolvalued_boolstrhint: "bool" = 4,
*,
kwonly_nonvalued_nohint,
kwonly_nonvalued_nonboolhint: int,
kwonly_nonvalued_boolhint: bool,
kwonly_nonvalued_boolstrhint: "bool",
kwonly_boolvalued_nohint=True,
kwonly_boolvalued_nonboolhint: int = False,
kwonly_boolvalued_boolhint: bool = True,
kwonly_boolvalued_boolstrhint: "bool" = True,
kwonly_nonboolvalued_nohint=5,
kwonly_nonboolvalued_nonboolhint: int = 1,
kwonly_nonboolvalued_boolhint: bool = 1,
kwonly_nonboolvalued_boolstrhint: "bool" = 1,
**kw,
):
...
def used(do):
return do
used("a", True)
used(do=True)

12
resources/test/fixtures/I252.py vendored Normal file
View File

@@ -0,0 +1,12 @@
from . import sibling
from .sibling import example
from .. import parent
from ..parent import example
from ... import grandparent
from ...grandparent import example
import other
import other.example
from other import example

View File

@@ -18,6 +18,14 @@ def f() -> None:
# Invalid (and unimplemented)
d = 1 # noqa: F841, W191
# fmt: off
# Invalid - no space before #
d = 1# noqa: E501
# Invalid - many spaces before #
d = 1 # noqa: E501
# fmt: on
# Valid
_ = """Lorem ipsum dolor sit amet.

View File

@@ -30,6 +30,14 @@ class Class:
def __init_subclass__(self, default_name, **kwargs):
...
@classmethod
def class_method_with_positional_only_argument(cls, x, /, other):
...
@classmethod
def bad_class_method_with_positional_only_argument(self, x, /, other):
...
class MetaClass(ABCMeta):
def bad_method(self):

View File

@@ -1,5 +1,7 @@
from abc import ABCMeta
import pydantic
class Class:
def bad_method(this):
@@ -21,6 +23,14 @@ class Class:
def static_method(x):
return x
@pydantic.validator
def lower(cls, my_field: str) -> str:
pass
@pydantic.validator("my_field")
def lower(cls, my_field: str) -> str:
pass
def __init__(self):
...

View File

@@ -1,5 +1,15 @@
import collections
from collections import namedtuple
GLOBAL: str = "foo"
def f():
global GLOBAL
GLOBAL = "bar"
lower = 0
Camel = 0
CONSTANT = 0
_ = 0
MyObj1 = collections.namedtuple("MyObj1", ["a", "b"])
MyObj2 = namedtuple("MyObj12", ["a", "b"])

View File

@@ -10,6 +10,27 @@ def good__():
pass
def nested():
def __bad__():
pass
def __good():
pass
def good__():
pass
class Class:
def __good__(self):
pass
# https://peps.python.org/pep-0562/
def __getattr__(name):
pass
# https://peps.python.org/pep-0562/
def __dir__():
pass

View File

@@ -1,6 +1,12 @@
import collections
from collections import namedtuple
class C:
lower = 0
CONSTANT = 0
mixedCase = 0
_mixedCase = 0
mixed_Case = 0
myObj1 = collections.namedtuple("MyObj1", ["a", "b"])
myObj2 = namedtuple("MyObj2", ["a", "b"])

View File

@@ -1,5 +1,10 @@
import collections
from collections import namedtuple
lower = 0
CONSTANT = 0
mixedCase = 0
_mixedCase = 0
mixed_Case = 0
myObj1 = collections.namedtuple("MyObj1", ["a", "b"])
myObj2 = namedtuple("MyObj2", ["a", "b"])

View File

@@ -8,3 +8,11 @@ class AnotherError(Exception):
class C(Exception):
pass
class D(BaseException):
pass
class E(AnotherError):
pass

5
resources/test/fixtures/RUF101_0.py vendored Normal file
View File

@@ -0,0 +1,5 @@
exit(0)
def main():
exit(2)

10
resources/test/fixtures/RUF101_1.py vendored Normal file
View File

@@ -0,0 +1,10 @@
import sys
exit(0)
def main():
exit(1)
sys.exit(2)

7
resources/test/fixtures/RUF101_2.py vendored Normal file
View File

@@ -0,0 +1,7 @@
import sys as sys2
exit(0)
def main():
exit(1)

7
resources/test/fixtures/RUF101_3.py vendored Normal file
View File

@@ -0,0 +1,7 @@
from sys import exit
exit(0)
def main():
exit(1)

7
resources/test/fixtures/RUF101_4.py vendored Normal file
View File

@@ -0,0 +1,7 @@
from sys import exit as exit2
exit(0)
def main():
exit(1)

7
resources/test/fixtures/RUF101_5.py vendored Normal file
View File

@@ -0,0 +1,7 @@
from sys import *
exit(0)
def main():
exit(1)

12
resources/test/fixtures/RUF101_6.py vendored Normal file
View File

@@ -0,0 +1,12 @@
exit(0)
def exit(e):
pass
exit(1)
def main():
exit(2)

14
resources/test/fixtures/T100.py vendored Normal file
View File

@@ -0,0 +1,14 @@
breakpoint()
import pdb
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

@@ -1,15 +0,0 @@
from os.path import abspath
x = abspath(__file__)
import os
y = os.path.abspath(__file__)
from os import path
z = path.abspath(__file__)

View File

@@ -1,3 +1,10 @@
import typing
def f(x: typing.List[str]) -> None:
...
from typing import List
@@ -5,8 +12,19 @@ def f(x: List[str]) -> None:
...
import typing
import typing as t
def f(x: typing.List[str]) -> None:
def f(x: t.List[str]) -> None:
...
from typing import List as IList
def f(x: IList[str]) -> None:
...
def f(x: "List[str]") -> None:
...

View File

@@ -1,40 +1,43 @@
import typing
from typing import Optional
from typing import Union
def f(x: Optional[str]) -> None:
...
import typing
def f(x: typing.Optional[str]) -> None:
...
from typing import Union
def f(x: Union[str, int, Union[float, bytes]]) -> None:
...
import typing
def f(x: typing.Union[str, int]) -> None:
...
from typing import Union
def f(x: typing.Union[(str, int)]) -> None:
...
def f(x: typing.Union[(str, int), float]) -> None:
...
def f(x: "Union[str, int, Union[float, bytes]]") -> None:
...
import typing
def f(x: "typing.Union[str, int]") -> None:
...
def f(x: Union["str", int]) -> None:
...
def f(x: Union[("str", "int"), float]) -> None:
...

View File

@@ -1,3 +1,3 @@
# coding=utf8
print('Hello world')
print("Hello world")

3
resources/test/fixtures/U009_4.py vendored Normal file
View File

@@ -0,0 +1,3 @@
# coding=utf8 # noqa: U009
print("Hello world")

33
resources/test/fixtures/U013.py vendored Normal file
View File

@@ -0,0 +1,33 @@
from typing import TypedDict, NotRequired, Literal
import typing
# dict literal
MyType1 = TypedDict("MyType1", {"a": int, "b": str})
# dict call
MyType2 = TypedDict("MyType2", dict(a=int, b=str))
# kwargs
MyType3 = TypedDict("MyType3", a=int, b=str)
# Empty TypedDict
MyType4 = TypedDict("MyType4")
# Literal values
MyType5 = TypedDict("MyType5", {"a": "hello"})
MyType6 = TypedDict("MyType6", a="hello")
# NotRequired
MyType7 = TypedDict("MyType7", {"a": NotRequired[dict]})
# total
MyType8 = TypedDict("MyType8", {"x": int, "y": int}, total=False)
# invalid identifiers
MyType9 = TypedDict("MyType9", {"in": int, "x-y": int})
# using Literal type
MyType10 = TypedDict("MyType10", {"key": Literal["value"]})
# using namespace TypedDict
MyType11 = typing.TypedDict("MyType11", {"key": int})

22
resources/test/fixtures/U014.py vendored Normal file
View File

@@ -0,0 +1,22 @@
from typing import NamedTuple
import typing
# with complex annotations
NT1 = NamedTuple("NT1", [("a", int), ("b", tuple[str, ...])])
# with default values as list
NT2 = NamedTuple(
"NT2",
[("a", int), ("b", str), ("c", list[bool])],
defaults=["foo", [True]],
)
# with namespace
NT3 = typing.NamedTuple("NT3", [("a", int), ("b", str)])
# with too many default values
NT4 = NamedTuple(
"NT4",
[("a", int), ("b", str)],
defaults=[1, "bar", "baz"],
)

80
resources/test/fixtures/U015.py vendored Normal file
View File

@@ -0,0 +1,80 @@
open("foo", "U")
open("foo", "Ur")
open("foo", "Ub")
open("foo", "rUb")
open("foo", "r")
open("foo", "rt")
open("f", "r", encoding="UTF-8")
open("f", "wt")
with open("foo", "U") as f:
pass
with open("foo", "Ur") as f:
pass
with open("foo", "Ub") as f:
pass
with open("foo", "rUb") as f:
pass
with open("foo", "r") as f:
pass
with open("foo", "rt") as f:
pass
with open("foo", "r", encoding="UTF-8") as f:
pass
with open("foo", "wt") as f:
pass
open(f("a", "b", "c"), "U")
open(f("a", "b", "c"), "Ub")
with open(f("a", "b", "c"), "U") as f:
pass
with open(f("a", "b", "c"), "Ub") as f:
pass
with open("foo", "U") as fa, open("bar", "U") as fb:
pass
with open("foo", "Ub") as fa, open("bar", "Ub") as fb:
pass
open("foo", mode="U")
open(name="foo", mode="U")
open(mode="U", name="foo")
with open("foo", mode="U") as f:
pass
with open(name="foo", mode="U") as f:
pass
with open(mode="U", name="foo") as f:
pass
open("foo", mode="Ub")
open(name="foo", mode="Ub")
open(mode="Ub", name="foo")
with open("foo", mode="Ub") as f:
pass
with open(name="foo", mode="Ub") as f:
pass
with open(mode="Ub", name="foo") as f:
pass
open(file="foo", mode='U', buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
open(file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None, mode='U')
open(file="foo", buffering=- 1, encoding=None, errors=None, mode='U', newline=None, closefd=True, opener=None)
open(mode='U', file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
open(file="foo", mode='Ub', buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
open(file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None, mode='Ub')
open(file="foo", buffering=- 1, encoding=None, errors=None, mode='Ub', newline=None, closefd=True, opener=None)
open(mode='Ub', file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
open = 1
open("foo", "U")
open("foo", "Ur")
open("foo", "Ub")
open("foo", "rUb")
open("foo", "r")
open("foo", "rt")
open("f", "r", encoding="UTF-8")
open("f", "wt")

View File

@@ -5,9 +5,8 @@ print(sys.version)
print(sys.version[:3])
print(version[:3])
# ignore from imports with aliases, patches welcome
print(v[:3])
# the tool is timid and only flags certain numeric slices
i = 3
print(sys.version[:i])

View File

@@ -44,7 +44,7 @@ expectation.expected.add((
@expect("D407: Missing dashed underline after section ('Returns')",
arg_count=3)
@expect("D413: Missing blank line after last section ('Raises')", arg_count=3)
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None, **kwargs):
"""Fetches rows from a Bigtable.
Retrieves rows pertaining to the given keys from the Table instance
@@ -57,6 +57,7 @@ def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
to fetch.
other_silly_variable: Another optional variable, that has a much
longer name than the other args, and which does nothing.
**kwargs: More keyword arguments.
Returns:
A dict mapping keys to the corresponding table row data

View File

@@ -73,7 +73,7 @@ expectation.expected.add((
"(found 'A')", arg_count=3)
@expect("D413: Missing blank line after last section ('Examples')",
arg_count=3)
def foo(var1, var2, long_var_name='hi'):
def foo(var1, var2, long_var_name='hi', **kwargs):
r"""A one-line summary that does not use variable names.
Several sentences providing an extended description. Refer to
@@ -91,6 +91,8 @@ def foo(var1, var2, long_var_name='hi'):
detail, e.g. ``(N,) ndarray`` or ``array_like``.
long_var_name : {'hi', 'ho'}, optional
Choices in brackets, default first when optional.
**kwargs : int
More keyword arguments.
Returns
-------

View File

@@ -0,0 +1,7 @@
import os
# This is a comment in the same section, so we need to add one newline.
import sys
import numpy as np
# This is a comment, but it starts a new section, so we don't need to add a newline
# before it.
import leading_prefix

View File

@@ -0,0 +1,25 @@
# Comment 1
# Comment 2
import D
# Comment 3a
import C
# Comment 3b
import C
import B # Comment 4
# Comment 5
# Comment 6
from A import (
a, # Comment 7
b,
c, # Comment 8
)
from A import (
a, # Comment 9
b, # Comment 10
c, # Comment 11
)

View File

@@ -0,0 +1,4 @@
import a
# Don't take this comment into account when determining whether the next import can fit on one line.
from b import c
from d import e # Do take this comment into account when determining whether the next import can fit on one line.

View File

@@ -0,0 +1,4 @@
from .a import a
from ..a import a
from ..b import a
from .b import a

View File

@@ -0,0 +1,11 @@
import io
# Old MacDonald had a farm,
# EIEIO
# And on his farm he had a cow,
# EIEIO
# With a moo-moo here and a moo-moo there
# Here a moo, there a moo, everywhere moo-moo
# Old MacDonald had a farm,
# EIEIO
from errno import EIO
import abc

View File

@@ -0,0 +1,26 @@
from a import b
from a import BAD as DEF
from a import B
from a import Boo as DEF
from a import B as Abc
from a import B as A
from a import B as DEF
from a import b as a
from a import b as x
from a import b as c
from b import c
from a import b as d
from a import b as y
from b import C
from b import c as d
import A
import a
import b
import B
import x as y
import x as A
import x as Y
import x
import x as a

View File

@@ -0,0 +1,2 @@
import A # type: ignore
from B import C # type: ignore

View File

@@ -16,6 +16,9 @@ multiline-quotes = "double"
docstring-quotes = "double"
avoid-escape = true
[tool.ruff.mccabe]
max-complexity = 10
[tool.ruff.pep8-naming]
ignore-names = [
"setUp",
@@ -33,7 +36,11 @@ ignore-names = [
]
classmethod-decorators = [
"classmethod",
"pydantic.validator",
]
staticmethod-decorators = [
"staticmethod",
]
[tool.ruff.flake8-tidy-imports]
ban-relative-imports = "parents"

View File

@@ -4,4 +4,4 @@ import sysconfig
if __name__ == "__main__":
ruff = os.path.join(sysconfig.get_path("scripts"), "ruff")
os.spawnv(os.P_WAIT, ruff, [ruff, *sys.argv[1:]])
sys.exit(os.spawnv(os.P_WAIT, ruff, [ruff, *sys.argv[1:]]))

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_dev"
version = "0.0.115"
version = "0.0.143"
edition = "2021"
[dependencies]
@@ -8,10 +8,10 @@ anyhow = { version = "1.0.66" }
clap = { version = "4.0.1", features = ["derive"] }
codegen = { version = "0.2.0" }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "a13ec97dd4eb925bde4d426c6e422582793b260c" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
ruff = { path = ".." }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
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" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }

View File

@@ -24,7 +24,7 @@ pub struct Cli {
pub fn main(cli: &Cli) -> Result<()> {
// Build up a map from prefix to matching CheckCodes.
let mut prefix_to_codes: BTreeMap<String, BTreeSet<CheckCode>> = Default::default();
let mut prefix_to_codes: BTreeMap<String, BTreeSet<CheckCode>> = BTreeMap::default();
for check_code in CheckCode::iter() {
let as_ref: String = check_code.as_ref().to_string();
let prefix_len = as_ref
@@ -77,6 +77,7 @@ pub fn main(cli: &Cli) -> Result<()> {
.arg_ref_self()
.ret(Type::new("Vec<CheckCode>"))
.vis("pub")
.line("#[allow(clippy::match_same_arms)]")
.line("match self {");
for (prefix, codes) in &prefix_to_codes {
gen = gen.line(format!(
@@ -96,6 +97,7 @@ pub fn main(cli: &Cli) -> Result<()> {
.arg_ref_self()
.ret(Type::new("PrefixSpecificity"))
.vis("pub")
.line("#[allow(clippy::match_same_arms)]")
.line("match self {");
for prefix in prefix_to_codes.keys() {
let num_numeric = prefix.chars().filter(|char| char.is_numeric()).count();
@@ -104,7 +106,7 @@ pub fn main(cli: &Cli) -> Result<()> {
2 => "Tens",
1 => "Hundreds",
0 => "Category",
_ => panic!("Invalid prefix: {}", prefix),
_ => panic!("Invalid prefix: {prefix}"),
};
gen = gen.line(format!(
"CheckCodePrefix::{prefix} => PrefixSpecificity::{},",
@@ -115,7 +117,8 @@ pub fn main(cli: &Cli) -> Result<()> {
// Construct the output contents.
let mut output = String::new();
output.push_str("//! File automatically generated by examples/generate_check_code_prefix.rs.");
output
.push_str("//! File automatically generated by `examples/generate_check_code_prefix.rs`.");
output.push('\n');
output.push('\n');
output.push_str("use serde::{{Serialize, Deserialize}};");
@@ -127,13 +130,28 @@ pub fn main(cli: &Cli) -> Result<()> {
output.push('\n');
output.push('\n');
output.push_str(&scope.to_string());
output.push('\n');
output.push('\n');
// Add the list of output categories (not generated).
output.push_str("pub const CATEGORIES: &[CheckCodePrefix] = &[");
output.push('\n');
for prefix in prefix_to_codes.keys() {
if prefix.chars().all(char::is_alphabetic) {
output.push_str(&format!("CheckCodePrefix::{prefix},"));
output.push('\n');
}
}
output.push_str("];");
output.push('\n');
output.push('\n');
// Write the output to `src/checks_gen.rs` (or stdout).
if cli.dry_run {
println!("{}", output);
println!("{output}");
} else {
let mut f = OpenOptions::new().write(true).truncate(true).open(FILE)?;
write!(f, "{}", output)?;
write!(f, "{output}")?;
}
Ok(())

View File

@@ -61,7 +61,7 @@ pub fn main(cli: &Cli) -> Result<()> {
}
if cli.dry_run {
print!("{}", output);
print!("{output}");
} else {
// Read the existing file.
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
@@ -84,9 +84,9 @@ pub fn main(cli: &Cli) -> Result<()> {
// Write the prefix, new contents, and suffix.
let mut f = OpenOptions::new().write(true).truncate(true).open(&file)?;
write!(f, "{}\n\n", prefix)?;
write!(f, "{}", output)?;
write!(f, "{}", suffix)?;
write!(f, "{prefix}\n\n")?;
write!(f, "{output}")?;
write!(f, "{suffix}")?;
}
Ok(())

View File

@@ -19,7 +19,7 @@ pub fn main(cli: &Cli) -> Result<()> {
let contents = fs::read_to_string(&cli.file)?;
let python_ast = parser::parse_program(&contents, &cli.file.to_string_lossy())?;
let mut generator = SourceGenerator::new();
generator.unparse_suite(&python_ast)?;
generator.unparse_suite(&python_ast);
println!("{}", generator.generate()?);
Ok(())
}

View File

@@ -1,3 +1,16 @@
#![allow(
clippy::collapsible_else_if,
clippy::collapsible_if,
clippy::implicit_hasher,
clippy::match_same_arms,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::module_name_repetitions,
clippy::must_use_candidate,
clippy::similar_names,
clippy::too_many_lines
)]
pub mod generate_check_code_prefix;
pub mod generate_rules_table;
pub mod generate_source_code;

View File

@@ -1,3 +1,16 @@
#![allow(
clippy::collapsible_else_if,
clippy::collapsible_if,
clippy::implicit_hasher,
clippy::match_same_arms,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::module_name_repetitions,
clippy::must_use_candidate,
clippy::similar_names,
clippy::too_many_lines
)]
use anyhow::Result;
use clap::{Parser, Subcommand};
use ruff_dev::{

View File

@@ -17,6 +17,6 @@ pub struct Cli {
pub fn main(cli: &Cli) -> Result<()> {
let contents = fs::read_to_string(&cli.file)?;
let python_ast = parser::parse_program(&contents, &cli.file.to_string_lossy())?;
println!("{:#?}", python_ast);
println!("{python_ast:#?}");
Ok(())
}

View File

@@ -1,4 +1,4 @@
//! Print the LibCST CST for a given Python file.
//! Print the `LibCST` CST for a given Python file.
use std::fs;
use std::path::PathBuf;
@@ -17,7 +17,7 @@ pub fn main(cli: &Cli) -> Result<()> {
let contents = fs::read_to_string(&cli.file)?;
match libcst_native::parse_module(&contents, None) {
Ok(python_cst) => {
println!("{:#?}", python_cst);
println!("{python_cst:#?}");
Ok(())
}
Err(_) => Err(anyhow::anyhow!("Failed to parse CST")),

View File

@@ -17,7 +17,7 @@ pub struct Cli {
pub fn main(cli: &Cli) -> Result<()> {
let contents = fs::read_to_string(&cli.file)?;
for (_, tok, _) in lexer::make_tokenizer(&contents).flatten() {
println!("{:#?}", tok);
println!("{tok:#?}");
}
Ok(())
}

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