Compare commits

...

196 Commits

Author SHA1 Message Date
Charlie Marsh
818582fe8a Bump version to 0.0.202 2022-12-30 08:16:32 -05:00
Charlie Marsh
90574c1088 Set editor background on top-level component (#1478) 2022-12-30 07:55:54 -05:00
Charlie Marsh
3061a35e7c Use more precise ranges for class and function checks (#1476) 2022-12-30 07:39:20 -05:00
Martin Fischer
87681697ae Improve CLI help for --select (#1471) 2022-12-30 07:16:44 -05:00
Martin Fischer
e9ec2a7b36 Add use test_case::test_case; to CONTRIBUTING.md
I previously tried adding the #[test_case()] attribute macro and got
confused because the Rust compilation suddenly failed with:

    error[E0658]: use of unstable library feature 'custom_test_frameworks': custom test frameworks are an unstable feature

which is a quite confusing error message.  The solution is to just add
`use test_case::test_case;`, so this commit adds that line to the
example in CONTRIBUTING.md to spare others this source of confusion.
2022-12-30 07:13:48 -05:00
Martin Fischer
b0bb75dc1c Add --select to command suggested in CONTRIBUTING.md
By default only E* and F* lints are enabled. I previously followed the
CONTRIBUTING.md instructions to implement a TID* lint and was confused
why my lint wasn't being run.
2022-12-30 07:13:48 -05:00
Martin Fischer
ebca5c2df8 Make banned-api config setting optional (#1465) 2022-12-30 07:09:56 -05:00
Charlie Marsh
16b10c42f0 Fix lint issues 2022-12-29 23:12:28 -05:00
Charlie Marsh
4a6e5d1549 Bump version to 0.0.201 2022-12-29 23:01:35 -05:00
Charlie Marsh
b078050732 Implicit flake8-implicit-str-concat (#1463) 2022-12-29 23:00:55 -05:00
Charlie Marsh
5f796b39b4 Run cargo fmt 2022-12-29 22:25:14 -05:00
Martin Fischer
9d34da23bd Implement TID251 (banning modules & module members) (#1436) 2022-12-29 22:11:12 -05:00
Charlie Marsh
bde12c3bb3 Restore quick fixes for playground 2022-12-29 20:16:19 -05:00
Colin Delahunty
f735660801 Removed unicode literals (#1448) 2022-12-29 20:11:33 -05:00
Charlie Marsh
34cd22dfc1 Copy URL but don't update the hash (#1458) 2022-12-29 19:46:50 -05:00
Charlie Marsh
9fafe16a55 Re-add GitHub badge to the bottom of the page 2022-12-29 19:38:33 -05:00
Charlie Marsh
e9a4cb1c1d Remove generated TypeScript options (#1456) 2022-12-29 19:37:49 -05:00
Charlie Marsh
9db825c731 Use trailingComma: 'all' (#1457) 2022-12-29 19:36:51 -05:00
Charlie Marsh
2c7464604a Implement dark mode (#1455) 2022-12-29 19:33:46 -05:00
Charlie Marsh
cd2099f772 Move default options into WASM interface (#1453) 2022-12-29 18:06:57 -05:00
Adam Turner
091d36cd30 Add Sphinx to user list (#1451) 2022-12-29 18:06:09 -05:00
Mathieu Kniewallner
02f156c6cb docs(README): add missing flake8-simplify (#1449) 2022-12-29 17:02:26 -05:00
Charlie Marsh
9f7350961e Rename config to settings in the playground (#1450) 2022-12-29 16:59:38 -05:00
Charlie Marsh
118a93260a Bump version to 0.0.200 2022-12-29 13:31:23 -05:00
Charlie Marsh
1c16255884 Include docstrings for settings enum members (#1446) 2022-12-29 13:15:44 -05:00
Charlie Marsh
16c4552946 Update snapshots 2022-12-29 13:13:43 -05:00
Charlie Marsh
0ba3989b3d Make update check enablement cofnigurable (#1445) 2022-12-29 13:06:22 -05:00
Charlie Marsh
3435e15cba Avoid caching diffs (#1441) 2022-12-29 12:51:58 -05:00
Maksudul Haque
781bbbc286 [pygrep-hooks] Adds Check for Blanket # noqa (#1440) 2022-12-29 12:43:16 -05:00
Charlie Marsh
acf0b82f19 Re-style the Ruff playground (#1438) 2022-12-29 11:47:27 -05:00
Charlie Marsh
057414ddd4 Bump version to 0.0.199 2022-12-28 20:58:43 -05:00
Charlie Marsh
ca94e9aa26 Warn the user when max iteration count is reached (#1433) 2022-12-28 20:56:43 -05:00
Charlie Marsh
797b5bd261 Split into lint and lint-and-fix methods (#1432) 2022-12-28 20:14:33 -05:00
Charlie Marsh
a64f62f439 Revert setup.py change 2022-12-28 19:34:20 -05:00
Charlie Marsh
058ee8e6bf Add a --diff flag to dry-run autofixes (#1431) 2022-12-28 19:21:29 -05:00
Charlie Marsh
39fc1f0c1b Add a note on autofix settings 2022-12-28 17:26:38 -05:00
Colin Delahunty
34842b4c4b PyUpgrade: Replace pipes with capture_output=True (#1415) 2022-12-28 16:53:35 -05:00
Charlie Marsh
dfa6fa8f83 Check in updated snapshots 2022-12-28 16:42:55 -05:00
Colin Delahunty
6131c819ed Rewrite xml.etree.cElementTree to xml.etree.ElementTree (#1426) 2022-12-28 16:30:36 -05:00
Hannes Käufler
79ba420faa Extract duplicated logic into method (#1428) 2022-12-28 16:10:53 -05:00
Charlie Marsh
d16ba890ae Turn off wasm-pack tests (#1427) 2022-12-28 12:55:25 -05:00
Charlie Marsh
6b6851bf1f Update JSON schema 2022-12-28 12:27:01 -05:00
Charlie Marsh
056718ce75 Remove stray Plugins doc 2022-12-28 12:24:48 -05:00
Charlie Marsh
4521fdf021 Only test --lib for wasm-pack 2022-12-28 10:28:40 -05:00
Maksudul Haque
8e479628f2 Add Support for GitLab CI Code Quality Report Format (#1424) 2022-12-28 10:10:43 -05:00
Charlie Marsh
2a11c4b1f1 Try increasing wasm-bindgen timeout 2022-12-28 07:39:23 -05:00
Anders Kaseorg
a8cde5a936 Check for keyword arguments before the last star argument (#1420) 2022-12-27 23:20:03 -05:00
Charlie Marsh
1822b57ed5 Remove 'static 2022-12-27 21:57:32 -05:00
Charlie Marsh
c679570041 Bump version to 0.0.198 2022-12-27 21:39:53 -05:00
Charlie Marsh
edcb3a7217 Support --select ALL to enable all error codes (#1418) 2022-12-27 21:38:26 -05:00
Charlie Marsh
6e43dc7270 Add nbQA support to the docs (#1417) 2022-12-27 21:24:07 -05:00
Charlie Marsh
570d0864f2 Add rule to detect keyword arguments before starred arguments (#1416) 2022-12-27 21:17:22 -05:00
Charlie Marsh
d22e96916c Automatically detect and respect indentation and quotation code style (#1413) 2022-12-27 19:45:50 -05:00
Charlie Marsh
043d31dcdf Bump version to 0.0.197 2022-12-27 17:05:15 -05:00
Charlie Marsh
1392e4cced Default to double quotes in code_gen.rs (#1412) 2022-12-27 16:17:49 -05:00
Charlie Marsh
59ee89a091 Fix it_converts_docstring_conventions test 2022-12-27 15:41:29 -05:00
Charlie Marsh
6a7c3728ee Set convention in flake8-to-ruff (#1410) 2022-12-27 13:51:24 -05:00
Charlie Marsh
0a60eb0aca Fix invalid reference to ruff_options.rs (#1409) 2022-12-27 12:12:28 -05:00
Charlie Marsh
3e96803033 Bump version to 0.0.196 2022-12-27 12:02:02 -05:00
Colin Delahunty
c59035139c Pyupgrade: converts universal_newlines to text in subprocess.run (#1403) 2022-12-27 12:01:27 -05:00
Charlie Marsh
7632d7eda7 Allow specification of explicit docstring convention (#1408) 2022-12-27 11:50:28 -05:00
Charlie Marsh
b4dbe62da0 Add cargo +nightly dev generate-all (#1404) 2022-12-27 10:07:18 -05:00
Harutaka Kawamura
9106d5338b Replace make_tokenize with make_tokenizer_located (#1405) 2022-12-27 10:07:03 -05:00
Reiner Gerecke
534d8d049c Support isort's force-single-line option (#1366) 2022-12-27 08:51:32 -05:00
Charlie Marsh
e692c4a2cc Tweak secret detection for playground releases (#1402) 2022-12-27 08:41:53 -05:00
Reiner Gerecke
e0b39fa63e Implement pyupgrade check for io.open alias (#1399) 2022-12-27 07:47:40 -05:00
Charlie Marsh
320a48977b Tweak README again 2022-12-26 21:17:18 -05:00
Charlie Marsh
0d05aaeb6e Add monorepo note 2022-12-26 21:00:14 -05:00
Charlie Marsh
1e4b1533ad Bump version to 0.0.195 2022-12-26 20:41:39 -05:00
Charlie Marsh
df4f5358f9 Bump pyupgrade implementation count 2022-12-26 19:56:12 -05:00
Colin Delahunty
58c383401c Replace typing.Text with str (#1391) 2022-12-26 19:55:41 -05:00
Charlie Marsh
018b9a2977 Only run playground release in main repo (#1396) 2022-12-26 19:45:04 -05:00
Charlie Marsh
658cb87ddd Enable Quick Fix in the playground (#1395) 2022-12-26 19:25:50 -05:00
Charlie Marsh
0d35087bc6 Choose a more interesting example snippet (#1394) 2022-12-26 15:19:17 -05:00
Charlie Marsh
b721125af9 Add badge to playground (#1393) 2022-12-26 15:13:55 -05:00
Charlie Marsh
20d6b21d77 Add ESLint, Prettier, and TypeScript checks (#1384) 2022-12-26 15:08:22 -05:00
Charlie Marsh
1a27992f47 Enable preview deployments for playground (#1383) 2022-12-26 14:52:13 -05:00
Charlie Marsh
89cebe1ce2 Update name of Playground job 2022-12-26 12:10:22 -05:00
Reiner Gerecke
bdb1505262 Web playground with WASM (#1279) 2022-12-26 12:09:17 -05:00
Charlie Marsh
8c018e8261 Add settings validation to lib.rs 2022-12-26 10:12:07 -05:00
Colin Delahunty
debd909b2c Magic Trailing Commas in isort (#1363) 2022-12-26 09:40:02 -05:00
Reiner Gerecke
fa54538bd1 Only re-associate inline comments during normalization when necessary (#1380) 2022-12-26 07:52:13 -05:00
Reiner Gerecke
939f738a71 Update rust python to handle files with BOM (#1379) 2022-12-26 07:03:13 -05:00
Charlie Marsh
b0f30bef8f Add support for ruff.toml (#1378) 2022-12-25 21:55:07 -05:00
Charlie Marsh
28c45eb2a3 Remove required versions from pyproject.toml 2022-12-25 20:21:01 -05:00
Charlie Marsh
4dc45912e8 Run cargo dev commands 2022-12-25 20:12:12 -05:00
Charlie Marsh
5ef8bff341 Bump version to 0.0.194 2022-12-25 19:54:45 -05:00
Charlie Marsh
2ab8f77223 Update command-line help text 2022-12-25 19:54:14 -05:00
Charlie Marsh
8b72f55a09 Add --required-version (#1376) 2022-12-25 19:53:50 -05:00
Charlie Marsh
19121219fb Avoid double-extending past the end when showing source (#1377) 2022-12-25 19:52:42 -05:00
Charlie Marsh
d9355c989a Add a --fix-only command-line and pyproject.toml option (#1375) 2022-12-25 18:49:56 -05:00
Charlie Marsh
ec80d1cd85 Respect natural ordering for imports (#1374) 2022-12-25 18:11:41 -05:00
Charlie Marsh
9bb470c7d4 Ignore unused arguments for @overload stubs (#1373) 2022-12-25 17:22:31 -05:00
Harutaka Kawamura
dca3fcd8d1 Improve excepthandler_name_range (#1368) 2022-12-25 00:12:12 -05:00
Harutaka Kawamura
10f75c9620 Fix F841 (UnusedVariable) range in except handler (#1367) 2022-12-24 22:55:55 -05:00
Charlie Marsh
cc2110449c Run cargo dev commands 2022-12-24 15:06:12 -05:00
Charlie Marsh
f6ca49e05f Bump version to 0.0.193 2022-12-24 14:56:26 -05:00
Charlie Marsh
9a7331b2e2 Annotate RUF100 messages with unmatched, disabled, and unknown codes (#1365) 2022-12-24 14:55:55 -05:00
Edgar R. M
4888afd423 Generate JSON schema for Ruff options (#1329) 2022-12-24 14:10:22 -05:00
Sawbez
0dc523b081 Add autofix for W605 [InvalidEscapeSequence] (#1361) 2022-12-24 13:46:28 -05:00
Harutaka Kawamura
63772e335d Fix B025 location (#1360) 2022-12-24 12:22:11 -05:00
Reiner Gerecke
7f4ff1e38f Fix false-positive in RET504 when referencing globals (#1358) 2022-12-24 12:02:57 -05:00
Reiner Gerecke
32ebc1d227 Don't trigger E721 when comparing with None (#1356) 2022-12-24 04:45:40 -05:00
Sawbez
4ded155dc0 Add autofix for W292 [NoNewLineAtEndOfFile] (#1354) 2022-12-23 23:14:17 -05:00
Harutaka Kawamura
201e1250de Update RustPython to use the correct BinOp location (#1355) 2022-12-23 22:58:39 -05:00
Reiner Gerecke
102b049a32 Add cache-dir to command-line and pyproject.toml (#1351) 2022-12-23 22:58:29 -05:00
Charlie Marsh
74f49eda64 Bump compatibility to 3.11 (#1352) 2022-12-23 12:12:11 -05:00
Reiner Gerecke
9da3e2cca1 Implement "native literals" check from pyupgrade (#1350) 2022-12-23 11:40:32 -05:00
Charlie Marsh
e290050821 Avoid enabling all EM checks at once (#1349) 2022-12-23 08:25:07 -05:00
Charlie Marsh
bc9ed0a4ef Tweak LSP docs 2022-12-22 22:16:32 -05:00
Charlie Marsh
20b9b44973 Link to ruff-lsp docs 2022-12-22 21:53:11 -05:00
Charlie Marsh
6e5a553235 Update Neovim instructions 2022-12-22 21:52:30 -05:00
Charlie Marsh
2a08a63f17 Add a link to the PyCharm plugin (#1345) 2022-12-22 21:50:24 -05:00
Colin Delahunty
d4290e6721 Update CONTRIBUTING.md (#1344) 2022-12-22 21:04:19 -05:00
Charlie Marsh
51bda28a7d Bump version to 0.0.192 2022-12-22 17:31:31 -05:00
Reiner Gerecke
cc26051b7a Implement "datetime.UTC alias" check from pyupgrade (#1341) 2022-12-22 17:21:36 -05:00
Charlie Marsh
3ac5a9aa31 Respect --force-exclude for files passed via stdin (#1342) 2022-12-22 16:40:15 -05:00
Charlie Marsh
451047c30d Exclude directly-passed files nested in excluded subdirectories 2022-12-22 15:08:11 -05:00
Charlie Marsh
6907df489b Extend false-positive list for flake8-boolean-trap (#1338) 2022-12-22 10:56:04 -05:00
Charlie Marsh
970f882b03 Set force-exclude for pre-commit in README (#1337) 2022-12-22 10:51:20 -05:00
Charlie Marsh
3eff9a2860 Allow unittest methods in flake8-boolean-trap (#1333) 2022-12-22 08:40:22 -05:00
Charlie Marsh
a4a24a0ef3 Add some more repositories to the user list (#1328) 2022-12-21 22:16:47 -05:00
Charlie Marsh
48e3c046b0 Fix integration tests 2022-12-21 21:25:37 -05:00
Charlie Marsh
03e4f5be8a Bump version to 0.0.191 2022-12-21 21:16:21 -05:00
Charlie Marsh
99657b7d92 Implement E401 ("multiple imports on one line") (#1326) 2022-12-21 21:15:57 -05:00
Charlie Marsh
40377aa1fc Move number of errors to the bottom of the output summary (#1325) 2022-12-21 21:04:26 -05:00
Charlie Marsh
2a37017e8c Add src to Settings hash 2022-12-21 21:01:20 -05:00
Charlie Marsh
ff66d08cef Run generate-options 2022-12-21 20:58:14 -05:00
Charlie Marsh
dad8035eef Support shell expansion in src field (#1324) 2022-12-21 20:57:20 -05:00
Charlie Marsh
bf5fec342c Support shell expansion in extend paths (#1323) 2022-12-21 20:46:38 -05:00
Charlie Marsh
66a6c81ebf Infer package roots when running via stdin (#1321) 2022-12-21 20:30:10 -05:00
Charlie Marsh
5c70f5044b Improve debug logging in flake8-to-ruff (#1320) 2022-12-21 20:05:48 -05:00
Charlie Marsh
953d141ab2 Support code redirects in flake8-to-ruff (#1318) 2022-12-21 19:31:20 -05:00
Charlie Marsh
07dba46039 Extract line length from pyproject.toml Black section (#1317) 2022-12-21 19:05:18 -05:00
Ran Benita
3b02da9d7b Fix false positive DTZ001 on datetime(2000, 1, 1, 0, 0, 0, 0, utc) (#1308) 2022-12-21 19:03:36 -05:00
Charlie Marsh
20234c6156 Bump version to 0.0.190 2022-12-21 16:01:48 -05:00
Charlie Marsh
de767cc026 Avoid used-prior-global-declaration false-positives in f-strings (#1314) 2022-12-21 14:34:09 -05:00
Charlie Marsh
ce1663d302 Allow overriding cache location via RUFF_CACHE_DIR (#1312) 2022-12-21 14:24:10 -05:00
Charlie Marsh
f40e4bcd14 Avoid flagging RUF100 as a RUF100 violation (#1305) 2022-12-20 17:40:10 -05:00
Charlie Marsh
e7d40d435f Avoid F821 false positives for Mypy extensions (#1304) 2022-12-20 16:29:33 -05:00
Charlie Marsh
ef8fe31c0c Bump version to 0.0.189 2022-12-20 13:26:17 -05:00
Charlie Marsh
226f682c99 Avoid DTZ007 false-positives for non-string arguments (#1300) 2022-12-20 13:20:53 -05:00
Hannes Käufler
468ffd29fb [Stylistic/non-functional] Use an r# format string to make json easier to read (#1299) 2022-12-20 12:55:21 -05:00
Charlie Marsh
a61126ab23 Run generate-options 2022-12-19 23:19:05 -05:00
Charlie Marsh
54c7c25861 Revert test changes to setup.py 2022-12-19 20:09:47 -05:00
Charlie Marsh
eff7700d92 Add --force-exclude setting to force exclusions with pre-commit (#1295) 2022-12-19 20:08:59 -05:00
Charlie Marsh
8934f6938d Avoid RET504 errors for intermediary function calls (#1294) 2022-12-19 19:48:09 -05:00
Charlie Marsh
8f0fc3033a Update Arg section checking to match latest pydocstyle (#1293) 2022-12-19 16:39:42 -05:00
Charlie Marsh
4107bc828d Bump version to 0.0.188 2022-12-19 12:18:06 -05:00
Charlie Marsh
706d28cabc Rename PDV checks to PD (#1288) 2022-12-19 00:20:28 -05:00
Charlie Marsh
4da2264722 Avoid T201 errors for print(..., file=fp)-like calls (#1287) 2022-12-19 00:10:07 -05:00
Charlie Marsh
bf88c815aa Move flake8-debugger tests into flake8-debugger subdirectory (#1286) 2022-12-18 22:06:42 -05:00
Yasu_umi
8a4831dd5b Implement flake8-datetimez (#1270) 2022-12-18 22:06:06 -05:00
Charlie Marsh
b5ab492a70 Bump version to 0.0.187 2022-12-18 20:09:02 -05:00
Charlie Marsh
1fc09ebd5c Fix inverted E501 condition (#1285) 2022-12-18 20:08:30 -05:00
Charlie Marsh
6cf047976c Bump pygrep-hooks tally in README 2022-12-18 18:05:32 -05:00
Reiner Gerecke
87465daacc pygrep-hooks - deprecated use of logging.warn & no blanket type ignore (#1275) 2022-12-18 18:04:21 -05:00
Chris Brendel
a52bed7101 Use --stdin-filename when resolving configuration files (#1281) 2022-12-18 17:51:55 -05:00
Anders Kaseorg
20ac823778 generate-check-code-prefix: Run rustfmt automatically; only write if changed (#1282) 2022-12-18 17:46:23 -05:00
Charlie Marsh
1028ed3565 Bump version to 0.0.186 2022-12-18 14:30:30 -05:00
Anders Kaseorg
98897db6ac Add packaging status badge from repology (#1276) 2022-12-18 14:27:29 -05:00
Honkertonken
5ce4262112 Readme : Fix incorrect exmaple. (#1277) 2022-12-18 12:04:48 -05:00
Charlie Marsh
6b2359384d Print redirect warnings exactly once per code (#1280) 2022-12-18 12:03:49 -05:00
Harutaka Kawamura
d3443d7c19 Update RustPython to use correct Tuple location (#1278) 2022-12-18 08:53:57 -05:00
Anders Kaseorg
04b1e1de6f README: Add missing backtick (#1274) 2022-12-18 00:29:33 -05:00
Anders Kaseorg
c93c85300f Repair corrupted PDV007, PDV009 messages (#1273) 2022-12-18 00:29:12 -05:00
Charlie Marsh
73ed6f8654 Touch-up README 2022-12-17 21:38:45 -05:00
Charlie Marsh
eb183645f3 Add ruff-lsp to README (#1272)
Add ruff-lsp to README
2022-12-17 21:37:11 -05:00
Charlie Marsh
ef17aa93da Add ruff-lsp to README (#1272) 2022-12-17 21:37:00 -05:00
Charlie Marsh
f366b0147f Add ruff-lsp to README (#1272) 2022-12-17 21:35:44 -05:00
Charlie Marsh
fc88fa35ff Add instructions for Sublime Text installation (#1271) 2022-12-17 16:22:50 -05:00
Charlie Marsh
a2806eb8ef Bump version to 0.0.185 2022-12-16 23:47:56 -05:00
Charlie Marsh
89d919eac5 Re-remove W605_1.py from Black compatibility test 2022-12-16 23:17:27 -05:00
Charlie Marsh
5ad77fbc8d Move checkers into their own module (#1268) 2022-12-16 22:55:47 -05:00
Charlie Marsh
9cb18a481b Separate line-based checker from noqa enforcement (#1267) 2022-12-16 22:49:27 -05:00
Charlie Marsh
2393e270ed Change a few more methods to take AsRef<Path> 2022-12-16 21:38:52 -05:00
Charlie Marsh
f36e6035c8 Change a few methods to take AsRef<Path> 2022-12-16 21:28:19 -05:00
Charlie Marsh
ecf0dd05d6 Auto-detect same-package imports (#1266) 2022-12-16 21:19:11 -05:00
Charlie Marsh
5f67ee93f7 Replace cache bool with an enum 2022-12-16 15:45:30 -05:00
Charlie Marsh
1e19142d0e Bump version to 0.0.184 2022-12-16 14:36:25 -05:00
Charlie Marsh
6a95dade6d Actually check-in snapshots for #1265 2022-12-16 14:36:00 -05:00
Charlie Marsh
d6e765877e Enable autofix for __init__ method with missing None-return (#1265) 2022-12-16 14:28:56 -05:00
Charlie Marsh
e4d36bae57 Replace ignore_noqa and autofix booleans with enums (#1264) 2022-12-16 14:01:25 -05:00
Harutaka Kawamura
e3531276a7 Fix F501 (line-too-long) start location (#1262) 2022-12-16 11:29:47 -05:00
Charlie Marsh
634553f188 Add ignore-variadic-names options to flake8-unused-arguments (#1261) 2022-12-16 00:22:38 -05:00
Edgar R. M
4ff0b75045 test: Fix flake8-errmsg snapshots (#1260) 2022-12-15 23:53:15 -05:00
Charlie Marsh
481d668511 Add flake8-errmsg to README 2022-12-15 23:16:38 -05:00
Charlie Marsh
a9f56ee76e Bump version to 0.0.183 2022-12-15 23:15:12 -05:00
Charlie Marsh
b4bfa87104 Avoid removing partially-unused imports (#1259) 2022-12-15 23:13:58 -05:00
Charlie Marsh
b9f42bf5e5 Remove extraneous test file 2022-12-15 23:12:19 -05:00
Edgar R. M
8281d414ca Implement flake8-errmsg (#1258) 2022-12-15 23:10:59 -05:00
Charlie Marsh
7e45a9f2e2 Avoid generating invalid statements when deleting from multi-statement lines (#1253) 2022-12-15 22:17:31 -05:00
Reiner Gerecke
a000cd4a09 Test to prevent continious reformatting when used together with black (#1206) 2022-12-15 15:26:41 -05:00
Martin Lehoux
d8b4b92733 Implement U016: Remove six compatibility code (#1013) 2022-12-15 14:16:58 -05:00
Edgar R. M
27de342e75 Implement pandas-vet (#1235) 2022-12-15 14:01:01 -05:00
Charlie Marsh
d805067683 Avoid fixing E711 and E712 issues that would cause F632 (#1248) 2022-12-15 12:08:31 -05:00
418 changed files with 26597 additions and 3106 deletions

View File

@@ -12,7 +12,7 @@ env:
RUSTUP_MAX_RETRIES: 10
jobs:
cargo_build:
cargo-build:
name: "cargo build"
runs-on: ubuntu-latest
steps:
@@ -36,14 +36,14 @@ jobs:
${{ runner.os }}-build-
${{ runner.os }}-
- run: cargo build --all --release
- run: ./target/release/ruff_dev generate-rules-table
- run: ./target/release/ruff_dev generate-options
- run: git diff --quiet README.md || echo "::error file=README.md::This file is outdated. You may have to rerun 'cargo dev generate-options' and/or 'cargo dev generate-rules-table'."
- run: ./target/release/ruff_dev generate-check-code-prefix && cargo fmt -- src/checks_gen.rs
- run: git diff --quiet src/checks_gen.rs || echo "::error file=src/checks_gen.rs::This file is outdated. You may have to rerun 'cargo dev generate-check-code-prefix'."
- run: git diff --exit-code -- README.md src/checks_gen.rs
- run: ./target/release/ruff_dev generate-all
- run: git diff --quiet README.md || echo "::error file=README.md::This file is outdated. Run 'cargo +nightly dev generate-all'."
- run: git diff --quiet src/checks_gen.rs || echo "::error file=src/checks_gen.rs::This file is outdated. Run 'cargo +nightly dev generate-all'."
- run: git diff --quiet ruff.schema.json || echo "::error file=ruff.schema.json::This file is outdated. Run 'cargo +nightly dev generate-all'."
- run: git diff --quiet playground/src/ruff_options.ts || echo "::error file=playground/src/ruff_options.ts::This file is outdated. Run 'cargo +nightly dev generate-all'."
- run: git diff --exit-code -- README.md src/checks_gen.rs ruff.schema.json playground/src/ruff_options.ts
cargo_fmt:
cargo-fmt:
name: "cargo fmt"
runs-on: ubuntu-latest
steps:
@@ -79,6 +79,7 @@ jobs:
toolchain: nightly-2022-11-01
override: true
components: clippy
target: wasm32-unknown-unknown
- uses: actions/cache@v3
env:
cache-name: cache-cargo
@@ -92,8 +93,9 @@ jobs:
${{ runner.os }}-build-
${{ runner.os }}-
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic
- run: cargo clippy --workspace --target wasm32-unknown-unknown --all-features -- -D warnings -W clippy::pedantic
cargo_test:
cargo-test:
name: "cargo test"
runs-on: ubuntu-latest
steps:
@@ -115,9 +117,41 @@ jobs:
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- run: pip install black[d]==22.12.0
- run: cargo test --all
- run: cargo test --package ruff --test black_compatibility_test -- --ignored
maturin_build:
# TODO(charlie): Re-enable the `wasm-pack` tests.
# See: https://github.com/charliermarsh/ruff/issues/1425
# wasm-pack-test:
# name: "wasm-pack test"
# runs-on: ubuntu-latest
# env:
# WASM_BINDGEN_TEST_TIMEOUT: 60
# steps:
# - uses: actions/checkout@v3
# - uses: actions-rs/toolchain@v1
# with:
# profile: minimal
# toolchain: nightly-2022-11-01
# override: true
# - uses: actions/cache@v3
# env:
# cache-name: cache-cargo
# with:
# path: |
# ~/.cargo/registry
# ~/.cargo/git
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
# restore-keys: |
# ${{ runner.os }}-build-${{ env.cache-name }}-
# ${{ runner.os }}-build-
# ${{ runner.os }}-
# - uses: jetli/wasm-pack-action@v0.4.0
# - uses: jetli/wasm-bindgen-action@v0.2.0
# - run: wasm-pack test --node
maturin-build:
name: "maturin build"
runs-on: ubuntu-latest
steps:
@@ -129,7 +163,7 @@ jobs:
override: true
- uses: actions/setup-python@v4
with:
python-version: "3.10"
python-version: "3.11"
- run: pip install maturin
- uses: actions/cache@v3
env:

50
.github/workflows/playground.yaml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: "[Playground] Release"
on:
workflow_dispatch:
push:
branches: [main]
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
RUSTUP_MAX_RETRIES: 10
jobs:
publish:
runs-on: ubuntu-latest
env:
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-11-01
override: true
target: wasm32-unknown-unknown
- uses: actions/setup-node@v3
with:
node-version: 18
cache: "npm"
cache-dependency-path: playground/package-lock.json
- uses: jetli/wasm-pack-action@v0.4.0
- uses: jetli/wasm-bindgen-action@v0.2.0
- name: "Run wasm-pack"
run: wasm-pack build --target web --out-dir playground/src/pkg
- name: "Install Node dependencies"
run: npm ci
working-directory: playground
- name: "Run TypeScript checks"
run: npm run check
working-directory: playground
- name: "Build JavaScript bundle"
run: npm run build
working-directory: playground
- name: "Deploy to Cloudflare Pages"
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
uses: cloudflare/wrangler-action@2.0.0
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
command: pages publish playground/dist --project-name=ruff --branch ${GITHUB_HEAD_REF} --commit-hash ${GITHUB_SHA}

View File

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

View File

@@ -48,8 +48,8 @@ prior to merging.
There are four phases to adding a new lint rule:
1. Define the rule in `src/checks.rs`.
2. Define the _logic_ for triggering the rule in `src/check_ast.rs` (for AST-based checks),
`src/check_tokens.rs` (for token-based checks), or `src/check_lines.rs` (for text-based checks).
2. Define the _logic_ for triggering the rule in `src/checkers/ast.rs` (for AST-based checks),
`src/checkers/tokens.rs` (for token-based checks), or `src/checkers/lines.rs` (for text-based checks).
3. Add a test fixture.
4. Update the generated files (documentation and generated code).
@@ -59,18 +59,20 @@ pattern implemented therein.
To trigger the rule, you'll likely want to augment the logic in `src/check_ast.rs`, which defines
the Python AST visitor, responsible for iterating over the abstract syntax tree and collecting
lint-rule violations as it goes. If you need to inspect the AST, you can run `cargo dev print-ast`
with a Python file. Grep for the `Check::new` invocations to understand how other, similar rules
are implemented.
lint-rule violations as it goes. If you need to inspect the AST, you can run
`cargo +nightly dev print-ast` with a Python file. Grep for the `Check::new` invocations to
understand how other, similar rules are implemented.
To add a test fixture, create a file under `resources/test/fixtures`, named to match the `CheckCode`
you defined earlier (e.g., `E402.py`). This file should contain a variety of violations and
non-violations designed to evaluate and demonstrate the behavior of your lint rule. Run Ruff locally
with (e.g.) `cargo run resources/test/fixtures/E402.py --no-cache`. Once you're satisfied with the
with (e.g.) `cargo run resources/test/fixtures/E402.py --no-cache --select E402`. Once you're satisfied with the
output, codify the behavior as a snapshot test by adding a new `testcase` macro to the `mod tests`
section of `src/linter.rs`, like so:
```rust
use test_case::test_case;
#[test_case(CheckCode::A001, Path::new("A001.py"); "A001")]
...
```
@@ -79,9 +81,7 @@ Then, run `cargo test`. Your test will fail, but you'll be prompted to follow-up
`cargo insta review`. Accept the generated snapshot, then commit the snapshot file alongside the
rest of your changes.
Finally, to update the documentation, run `cargo dev generate-rules-table` from the repo root. To
update the generated prefix map, run `cargo dev generate-check-code-prefix`. Both of these commands
should be run whenever a new check is added to the codebase.
Finally, regenerate the documentation and generated code with `cargo +nightly dev generate-all`.
### Example: Adding a new configuration option
@@ -105,7 +105,7 @@ You may also want to add the new configuration option to the `flake8-to-ruff` to
responsible for converting `flake8` configuration files to Ruff's TOML format. This logic
lives in `flake8_to_ruff/src/converter.rs`.
To update the documentation for supported configuration options, run `cargo dev generate-options`.
Finally, regenerate the documentation and generated code with `cargo +nightly dev generate-all`.
## Release process

326
Cargo.lock generated
View File

@@ -61,9 +61,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.66"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
[[package]]
name = "ascii"
@@ -86,7 +86,7 @@ version = "2.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa3d466004a8b4cb1bc34044240a2fd29d17607e2e3bd613eb44fd48e8100da3"
dependencies = [
"bstr 1.0.1",
"bstr 1.1.0",
"doc-comment",
"predicates",
"predicates-core",
@@ -160,9 +160,9 @@ dependencies = [
[[package]]
name = "bstr"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca0852af221f458706eb0725c03e4ed6c46af9ac98e6a689d5e634215d594dd"
checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b"
dependencies = [
"memchr",
"once_cell",
@@ -193,9 +193,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
version = "1.0.77"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
[[package]]
name = "cfg-if"
@@ -280,9 +280,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.0.29"
version = "4.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
dependencies = [
"bitflags",
"clap_derive",
@@ -295,11 +295,11 @@ dependencies = [
[[package]]
name = "clap_complete"
version = "4.0.6"
version = "4.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7b3c9eae0de7bf8e3f904a5e40612b21fb2e2e566456d177809a48b892d24da"
checksum = "10861370d2ba66b0f5989f83ebf35db6421713fd92351790e7fdd6c36774c56b"
dependencies = [
"clap 4.0.29",
"clap 4.0.32",
]
[[package]]
@@ -308,7 +308,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4160b4a4f72ef58bd766bad27c09e6ef1cc9d82a22f6a0f55d152985a4a48e31"
dependencies = [
"clap 4.0.29",
"clap 4.0.32",
"clap_complete",
"clap_complete_fig",
]
@@ -319,7 +319,7 @@ version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46b30e010e669cd021e5004f3be26cff6b7c08d2a8a0d65b48d43a8cc0efd6c3"
dependencies = [
"clap 4.0.29",
"clap 4.0.32",
"clap_complete",
]
@@ -422,6 +422,26 @@ dependencies = [
"winapi",
]
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen",
]
[[package]]
name = "console_log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494"
dependencies = [
"log",
"web-sys",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
@@ -524,9 +544,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "cxx"
version = "1.0.83"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf"
checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd"
dependencies = [
"cc",
"cxxbridge-flags",
@@ -536,9 +556,9 @@ dependencies = [
[[package]]
name = "cxx-build"
version = "1.0.83"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39"
checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0"
dependencies = [
"cc",
"codespan-reporting",
@@ -551,15 +571,15 @@ dependencies = [
[[package]]
name = "cxxbridge-flags"
version = "1.0.83"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12"
checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59"
[[package]]
name = "cxxbridge-macro"
version = "1.0.83"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6"
checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6"
dependencies = [
"proc-macro2",
"quote",
@@ -644,6 +664,12 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "dyn-clone"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60"
[[package]]
name = "either"
version = "1.8.0"
@@ -706,9 +732,9 @@ dependencies = [
[[package]]
name = "filetime"
version = "0.2.18"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3"
checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9"
dependencies = [
"cfg-if 1.0.0",
"libc",
@@ -724,10 +750,10 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.182-dev.0"
version = "0.0.202-dev.0"
dependencies = [
"anyhow",
"clap 4.0.29",
"clap 4.0.32",
"configparser",
"once_cell",
"regex",
@@ -735,6 +761,8 @@ dependencies = [
"rustc-hash",
"serde",
"serde_json",
"strum",
"strum_macros",
"toml",
]
@@ -944,9 +972,9 @@ dependencies = [
[[package]]
name = "insta"
version = "1.22.0"
version = "1.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "197f4e300af8b23664d4077bf5c40e0afa9ba66a567bb5a51d3def3c7b287d1c"
checksum = "e48b08a091dfe5b09a6a9688c468fdd5b4396e92ce09e2eb932f0884b02788a4"
dependencies = [
"console",
"lazy_static",
@@ -977,9 +1005,9 @@ dependencies = [
[[package]]
name = "is-terminal"
version = "0.4.1"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
dependencies = [
"hermit-abi 0.2.6",
"io-lifetimes",
@@ -998,9 +1026,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.4"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "joinery"
@@ -1107,9 +1135,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.138"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "libcst"
@@ -1137,9 +1165,9 @@ dependencies = [
[[package]]
name = "link-cplusplus"
version = "1.0.7"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
dependencies = [
"cc",
]
@@ -1152,9 +1180,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "lock_api"
@@ -1226,6 +1254,12 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "natord"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308d96db8debc727c3fd9744aac51751243420e46edf401010908da7f8d5e57c"
[[package]]
name = "new_debug_unreachable"
version = "1.0.4"
@@ -1326,11 +1360,11 @@ dependencies = [
[[package]]
name = "num_cpus"
version = "1.14.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi 0.1.19",
"hermit-abi 0.2.6",
"libc",
]
@@ -1377,9 +1411,9 @@ dependencies = [
[[package]]
name = "paste"
version = "1.0.9"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
[[package]]
name = "path-absolutize"
@@ -1617,9 +1651,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.47"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
dependencies = [
"unicode-ident",
]
@@ -1649,9 +1683,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.21"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
@@ -1745,11 +1779,10 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.6.0"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
dependencies = [
"crossbeam-deque",
"either",
"rayon-core",
]
@@ -1845,7 +1878,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.182"
version = "0.0.202"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -1854,12 +1887,15 @@ dependencies = [
"bincode",
"bitflags",
"cachedir",
"cfg-if 1.0.0",
"chrono",
"clap 4.0.29",
"clap 4.0.32",
"clap_complete_command",
"clearscreen",
"colored",
"common-path",
"console_error_panic_hook",
"console_log",
"criterion",
"dirs 4.0.0",
"fern",
@@ -1870,8 +1906,10 @@ dependencies = [
"ignore",
"insta",
"itertools",
"js-sys",
"libcst",
"log",
"natord",
"nohash-hasher",
"notify",
"num-bigint",
@@ -1886,8 +1924,13 @@ dependencies = [
"rustpython-ast",
"rustpython-common",
"rustpython-parser",
"schemars",
"semver",
"serde",
"serde-wasm-bindgen",
"serde_json",
"shellexpand",
"similar",
"strum",
"strum_macros",
"test-case",
@@ -1895,15 +1938,18 @@ dependencies = [
"titlecase",
"toml",
"update-informer",
"ureq",
"walkdir",
"wasm-bindgen",
"wasm-bindgen-test",
]
[[package]]
name = "ruff_dev"
version = "0.0.182"
version = "0.0.202"
dependencies = [
"anyhow",
"clap 4.0.29",
"clap 4.0.32",
"codegen",
"itertools",
"libcst",
@@ -1912,13 +1958,16 @@ dependencies = [
"rustpython-ast",
"rustpython-common",
"rustpython-parser",
"schemars",
"serde_json",
"strum",
"strum_macros",
"textwrap",
]
[[package]]
name = "ruff_macros"
version = "0.0.182"
version = "0.0.202"
dependencies = [
"proc-macro2",
"quote",
@@ -1934,9 +1983,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.36.4"
version = "0.36.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb93e85278e08bb5788653183213d3a60fc242b10cb9be96586f5a73dcb67c23"
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
dependencies = [
"bitflags",
"errno",
@@ -1961,7 +2010,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.1.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=8d879a53197f9c73062f6160410bdba796a71cbf#8d879a53197f9c73062f6160410bdba796a71cbf"
source = "git+https://github.com/RustPython/RustPython.git?rev=68d26955b3e24198a150315e7959719b03709dee#68d26955b3e24198a150315e7959719b03709dee"
dependencies = [
"num-bigint",
"rustpython-common",
@@ -1971,7 +2020,7 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.0.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=8d879a53197f9c73062f6160410bdba796a71cbf#8d879a53197f9c73062f6160410bdba796a71cbf"
source = "git+https://github.com/RustPython/RustPython.git?rev=68d26955b3e24198a150315e7959719b03709dee#68d26955b3e24198a150315e7959719b03709dee"
dependencies = [
"ascii",
"cfg-if 1.0.0",
@@ -1994,7 +2043,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=8d879a53197f9c73062f6160410bdba796a71cbf#8d879a53197f9c73062f6160410bdba796a71cbf"
source = "git+https://github.com/RustPython/RustPython.git?rev=68d26955b3e24198a150315e7959719b03709dee#68d26955b3e24198a150315e7959719b03709dee"
dependencies = [
"bincode",
"bitflags",
@@ -2011,7 +2060,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=8d879a53197f9c73062f6160410bdba796a71cbf#8d879a53197f9c73062f6160410bdba796a71cbf"
source = "git+https://github.com/RustPython/RustPython.git?rev=68d26955b3e24198a150315e7959719b03709dee#68d26955b3e24198a150315e7959719b03709dee"
dependencies = [
"ahash",
"anyhow",
@@ -2035,15 +2084,15 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.9"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
[[package]]
name = "ryu"
version = "1.0.11"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "same-file"
@@ -2054,6 +2103,36 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "schemars"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307"
dependencies = [
"dyn-clone",
"schemars_derive",
"serde",
"serde_json",
]
[[package]]
name = "schemars_derive"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scopeguard"
version = "1.1.0"
@@ -2062,9 +2141,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "scratch"
version = "1.0.2"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
[[package]]
name = "sct"
@@ -2078,24 +2157,46 @@ dependencies = [
[[package]]
name = "semver"
version = "1.0.14"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
[[package]]
name = "serde"
version = "1.0.148"
version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc"
checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.148"
name = "serde-wasm-bindgen"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c"
checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]]
name = "serde_derive"
version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_derive_internals"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
dependencies = [
"proc-macro2",
"quote",
@@ -2104,15 +2205,24 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.89"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "shellexpand"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd1c7ddea665294d484c39fd0c0d2b7e35bbfe10035c5fe1854741a57f6880e1"
dependencies = [
"dirs 4.0.0",
]
[[package]]
name = "similar"
version = "2.2.1"
@@ -2151,9 +2261,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "str_indices"
version = "0.4.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d9199fa80c817e074620be84374a520062ebac833f358d74b37060ce4a0f2c0"
checksum = "5f026164926842ec52deb1938fae44f83dfdb82d0a5b0270c5bd5935ab74d6dd"
[[package]]
name = "string_cache"
@@ -2198,9 +2308,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.105"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
@@ -2305,18 +2415,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.37"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.37"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
@@ -2390,9 +2500,9 @@ dependencies = [
[[package]]
name = "toml"
version = "0.5.9"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
dependencies = [
"serde",
]
@@ -2479,9 +2589,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-ident"
version = "1.0.5"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-linebreak"
@@ -2650,6 +2760,18 @@ dependencies = [
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
@@ -2679,6 +2801,30 @@ version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]]
name = "wasm-bindgen-test"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d2fff962180c3fadf677438054b1db62bee4aa32af26a45388af07d1287e1d"
dependencies = [
"console_error_panic_hook",
"js-sys",
"scoped-tls",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test-macro",
]
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4683da3dfc016f704c9f82cf401520c4f1cb3ee440f7f52b3d6ac29506a49ca7"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "web-sys"
version = "0.3.60"
@@ -2701,9 +2847,9 @@ dependencies = [
[[package]]
name = "webpki-roots"
version = "0.22.5"
version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be"
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
dependencies = [
"webpki",
]

View File

@@ -6,12 +6,19 @@ members = [
[package]
name = "ruff"
version = "0.0.182"
version = "0.0.202"
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
edition = "2021"
rust-version = "1.65.0"
documentation = "https://github.com/charliermarsh/ruff"
homepage = "https://github.com/charliermarsh/ruff"
repository = "https://github.com/charliermarsh/ruff"
readme = "README.md"
license = "MIT"
[lib]
name = "ruff"
crate-type = ["cdylib", "rlib"]
[dependencies]
annotate-snippets = { version = "0.9.1", features = ["color"] }
@@ -20,9 +27,10 @@ atty = { version = "0.2.14" }
bincode = { version = "1.3.3" }
bitflags = { version = "1.3.2" }
cachedir = { version = "0.3.0" }
cfg-if = { version = "1.0.0" }
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
clap = { version = "4.0.1", features = ["derive"] }
clap_complete_command = "0.4.0"
clap_complete_command = { version = "0.4.0" }
colored = { version = "2.0.0" }
common-path = { version = "1.0.0" }
dirs = { version = "4.0.0" }
@@ -34,43 +42,57 @@ ignore = { version = "0.4.18" }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
log = { version = "0.4.17" }
natord = { version = "1.0.9" }
nohash-hasher = { version = "0.2.0" }
notify = { version = "5.0.0" }
num-bigint = { version = "0.4.3" }
once_cell = { version = "1.16.0" }
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
quick-junit = { version = "0.3.2" }
rayon = { version = "1.5.3" }
regex = { version = "1.6.0" }
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
ruff_macros = { version = "0.0.182", path = "ruff_macros" }
ruff_macros = { version = "0.0.202", path = "ruff_macros" }
rustc-hash = { version = "1.1.0" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "8d879a53197f9c73062f6160410bdba796a71cbf" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "8d879a53197f9c73062f6160410bdba796a71cbf" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "8d879a53197f9c73062f6160410bdba796a71cbf" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "68d26955b3e24198a150315e7959719b03709dee" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "68d26955b3e24198a150315e7959719b03709dee" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "68d26955b3e24198a150315e7959719b03709dee" }
schemars = { version = "0.8.11" }
semver = { version = "1.0.16" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
shellexpand = { version = "3.0.0" }
similar = { version = "2.2.1" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }
textwrap = { version = "0.16.0" }
titlecase = { version = "2.2.1" }
toml = { version = "0.5.9" }
update-informer = { version = "0.5.0", default-features = false, features = ["pypi"], optional = true }
walkdir = { version = "2.3.2" }
[target.'cfg(not(target_family = "wasm"))'.dependencies]
clearscreen = { version = "1.0.10" } # uses which
clearscreen = { version = "1.0.10" }
rayon = { version = "1.5.3" }
update-informer = { version = "0.5.0", default-features = false, features = ["pypi"], optional = true }
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
# For (future) wasm-pack support
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
getrandom = { version = "0.2.7", features = ["js"] }
console_error_panic_hook = { version = "0.1.7" }
console_log = { version = "0.2.0" }
serde-wasm-bindgen = { version = "0.4" }
js-sys = { version = "0.3.60" }
wasm-bindgen = { version = "0.2.83" }
[dev-dependencies]
assert_cmd = { version = "2.0.4" }
criterion = { version = "0.4.0" }
insta = { version = "1.19.1", features = ["yaml"] }
test-case = { version = "2.2.2" }
ureq = { version = "2.5.0", features = [] }
wasm-bindgen-test = { version = "0.3.33" }
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
assert_cmd = { version = "2.0.4" }
criterion = { version = "0.4.0" }
[features]
default = ["update-informer"]
@@ -88,6 +110,11 @@ opt-level = 3
[profile.dev.package.similar]
opt-level = 3
# Reduce complexity of a parser function that would trigger a locals limit in a wasm tool.
# https://github.com/bytecodealliance/wasm-tools/blob/b5c3d98e40590512a3b12470ef358d5c7b983b15/crates/wasmparser/src/limits.rs#L29
[profile.dev.package.rustpython-parser]
opt-level = 1
[[bench]]
name = "source_code_locator"
harness = false

25
LICENSE
View File

@@ -388,6 +388,31 @@ are:
SOFTWARE.
"""
- flake8-implicit-str-concat, licensed as follows:
"""
The MIT License (MIT)
Copyright (c) 2019 Dylan Turner
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
- flake8-import-conventions, licensed as follows:
"""
MIT License

860
README.md

File diff suppressed because it is too large Load Diff

View File

@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8_to_ruff"
version = "0.0.182"
version = "0.0.202"
dependencies = [
"anyhow",
"clap",
@@ -1975,7 +1975,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.182"
version = "0.0.202"
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=8d879a53197f9c73062f6160410bdba796a71cbf#8d879a53197f9c73062f6160410bdba796a71cbf"
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
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=8d879a53197f9c73062f6160410bdba796a71cbf#8d879a53197f9c73062f6160410bdba796a71cbf"
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
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=8d879a53197f9c73062f6160410bdba796a71cbf#8d879a53197f9c73062f6160410bdba796a71cbf"
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
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=8d879a53197f9c73062f6160410bdba796a71cbf#8d879a53197f9c73062f6160410bdba796a71cbf"
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
dependencies = [
"ahash",
"anyhow",

View File

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

View File

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

View File

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

View File

@@ -12,6 +12,7 @@ classifiers = [
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Quality Assurance",

View File

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

View File

@@ -4,19 +4,28 @@ use anyhow::Result;
use ruff::checks_gen::CheckCodePrefix;
use ruff::flake8_quotes::settings::Quote;
use ruff::flake8_tidy_imports::settings::Strictness;
use ruff::pydocstyle::settings::Convention;
use ruff::settings::options::Options;
use ruff::settings::pyproject::Pyproject;
use ruff::{
flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, mccabe, pep8_naming,
flake8_annotations, flake8_bugbear, flake8_errmsg, flake8_quotes, flake8_tidy_imports, mccabe,
pep8_naming, pydocstyle,
};
use crate::black::Black;
use crate::plugin::Plugin;
use crate::{parser, plugin};
pub fn convert(
flake8: &HashMap<String, Option<String>>,
config: &HashMap<String, HashMap<String, Option<String>>>,
black: Option<&Black>,
plugins: Option<Vec<Plugin>>,
) -> Result<Pyproject> {
// Extract the Flake8 section.
let flake8 = config
.get("flake8")
.expect("Unable to find flake8 section in INI file");
// Extract all referenced check code prefixes, to power plugin inference.
let mut referenced_codes: BTreeSet<CheckCodePrefix> = BTreeSet::default();
for (key, value) in flake8 {
@@ -53,10 +62,15 @@ pub fn convert(
plugin::resolve_select(
flake8,
&plugins.unwrap_or_else(|| {
plugin::infer_plugins_from_options(flake8)
.into_iter()
.chain(plugin::infer_plugins_from_codes(&referenced_codes))
.collect()
let from_options = plugin::infer_plugins_from_options(flake8);
if !from_options.is_empty() {
eprintln!("Inferred plugins from settings: {from_options:#?}");
}
let from_codes = plugin::infer_plugins_from_codes(&referenced_codes);
if !from_codes.is_empty() {
eprintln!("Inferred plugins from referenced check codes: {from_codes:#?}");
}
from_options.into_iter().chain(from_codes).collect()
}),
)
});
@@ -73,10 +87,12 @@ pub fn convert(
let mut options = Options::default();
let mut flake8_annotations = flake8_annotations::settings::Options::default();
let mut flake8_bugbear = flake8_bugbear::settings::Options::default();
let mut flake8_errmsg = flake8_errmsg::settings::Options::default();
let mut flake8_quotes = flake8_quotes::settings::Options::default();
let mut flake8_tidy_imports = flake8_tidy_imports::settings::Options::default();
let mut mccabe = mccabe::settings::Options::default();
let mut pep8_naming = pep8_naming::settings::Options::default();
let mut pydocstyle = pydocstyle::settings::Options::default();
for (key, value) in flake8 {
if let Some(value) = value {
match key.as_str() {
@@ -148,17 +164,17 @@ pub fn convert(
// flake8-quotes
"quotes" | "inline-quotes" | "inline_quotes" => match value.trim() {
"'" | "single" => flake8_quotes.inline_quotes = Some(Quote::Single),
"\"" | "double" => flake8_quotes.inline_quotes = Some(Quote::Single),
"\"" | "double" => flake8_quotes.inline_quotes = Some(Quote::Double),
_ => eprintln!("Unexpected '{key}' value: {value}"),
},
"multiline-quotes" | "multiline_quotes" => match value.trim() {
"'" | "single" => flake8_quotes.multiline_quotes = Some(Quote::Single),
"\"" | "double" => flake8_quotes.multiline_quotes = Some(Quote::Single),
"\"" | "double" => flake8_quotes.multiline_quotes = Some(Quote::Double),
_ => eprintln!("Unexpected '{key}' value: {value}"),
},
"docstring-quotes" | "docstring_quotes" => match value.trim() {
"'" | "single" => flake8_quotes.docstring_quotes = Some(Quote::Single),
"\"" | "double" => flake8_quotes.docstring_quotes = Some(Quote::Single),
"\"" | "double" => flake8_quotes.docstring_quotes = Some(Quote::Double),
_ => eprintln!("Unexpected '{key}' value: {value}"),
},
"avoid-escape" | "avoid_escape" => match parser::parse_bool(value.as_ref()) {
@@ -186,14 +202,26 @@ pub fn convert(
_ => eprintln!("Unexpected '{key}' value: {value}"),
},
// flake8-docstrings
"docstring-convention" => {
// No-op (handled above).
}
"docstring-convention" => match value.trim() {
"google" => pydocstyle.convention = Some(Convention::Google),
"numpy" => pydocstyle.convention = Some(Convention::Numpy),
"pep257" | "all" => pydocstyle.convention = None,
_ => eprintln!("Unexpected '{key}' value: {value}"),
},
// 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}"),
},
// flake8-errmsg
"errmsg-max-string-length" | "errmsg_max_string_length" => {
match value.clone().parse::<usize>() {
Ok(max_string_length) => {
flake8_errmsg.max_string_length = Some(max_string_length);
}
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
}
}
// Unknown
_ => eprintln!("Skipping unsupported property: {key}"),
}
@@ -209,6 +237,9 @@ pub fn convert(
if flake8_bugbear != flake8_bugbear::settings::Options::default() {
options.flake8_bugbear = Some(flake8_bugbear);
}
if flake8_errmsg != flake8_errmsg::settings::Options::default() {
options.flake8_errmsg = Some(flake8_errmsg);
}
if flake8_quotes != flake8_quotes::settings::Options::default() {
options.flake8_quotes = Some(flake8_quotes);
}
@@ -221,6 +252,22 @@ pub fn convert(
if pep8_naming != pep8_naming::settings::Options::default() {
options.pep8_naming = Some(pep8_naming);
}
if pydocstyle != pydocstyle::settings::Options::default() {
options.pydocstyle = Some(pydocstyle);
}
// Extract any settings from the existing `pyproject.toml`.
if let Some(black) = black {
if let Some(line_length) = &black.line_length {
options.line_length = Some(*line_length);
}
if let Some(target_version) = &black.target_version {
if let Some(target_version) = target_version.iter().min() {
options.target_version = Some(*target_version);
}
}
}
// Create the pyproject.toml.
Ok(Pyproject::new(options))
@@ -232,18 +279,24 @@ mod tests {
use anyhow::Result;
use ruff::checks_gen::CheckCodePrefix;
use ruff::flake8_quotes;
use ruff::pydocstyle::settings::Convention;
use ruff::settings::options::Options;
use ruff::settings::pyproject::Pyproject;
use ruff::{flake8_quotes, pydocstyle};
use crate::converter::convert;
use crate::plugin::Plugin;
#[test]
fn it_converts_empty() -> Result<()> {
let actual = convert(&HashMap::from([]), None)?;
let actual = convert(
&HashMap::from([("flake8".to_string(), HashMap::default())]),
None,
None,
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
cache_dir: None,
dummy_variable_rgx: None,
exclude: None,
extend: None,
@@ -252,12 +305,15 @@ mod tests {
extend_select: None,
external: None,
fix: None,
fix_only: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
@@ -268,14 +324,18 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
update_check: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
pydocstyle: None,
pyupgrade: None,
});
assert_eq!(actual, expected);
@@ -286,11 +346,16 @@ mod tests {
#[test]
fn it_converts_dashes() -> Result<()> {
let actual = convert(
&HashMap::from([("max-line-length".to_string(), Some("100".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("max-line-length".to_string(), Some("100".to_string()))]),
)]),
None,
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
cache_dir: None,
dummy_variable_rgx: None,
exclude: None,
extend: None,
@@ -299,12 +364,15 @@ mod tests {
extend_select: None,
external: None,
fix: None,
fix_only: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: Some(100),
per_file_ignores: None,
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
@@ -315,14 +383,18 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
update_check: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
pydocstyle: None,
pyupgrade: None,
});
assert_eq!(actual, expected);
@@ -333,11 +405,16 @@ mod tests {
#[test]
fn it_converts_underscores() -> Result<()> {
let actual = convert(
&HashMap::from([("max_line_length".to_string(), Some("100".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("max_line_length".to_string(), Some("100".to_string()))]),
)]),
None,
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
cache_dir: None,
dummy_variable_rgx: None,
exclude: None,
extend: None,
@@ -346,12 +423,15 @@ mod tests {
extend_select: None,
external: None,
fix: None,
fix_only: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: Some(100),
per_file_ignores: None,
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
@@ -362,14 +442,18 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
update_check: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
pydocstyle: None,
pyupgrade: None,
});
assert_eq!(actual, expected);
@@ -380,11 +464,16 @@ mod tests {
#[test]
fn it_ignores_parse_errors() -> Result<()> {
let actual = convert(
&HashMap::from([("max_line_length".to_string(), Some("abc".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("max_line_length".to_string(), Some("abc".to_string()))]),
)]),
None,
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
cache_dir: None,
dummy_variable_rgx: None,
exclude: None,
extend: None,
@@ -393,12 +482,15 @@ mod tests {
extend_select: None,
external: None,
fix: None,
fix_only: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
@@ -409,14 +501,18 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
update_check: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
pydocstyle: None,
pyupgrade: None,
});
assert_eq!(actual, expected);
@@ -427,11 +523,16 @@ mod tests {
#[test]
fn it_converts_plugin_options() -> Result<()> {
let actual = convert(
&HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]),
)]),
None,
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
cache_dir: None,
dummy_variable_rgx: None,
exclude: None,
extend: None,
@@ -440,12 +541,15 @@ mod tests {
extend_select: None,
external: None,
fix: None,
fix_only: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
@@ -456,8 +560,10 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
update_check: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: Some(flake8_quotes::settings::Options {
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
multiline_quotes: None,
@@ -466,9 +572,11 @@ mod tests {
}),
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
pydocstyle: None,
pyupgrade: None,
});
assert_eq!(actual, expected);
@@ -480,13 +588,18 @@ mod tests {
fn it_converts_docstring_conventions() -> Result<()> {
let actual = convert(
&HashMap::from([(
"docstring-convention".to_string(),
Some("numpy".to_string()),
"flake8".to_string(),
HashMap::from([(
"docstring-convention".to_string(),
Some("numpy".to_string()),
)]),
)]),
None,
Some(vec![Plugin::Flake8Docstrings]),
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
cache_dir: None,
dummy_variable_rgx: None,
exclude: None,
extend: None,
@@ -495,12 +608,15 @@ mod tests {
extend_select: None,
external: None,
fix: None,
fix_only: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::D100,
@@ -547,14 +663,20 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
update_check: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
pydocstyle: Some(pydocstyle::settings::Options {
convention: Some(Convention::Numpy),
}),
pyupgrade: None,
});
assert_eq!(actual, expected);
@@ -565,11 +687,16 @@ mod tests {
#[test]
fn it_infers_plugins_if_omitted() -> Result<()> {
let actual = convert(
&HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]),
)]),
None,
None,
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
cache_dir: None,
dummy_variable_rgx: None,
exclude: None,
extend: None,
@@ -578,12 +705,15 @@ mod tests {
extend_select: None,
external: None,
fix: None,
fix_only: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
@@ -595,8 +725,10 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
update_check: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: Some(flake8_quotes::settings::Options {
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
multiline_quotes: None,
@@ -605,9 +737,11 @@ mod tests {
}),
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
pydocstyle: None,
pyupgrade: None,
});
assert_eq!(actual, expected);

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ use std::str::FromStr;
use anyhow::{bail, Result};
use once_cell::sync::Lazy;
use regex::Regex;
use ruff::checks::PREFIX_REDIRECTS;
use ruff::checks_gen::CheckCodePrefix;
use ruff::settings::types::PatternPrefixPair;
use rustc_hash::FxHashMap;
@@ -18,7 +19,9 @@ pub fn parse_prefix_codes(value: &str) -> Vec<CheckCodePrefix> {
if code.is_empty() {
continue;
}
if let Ok(code) = CheckCodePrefix::from_str(code) {
if let Some(code) = PREFIX_REDIRECTS.get(code) {
codes.push(code.clone());
} else if let Ok(code) = CheckCodePrefix::from_str(code) {
codes.push(code);
} else {
eprintln!("Unsupported prefix code: {code}");
@@ -83,16 +86,22 @@ impl State {
fn parse(&self) -> Vec<PatternPrefixPair> {
let mut codes: Vec<PatternPrefixPair> = vec![];
for code in &self.codes {
match CheckCodePrefix::from_str(code) {
Ok(code) => {
for filename in &self.filenames {
codes.push(PatternPrefixPair {
pattern: filename.clone(),
prefix: code.clone(),
});
}
if let Some(code) = PREFIX_REDIRECTS.get(code.as_str()) {
for filename in &self.filenames {
codes.push(PatternPrefixPair {
pattern: filename.clone(),
prefix: code.clone(),
});
}
Err(_) => eprintln!("Skipping unrecognized prefix: {code}"),
} else if let Ok(code) = CheckCodePrefix::from_str(code) {
for filename in &self.filenames {
codes.push(PatternPrefixPair {
pattern: filename.clone(),
prefix: code.clone(),
});
}
} else {
eprintln!("Unsupported prefix code: {code}");
}
}
codes

View File

@@ -1,10 +1,11 @@
use std::collections::{BTreeSet, HashMap};
use std::fmt;
use std::str::FromStr;
use anyhow::anyhow;
use ruff::checks_gen::CheckCodePrefix;
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Plugin {
Flake8Annotations,
Flake8Bandit,
@@ -12,9 +13,12 @@ pub enum Plugin {
Flake8Bugbear,
Flake8Builtins,
Flake8Comprehensions,
Flake8Datetimez,
Flake8Debugger,
Flake8Docstrings,
Flake8Eradicate,
Flake8ErrMsg,
Flake8ImplicitStrConcat,
Flake8Print,
Flake8Quotes,
Flake8Return,
@@ -22,6 +26,7 @@ pub enum Plugin {
Flake8TidyImports,
McCabe,
PEP8Naming,
PandasVet,
Pyupgrade,
}
@@ -36,15 +41,19 @@ impl FromStr for Plugin {
"flake8-bugbear" => Ok(Plugin::Flake8Bugbear),
"flake8-builtins" => Ok(Plugin::Flake8Builtins),
"flake8-comprehensions" => Ok(Plugin::Flake8Comprehensions),
"flake8-datetimez" => Ok(Plugin::Flake8Datetimez),
"flake8-debugger" => Ok(Plugin::Flake8Debugger),
"flake8-docstrings" => Ok(Plugin::Flake8Docstrings),
"flake8-eradicate" => Ok(Plugin::Flake8BlindExcept),
"flake8-eradicate" => Ok(Plugin::Flake8Eradicate),
"flake8-errmsg" => Ok(Plugin::Flake8ErrMsg),
"flake8-implicit-str-concat" => Ok(Plugin::Flake8ImplicitStrConcat),
"flake8-print" => Ok(Plugin::Flake8Print),
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
"flake8-return" => Ok(Plugin::Flake8Return),
"flake8-simplify" => Ok(Plugin::Flake8Simplify),
"flake8-tidy-imports" => Ok(Plugin::Flake8TidyImports),
"mccabe" => Ok(Plugin::McCabe),
"pandas-vet" => Ok(Plugin::PandasVet),
"pep8-naming" => Ok(Plugin::PEP8Naming),
"pyupgrade" => Ok(Plugin::Pyupgrade),
_ => Err(anyhow!("Unknown plugin: {string}")),
@@ -52,26 +61,64 @@ impl FromStr for Plugin {
}
}
impl fmt::Debug for Plugin {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Plugin::Flake8Annotations => "flake8-annotations",
Plugin::Flake8Bandit => "flake8-bandit",
Plugin::Flake8BlindExcept => "flake8-blind-except",
Plugin::Flake8Bugbear => "flake8-bugbear",
Plugin::Flake8Builtins => "flake8-builtins",
Plugin::Flake8Comprehensions => "flake8-comprehensions",
Plugin::Flake8Datetimez => "flake8-datetimez",
Plugin::Flake8Debugger => "flake8-debugger",
Plugin::Flake8Docstrings => "flake8-docstrings",
Plugin::Flake8Eradicate => "flake8-eradicate",
Plugin::Flake8ErrMsg => "flake8-errmsg",
Plugin::Flake8ImplicitStrConcat => "flake8-implicit-str-concat",
Plugin::Flake8Print => "flake8-print",
Plugin::Flake8Quotes => "flake8-quotes",
Plugin::Flake8Return => "flake8-return",
Plugin::Flake8Simplify => "flake8-simplify",
Plugin::Flake8TidyImports => "flake8-tidy-imports",
Plugin::McCabe => "mccabe",
Plugin::PEP8Naming => "pep8-naming",
Plugin::PandasVet => "pandas-vet",
Plugin::Pyupgrade => "pyupgrade",
}
)
}
}
impl Plugin {
pub fn default(&self) -> CheckCodePrefix {
match self {
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
Plugin::Flake8Bandit => CheckCodePrefix::S,
// TODO(charlie): Handle rename of `B` to `BLE`.
Plugin::Flake8BlindExcept => CheckCodePrefix::BLE,
Plugin::Flake8Bugbear => CheckCodePrefix::B,
Plugin::Flake8Builtins => CheckCodePrefix::A,
Plugin::Flake8Comprehensions => CheckCodePrefix::C4,
Plugin::Flake8Datetimez => CheckCodePrefix::DTZ,
Plugin::Flake8Debugger => CheckCodePrefix::T1,
Plugin::Flake8Docstrings => CheckCodePrefix::D,
// TODO(charlie): Handle rename of `E` to `ERA`.
Plugin::Flake8Eradicate => CheckCodePrefix::ERA,
Plugin::Flake8ErrMsg => CheckCodePrefix::EM,
Plugin::Flake8ImplicitStrConcat => CheckCodePrefix::ISC,
Plugin::Flake8Print => CheckCodePrefix::T2,
Plugin::Flake8Quotes => CheckCodePrefix::Q,
Plugin::Flake8Return => CheckCodePrefix::RET,
Plugin::Flake8Simplify => CheckCodePrefix::SIM,
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
Plugin::Flake8TidyImports => CheckCodePrefix::TID25,
Plugin::McCabe => CheckCodePrefix::C9,
Plugin::PandasVet => CheckCodePrefix::PD,
Plugin::PEP8Naming => CheckCodePrefix::N,
Plugin::Pyupgrade => CheckCodePrefix::U,
Plugin::Pyupgrade => CheckCodePrefix::UP,
}
}
@@ -83,6 +130,7 @@ impl Plugin {
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
Plugin::Flake8Builtins => vec![CheckCodePrefix::A],
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C4],
Plugin::Flake8Datetimez => vec![CheckCodePrefix::DTZ],
Plugin::Flake8Debugger => vec![CheckCodePrefix::T1],
Plugin::Flake8Docstrings => {
// Use the user-provided docstring.
@@ -101,14 +149,17 @@ impl Plugin {
DocstringConvention::PEP8.select()
}
Plugin::Flake8Eradicate => vec![CheckCodePrefix::ERA],
Plugin::Flake8ErrMsg => vec![CheckCodePrefix::EM],
Plugin::Flake8ImplicitStrConcat => vec![CheckCodePrefix::ISC],
Plugin::Flake8Print => vec![CheckCodePrefix::T2],
Plugin::Flake8Quotes => vec![CheckCodePrefix::Q],
Plugin::Flake8Return => vec![CheckCodePrefix::RET],
Plugin::Flake8Simplify => vec![CheckCodePrefix::SIM],
Plugin::Flake8TidyImports => vec![CheckCodePrefix::I25],
Plugin::Flake8TidyImports => vec![CheckCodePrefix::TID],
Plugin::McCabe => vec![CheckCodePrefix::C9],
Plugin::PandasVet => vec![CheckCodePrefix::PD],
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
Plugin::Pyupgrade => vec![CheckCodePrefix::U],
Plugin::Pyupgrade => vec![CheckCodePrefix::UP],
}
}
}
@@ -377,6 +428,9 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
"staticmethod-decorators" | "staticmethod_decorators" => {
plugins.insert(Plugin::PEP8Naming);
}
"max-string-length" | "max_string_length" => {
plugins.insert(Plugin::Flake8ErrMsg);
}
_ => {}
}
}
@@ -395,16 +449,19 @@ pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin
Plugin::Flake8Bugbear,
Plugin::Flake8Builtins,
Plugin::Flake8Comprehensions,
Plugin::Flake8Datetimez,
Plugin::Flake8Debugger,
Plugin::Flake8Docstrings,
Plugin::Flake8Eradicate,
Plugin::Flake8ErrMsg,
Plugin::Flake8ImplicitStrConcat,
Plugin::Flake8Print,
Plugin::Flake8Quotes,
Plugin::Flake8Return,
Plugin::Flake8Simplify,
Plugin::Flake8TidyImports,
Plugin::PandasVet,
Plugin::PEP8Naming,
Plugin::Pyupgrade,
]
.into_iter()
.filter(|plugin| {

25
playground/.eslintrc Normal file
View File

@@ -0,0 +1,25 @@
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "prettier"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react/jsx-runtime",
"plugin:react-hooks/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
"plugin:prettier/recommended"
],
"rules": {
// Disable some recommended rules that we don't want to enforce.
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off"
},
"settings": {
"react": {
"version": "detect"
}
}
}

130
playground/.gitignore vendored Normal file
View File

@@ -0,0 +1,130 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

View File

@@ -0,0 +1,3 @@
{
"trailingComma": "all"
}

14
playground/README.md Normal file
View File

@@ -0,0 +1,14 @@
# playground
In-browser playground for Ruff. Available [https://ruff.pages.dev/](https://ruff.pages.dev/).
## Getting started
- To build the WASM module, run `wasm-pack build --target web --out-dir playground/src/pkg` from the
root directory.
- Install TypeScript dependencies with: `npm install`.
- Start the development server with: `npm run dev`.
## Implementation
Design based on [Tailwind Play](https://play.tailwindcss.com/). Themed with [`ayu`](https://github.com/dempfi/ayu).

30
playground/index.html Normal file
View File

@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="An in-browser playground for Ruff, an extremely fast Python linter written in Rust."
/>
<meta name="keywords" content="ruff, python, rust, webassembly, wasm" />
<title>Ruff Playground</title>
<link
rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🛠️</text></svg>"
/>
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
</head>
<body>
<div id="root"></div>
<div style="display: flex; position: fixed; right: 16px; bottom: 16px">
<a href="https://GitHub.com/charliermarsh/ruff"
><img
src="https://img.shields.io/github/stars/charliermarsh/ruff.svg?style=social&label=GitHub&maxAge=2592000&?logoWidth=100"
alt="GitHub stars"
style="width: 120px"
/></a>
</div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

6785
playground/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

42
playground/package.json Normal file
View File

@@ -0,0 +1,42 @@
{
"name": "playground",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"build": "tsc && vite build",
"check": "npm run lint && npm run tsc",
"dev": "vite",
"fmt": "prettier --cache -w .",
"lint": "eslint --cache --ext .ts,.tsx src",
"preview": "vite preview",
"tsc": "tsc"
},
"dependencies": {
"@monaco-editor/react": "^4.4.6",
"classnames": "^2.3.2",
"lz-string": "^1.4.4",
"monaco-editor": "^0.34.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"@typescript-eslint/eslint-plugin": "^5.47.1",
"@typescript-eslint/parser": "^5.47.1",
"@vitejs/plugin-react-swc": "^3.0.0",
"autoprefixer": "^10.4.13",
"eslint": "^8.30.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.31.11",
"eslint-plugin-react-hooks": "^4.6.0",
"postcss": "^8.4.20",
"prettier": "^2.8.1",
"tailwindcss": "^3.2.4",
"typescript": "^4.9.3",
"vite": "^4.0.0"
}
}

View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

@@ -0,0 +1,151 @@
import { useCallback, useEffect, useState } from "react";
import { DEFAULT_PYTHON_SOURCE } from "../constants";
import init, { check, Check, currentVersion, defaultSettings } from "../pkg";
import { ErrorMessage } from "./ErrorMessage";
import Header from "./Header";
import { useTheme } from "./theme";
import { persist, restore, stringify } from "./settings";
import SettingsEditor from "./SettingsEditor";
import SourceEditor from "./SourceEditor";
import MonacoThemes from "./MonacoThemes";
type Tab = "Source" | "Settings";
export default function Editor() {
const [initialized, setInitialized] = useState<boolean>(false);
const [version, setVersion] = useState<string | null>(null);
const [tab, setTab] = useState<Tab>("Source");
const [edit, setEdit] = useState<number>(0);
const [settingsSource, setSettingsSource] = useState<string | null>(null);
const [pythonSource, setPythonSource] = useState<string | null>(null);
const [checks, setChecks] = useState<Check[]>([]);
const [error, setError] = useState<string | null>(null);
const [theme, setTheme] = useTheme();
useEffect(() => {
init().then(() => setInitialized(true));
}, []);
useEffect(() => {
if (!initialized || settingsSource == null || pythonSource == null) {
return;
}
let config: any;
let checks: Check[];
try {
config = JSON.parse(settingsSource);
} catch (e) {
setChecks([]);
setError((e as Error).message);
return;
}
try {
checks = check(pythonSource, config);
} catch (e) {
setError(e as string);
return;
}
setError(null);
setChecks(checks);
}, [initialized, settingsSource, pythonSource]);
useEffect(() => {
if (!initialized) {
return;
}
if (settingsSource == null || pythonSource == null) {
const payload = restore();
if (payload) {
const [settingsSource, pythonSource] = payload;
setSettingsSource(settingsSource);
setPythonSource(pythonSource);
} else {
setSettingsSource(stringify(defaultSettings()));
setPythonSource(DEFAULT_PYTHON_SOURCE);
}
}
}, [initialized, settingsSource, pythonSource]);
useEffect(() => {
if (!initialized) {
return;
}
setVersion(currentVersion());
}, [initialized]);
const handleShare = useCallback(() => {
if (!initialized || settingsSource == null || pythonSource == null) {
return;
}
persist(settingsSource, pythonSource);
}, [initialized, settingsSource, pythonSource]);
const handlePythonSourceChange = useCallback((pythonSource: string) => {
setEdit((edit) => edit + 1);
setPythonSource(pythonSource);
}, []);
const handleSettingsSourceChange = useCallback((settingsSource: string) => {
setEdit((edit) => edit + 1);
setSettingsSource(settingsSource);
}, []);
return (
<main
className={
"h-full w-full flex flex-auto bg-ayu-background dark:bg-ayu-background-dark"
}
>
<Header
edit={edit}
tab={tab}
theme={theme}
version={version}
onChangeTab={setTab}
onChangeTheme={setTheme}
onShare={initialized ? handleShare : undefined}
/>
<MonacoThemes />
<div className={"mt-12 relative flex-auto"}>
{initialized && settingsSource != null && pythonSource != null ? (
<>
<SourceEditor
visible={tab === "Source"}
source={pythonSource}
theme={theme}
checks={checks}
onChange={handlePythonSourceChange}
/>
<SettingsEditor
visible={tab === "Settings"}
source={settingsSource}
theme={theme}
onChange={handleSettingsSourceChange}
/>
</>
) : null}
</div>
{error && tab === "Source" ? (
<div
style={{
position: "fixed",
left: "10%",
right: "10%",
bottom: "10%",
}}
>
<ErrorMessage>{error}</ErrorMessage>
</div>
) : null}
</main>
);
}

View File

@@ -0,0 +1,26 @@
function truncate(str: string, length: number) {
if (str.length > length) {
return str.slice(0, length) + "...";
} else {
return str;
}
}
export function ErrorMessage({ children }: { children: string }) {
return (
<div
className="bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-4"
role="alert"
>
<p className="font-bold">Error</p>
<p className="block sm:inline">
{truncate(
children.startsWith("Error: ")
? children.slice("Error: ".length)
: children,
120,
)}
</p>
</div>
);
}

View File

@@ -0,0 +1,99 @@
import classNames from "classnames";
import ThemeButton from "./ThemeButton";
import ShareButton from "./ShareButton";
import { Theme } from "./theme";
import VersionTag from "./VersionTag";
export type Tab = "Source" | "Settings";
export default function Header({
edit,
tab,
theme,
version,
onChangeTab,
onChangeTheme,
onShare,
}: {
edit: number;
tab: Tab;
theme: Theme;
version: string | null;
onChangeTab: (tab: Tab) => void;
onChangeTheme: (theme: Theme) => void;
onShare?: () => void;
}) {
return (
<div
className={classNames(
"w-full",
"flex",
"items-center",
"justify-between",
"flex-none",
"pl-5",
"sm:pl-6",
"pr-4",
"lg:pr-6",
"absolute",
"z-10",
"top-0",
"left-0",
"-mb-px",
"antialiased",
"border-b",
"border-gray-200",
"dark:border-gray-800",
)}
>
<div className="flex space-x-5">
<button
type="button"
className={classNames(
"relative flex py-3 text-sm leading-6 font-semibold focus:outline-none",
tab === "Source"
? "text-ayu-accent"
: "text-gray-700 hover:text-gray-900 focus:text-gray-900 dark:text-gray-300 dark:hover:text-white",
)}
onClick={() => onChangeTab("Source")}
>
<span
className={classNames(
"absolute bottom-0 inset-x-0 bg-ayu-accent h-0.5 rounded-full transition-opacity duration-150",
tab === "Source" ? "opacity-100" : "opacity-0",
)}
/>
Source
</button>
<button
type="button"
className={classNames(
"relative flex py-3 text-sm leading-6 font-semibold focus:outline-none",
tab === "Settings"
? "text-ayu-accent"
: "text-gray-700 hover:text-gray-900 focus:text-gray-900 dark:text-gray-300 dark:hover:text-white",
)}
onClick={() => onChangeTab("Settings")}
>
<span
className={classNames(
"absolute bottom-0 inset-x-0 bg-ayu-accent h-0.5 rounded-full transition-opacity duration-150",
tab === "Settings" ? "opacity-100" : "opacity-0",
)}
/>
Settings
</button>
{version ? (
<div className={"flex items-center"}>
<VersionTag>v{version}</VersionTag>
</div>
) : null}
</div>
<div className={"hidden sm:flex items-center min-w-0"}>
<ShareButton key={edit} onShare={onShare} />
<div className="hidden sm:block mx-6 lg:mx-4 w-px h-6 bg-gray-200 dark:bg-gray-700" />
<ThemeButton theme={theme} onChange={onChangeTheme} />
</div>
</div>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,57 @@
/**
* Editor for the settings JSON.
*/
import Editor, { useMonaco } from "@monaco-editor/react";
import { useCallback, useEffect } from "react";
import schema from "../../../ruff.schema.json";
import { Theme } from "./theme";
export default function SettingsEditor({
visible,
source,
theme,
onChange,
}: {
visible: boolean;
source: string;
theme: Theme;
onChange: (source: string) => void;
}) {
const monaco = useMonaco();
useEffect(() => {
monaco?.languages.json.jsonDefaults.setDiagnosticsOptions({
schemas: [
{
uri: "https://raw.githubusercontent.com/charliermarsh/ruff/main/ruff.schema.json",
fileMatch: ["*"],
schema,
},
],
});
}, [monaco]);
const handleChange = useCallback(
(value: string | undefined) => {
onChange(value ?? "");
},
[onChange],
);
return (
<Editor
options={{
readOnly: false,
minimap: { enabled: false },
fontSize: 14,
roundedSelection: false,
scrollBeyondLastLine: false,
}}
wrapperProps={visible ? {} : { style: { display: "none" } }}
language={"json"}
value={source}
theme={theme === "light" ? "Ayu-Light" : "Ayu-Dark"}
onChange={handleChange}
/>
);
}

View File

@@ -0,0 +1,53 @@
import { useEffect, useState } from "react";
export default function ShareButton({ onShare }: { onShare?: () => void }) {
const [copied, setCopied] = useState(false);
useEffect(() => {
if (copied) {
const timeout = setTimeout(() => setCopied(false), 2000);
return () => clearTimeout(timeout);
}
}, [copied]);
return copied ? (
<button
type="button"
className="relative flex-none rounded-md text-sm font-semibold leading-6 py-1.5 px-3 cursor-auto text-ayu-accent shadow-copied dark:bg-ayu-accent/10"
>
<span
className="absolute inset-0 flex items-center justify-center invisible"
aria-hidden="true"
>
Share
</span>
<span className="" aria-hidden="false">
Copied!
</span>
</button>
) : (
<button
type="button"
className="relative flex-none rounded-md text-sm font-semibold leading-6 py-1.5 px-3 enabled:hover:bg-ayu-accent/80 bg-ayu-accent text-white shadow-sm dark:shadow-highlight/20 disabled:opacity-50"
disabled={!onShare || copied}
onClick={
onShare
? () => {
setCopied(true);
onShare();
}
: undefined
}
>
<span
className="absolute inset-0 flex items-center justify-center"
aria-hidden="false"
>
Share
</span>
<span className="invisible" aria-hidden="true">
Copied!
</span>
</button>
);
}

View File

@@ -0,0 +1,115 @@
/**
* Editor for the Python source code.
*/
import Editor, { useMonaco } from "@monaco-editor/react";
import { MarkerSeverity, MarkerTag } from "monaco-editor";
import { useCallback, useEffect } from "react";
import { Check } from "../pkg";
import { Theme } from "./theme";
export default function SourceEditor({
visible,
source,
theme,
checks,
onChange,
}: {
visible: boolean;
source: string;
checks: Check[];
theme: Theme;
onChange: (pythonSource: string) => void;
}) {
const monaco = useMonaco();
useEffect(() => {
const editor = monaco?.editor;
const model = editor?.getModels()[0];
if (!editor || !model) {
return;
}
editor.setModelMarkers(
model,
"owner",
checks.map((check) => ({
startLineNumber: check.location.row,
startColumn: check.location.column + 1,
endLineNumber: check.end_location.row,
endColumn: check.end_location.column + 1,
message: `${check.code}: ${check.message}`,
severity: MarkerSeverity.Error,
tags:
check.code === "F401" || check.code === "F841"
? [MarkerTag.Unnecessary]
: [],
})),
);
const codeActionProvider = monaco?.languages.registerCodeActionProvider(
"python",
{
// @ts-expect-error: The type definition is wrong.
provideCodeActions: function (model, position) {
const actions = checks
.filter((check) => position.startLineNumber === check.location.row)
.filter((check) => check.fix)
.map((check) => ({
title: `Fix ${check.code}`,
id: `fix-${check.code}`,
kind: "quickfix",
edit: check.fix
? {
edits: [
{
resource: model.uri,
versionId: model.getVersionId(),
edit: {
range: {
startLineNumber: check.fix.location.row,
startColumn: check.fix.location.column + 1,
endLineNumber: check.fix.end_location.row,
endColumn: check.fix.end_location.column + 1,
},
text: check.fix.content,
},
},
],
}
: undefined,
}));
return { actions, dispose: () => {} };
},
},
);
return () => {
codeActionProvider?.dispose();
};
}, [checks, monaco]);
const handleChange = useCallback(
(value: string | undefined) => {
onChange(value ?? "");
},
[onChange],
);
return (
<Editor
options={{
readOnly: false,
minimap: { enabled: false },
fontSize: 14,
roundedSelection: false,
scrollBeyondLastLine: false,
}}
language={"python"}
wrapperProps={visible ? {} : { style: { display: "none" } }}
theme={theme === "light" ? "Ayu-Light" : "Ayu-Dark"}
value={source}
onChange={handleChange}
/>
);
}

View File

@@ -0,0 +1,49 @@
/**
* Button to toggle between light and dark mode themes.
*/
import { Theme } from "./theme";
export default function ThemeButton({
theme,
onChange,
}: {
theme: Theme;
onChange: (theme: Theme) => void;
}) {
return (
<button
type="button"
className="ml-4 sm:ml-0 ring-1 ring-gray-900/5 shadow-sm hover:bg-gray-50 dark:ring-0 dark:bg-gray-800 dark:hover:bg-gray-700 dark:shadow-highlight/4 group focus:outline-none focus-visible:ring-2 rounded-md focus-visible:ring-ayu-accent dark:focus-visible:ring-2 dark:focus-visible:ring-gray-400"
onClick={() => onChange(theme === "light" ? "dark" : "light")}
>
<span className="sr-only">
<span className="dark:hidden">Switch to dark theme</span>
<span className="hidden dark:inline">Switch to light theme</span>
</span>
<svg
width="36"
height="36"
viewBox="-6 -6 36 36"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="stroke-ayu-accent fill-ayu-accent/10 group-hover:stroke-ayu-accent/80 dark:stroke-gray-400 dark:fill-gray-400/20 dark:group-hover:stroke-gray-300"
>
<g className="dark:opacity-0">
<path d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"></path>
<path
d="M12 4v.01M17.66 6.345l-.007.007M20.005 12.005h-.01M17.66 17.665l-.007-.007M12 20.01V20M6.34 17.665l.007-.007M3.995 12.005h.01M6.34 6.344l.007.007"
fill="none"
/>
</g>
<g className="opacity-0 dark:opacity-100">
<path d="M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z" />
<path
d="M12 3v1M18.66 5.345l-.828.828M21.005 12.005h-1M18.66 18.665l-.828-.828M12 21.01V20M5.34 18.666l.835-.836M2.995 12.005h1.01M5.34 5.344l.835.836"
fill="none"
/>
</g>
</svg>
</button>
);
}

View File

@@ -0,0 +1,26 @@
import classNames from "classnames";
import { ReactNode } from "react";
export default function VersionTag({ children }: { children: ReactNode }) {
return (
<div
className={classNames(
"text-gray-500",
"text-xs",
"leading-5",
"font-semibold",
"bg-gray-400/10",
"rounded-full",
"py-1",
"px-3",
"flex",
"items-center",
"dark:bg-gray-800",
"dark:text-gray-400",
"dark:shadow-highlight/4",
)}
>
{children}
</div>
);
}

View File

@@ -0,0 +1,3 @@
import Editor from "./Editor";
export default Editor;

View File

@@ -0,0 +1,50 @@
import lzstring from "lz-string";
export type Settings = { [K: string]: any };
/**
* Stringify a settings object to JSON.
*/
export function stringify(settings: Settings): string {
return JSON.stringify(
settings,
(k, v) => {
if (v instanceof Map) {
return Object.fromEntries(v.entries());
} else {
return v;
}
},
2,
);
}
/**
* Persist the configuration to a URL.
*/
export async function persist(settingsSource: string, pythonSource: string) {
const hash = lzstring.compressToEncodedURIComponent(
settingsSource + "$$$" + pythonSource,
);
await navigator.clipboard.writeText(
window.location.href.split("#")[0] + "#" + hash,
);
}
/**
* Restore the configuration from a URL.
*/
export function restore(): [string, string] | null {
const value = lzstring.decompressFromEncodedURIComponent(
window.location.hash.slice(1),
);
if (value) {
const parts = value.split("$$$");
const settingsSource = parts[0];
const pythonSource = parts[1];
return [settingsSource, pythonSource];
} else {
return null;
}
}

View File

@@ -0,0 +1,35 @@
/**
* Light and dark mode theming.
*/
import { useEffect, useState } from "react";
export type Theme = "dark" | "light";
export function useTheme(): [Theme, (theme: Theme) => void] {
const [localTheme, setLocalTheme] = useState<Theme>("light");
const setTheme = (mode: Theme) => {
if (mode === "dark") {
document.body.classList.add("dark");
} else {
document.body.classList.remove("dark");
}
localStorage.setItem("theme", mode);
setLocalTheme(mode);
};
useEffect(() => {
const initialTheme = localStorage.getItem("theme");
if (initialTheme === "dark") {
setTheme("dark");
} else if (initialTheme === "light") {
setTheme("light");
} else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
setTheme("dark");
} else {
setTheme("light");
}
}, []);
return [localTheme, setTheme];
}

View File

@@ -0,0 +1,7 @@
export async function copyTextToClipboard(text: string) {
if ("clipboard" in navigator) {
return await navigator.clipboard.writeText(text);
} else {
return document.execCommand("copy", true, text);
}
}

View File

@@ -0,0 +1,31 @@
export const DEFAULT_PYTHON_SOURCE =
"import os\n" +
"\n" +
"# Define a function that takes an integer n and returns the nth number in the Fibonacci\n" +
"# sequence.\n" +
"def fibonacci(n):\n" +
' """Compute the nth number in the Fibonacci sequence."""\n' +
" x = 1\n" +
" if n == 0:\n" +
" return 0\n" +
" elif n == 1:\n" +
" return 1\n" +
" else:\n" +
" return fibonacci(n - 1) + fibonacci(n - 2)\n" +
"\n" +
"\n" +
"# Use a for loop to generate and print the first 10 numbers in the Fibonacci sequence.\n" +
"for i in range(10):\n" +
" print(fibonacci(i))\n" +
"\n" +
"# Output:\n" +
"# 0\n" +
"# 1\n" +
"# 1\n" +
"# 2\n" +
"# 3\n" +
"# 5\n" +
"# 8\n" +
"# 13\n" +
"# 21\n" +
"# 34\n";

24
playground/src/index.css Normal file
View File

@@ -0,0 +1,24 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
* {
box-sizing: border-box;
}
body,
html,
#root {
margin: 0;
height: 100%;
width: 100%;
}
.shadow-copied {
--tw-shadow: 0 0 0 1px #f07171, inset 0 0 0 1px #f07171;
--tw-shadow-colored: 0 0 0 1px var(--tw-shadow-color),
inset 0 0 0 1px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}

10
playground/src/main.tsx Normal file
View File

@@ -0,0 +1,10 @@
import React from "react";
import ReactDOM from "react-dom/client";
import Editor from "./Editor";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<Editor />
</React.StrictMode>,
);

6
playground/src/third-party.d.ts vendored Normal file
View File

@@ -0,0 +1,6 @@
declare module "lz-string" {
function decompressFromEncodedURIComponent(
input: string | null,
): string | null;
function compressToEncodedURIComponent(input: string | null): string;
}

1
playground/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@@ -0,0 +1,22 @@
/** @type {import('tailwindcss').Config} */
const defaultTheme = require("tailwindcss/defaultTheme");
module.exports = {
darkMode: "class",
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
colors: {
"ayu-accent": "#f07171",
"ayu-background": {
DEFAULT: "#f8f9fa",
dark: "#0b0e14",
},
},
fontFamily: {
sans: ["Inter var", ...defaultTheme.fontFamily.sans],
},
},
},
plugins: [],
};

21
playground/tsconfig.json Normal file
View File

@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

View File

@@ -0,0 +1,7 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
});

View File

@@ -12,6 +12,7 @@ classifiers = [
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Quality Assurance",
@@ -32,6 +33,14 @@ build-backend = "maturin"
bindings = "bin"
strip = true
[tool.ruff]
update-check = true
[tool.ruff.isort]
force-wrap-aliases = true
combine-as-imports = true
force-single-line = true
single-line-exclusions = ["os", "logging.handlers"]
[tool.ruff.pydocstyle]
convention = "google"

View File

@@ -55,3 +55,5 @@ a.get("hello", False)
{}.pop(True, False)
dict.fromkeys(("world",), True)
{}.deploy(True, False)
getattr(someobj, attrname, False)
mylist.index(True)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
breakpoint()
import pdb
import builtins
from builtins import breakpoint
@@ -9,7 +8,6 @@ from celery.contrib.rdb import set_trace
from celery.contrib import rdb
import celery.contrib.rdb
breakpoint()
st()
set_trace()

View File

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

View File

@@ -0,0 +1,36 @@
_ = "a" "b" "c"
_ = "abc" + "def"
_ = "abc" \
"def"
_ = (
"abc" +
"def"
)
_ = (
f"abc" +
"def"
)
_ = (
b"abc" +
b"def"
)
_ = (
"abc"
"def"
)
_ = (
f"abc"
"def"
)
_ = (
b"abc"
b"def"
)

View File

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

View File

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

View File

@@ -6,18 +6,6 @@ def x():
return a # error
def x():
b, a = 1, 2
print(b)
return a # error
def x():
a = 1
print()
return a # error
def x():
a = 1
print(a)
@@ -53,7 +41,6 @@ def x():
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
def user_agent_username(username=None):
if not username:
return ""
@@ -136,6 +123,20 @@ def x():
return a
# Considered OK, since functions can have side effects.
def x():
b, a = 1, 2
print(b)
return a
# Considered OK, since functions can have side effects.
def x():
a = 1
print()
return a
# Test cases for using value for assignment then returning it
# See:https://github.com/afonasev/flake8-return/issues/47
def resolve_from_url(self, url: str) -> dict:
@@ -236,3 +237,15 @@ def close(self):
any_failed = True
report(traceback.format_exc())
return any_failed
def global_assignment():
global X
X = 1
return X
def nonlocal_assignment():
X = 1
def inner():
nonlocal X
X = 1
return X

View File

@@ -0,0 +1,33 @@
## Banned modules ##
import cgi
from cgi import *
from cgi import a, b, c
# banning a module also bans any submodules
import cgi.foo.bar
from cgi.foo import bar
from cgi.foo.bar import *
## Banned module members ##
from typing import TypedDict
import typing
# attribute access is checked
typing.TypedDict
typing.TypedDict.anything
# function calls are checked
typing.TypedDict()
typing.TypedDict.anything()
# import aliases are resolved
import typing as totally_not_typing
totally_not_typing.TypedDict

View File

@@ -0,0 +1,11 @@
# module members cannot be imported with that syntax
import typing.TypedDict
# we don't track reassignments
import typing, other
typing = other
typing.TypedDict()
# yet another false positive
def foo(typing):
typing.TypedDict()

View File

@@ -1,4 +1,5 @@
from abc import abstractmethod
from typing import overload
from typing_extensions import override
@@ -27,7 +28,7 @@ def f(cls, x):
lambda x: print("Hello, world!")
class X:
class C:
###
# Unused arguments.
###
@@ -135,3 +136,20 @@ class X:
@override
def f(x):
print("Hello, world!")
###
# Unused arguments attached to overloads (OK).
###
@overload
def f(a: str, b: str) -> str:
...
@overload
def f(a: int, b: int) -> str:
...
def f(a, b):
return f"{a}{b}"

View File

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

View File

@@ -23,3 +23,6 @@ from A import (
b, # Comment 10
c, # Comment 11
)
from D import a_long_name_to_force_multiple_lines # Comment 12
from D import another_long_name_to_force_multiple_lines # Comment 13

View File

@@ -0,0 +1,18 @@
import sys, math
from os import path, uname
from logging.handlers import StreamHandler, FileHandler
# comment 1
from third_party import lib1, lib2, \
lib3, lib7, lib5, lib6
# comment 2
from third_party import lib4
from foo import bar # comment 3
from foo2 import bar2 # comment 4
# comment 5
from bar import (
a, # comment 6
b, # comment 7
)

View File

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

View File

@@ -0,0 +1,38 @@
# This has a magic trailing comma, will be sorted, but not rolled into one line
from sys import (
stderr,
argv,
stdout,
exit,
)
# No magic comma, this will be rolled into one line.
from os import (
path,
environ,
execl,
execv
)
from glob import (
glob,
iglob,
escape, # Ends with a comment, should still treat as magic trailing comma.
)
# These will be combined, but without a trailing comma.
from foo import bar
from foo import baz
# These will be combined, _with_ a trailing comma.
from module1 import member1
from module1 import (
member2,
member3,
)
# These will be combined, _with_ a trailing comma.
from module2 import member1, member2
from module2 import (
member3,
)

View File

@@ -0,0 +1,16 @@
import numpy1
import numpy10
import numpy2
from numpy import (
cos,
int8,
sin,
int32,
int64,
tan,
uint8,
uint16,
int16,
uint32,
uint64,
)

View File

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

View File

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

View File

@@ -15,7 +15,7 @@ import types
if type(res) is not types.ListType:
pass
#: E721
assert type(res) == type(False) or type(res) == type(None)
assert type(res) == type(False)
#: E721
assert type(res) == type([])
#: E721
@@ -52,3 +52,5 @@ if isinstance(res, types.MethodType):
pass
if type(a) != type(b) or type(a) == type(ccc):
pass
assert type(res) == type(None)

View File

@@ -1,2 +1,2 @@
def fn() -> None:
pass
print("Newline present (no W292)")

View File

@@ -0,0 +1 @@

View File

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

View File

@@ -0,0 +1,84 @@
def f(x, y, z):
"""Do f.
Args:
x: the value
with a hanging indent
Returns:
the value
"""
return x
def f(x, y, z):
"""Do f.
Args:
x:
The whole thing has a hanging indent.
Returns:
the value
"""
return x
def f(x, y, z):
"""Do f.
Args:
x:
The whole thing has a hanging indent.
Returns: the value
"""
return x
def f(x, y, z):
"""Do f.
Args:
x: the value def
ghi
Returns:
the value
"""
return x
def f(x, y, z):
"""Do f.
Args:
x: the value
z: A final argument
Returns:
the value
"""
return x
def f(x, y, z):
"""Do g.
Args:
x: the value
z: A final argument
Returns: the value
"""
return x
def f(x, y, z):
"""Do h.
Args:
x: the value
z: A final argument
"""
return x

View File

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

View File

@@ -7,7 +7,6 @@ import fu
class bar:
# STOPSHIP: This errors.
fu = 1

View File

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

View File

@@ -0,0 +1,51 @@
if True:
import foo; x = 1
import foo; x = 1
if True:
import foo; \
x = 1
if True:
import foo \
; x = 1
if True:
x = 1; import foo
if True:
x = 1; \
import foo
if True:
x = 1 \
; import foo
if True:
x = 1; import foo; x = 1
x = 1; import foo; x = 1
if True:
x = 1; \
import foo; \
x = 1
if True:
x = 1 \
;import foo \
;x = 1
# Continuation, but not as the last content in the file.
x = 1; \
import foo
# Continuation, followed by end-of-file. (Removing `import foo` would cause a syntax
# error.)
x = 1; \
import foo

View File

@@ -0,0 +1,8 @@
import logging
import warnings
from warnings import warn
warnings.warn("this is ok")
warn("by itself is also ok")
logging.warning("this is fine")
log.warning("this is ok")

View File

@@ -0,0 +1,15 @@
import logging
from logging import warn
logging.warn("this is not ok")
log.warn("this is also not ok")
warn("not ok")
def foo():
from logging import warn
def warn():
pass
warn("has been redefined, but we will still report it")

View File

@@ -0,0 +1,11 @@
x = 1 # type: ignore
x = 1 # type ignore
x = 1 # type:ignore
x = 1
x = 1 # type ignore # noqa
x = 1 # type: ignore[attr-defined]
x = 1 # type: ignore[attr-defined, name-defined]
x = 1 # type: ignore[type-mismatch] # noqa
x = 1 # type: Union[int, str]
x = 1 # type: ignoreme

View File

@@ -0,0 +1,11 @@
x = 1 # noqa
x = 1 # NOQA:F401,W203
# noqa
# NOQA
# noqa:F401
# noqa:F401,W203
x = 1
x = 1 # noqa: F401, W203
# noqa: F401
# noqa: F401, W203

View File

@@ -109,6 +109,11 @@ def f():
del x
def f():
print(f"{x=}")
global x
###
# Non-errors.
###
@@ -146,3 +151,8 @@ def f():
global x, y
del x
def f():
global x
print(f"{x=}")

View File

@@ -42,6 +42,13 @@ staticmethod-decorators = ["staticmethod"]
[tool.ruff.flake8-tidy-imports]
ban-relative-imports = "parents"
[tool.ruff.flake8-tidy-imports.banned-api]
"cgi".msg = "The cgi module is deprecated."
"typing.TypedDict".msg = "Use typing_extensions.TypedDict instead."
[tool.ruff.flake8-errmsg]
max-string-length = 20
[tool.ruff.flake8-import-conventions.aliases]
pandas = "pd"

View File

@@ -0,0 +1,87 @@
# Replace names by built-in names, whether namespaced or not
# https://github.com/search?q=%22from+six+import%22&type=code
import six
from six.moves import map # No need
from six import text_type
six.text_type # str
six.binary_type # bytes
six.class_types # (type,)
six.string_types # (str,)
six.integer_types # (int,)
six.unichr # chr
six.iterbytes # iter
six.print_(...) # print(...)
six.exec_(c, g, l) # exec(c, g, l)
six.advance_iterator(it) # next(it)
six.next(it) # next(it)
six.callable(x) # callable(x)
six.moves.range(x) # range(x)
six.moves.xrange(x) # range(x)
isinstance(..., six.class_types) # isinstance(..., type)
issubclass(..., six.integer_types) # issubclass(..., int)
isinstance(..., six.string_types) # isinstance(..., str)
# Replace call on arg by method call on arg
six.iteritems(dct) # dct.items()
six.iterkeys(dct) # dct.keys()
six.itervalues(dct) # dct.values()
six.viewitems(dct) # dct.items()
six.viewkeys(dct) # dct.keys()
six.viewvalues(dct) # dct.values()
six.assertCountEqual(self, a1, a2) # self.assertCountEqual(a1, a2)
six.assertRaisesRegex(self, e, r, fn) # self.assertRaisesRegex(e, r, fn)
six.assertRegex(self, s, r) # self.assertRegex(s, r)
# Replace call on arg by arg attribute
six.get_method_function(meth) # meth.__func__
six.get_method_self(meth) # meth.__self__
six.get_function_closure(fn) # fn.__closure__
six.get_function_code(fn) # fn.__code__
six.get_function_defaults(fn) # fn.__defaults__
six.get_function_globals(fn) # fn.__globals__
# Replace by string literal
six.b("...") # b'...'
six.u("...") # '...'
six.ensure_binary("...") # b'...'
six.ensure_str("...") # '...'
six.ensure_text("...") # '...'
six.b(string) # no change
# Replace by simple expression
six.get_unbound_function(meth) # meth
six.create_unbound_method(fn, cls) # fn
# Raise exception
six.raise_from(exc, exc_from) # raise exc from exc_from
six.reraise(tp, exc, tb) # raise exc.with_traceback(tb)
six.reraise(*sys.exc_info()) # raise
# Int / Bytes conversion
six.byte2int(bs) # bs[0]
six.indexbytes(bs, i) # bs[i]
six.int2byte(i) # bytes((i, ))
# Special cases for next calls
next(six.iteritems(dct)) # next(iter(dct.items()))
next(six.iterkeys(dct)) # next(iter(dct.keys()))
next(six.itervalues(dct)) # next(iter(dct.values()))
# TODO: To implement
# Rewrite classes
@six.python_2_unicode_compatible # Remove
class C(six.Iterator):
pass # class C: pass
class C(six.with_metaclass(M, B)):
pass # class C(B, metaclass=M): pass
# class C(B, metaclass=M): pass
@six.add_metaclass(M)
class C(B):
pass

View File

@@ -0,0 +1,11 @@
import datetime
import datetime as dt
from datetime import timezone
from datetime import timezone as tz
print(datetime.timezone(-1))
print(timezone.utc)
print(tz.utc)
print(datetime.timezone.utc)
print(dt.timezone.utc)

View File

@@ -0,0 +1,25 @@
# These remain unchanged
str(1)
str(*a)
str("foo", *a)
str(**k)
str("foo", **k)
str("foo", encoding="UTF-8")
str("foo"
"bar")
bytes("foo", encoding="UTF-8")
bytes(*a)
bytes("foo", *a)
bytes("foo", **a)
bytes(b"foo"
b"bar")
# These become string or byte literals
str()
str("foo")
str("""
foo""")
bytes()
bytes(b"foo")
bytes(b"""
foo""")

View File

@@ -0,0 +1,20 @@
import typing
import typing as Hello
from typing import Text
from typing import Text as Goodbye
def print_word(word: Text) -> None:
print(word)
def print_second_word(word: typing.Text) -> None:
print(word)
def print_third_word(word: Hello.Text) -> None:
print(word)
def print_fourth_word(word: Goodbye) -> None:
print(word)

View File

@@ -0,0 +1,9 @@
from io import open
with open("f.txt") as f:
print(f.read())
import io
with io.open("f.txt", mode="r", buffering=-1, **kwargs) as f:
print(f.read())

View File

@@ -0,0 +1,12 @@
import subprocess
import subprocess as somename
from subprocess import run
from subprocess import run as anothername
subprocess.run(["foo"], universal_newlines=True, check=True)
somename.run(["foo"], universal_newlines=True)
run(["foo"], universal_newlines=True, check=False)
anothername(["foo"], universal_newlines=True)
subprocess.run(["foo"], check=True)

View File

@@ -0,0 +1,42 @@
from subprocess import run
import subprocess
output = run(["foo"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output = subprocess.run(["foo"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output = subprocess.run(stdout=subprocess.PIPE, args=["foo"], stderr=subprocess.PIPE)
output = subprocess.run(
["foo"], stdout=subprocess.PIPE, check=True, stderr=subprocess.PIPE
)
output = subprocess.run(
["foo"], stderr=subprocess.PIPE, check=True, stdout=subprocess.PIPE
)
output = subprocess.run(
["foo"],
stdout=subprocess.PIPE,
check=True,
stderr=subprocess.PIPE,
text=True,
encoding="utf-8",
close_fds=True,
)
if output:
output = subprocess.run(
["foo"],
stdout=subprocess.PIPE,
check=True,
stderr=subprocess.PIPE,
text=True,
encoding="utf-8",
)
# Examples that should NOT trigger the rule
from foo import PIPE
subprocess.run(["foo"], stdout=PIPE, stderr=PIPE)
run(["foo"], stdout=None, stderr=PIPE)

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