Compare commits

...

163 Commits

Author SHA1 Message Date
Charlie Marsh
ee4cae97d5 Bump version to 0.0.212 2023-01-05 21:25:42 -05:00
Anders Kaseorg
2e3787adff Remove an unneeded .to_string() in tokenize_files_to_codes_mapping (#1676)
Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2023-01-05 20:56:11 -05:00
Anders Kaseorg
81b211d1b7 Simplify Option<String> → Option<&str> conversion using as_deref (#1675)
Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2023-01-05 20:55:59 -05:00
Anders Kaseorg
1ad72261f1 Replace &String with &str in AnnotatedImport::ImportFrom (#1674)
Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2023-01-05 20:55:46 -05:00
Charlie Marsh
914287d31b Fix format and lint errors 2023-01-05 20:30:01 -05:00
Charlie Marsh
75bb6ad456 Implement duplicate isinstance detection (SIM101) (#1673)
See: #998.
2023-01-05 20:21:40 -05:00
Charlie Marsh
04111da3f3 Improve Pandas call and attribute detection (#1671)
This PR adds some guardrails to avoid common false positives in our
`pandas-vet` rules. Specifically, we now avoid triggering `pandas-vet`
rules if the target of the call or attribute (i.e., the `x` in
`x.stack(...)`) is unbound, or bound to something that couldn't be a
DataFrame (like an import that _isn't_ `pandas`, or a class definition).
This lets us avoid common false positives like `np.stack(...)`.

Resolves #1659.
2023-01-05 19:30:54 -05:00
Charlie Marsh
2464cf6fe9 Fix some &String, &Option, and &Vec usages (#1670) 2023-01-05 18:56:03 -05:00
Charlie Marsh
d34e6c02a1 Allow overhang in Google-style docstring arguments (#1668)
Resolves #1662.
2023-01-05 14:36:19 -05:00
Diego Palacios
e6611c4830 Fix flake8-import-conventions configuration examples (#1660) 2023-01-05 13:37:25 -05:00
Maksudul Haque
2d23b1ae69 [flake8-bandit] Add Rule for S506 (unsafe use of yaml load) (#1664)
See: https://github.com/charliermarsh/ruff/issues/1646.

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2023-01-05 13:35:01 -05:00
Charlie Marsh
5eb03d5e09 Avoid false-positives for yields with non-identical references (#1665)
Resolves #1663.
2023-01-05 12:14:15 -05:00
Maksudul Haque
9f8ef1737e [flake8-bandit] Add Rule for S324 (Insecure hash functions in hashlib) (#1661)
ref: https://github.com/charliermarsh/ruff/issues/1646
2023-01-05 11:45:47 -05:00
messense
1991d618a3 Add proc-macro to derive CheckCodePrefix (#1656)
IMO a derive macro is a natural way to generate new code, and it reduces
the chance of merge conflicts.
2023-01-05 11:39:16 -05:00
Martin Fischer
2045b739a9 Stop highlighting --help output in README as shell (#1655)
This PR is meant to address the following obviously unintended GitHub
rendering:

![image](https://user-images.githubusercontent.com/73739153/210713719-7fb465b1-db91-4074-8a0c-4efa3c47c2f4.png)
2023-01-05 09:46:49 -05:00
Martin Fischer
53e3dd8548 Add ignore-overlong-task-comments setting
Imagine a .py file containing the following comment:

    # TODO: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed
    # do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Since `git grep` only matches individual lines `git grep TODO` would
only output the first line of the comment, cutting off potentially
important information. (git grep currently doesn't support multiline
grepping). Projects using such a workflow therefore probably format
the comment in a single line instead:

    # TODO: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

This commit introduces a setting to accomdate this workflow by making
the line-length checks (`E501`) optionally ignore overlong lines
if they start with a recognized task tag.

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2023-01-04 23:54:50 -05:00
Martin Fischer
78c9056173 Add pycodestyle::settings
This step is split up into a separate commit so
that the following commit has a cleaner diff.
2023-01-04 23:54:50 -05:00
Martin Fischer
8d56e412ef Add task-tags setting
Programmers often leave comments to themselves and others such as:

    # TODO: Use a faster algorithm?

The keywords used to prefix such comments are just a convention and vary
from project to project. Other common keywords include FIXME and HACK.

The keywords in use for the codebase are of interest to ruff because
ruff does also lint comments. For example the ERA lint detects
commented-out code but ignores comments starting with such a keyword.
Previously the ERA lint simply hardcoded the regular expression
TODO|FIXME|XXX to achieve that. This commit introduces a new `task-tags`
setting to make this configurable (and to allow other comment lints to
recognize the same set of keywords).

The term "task tags" has probably been popularized by the Eclipse
IDE.[1] For Python there has been the proposal PEP 350[2], which
referred to such keywords as "codetags". That proposal however has been
rejected. We are choosing the term "task tags" over "code tags" because
the former is more descriptive: a task tag describes a task.

While according to the PEP 350 such keywords are also sometimes used for
non-tasks e.g. NOBUG to describe a well-known problem that will never be
addressed due to design problems or domain limitations, such keywords
are so rare that we are neglecting them here in favor of more
descriptive terminology. The vast majority of such keywords does
describe tasks, so naming the setting "task-tags" is apt.

[1]: https://www.eclipse.org/pdt/help/html/task_tags.htm
[2]: https://peps.python.org/pep-0350/

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2023-01-04 23:54:50 -05:00
Charlie Marsh
3400be18a6 Revert "Add task-tags & ignore-overlong-task-comments settings (#1550)"
This reverts commit ca48492137.
2023-01-04 23:54:50 -05:00
Charlie Marsh
7b59cd2d32 Bump version to 0.0.211 2023-01-04 23:13:04 -05:00
Charlie Marsh
b8ed4d402a Implement SIM110 and SIM111 (conversion to any and all) (#1653) 2023-01-04 23:08:12 -05:00
messense
46dcf3c4c0 Implement flake8-simplify SIM107 (#1650) 2023-01-04 23:02:25 -05:00
Charlie Marsh
ef7777703b Run generation steps 2023-01-04 22:05:04 -05:00
messense
6a1edeb694 Cancel outdated in-progress workflow automatically (#1652) 2023-01-04 22:03:26 -05:00
Charlie Marsh
fb8024a6ac Implement nested with detection (SIM117) (#1651) 2023-01-04 21:22:25 -05:00
Edgar R. M
2f71bdfbfc Implement flake8-bandit rule S108 (#1644) 2023-01-04 21:11:13 -05:00
Charlie Marsh
30d6688c26 Implement nested-if detection (#1649) 2023-01-04 20:55:01 -05:00
Charlie Marsh
9041423b92 Move external licenses to separate directory (#1648) 2023-01-04 20:07:51 -05:00
Charlie Marsh
c91c435dc9 Fix URL in pyproject.toml 2023-01-04 19:23:40 -05:00
Charlie Marsh
5cb162bd7b Add missing McCabe comment 2023-01-04 19:16:36 -05:00
Charlie Marsh
7339d7eccf Implement builtin import removal (#1645) 2023-01-04 19:10:16 -05:00
Charlie Marsh
2ff816f108 DRY up unused import removal code (#1643) 2023-01-04 19:03:52 -05:00
Charlie Marsh
980d10b952 Add Ruff badge to README (#1642) 2023-01-04 17:27:01 -05:00
Charlie Marsh
6c5db1008d Add badge JSON (#1641) 2023-01-04 17:24:27 -05:00
Martin Fischer
ca48492137 Add task-tags & ignore-overlong-task-comments settings (#1550) 2023-01-04 17:10:21 -05:00
Charlie Marsh
7c23701b62 Note a few more incompatibilities (#1639) 2023-01-04 16:05:15 -05:00
Charlie Marsh
8da2c4815a Tweak Yoda condition message (#1638) 2023-01-04 15:58:01 -05:00
Charlie Marsh
34fec8cbd0 Rename flake8-bandit rules from plugins to checks (#1637) 2023-01-04 15:49:05 -05:00
Edgar R. M
1817f8752b Implement flake8-bandit rule S103 (#1636) 2023-01-04 15:47:38 -05:00
Colin Delahunty
8b8e6e44ea Adding my company to the "used in" category of the Readme. (#1631) 2023-01-04 12:31:18 -05:00
Harutaka Kawamura
12166584c4 Minor fixes for SIM105 (#1633) 2023-01-04 11:01:06 -05:00
Charlie Marsh
9ede902328 Run generate-all 2023-01-04 10:46:24 -05:00
messense
0df28bdd4e Implement flake8-simplify SIM105 rule (#1621) 2023-01-04 08:10:59 -05:00
Charlie Marsh
8b07f9517a Implement SIM220 and SIM221 (#1630) 2023-01-04 08:07:00 -05:00
Harutaka Kawamura
0a0e1926f2 Check SIM118 in comprehension (#1627) 2023-01-04 07:05:13 -05:00
Charlie Marsh
4473c7e905 Update stale references in CONTRIBUTING.md 2023-01-03 23:44:10 -05:00
Charlie Marsh
20930b3675 Add some more users to the README (#1623) 2023-01-03 23:06:08 -05:00
Colin Delahunty
fb1a638a96 Implement yield-to-yield from conversion (#1544) 2023-01-03 22:56:52 -05:00
Edgar R. M
16964409a8 Implement missing fixes for PT006 (#1622) 2023-01-03 22:52:51 -05:00
Charlie Marsh
aacfc9ee0b Bump version to 0.0.210 2023-01-03 21:46:08 -05:00
Charlie Marsh
bd14f92898 Avoid byte-string conversions (#1618) 2023-01-03 21:45:14 -05:00
Charlie Marsh
cc116b0192 Treat convention as setting ignore, rather than select (#1611) 2023-01-03 21:27:53 -05:00
Charlie Marsh
0d27c0be27 Treat .pyi files as __future__ annotations-enabled (#1616) 2023-01-03 21:27:26 -05:00
Edgar R. M
60359c6adf Fix leftover whitespace when removing pass for PIE790 (#1612) 2023-01-03 20:44:59 -05:00
Charlie Marsh
77692e4b5f Associate inline comments with parenthesized ImportFrom statements (#1609) 2023-01-03 20:02:12 -05:00
Charlie Marsh
731f3a74a9 Fix *arg and **kwarg handling for Google docstrings (#1608) 2023-01-03 18:17:42 -05:00
Matt Oberle
03275c9c98 Add isort.order-by-type boolean setting (#1607) 2023-01-03 18:07:44 -05:00
Harutaka Kawamura
8d99e317b8 Implement autofix for PT022 (#1604) 2023-01-03 13:36:28 -05:00
Maksudul Haque
d4d67e3014 Do not Change Quotation Style for PT006 Autofix (#1600) 2023-01-03 10:16:49 -05:00
Charlie Marsh
e9a236f740 Bump version to 0.0.209 2023-01-03 08:27:28 -05:00
Ran Benita
ebb31dc29b Fix PT006 autofix of parametrize name strings like ' first, , second ' (#1591) 2023-01-03 08:12:09 -05:00
Charlie Marsh
b9e92affb1 Avoid silently dropping code generator errors (#1598) 2023-01-03 08:11:18 -05:00
Charlie Marsh
68fbd0f029 Preserve style when generating flake8-simplify messages (#1599) 2023-01-03 08:05:55 -05:00
Charlie Marsh
8d01efb571 Avoid hard unwrap in PT checks (#1597) 2023-01-03 07:39:52 -05:00
Pedram Navid
da5a25b421 Add autofix for SIM300 (#1588) 2023-01-03 07:19:04 -05:00
Charlie Marsh
bfdab4ac94 Add flake8-pytest-style settings to hash (#1595) 2023-01-03 07:12:33 -05:00
jvstme
d1389894a4 Fix several typos in README (#1590) 2023-01-03 07:06:43 -05:00
Charlie Marsh
8b277138de Bump version to 0.0.208 2023-01-02 23:19:03 -05:00
Charlie Marsh
e0fe34c523 Implement and-false and or-true rules (#1586) 2023-01-02 23:10:42 -05:00
Charlie Marsh
995fee5ddd Increment flake8-pie implementation count 2023-01-02 22:47:05 -05:00
Charlie Marsh
99906c16db Prefer GitHub icon on mobile (#1585) 2023-01-02 22:46:23 -05:00
Charlie Marsh
b6cb35414e Swap accent color for playground (#1584) 2023-01-02 22:44:09 -05:00
Harutaka Kawamura
b351221049 Mark FStringMissingPlaceholders as fixable (#1582) 2023-01-02 22:41:40 -05:00
Charlie Marsh
afb6f55b8d Add a link to GitHub from the playground (#1583) 2023-01-02 22:40:38 -05:00
Harutaka Kawamura
03a8ece954 Implement autofix for F541 (#1577) 2023-01-02 22:28:32 -05:00
Charlie Marsh
8aeec35bfb Implement dupe-class-field-definitions (#1581) 2023-01-02 22:26:01 -05:00
Charlie Marsh
93259acb31 Implement unnecessary-pass-statement (#1580) 2023-01-02 22:15:24 -05:00
Charlie Marsh
ca7fe686d5 Add scripts to generate plugin and check boilerplate (#1579) 2023-01-02 22:10:31 -05:00
Charlie Marsh
5dd9e99a4b Add flake8-pie plugin with prefer_list_builtin (#1578) 2023-01-02 21:47:38 -05:00
Charlie Marsh
8f0270acfe Remove registry_gen.rs at root 2023-01-02 21:31:19 -05:00
Charlie Marsh
7ce38840a2 Re-run registry_gen.rs generation 2023-01-02 21:29:53 -05:00
Charlie Marsh
8ab8217ca5 Fix destination for registry_gen.rs 2023-01-02 21:29:08 -05:00
Charlie Marsh
5d3ff69053 Add comment annotations to plugin match 2023-01-02 20:49:30 -05:00
Charlie Marsh
726399b2b3 Move Ruff checks to the end of each list 2023-01-02 20:44:27 -05:00
Charlie Marsh
8329237f19 Warn user when D203 and D211 are enabled (#1576) 2023-01-02 19:54:55 -05:00
Oliver Margetts
cd5882c66d Remove need for vendored format/cformat code (#1573) 2023-01-02 19:37:31 -05:00
Charlie Marsh
0c05488740 Automatically set baseline D codes based on convention (#1574) 2023-01-02 19:08:56 -05:00
Charlie Marsh
1425b21d93 Avoid invalid trailing comma fixes for mock rewrites (#1570) 2023-01-02 18:03:43 -05:00
Charlie Marsh
e5a59f41b0 Remove extend- from docstring configuration examples (#1571) 2023-01-02 17:53:41 -05:00
Charlie Marsh
8647bec3cb Rename checks.rs to registry.rs (#1566) 2023-01-02 17:26:51 -05:00
Charlie Marsh
14042800c2 Remove common-path dependency (#1565) 2023-01-02 17:23:29 -05:00
Charlie Marsh
21986e89fd Always check directly-passed-in files (#1564) 2023-01-02 16:49:44 -05:00
Edgar R. M
c4014ef2d3 Implement flake8-pytest-style (#1506) 2023-01-02 16:34:17 -05:00
Charlie Marsh
9ffd20707f Avoid PEP 604 rewrites for runtime annotations (#1563) 2023-01-02 16:33:52 -05:00
Charlie Marsh
6d5aa344a1 Avoid merging import from statements with inline comments (#1562) 2023-01-02 16:24:41 -05:00
Colin Delahunty
e9be5fc7be Add typo linter (#1553) 2023-01-02 15:57:59 -05:00
Charlie Marsh
f74050e5b1 Bump version to 0.0.207 2023-01-02 14:39:32 -05:00
Martin Fischer
90b2d85c85 Fix __init__.py being private (#1556)
Previously visibility::module_visibility() returned Private
for any module name starting with an underscore, resulting in
__init__.py being categorized as private, which in turn resulted
in D104 (Missing docstring in public package) never being reported
for __init__.py files.
2023-01-02 14:39:23 -05:00
Charlie Marsh
ccf848705d Detect unpacking assignments in eradicate (#1559) 2023-01-02 14:05:58 -05:00
Charlie Marsh
3b535fcc74 Add explicit new-rule recommendation in CONTRIBUTING.md (#1558) 2023-01-02 13:50:11 -05:00
Víctor
06321fd240 Add usage clarification to README (#1557) 2023-01-02 13:40:16 -05:00
Martin Fischer
cdae2f0e67 Fix typing::match_annotated_subscript matching ExprKind::Call (#1554) 2023-01-02 12:13:45 -05:00
Martin Fischer
f52691a90a Print warning when running debug builds without --no-cache (#1549) 2023-01-02 12:12:04 -05:00
Pedram Navid
07e47bef4b Add flake8-simplify SIM300 check for Yoda Conditions (#1539) 2023-01-01 18:37:40 -05:00
Anders Kaseorg
86b61806a5 Correct UP027 message to “generator expression” (#1540) 2023-01-01 18:30:58 -05:00
Charlie Marsh
31ce37dd8e Avoid PD false positives on some non-DataFrame expressions (#1538) 2023-01-01 17:05:57 -05:00
Charlie Marsh
2cf6d05586 Avoid triggering PD errors on method calls (#1537) 2023-01-01 17:00:17 -05:00
Colin Delahunty
65c34c56d6 Implement list-to-tuple comprehension unpacking (#1534) 2023-01-01 16:53:26 -05:00
Charlie Marsh
2315db7d13 Bump version to 0.0.206 2023-01-01 16:39:29 -05:00
Charlie Marsh
f1a183c171 Rewrite mock.mock attribute accesses (#1533) 2023-01-01 13:14:09 -05:00
Harutaka Kawamura
509c6d5ec7 Add visit_format_spec to avoid false positives for F541 in f-string format specifier (#1528) 2023-01-01 13:03:32 -05:00
Maksudul Haque
6695988b59 Do not Change Quotation Style for SIM118 Autofix (#1529) 2023-01-01 12:53:46 -05:00
Harutaka Kawamura
e3867b172d Simplify unused snapshot check (#1525) 2023-01-01 02:43:07 -05:00
Harutaka Kawamura
4b8e30f350 Fix Name node range in NamedExpr node (#1526) 2023-01-01 02:41:49 -05:00
Charlie Marsh
8fd0d8e9d8 Bump pyupgrade implementation count 2022-12-31 21:25:34 -05:00
Colin Delahunty
70895a8f1e Pyupgrade: import mock to from unittest import mock (#1488) 2022-12-31 21:25:06 -05:00
Charlie Marsh
f2c9f94f73 Avoid some false positives for ends-in-period checks (#1521) 2022-12-31 18:38:22 -05:00
Charlie Marsh
605c6069e2 Ignore property assignments in RET504 (#1520) 2022-12-31 18:04:13 -05:00
Charlie Marsh
92c2981b6d Add dark mode variant for benchmark image (#1519) 2022-12-31 17:47:32 -05:00
Colin Delahunty
4ad8db3d61 Pyupgrade: Turn errors into OSError (#1434) 2022-12-31 16:36:05 -05:00
Charlie Marsh
0e8c237167 Bump version to 0.0.205 2022-12-31 13:44:39 -05:00
Harutaka Kawamura
960c5e2006 Use more precise error ranges for names (#1513) 2022-12-31 13:42:39 -05:00
Charlie Marsh
9ba17fbf92 Avoid flagging nested f-strings (#1516) 2022-12-31 13:41:21 -05:00
Charlie Marsh
bfdf972a5d Add code kind to Quick Fix action 2022-12-31 10:26:47 -05:00
Charlie Marsh
0c215365ae Bump version to 0.0.204 2022-12-31 08:20:09 -05:00
Maksudul Haque
815284f890 Check for Unsupported Files and Display Errors and Warnings (#1509) 2022-12-31 08:12:55 -05:00
Charlie Marsh
6880338a9a Restore pyproject.toml 2022-12-31 08:06:08 -05:00
Charlie Marsh
68b749c67d Remove foo directory 2022-12-31 08:05:04 -05:00
Reiner Gerecke
c0fc55b812 Generate source code with detected line ending (#1487) 2022-12-31 08:02:29 -05:00
Reiner Gerecke
ba9cf70917 Adjust test_path helper to detect round-trip autofix issues (#1501) 2022-12-31 08:02:13 -05:00
Harutaka Kawamura
f73dfbbfd3 Fix E722 and F707 ranges (#1508) 2022-12-31 07:58:46 -05:00
Reiner Gerecke
62c273cd22 Include fix commit message when showing violations together with source (#1505) 2022-12-31 07:54:41 -05:00
Harutaka Kawamura
938ad9a39e Fix N818 range (#1503) 2022-12-31 07:43:03 -05:00
Harutaka Kawamura
14248cb8cb Improve PLW0120 range (#1500) 2022-12-31 07:42:49 -05:00
Harutaka Kawamura
3a280039e1 Improve F811 range for function and class definitions (#1499) 2022-12-31 07:42:18 -05:00
Harutaka Kawamura
4e9e58bdc0 Improve T20X ranges (#1502) 2022-12-31 07:41:53 -05:00
Harutaka Kawamura
926b5494ad Remove unused snapshots (#1497) 2022-12-31 07:40:38 -05:00
Reiner Gerecke
6717b48ca5 Fix detection of changed imports in isort plugin (#1504) 2022-12-31 07:37:27 -05:00
Harutaka Kawamura
f7bb5bc858 Remove F831 (#1495) 2022-12-30 23:57:51 -05:00
Harutaka Kawamura
3e23fd1487 Stop overriding locations for expressions within f-strings (#1494) 2022-12-30 23:43:59 -05:00
Charlie Marsh
01c74e0629 Add a "fix message" to every autofix-able check (#1489) 2022-12-30 23:16:03 -05:00
Charlie Marsh
1e3cf87f67 Escape strings when formatting check messages (#1493) 2022-12-30 22:11:01 -05:00
Charlie Marsh
248447e139 Trim CLI help during generation (#1492) 2022-12-30 22:03:58 -05:00
Charlie Marsh
95f139583a Modify pyproject.toml to meet schema compliance 2022-12-30 15:51:35 -05:00
Charlie Marsh
74903f23d6 Bump version to 0.0.203 2022-12-30 15:33:30 -05:00
Charlie Marsh
3ee20a70d3 Remove lingering ruff_options.ts references 2022-12-30 15:33:09 -05:00
Charlie Marsh
4c2fbb7ac0 Remove hidden autoformat command (#1486) 2022-12-30 15:32:05 -05:00
Charlie Marsh
7a66f98590 Move some argument validation into Clap (#1485) 2022-12-30 15:29:41 -05:00
Charlie Marsh
a2bf3916f3 Make clean a standalone command 2022-12-30 15:20:18 -05:00
Reiner Gerecke
c9aa7b9308 Generate the README's --help output automatically via cargo +nightly dev generate-all (#1483) 2022-12-30 15:06:32 -05:00
Charlie Marsh
d880ca6cc6 Add a command to clear the Ruff cache (#1484) 2022-12-30 13:52:16 -05:00
Reiner Gerecke
080f99b908 Detect line endings and use them during code generation (#1482) 2022-12-30 12:59:40 -05:00
Charlie Marsh
a7dc491ff1 Fix clippy 2022-12-30 12:34:43 -05:00
Charlie Marsh
cdc8f8c91a Remove support for ur prefixes (#1481) 2022-12-30 11:21:05 -05:00
Colin Delahunty
138c46e793 Simplified code for unicode fix (#1475) 2022-12-30 11:18:52 -05:00
Charlie Marsh
a86c57a832 Support multi-line noqa directives for 'import from' (#1479) 2022-12-30 11:16:50 -05:00
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
850 changed files with 20582 additions and 9645 deletions

View File

@@ -6,6 +6,10 @@ on:
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
@@ -38,10 +42,8 @@ jobs:
- run: cargo build --all --release
- 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
- run: git diff --exit-code -- README.md ruff.schema.json
cargo-fmt:
name: "cargo fmt"
@@ -117,8 +119,12 @@ jobs:
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- run: cargo install cargo-insta
- run: pip install black[d]==22.12.0
- run: cargo test --all
- name: Run tests
run: |
cargo insta test --all --delete-unreferenced-snapshots
git diff --exit-code
- run: cargo test --package ruff --test black_compatibility_test -- --ignored
# TODO(charlie): Re-enable the `wasm-pack` tests.
@@ -178,3 +184,15 @@ jobs:
${{ runner.os }}-build-
${{ runner.os }}-
- run: maturin build -b bin
typos:
name: Spell Check with Typos
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v2
- name: Check spelling of file.txt
uses: crate-ci/typos@master
with:
files: .

1
.gitignore vendored
View File

@@ -181,3 +181,4 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
.vimspector.json

View File

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

View File

@@ -9,6 +9,15 @@ free to submit a PR. For larger changes (e.g., new lint rules, new functionality
options), consider submitting an [Issue](https://github.com/charliermarsh/ruff/issues) outlining
your proposed change.
If you're looking for a place to start, we recommend implementing a new lint rule (see:
[_Adding a new lint rule_](#example-adding-a-new-lint-rule), which will allow you to learn from and
pattern-match against the examples in the existing codebase. Many lint rules are inspired by
existing Python plugins, which can be used as a reference implementation.
As a concrete example: consider taking on one of the rules in [`flake8-simplify`](https://github.com/charliermarsh/ruff/issues/998),
and looking to the originating [Python source](https://github.com/MartinThoma/flake8-simplify) for
guidance.
### Prerequisites
Ruff is written in Rust. You'll need to install the
@@ -47,57 +56,55 @@ prior to merging.
There are four phases to adding a new lint rule:
1. Define the rule in `src/checks.rs`.
1. Define the rule in `src/registry.rs`.
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).
To define the rule, open up `src/checks.rs`. You'll need to define both a `CheckCode` and
To define the rule, open up `src/registry.rs`. You'll need to define both a `CheckCode` and
`CheckKind`. As an example, you can grep for `E402` and `ModuleImportNotAtTopOfFile`, and follow the
pattern implemented therein.
To trigger the rule, you'll likely want to augment the logic in `src/check_ast.rs`, which defines
To trigger the rule, you'll likely want to augment the logic in `src/checkers/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 +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
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:
To add a test fixture, create a file under `resources/test/fixtures/[plugin-name]`, 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.
```rust
#[test_case(CheckCode::A001, Path::new("A001.py"); "A001")]
...
```
Run `cargo +nightly dev generate-all` to generate the code for your new fixture. Then run Ruff
locally with (e.g.) `cargo run resources/test/fixtures/pycodestyle/E402.py --no-cache --select E402`.
Then, run `cargo test`. Your test will fail, but you'll be prompted to follow-up with
`cargo insta review`. Accept the generated snapshot, then commit the snapshot file alongside the
rest of your changes.
Once you're satisfied with the output, codify the behavior as a snapshot test by adding a new
`test_case` macro in the relevant `src/[plugin-name]/mod.rs` file. Then, run `cargo test`. Your
test will fail, but you'll be prompted to follow-up with `cargo insta review`. Accept the generated
snapshot, then commit the snapshot file alongside the rest of your changes.
Finally, regenerate the documentation and generated code with `cargo +nightly dev generate-all`.
### Example: Adding a new configuration option
Ruff's user-facing settings live in two places: first, the command-line options defined with
[clap](https://docs.rs/clap/latest/clap/) via the `Cli` struct in `src/main.rs`; and second, the
`Config` struct defined `src/pyproject.rs`, which is responsible for extracting user-defined
settings from a `pyproject.toml` file.
Ruff's user-facing settings live in a few different places.
Ultimately, these two sources of configuration are merged into the `Settings` struct defined
in `src/settings.rs`, which is then threaded through the codebase.
First, the command-line options are defined via the `Cli` struct in `src/cli.rs`.
To add a new configuration option, you'll likely want to _both_ add a CLI option to `src/main.rs`
_and_ a `pyproject.toml` parameter to `src/pyproject.rs`. If you want to pattern-match against an
existing example, grep for `dummy_variable_rgx`, which defines a regular expression to match against
acceptable unused variables (e.g., `_`).
Second, the `pyproject.toml` options are defined in `src/settings/options.rs` (via the `Options`
struct), `src/settings/configuration.rs` (via the `Configuration` struct), and `src/settings/mod.rs`
(via the `Settings` struct). These represent, respectively: the schema used to parse the
`pyproject.toml` file; an internal, intermediate representation; and the final, internal
representation used to power Ruff.
If the new plugin's configuration should be cached between runs, you'll need to add it to the
`Hash` implementation for `Settings` in `src/settings/mod.rs`.
To add a new configuration option, you'll likely want to modify these latter few files (along with
`cli.rs`, if appropriate). If you want to pattern-match against an existing example, grep for
`dummy_variable_rgx`, which defines a regular expression to match against acceptable unused
variables (e.g., `_`).
Note that plugin-specific configuration options are defined in their own modules (e.g.,
`src/flake8_unused_arguments/settings.rs`).
You may also want to add the new configuration option to the `flake8-to-ruff` tool, which is
responsible for converting `flake8` configuration files to Ruff's TOML format. This logic

54
Cargo.lock generated
View File

@@ -356,9 +356,9 @@ dependencies = [
[[package]]
name = "clearscreen"
version = "1.0.10"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c969a6b6dadff9f3349b1f783f553e2411104763ca4789e1c6ca6a41f46a57b0"
checksum = "41aa24cc5e1d6b3fc49ad4cd540b522fedcbe88bc6f259ff16e20e7010b6f8c7"
dependencies = [
"nix",
"terminfo",
@@ -367,15 +367,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "codegen"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff61280aed771c3070e7dcc9e050c66f1eb1e3b96431ba66f9f74641d02fc41d"
dependencies = [
"indexmap",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
@@ -397,12 +388,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "common-path"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101"
[[package]]
name = "configparser"
version = "3.0.2"
@@ -750,7 +735,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.201-dev.0"
version = "0.0.212-dev.0"
dependencies = [
"anyhow",
"clap 4.0.32",
@@ -1274,13 +1259,14 @@ checksum = "d906846a98739ed9d73d66e62c2641eef8321f1734b7a1156ab045a0248fb2b3"
[[package]]
name = "nix"
version = "0.24.3"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694"
dependencies = [
"bitflags",
"cfg-if 1.0.0",
"libc",
"static_assertions",
]
[[package]]
@@ -1370,9 +1356,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.16.0"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
[[package]]
name = "oorandom"
@@ -1878,7 +1864,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.201"
version = "0.0.212"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -1893,7 +1879,6 @@ dependencies = [
"clap_complete_command",
"clearscreen",
"colored",
"common-path",
"console_error_panic_hook",
"console_log",
"criterion",
@@ -1913,6 +1898,7 @@ dependencies = [
"nohash-hasher",
"notify",
"num-bigint",
"num-traits",
"once_cell",
"path-absolutize",
"quick-junit",
@@ -1946,11 +1932,10 @@ dependencies = [
[[package]]
name = "ruff_dev"
version = "0.0.201"
version = "0.0.212"
dependencies = [
"anyhow",
"clap 4.0.32",
"codegen",
"itertools",
"libcst",
"once_cell",
@@ -1967,8 +1952,9 @@ dependencies = [
[[package]]
name = "ruff_macros"
version = "0.0.201"
version = "0.0.212"
dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
@@ -2010,7 +1996,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.1.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=68d26955b3e24198a150315e7959719b03709dee#68d26955b3e24198a150315e7959719b03709dee"
source = "git+https://github.com/RustPython/RustPython.git?rev=4d53c7cb27c0379adf8b51c4d3d0d2174f41d590#4d53c7cb27c0379adf8b51c4d3d0d2174f41d590"
dependencies = [
"num-bigint",
"rustpython-common",
@@ -2020,11 +2006,13 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.0.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=68d26955b3e24198a150315e7959719b03709dee#68d26955b3e24198a150315e7959719b03709dee"
source = "git+https://github.com/RustPython/RustPython.git?rev=4d53c7cb27c0379adf8b51c4d3d0d2174f41d590#4d53c7cb27c0379adf8b51c4d3d0d2174f41d590"
dependencies = [
"ascii",
"bitflags",
"cfg-if 1.0.0",
"hexf-parse",
"itertools",
"lexical-parse-float",
"libc",
"lock_api",
@@ -2043,7 +2031,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=68d26955b3e24198a150315e7959719b03709dee#68d26955b3e24198a150315e7959719b03709dee"
source = "git+https://github.com/RustPython/RustPython.git?rev=4d53c7cb27c0379adf8b51c4d3d0d2174f41d590#4d53c7cb27c0379adf8b51c4d3d0d2174f41d590"
dependencies = [
"bincode",
"bitflags",
@@ -2060,7 +2048,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=68d26955b3e24198a150315e7959719b03709dee#68d26955b3e24198a150315e7959719b03709dee"
source = "git+https://github.com/RustPython/RustPython.git?rev=4d53c7cb27c0379adf8b51c4d3d0d2174f41d590#4d53c7cb27c0379adf8b51c4d3d0d2174f41d590"
dependencies = [
"ahash",
"anyhow",
@@ -2638,9 +2626,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "update-informer"
version = "0.5.0"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f154aee470c0882ea0f3b1cc2a46c5f4d24f282655f7b0cec065614fe24c447f"
checksum = "152ff185ca29f7f487c51ca785b0f1d85970c4581f4cdd12ed499227890200f5"
dependencies = [
"directories",
"semver",

View File

@@ -6,7 +6,7 @@ members = [
[package]
name = "ruff"
version = "0.0.201"
version = "0.0.212"
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
edition = "2021"
rust-version = "1.65.0"
@@ -32,7 +32,6 @@ chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
clap = { version = "4.0.1", features = ["derive"] }
clap_complete_command = { version = "0.4.0" }
colored = { version = "2.0.0" }
common-path = { version = "1.0.0" }
dirs = { version = "4.0.0" }
fern = { version = "0.6.1" }
filetime = { version = "0.2.17" }
@@ -46,16 +45,17 @@ natord = { version = "1.0.9" }
nohash-hasher = { version = "0.2.0" }
notify = { version = "5.0.0" }
num-bigint = { version = "0.4.3" }
num-traits = "0.2.15"
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" }
regex = { version = "1.6.0" }
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
ruff_macros = { version = "0.0.201", path = "ruff_macros" }
ruff_macros = { version = "0.0.212", path = "ruff_macros" }
rustc-hash = { version = "1.1.0" }
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" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "4d53c7cb27c0379adf8b51c4d3d0d2174f41d590" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "4d53c7cb27c0379adf8b51c4d3d0d2174f41d590" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "4d53c7cb27c0379adf8b51c4d3d0d2174f41d590" }
schemars = { version = "0.8.11" }
semver = { version = "1.0.16" }
serde = { version = "1.0.147", features = ["derive"] }
@@ -70,9 +70,9 @@ toml = { version = "0.5.9" }
walkdir = { version = "2.3.2" }
[target.'cfg(not(target_family = "wasm"))'.dependencies]
clearscreen = { version = "1.0.10" }
clearscreen = { version = "2.0.0" }
rayon = { version = "1.5.3" }
update-informer = { version = "0.5.0", default-features = false, features = ["pypi"], optional = true }
update-informer = { version = "0.6.0", default-features = false, features = ["pypi"], optional = true }
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
# For (future) wasm-pack support

647
LICENSE
View File

@@ -19,650 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The externally maintained libraries from which parts of the Software is derived
are:
- Pyflakes, licensed as follows:
"""
Copyright 2005-2011 Divmod, Inc.
Copyright 2013-2014 Florent Xicluna
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
- autoflake, licensed as follows:
"""
Copyright (C) 2012-2018 Steven Myint
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- Flake8, licensed as follows:
"""
== Flake8 License (MIT) ==
Copyright (C) 2011-2013 Tarek Ziade <tarek@ziade.org>
Copyright (C) 2012-2016 Ian Cordasco <graffatcolmingov@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-2020, licensed as follows:
"""
Copyright (c) 2019 Anthony Sottile
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
- flake8-annotations, licensed as follows:
"""
MIT License
Copyright (c) 2019 - Present S. Co1
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-bandit, licensed as follows:
"""
Copyright (c) 2017 Tyler Wince
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
- flake8-blind-except, licensed as follows:
"""
The MIT License (MIT)
Copyright (c) 2014 Elijah Andrews
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
- flake8-bugbear, licensed as follows:
"""
The MIT License (MIT)
Copyright (c) 2016 Łukasz Langa
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-comprehensions, licensed as follows:
"""
MIT License
Copyright (c) 2017 Adam Johnson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-debugger, licensed as follows:
"""
MIT License
Copyright (c) 2016 Joseph Kahn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-eradicate, licensed as follows:
"""
MIT License
Copyright (c) 2018 Nikita Sobolev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-tidy-imports, licensed as follows:
"""
MIT License
Copyright (c) 2017 Adam Johnson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-print, licensed as follows:
"""
MIT License
Copyright (c) 2016 Joseph Kahn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-quotes, licensed as follows:
"""
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
- flake8-return, licensed as follows:
"""
MIT License
Copyright (c) 2019 Afonasev Evgeniy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-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
Copyright (c) 2021 João Palmeiro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-unused-arguments, licensed as follows:
"""
MIT License
Copyright (c) 2019 Nathan Hoad
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-simplify, licensed as follows:
"""
MIT License
Copyright (c) 2020 Martin Thoma
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- isort, licensed as follows:
"""
The MIT License (MIT)
Copyright (c) 2013 Timothy Edmund Crosley
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
- pep8-naming, licensed as follows:
"""
Copyright © 2013 Florent Xicluna <florent.xicluna@gmail.com>
Licensed under the terms of the Expat License
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- pycodestyle, licensed as follows:
"""
Copyright © 2006-2009 Johann C. Rocholl <johann@rocholl.net>
Copyright © 2009-2014 Florent Xicluna <florent.xicluna@gmail.com>
Copyright © 2014-2020 Ian Lee <IanLee1521@gmail.com>
Licensed under the terms of the Expat License
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- pydocstyle, licensed as follows:
"""
Copyright (c) 2012 GreenSteam, <http://greensteam.dk/>
Copyright (c) 2014-2020 Amir Rachum, <http://amir.rachum.com/>
Copyright (c) 2020 Sambhav Kothari, <https://github.com/samj1912>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- pygrep-hooks, licensed as follows:
"""
Copyright (c) 2018 Anthony Sottile
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
- pyupgrade, licensed as follows:
"""
Copyright (c) 2017 Anthony Sottile
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
- RustPython, licensed as follows:
"""
MIT License
Copyright (c) 2020 RustPython Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

526
README.md
View File

@@ -1,5 +1,6 @@
# Ruff
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v0.json)](https://github.com/charliermarsh/ruff)
[![image](https://img.shields.io/pypi/v/ruff.svg)](https://pypi.python.org/pypi/ruff)
[![image](https://img.shields.io/pypi/l/ruff.svg)](https://pypi.python.org/pypi/ruff)
[![image](https://img.shields.io/pypi/pyversions/ruff.svg)](https://pypi.python.org/pypi/ruff)
@@ -8,7 +9,11 @@
An extremely fast Python linter, written in Rust.
<p align="center">
<img alt="Bar chart with benchmark results" src="https://user-images.githubusercontent.com/1309177/187504482-6d9df992-a81d-4e86-9f6a-d958741c8182.svg">
<picture align="center">
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/1309177/210156880-a97c2a0d-2c03-4393-8695-36547935a94e.svg">
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/1309177/210156881-a88fd142-5008-4695-9407-d028cec3eff7.svg">
<img alt="Shows a bar chart with benchmark results." src="https://user-images.githubusercontent.com/1309177/210156881-a88fd142-5008-4695-9407-d028cec3eff7.svg">
</picture>
</p>
<p align="center">
@@ -22,8 +27,9 @@ An extremely fast Python linter, written in Rust.
- 📦 Built-in caching, to avoid re-analyzing unchanged files
- 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
- ⚖️ [Near-parity](#how-does-ruff-compare-to-flake8) with the built-in Flake8 rule set
- 🔌 Native re-implementations of popular Flake8 plugins, like [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
- 🌎 Monorepo-friendly configuration via hierarchical and cascading settings
- 🔌 Native re-implementations of dozens of Flake8 plugins, like [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
- ⌨️ First-party editor integrations for [VS Code](https://github.com/charliermarsh/ruff-vscode) and [more](https://github.com/charliermarsh/ruff-lsp)
- 🌎 Monorepo-friendly, with [hierarchical and cascading configuration](#pyprojecttoml-discovery)
Ruff aims to be orders of magnitude faster than alternative tools while integrating more
functionality behind a single, common interface.
@@ -48,8 +54,11 @@ Ruff is extremely actively developed and used in major open-source projects like
- [Hatch](https://github.com/pypa/hatch)
- [Jupyter](https://github.com/jupyter-server/jupyter_server)
- [Synapse](https://github.com/matrix-org/synapse)
- [Ibis](https://github.com/ibis-project/ibis)
- [Saleor](https://github.com/saleor/saleor)
- [Polars](https://github.com/pola-rs/polars)
- [Ibis](https://github.com/ibis-project/ibis)
- [OpenBB](https://github.com/OpenBB-finance/OpenBBTerminal)
- [`pyca/cryptography`](https://github.com/pyca/cryptography)
Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-much-much-faster).
@@ -98,6 +107,7 @@ of [Conda](https://docs.conda.io/en/latest/):
1. [flake8-implicit-str-concat (ISC)](#flake8-implicit-str-concat-isc)
1. [flake8-import-conventions (ICN)](#flake8-import-conventions-icn)
1. [flake8-print (T20)](#flake8-print-t20)
1. [flake8-pytest-style (PT)](#flake8-pytest-style-pt)
1. [flake8-quotes (Q)](#flake8-quotes-q)
1. [flake8-return (RET)](#flake8-return-ret)
1. [flake8-simplify (SIM)](#flake8-simplify-sim)
@@ -108,6 +118,7 @@ of [Conda](https://docs.conda.io/en/latest/):
1. [pandas-vet (PD)](#pandas-vet-pd)
1. [pygrep-hooks (PGH)](#pygrep-hooks-pgh)
1. [Pylint (PLC, PLE, PLR, PLW)](#pylint-plc-ple-plr-plw)
1. [flake8-pie (PIE)](#flake8-pie-pie)
1. [Ruff-specific rules (RUF)](#ruff-specific-rules-ruf)<!-- End auto-generated table of contents. -->
1. [Editor Integrations](#editor-integrations)
1. [FAQ](#faq)
@@ -153,9 +164,9 @@ pacman -S ruff
To run Ruff, try any of the following:
```shell
ruff path/to/code/to/check.py
ruff path/to/code/
ruff path/to/code/*.py
ruff path/to/code/to/check.py # Run Ruff over `check.py`
ruff path/to/code/ # Run Ruff over all files in `/path/to/code` (and any subdirectories)
ruff path/to/code/*.py # Run Ruff over all `.py` files in `/path/to/code`
```
You can run Ruff in `--watch` mode to automatically re-run on-change:
@@ -169,7 +180,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
```yaml
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: 'v0.0.201'
rev: 'v0.0.212'
hooks:
- id: ruff
# Respect `exclude` and `extend-exclude` settings.
@@ -270,7 +281,7 @@ alternative docstring formats. Enabling `ALL` without further configuration may
behavior, especially for the `pydocstyle` plugin.
As an alternative to `pyproject.toml`, Ruff will also respect a `ruff.toml` file, which implements
an equivalent schema (though the `[tool.ruff]` hierarchy can be omitted). For example, the above
an equivalent schema (though the `[tool.ruff]` hierarchy can be omitted). For example, the
`pyproject.toml` described above would be represented via the following `ruff.toml`:
```toml
@@ -300,7 +311,8 @@ ruff path/to/code/ --select F401 --select F403
See `ruff --help` for more:
```shell
<!-- Begin auto-generated cli help. -->
```
Ruff: An extremely fast Python linter.
Usage: ruff [OPTIONS] [FILES]...
@@ -330,11 +342,11 @@ Options:
-n, --no-cache
Disable cache reads
--select <SELECT>
List of error codes to enable
Comma-separated list of error codes to enable (or ALL, to enable all checks)
--extend-select <EXTEND_SELECT>
Like --select, but adds additional error codes on top of the selected ones
--ignore <IGNORE>
List of error codes to ignore
Comma-separated list of error codes to disable
--extend-ignore <EXTEND_IGNORE>
Like --ignore, but adds additional error codes on top of the ignored ones
--exclude <EXCLUDE>
@@ -349,6 +361,10 @@ Options:
List of mappings from file pattern to code to exclude
--format <FORMAT>
Output serialization format for error messages [possible values: text, json, junit, grouped, github, gitlab]
--stdin-filename <STDIN_FILENAME>
The name of the file when passing it through stdin
--cache-dir <CACHE_DIR>
Path to the cache directory
--show-source
Show violations with source code
--respect-gitignore
@@ -357,12 +373,6 @@ Options:
Enforce exclusions, even for paths passed to Ruff directly on the command-line
--update-check
Enable or disable automatic update checks
--show-files
See the files Ruff will be run against with the current settings
--show-settings
See the settings Ruff will use to check a given Python file
--add-noqa
Enable automatic additions of noqa directives to failing lines
--dummy-variable-rgx <DUMMY_VARIABLE_RGX>
Regular expression matching the name of dummy variables
--target-version <TARGET_VERSION>
@@ -370,18 +380,23 @@ Options:
--line-length <LINE_LENGTH>
Set the line-length for length-associated checks and automatic formatting
--max-complexity <MAX_COMPLEXITY>
Max McCabe complexity allowed for a function
--stdin-filename <STDIN_FILENAME>
The name of the file when passing it through stdin
Maximum McCabe complexity allowed for a given function
--add-noqa
Enable automatic additions of `noqa` directives to failing lines
--clean
Clear any caches in the current directory or any subdirectories
--explain <EXPLAIN>
Explain a rule
--cache-dir <CACHE_DIR>
Path to the cache directory
--show-files
See the files Ruff will be run against with the current settings
--show-settings
See the settings Ruff will use to check a given Python file
-h, --help
Print help information
-V, --version
Print version information
```
<!-- End auto-generated cli help. -->
### `pyproject.toml` discovery
@@ -393,7 +408,7 @@ directory hierarchy is used for every individual file, with all paths in the `py
There are a few exceptions to these rules:
1. In locating the "closest" `pyproject.toml` file for a given path, Ruff ignore any
1. In locating the "closest" `pyproject.toml` file for a given path, Ruff ignores any
`pyproject.toml` files that lack a `[tool.ruff]` section.
2. If a configuration file is passed directly via `--config`, those settings are used for across
files. Any relative paths in that configuration file (like `exclude` globs or `src` paths) are
@@ -438,7 +453,7 @@ For example, `ruff /path/to/excluded/file.py` will always check `file.py`.
### Ignoring errors
To omit a lint check entirely, add it to the "ignore" list via [`ignore`](#ignore) or
[`extend-ignore`](#extend-ignore), either on the command-line or in your `project.toml` file.
[`extend-ignore`](#extend-ignore), either on the command-line or in your `pyproject.toml` file.
To ignore an error inline, Ruff uses a `noqa` system similar to [Flake8](https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html).
To ignore an individual error, add `# noqa: {code}` to the end of the line, like so:
@@ -535,13 +550,13 @@ For more, see [Pyflakes](https://pypi.org/project/pyflakes/2.5.0/) on PyPI.
| F523 | StringDotFormatExtraPositionalArguments | '...'.format(...) has unused arguments at position(s): ... | |
| F524 | StringDotFormatMissingArguments | '...'.format(...) is missing argument(s) for placeholder(s): ... | |
| F525 | StringDotFormatMixingAutomatic | '...'.format(...) mixes automatic and manual numbering | |
| F541 | FStringMissingPlaceholders | f-string without any placeholders | |
| F541 | FStringMissingPlaceholders | f-string without any placeholders | 🛠 |
| F601 | MultiValueRepeatedKeyLiteral | Dictionary key literal repeated | |
| F602 | MultiValueRepeatedKeyVariable | Dictionary key `...` repeated | |
| F621 | ExpressionsInStarAssignment | Too many expressions in star-unpacking assignment | |
| F622 | TwoStarredExpressions | Two starred expressions in assignment | |
| F631 | AssertTuple | Assert test is a non-empty tuple, which is always `True` | |
| F632 | IsLiteral | Use `==` and `!=` to compare constant literals | 🛠 |
| F632 | IsLiteral | Use `==` to compare constant literals | 🛠 |
| F633 | InvalidPrintSyntax | Use of `>>` is invalid with `print` function | |
| F634 | IfTuple | If test is a tuple, which is always `True` | |
| F701 | BreakOutsideLoop | `break` outside loop | |
@@ -554,7 +569,6 @@ For more, see [Pyflakes](https://pypi.org/project/pyflakes/2.5.0/) on PyPI.
| F821 | UndefinedName | Undefined name `...` | |
| F822 | UndefinedExport | Undefined name `...` in `__all__` | |
| F823 | UndefinedLocal | Local variable `...` referenced before assignment | |
| F831 | DuplicateArgumentName | Duplicate argument name in function definition | |
| F841 | UnusedVariable | Local variable `...` is assigned to but never used | |
| F842 | UnusedAnnotation | Local variable `...` is annotated but never used | |
| F901 | RaiseNotImplemented | `raise NotImplemented` should be `raise NotImplementedError` | 🛠 |
@@ -574,7 +588,7 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI
| E714 | NotIsTest | Test for object identity should be `is not` | 🛠 |
| E721 | TypeComparison | Do not compare types, use `isinstance()` | |
| E722 | DoNotUseBareExcept | Do not use bare `except` | |
| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def | 🛠 |
| E731 | DoNotAssignLambda | Do not assign a `lambda` expression, use a `def` | 🛠 |
| E741 | AmbiguousVariableName | Ambiguous variable name: `...` | |
| E742 | AmbiguousClassName | Ambiguous class name: `...` | |
| E743 | AmbiguousFunctionName | Ambiguous function name: `...` | |
@@ -659,7 +673,7 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
| ---- | ---- | ------- | --- |
| UP001 | UselessMetaclassType | `__metaclass__ = type` is implied | 🛠 |
| UP003 | TypeOfPrimitive | Use `str` instead of `type(...)` | 🛠 |
| UP004 | UselessObjectInheritance | Class `...` inherits from object | 🛠 |
| UP004 | UselessObjectInheritance | Class `...` inherits from `object` | 🛠 |
| UP005 | DeprecatedUnittestAlias | `assertEquals` is deprecated, use `assertEqual` | 🛠 |
| UP006 | UsePEP585Annotation | Use `list` instead of `List` for type annotations | 🛠 |
| UP007 | UsePEP604Annotation | Use `X \| Y` for type annotations | 🛠 |
@@ -673,13 +687,18 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
| UP015 | RedundantOpenModes | Unnecessary open mode parameters | 🛠 |
| UP016 | RemoveSixCompat | Unnecessary `six` compatibility usage | 🛠 |
| UP017 | DatetimeTimezoneUTC | Use `datetime.UTC` alias | 🛠 |
| UP018 | NativeLiterals | Unnecessary call to `str` and `bytes` | 🛠 |
| UP018 | NativeLiterals | Unnecessary call to `str` | 🛠 |
| UP019 | TypingTextStrAlias | `typing.Text` is deprecated, use `str` | 🛠 |
| UP020 | OpenAlias | Use builtin `open` | 🛠 |
| UP021 | ReplaceUniversalNewlines | `universal_newlines` is deprecated, use `text` | 🛠 |
| UP022 | ReplaceStdoutStderr | Sending stdout and stderr to pipe is deprecated, use `capture_output` | 🛠 |
| UP023 | RewriteCElementTree | `cElementTree` is deprecated, use `ElementTree` | 🛠 |
| UP024 | OSErrorAlias | Replace aliased errors with `OSError` | 🛠 |
| UP025 | RewriteUnicodeLiteral | Remove unicode literals from strings | 🛠 |
| UP026 | RewriteMockImport | `mock` is deprecated, use `unittest.mock` | 🛠 |
| UP027 | RewriteListComprehension | Replace unpacked list comprehension with a generator expression | 🛠 |
| UP028 | RewriteYieldFrom | Replace `yield` over `for` loop with `yield from` | 🛠 |
| UP029 | UnnecessaryBuiltinImport | Unnecessary builtin import: `...` | 🛠 |
### pep8-naming (N)
@@ -746,10 +765,14 @@ For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/4.1.1/) on
| ---- | ---- | ------- | --- |
| S101 | AssertUsed | Use of `assert` detected | |
| S102 | ExecUsed | Use of `exec` detected | |
| S103 | BadFilePermissions | `os.chmod` setting a permissive mask `0o777` on file or directory | |
| S104 | HardcodedBindAllInterfaces | Possible binding to all interfaces | |
| S105 | HardcodedPasswordString | Possible hardcoded password: `"..."` | |
| S106 | HardcodedPasswordFuncArg | Possible hardcoded password: `"..."` | |
| S107 | HardcodedPasswordDefault | Possible hardcoded password: `"..."` | |
| S105 | HardcodedPasswordString | Possible hardcoded password: "..." | |
| S106 | HardcodedPasswordFuncArg | Possible hardcoded password: "..." | |
| S107 | HardcodedPasswordDefault | Possible hardcoded password: "..." | |
| S108 | HardcodedTempFile | Probable insecure usage of temporary file or directory: "..." | |
| S324 | HashlibInsecureHashFunction | Probable use of insecure hash functions in `hashlib`: "..." | |
| S506 | UnsafeYAMLLoad | Probable use of unsafe `yaml.load`. Allows instantiation of arbitrary objects. Consider `yaml.safe_load`. | |
### flake8-blind-except (BLE)
@@ -880,6 +903,38 @@ For more, see [flake8-print](https://pypi.org/project/flake8-print/5.0.0/) on Py
| T201 | PrintFound | `print` found | 🛠 |
| T203 | PPrintFound | `pprint` found | 🛠 |
### flake8-pytest-style (PT)
For more, see [flake8-pytest-style](https://pypi.org/project/flake8-pytest-style/1.6.0/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| PT001 | IncorrectFixtureParenthesesStyle | Use `@pytest.fixture()` over `@pytest.fixture` | 🛠 |
| PT002 | FixturePositionalArgs | Configuration for fixture `...` specified via positional args, use kwargs | |
| PT003 | ExtraneousScopeFunction | `scope='function'` is implied in `@pytest.fixture()` | |
| PT004 | MissingFixtureNameUnderscore | Fixture `...` does not return anything, add leading underscore | |
| PT005 | IncorrectFixtureNameUnderscore | Fixture `...` returns a value, remove leading underscore | |
| PT006 | ParametrizeNamesWrongType | Wrong name(s) type in `@pytest.mark.parametrize`, expected `tuple` | 🛠 |
| PT007 | ParametrizeValuesWrongType | Wrong values type in `@pytest.mark.parametrize` expected `list` of `tuple` | |
| PT008 | PatchWithLambda | Use `return_value=` instead of patching with lambda | |
| PT009 | UnittestAssertion | Use a regular assert instead of unittest-style '...' | |
| PT010 | RaisesWithoutException | set the expected exception in `pytest.raises()` | |
| PT011 | RaisesTooBroad | `pytest.raises(...)` is too broad, set the `match` parameter or use a more specific exception | |
| PT012 | RaisesWithMultipleStatements | `pytest.raises()` block should contain a single simple statement | |
| PT013 | IncorrectPytestImport | Found incorrect import of pytest, use simple `import pytest` instead | |
| PT015 | AssertAlwaysFalse | Assertion always fails, replace with `pytest.fail()` | |
| PT016 | FailWithoutMessage | No message passed to `pytest.fail()` | |
| PT017 | AssertInExcept | Found assertion on exception ... in except block, use pytest.raises() instead | |
| PT018 | CompositeAssertion | Assertion should be broken down into multiple parts | |
| PT019 | FixtureParamWithoutValue | Fixture ... without value is injected as parameter, use @pytest.mark.usefixtures instead | |
| PT020 | DeprecatedYieldFixture | `@pytest.yield_fixture` is deprecated, use `@pytest.fixture` | |
| PT021 | FixtureFinalizerCallback | Use `yield` instead of `request.addfinalizer` | |
| PT022 | UselessYieldFixture | No teardown in fixture ..., use `return` instead of `yield` | 🛠 |
| PT023 | IncorrectMarkParenthesesStyle | Use `@pytest.mark....` over `@pytest.mark....()` | 🛠 |
| PT024 | UnnecessaryAsyncioMarkOnFixture | `pytest.mark.asyncio` is unnecessary for fixtures | |
| PT025 | ErroneousUseFixturesOnFixture | `pytest.mark.usefixtures` has no effect on fixtures | |
| PT026 | UseFixturesWithoutParameters | Useless `pytest.mark.usefixtures` without parameters | 🛠 |
### flake8-quotes (Q)
For more, see [flake8-quotes](https://pypi.org/project/flake8-quotes/3.3.1/) on PyPI.
@@ -912,7 +967,19 @@ For more, see [flake8-simplify](https://pypi.org/project/flake8-simplify/0.19.3/
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| SIM101 | DuplicateIsinstanceCall | Multiple `isinstance` calls for `...`, merge into a single call | 🛠 |
| SIM102 | NestedIfStatements | Use a single `if` statement instead of nested `if` statements | |
| SIM105 | UseContextlibSuppress | Use `contextlib.suppress(...)` instead of try-except-pass | |
| SIM107 | ReturnInTryExceptFinally | Don't use `return` in `try`/`except` and `finally` | |
| SIM110 | ConvertLoopToAny | Use `return any(x for x in y)` instead of `for` loop | 🛠 |
| SIM111 | ConvertLoopToAll | Use `return all(x for x in y)` instead of `for` loop | 🛠 |
| SIM117 | MultipleWithStatements | Use a single `with` statement with multiple contexts instead of nested `with` statements | |
| SIM118 | KeyInDict | Use `key in dict` instead of `key in dict.keys()` | 🛠 |
| SIM220 | AAndNotA | Use `False` instead of `... and not ...` | 🛠 |
| SIM221 | AOrNotA | Use `True` instead of `... or not ...` | 🛠 |
| SIM222 | OrTrue | Use `True` instead of `... or True` | 🛠 |
| SIM223 | AndFalse | Use `False` instead of `... and False` | 🛠 |
| SIM300 | YodaConditions | Yoda conditions are discouraged, use `left == right` instead | 🛠 |
### flake8-tidy-imports (TID)
@@ -1008,13 +1075,23 @@ For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) on PyPI.
| PLW0120 | UselessElseOnLoop | Else clause on loop without a break statement, remove the else and de-indent all the code inside it | |
| PLW0602 | GlobalVariableNotAssigned | Using global for `...` but no assignment is done | |
### flake8-pie (PIE)
For more, see [flake8-pie](https://pypi.org/project/flake8-pie/0.16.0/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| PIE790 | NoUnnecessaryPass | Unnecessary `pass` statement | 🛠 |
| PIE794 | DupeClassFieldDefinitions | Class field `...` is defined multiple times | 🛠 |
| PIE807 | PreferListBuiltin | Prefer `list()` over useless lambda | 🛠 |
### Ruff-specific rules (RUF)
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| RUF001 | AmbiguousUnicodeCharacterString | String contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
| RUF002 | AmbiguousUnicodeCharacterDocstring | Docstring contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
| RUF003 | AmbiguousUnicodeCharacterComment | Comment contains ambiguous unicode character '𝐁' (did you mean 'B'?) | |
| RUF003 | AmbiguousUnicodeCharacterComment | Comment contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
| RUF004 | KeywordArgumentBeforeStarArgument | Keyword argument `...` must come after starred arguments | |
| RUF100 | UnusedNOQA | Unused blanket `noqa` directive | 🛠 |
@@ -1270,11 +1347,11 @@ Under those conditions, Ruff implements every rule in Flake8.
Ruff also re-implements some of the most popular Flake8 plugins and related code quality tools
natively, including:
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
- [`autoflake`](https://pypi.org/project/autoflake/) ([#1647](https://github.com/charliermarsh/ruff/issues/1647))
- [`eradicate`](https://pypi.org/project/eradicate/)
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) ([#1646](https://github.com/charliermarsh/ruff/issues/1646))
- [`flake8-blind-except`](https://pypi.org/project/flake8-blind-except/)
- [`flake8-boolean-trap`](https://pypi.org/project/flake8-boolean-trap/)
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
@@ -1287,18 +1364,19 @@ natively, including:
- [`flake8-errmsg`](https://pypi.org/project/flake8-errmsg/)
- [`flake8-implicit-str-concat`](https://pypi.org/project/flake8-implicit-str-concat/)
- [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions)
- [`flake8-pie`](https://pypi.org/project/flake8-pie/) ([#1543](https://github.com/charliermarsh/ruff/issues/1543))
- [`flake8-print`](https://pypi.org/project/flake8-print/)
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
- [`flake8-return`](https://pypi.org/project/flake8-return/)
- [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) (1/37)
- [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) ([#998](https://github.com/charliermarsh/ruff/issues/998))
- [`flake8-super`](https://pypi.org/project/flake8-super/)
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/)
- [`isort`](https://pypi.org/project/isort/)
- [`mccabe`](https://pypi.org/project/mccabe/)
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
- [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/10)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (20/33)
- [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) ([#980](https://github.com/charliermarsh/ruff/issues/980))
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) ([#827](https://github.com/charliermarsh/ruff/issues/827))
- [`yesqa`](https://github.com/asottile/yesqa)
Note that, in some cases, Ruff uses different error code prefixes than would be found in the
@@ -1313,6 +1391,13 @@ Beyond the rule set, Ruff suffers from the following limitations vis-à-vis Flak
2. Flake8 has a plugin architecture and supports writing custom lint rules. (Instead, popular Flake8
plugins are re-implemented in Rust as part of Ruff itself.)
There are a few other minor incompatibilities between Ruff and the originating Flake8 plugins:
- Ruff doesn't implement the "opinionated" lint rules from `flake8-bugbear`.
- Depending on your project structure, Ruff and `isort` can differ in their detection of first-party
code. (This is often solved by modifying the `src` property, e.g., to `src = ["src"]`, if your
code is nested in a `src` directory.)
### How does Ruff compare to Pylint?
At time of writing, Pylint implements 409 total rules, while Ruff implements 224, of which
@@ -1331,7 +1416,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) ([#1646](https://github.com/charliermarsh/ruff/issues/1646))
- [`flake8-blind-except`](https://pypi.org/project/flake8-blind-except/)
- [`flake8-boolean-trap`](https://pypi.org/project/flake8-boolean-trap/)
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
@@ -1344,22 +1429,23 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
- [`flake8-errmsg`](https://pypi.org/project/flake8-errmsg/)
- [`flake8-implicit-str-concat`](https://pypi.org/project/flake8-implicit-str-concat/)
- [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions)
- [`flake8-pie`](https://pypi.org/project/flake8-pie/) ([#1543](https://github.com/charliermarsh/ruff/issues/1543))
- [`flake8-print`](https://pypi.org/project/flake8-print/)
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
- [`flake8-return`](https://pypi.org/project/flake8-return/)
- [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) (1/37)
- [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) ([#998](https://github.com/charliermarsh/ruff/issues/998))
- [`flake8-super`](https://pypi.org/project/flake8-super/)
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/)
- [`mccabe`](https://pypi.org/project/mccabe/)
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
Ruff can also replace [`isort`](https://pypi.org/project/isort/),
[`yesqa`](https://github.com/asottile/yesqa), [`eradicate`](https://pypi.org/project/eradicate/),
[`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/10), and a subset of the rules
implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (20/33).
[`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) ([#980](https://github.com/charliermarsh/ruff/issues/980)), and a subset of the rules
implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) ([#827](https://github.com/charliermarsh/ruff/issues/827)).
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, free to file an Issue.
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, feel free to file an Issue.
### Do I need to install Rust to use Ruff?
@@ -1422,85 +1508,35 @@ Found 3 error(s).
### Does Ruff support NumPy- or Google-style docstrings?
Yes! To enable a specific docstring convention, start by enabling all `pydocstyle` error codes, and
then selectively disabling based on your [preferred convention](https://www.pydocstyle.org/en/latest/error_codes.html#default-conventions).
Yes! To enable specific docstring convention, add the following to your `pyproject.toml`:
For example, if you're coming from `flake8-docstrings`, the following configuration is equivalent to
`--docstring-convention=numpy`:
```toml
[tool.ruff.pydocstyle]
convention = "google" # Accepts: "google", "numpy", or "pep257".
```
For example, if you're coming from `flake8-docstrings`, and your originating configuration uses
`--docstring-convention=numpy`, you'd instead set `convention = "numpy"` in your `pyproject.toml`,
as above.
Alongside `convention`, you'll want to explicitly enable the `D` error code class, like so:
```toml
[tool.ruff]
extend-select = ["D"]
extend-ignore = [
"D107",
"D203",
"D212",
"D213",
"D402",
"D413",
"D415",
"D416",
"D417",
select = [
"D",
]
```
Similarly, the following is equivalent to `--docstring-convention=google`:
```toml
[tool.ruff]
extend-select = ["D"]
extend-ignore = [
"D203",
"D204",
"D213",
"D215",
"D400",
"D404",
"D406",
"D407",
"D408",
"D409",
"D413",
]
```
Similarly, the following is equivalent to `--docstring-convention=pep8`:
```toml
[tool.ruff]
extend-select = ["D"]
extend-ignore = [
"D203",
"D212",
"D213",
"D214",
"D215",
"D404",
"D405",
"D406",
"D407",
"D408",
"D409",
"D410",
"D411",
"D413",
"D415",
"D416",
"D417",
]
```
Note that Ruff _also_ supports a [`convention`](#convention) setting:
```toml
[tool.ruff.pydocstyle]
convention = "google"
```
However, this setting is purely used to implement robust detection of Google and NumPy-style
sections, and thus avoid the [false negatives](https://github.com/PyCQA/pydocstyle/issues/459) seen
in `pydocstyle`; it does not affect which errors are enabled, which is driven by the `select` and
`ignore` settings, as described above.
Setting a `convention` force-disables any rules that are incompatible with that convention, no
matter how they're provided, which avoids accidental incompatibilities and simplifies configuration.
### How can I tell what settings Ruff is using to check my code?
Run `ruff /path/to/code.py --show-settings` to view the resolved settings for a given file.
## Development
@@ -1669,7 +1705,6 @@ Summary
<!-- Sections automatically generated by `cargo dev generate-options`. -->
<!-- Begin auto-generated options sections. -->
#### [`allowed-confusables`](#allowed-confusables)
A list of allowed "confusable" Unicode characters to ignore when
@@ -2200,6 +2235,27 @@ target-version = "py37"
---
#### [`task-tags`](#task-tags)
A list of task tags to recognize (e.g., "TODO", "FIXME", "XXX").
Comments starting with these tags will be ignored by commented-out code
detection (`ERA`), and skipped by line-length checks (`E501`) if
`ignore-overlong-task-comments` is set to `true`.
**Default value**: `["TODO", "FIXME", "XXX"]`
**Type**: `Vec<String>`
**Example usage**:
```toml
[tool.ruff]
task-tags = ["HACK"]
```
---
#### [`unfixable`](#unfixable)
A list of check code prefixes to consider un-autofix-able.
@@ -2314,6 +2370,43 @@ suppress-none-returning = true
---
### `flake8-bandit`
#### [`hardcoded-tmp-directory`](#hardcoded-tmp-directory)
A list of directories to consider temporary.
**Default value**: `["/tmp", "/var/tmp", "/dev/shm"]`
**Type**: `Vec<String>`
**Example usage**:
```toml
[tool.ruff.flake8-bandit]
hardcoded-tmp-directory = ["/foo/bar"]
```
---
#### [`hardcoded-tmp-directory-extend`](#hardcoded-tmp-directory-extend)
A list of directories to consider temporary, in addition to those
specified by `hardcoded-tmp-directory`.
**Default value**: `[]`
**Type**: `Vec<String>`
**Example usage**:
```toml
[tool.ruff.flake8-bandit]
extend-hardcoded-tmp-directory = ["/foo/bar"]
```
---
### `flake8-bugbear`
#### [`extend-immutable-calls`](#extend-immutable-calls)
@@ -2369,9 +2462,10 @@ the `extend_aliases` option.
```toml
[tool.ruff.flake8-import-conventions]
[tool.ruff.flake8-import-conventions.aliases]
# Declare the default aliases.
altair = "alt"
matplotlib.pyplot = "plt"
"matplotlib.pyplot" = "plt"
numpy = "np"
pandas = "pd"
seaborn = "sns"
@@ -2392,12 +2486,164 @@ will be added to the `aliases` mapping.
```toml
[tool.ruff.flake8-import-conventions]
[tool.ruff.flake8-import-conventions.extend-aliases]
# Declare a custom alias for the `matplotlib` module.
"dask.dataframe" = "dd"
```
---
### `flake8-pytest-style`
#### [`fixture-parentheses`](#fixture-parentheses)
Boolean flag specifying whether `@pytest.fixture()` without parameters
should have parentheses. If the option is set to `true` (the
default), `@pytest.fixture()` is valid and `@pytest.fixture` is an
error. If set to `false`, `@pytest.fixture` is valid and
`@pytest.fixture()` is an error.
**Default value**: `true`
**Type**: `bool`
**Example usage**:
```toml
[tool.ruff.flake8-pytest-style]
fixture-parentheses = true
```
---
#### [`mark-parentheses`](#mark-parentheses)
Boolean flag specifying whether `@pytest.mark.foo()` without parameters
should have parentheses. If the option is set to `true` (the
default), `@pytest.mark.foo()` is valid and `@pytest.mark.foo` is an
error. If set to `false`, `@pytest.fixture` is valid and
`@pytest.mark.foo()` is an error.
**Default value**: `true`
**Type**: `bool`
**Example usage**:
```toml
[tool.ruff.flake8-pytest-style]
mark-parentheses = true
```
---
#### [`parametrize-names-type`](#parametrize-names-type)
Expected type for multiple argument names in `@pytest.mark.parametrize`.
The following values are supported:
* `csv` — a comma-separated list, e.g.
`@pytest.mark.parametrize('name1,name2', ...)`
* `tuple` (default) — e.g. `@pytest.mark.parametrize(('name1', 'name2'),
...)`
* `list` — e.g. `@pytest.mark.parametrize(['name1', 'name2'], ...)`
**Default value**: `tuple`
**Type**: `ParametrizeNameType`
**Example usage**:
```toml
[tool.ruff.flake8-pytest-style]
parametrize-names-type = "list"
```
---
#### [`parametrize-values-row-type`](#parametrize-values-row-type)
Expected type for each row of values in `@pytest.mark.parametrize` in
case of multiple parameters. The following values are supported:
* `tuple` (default) — e.g. `@pytest.mark.parametrize(('name1', 'name2'),
[(1, 2), (3, 4)])`
* `list` — e.g. `@pytest.mark.parametrize(('name1', 'name2'), [[1, 2],
[3, 4]])`
**Default value**: `tuple`
**Type**: `ParametrizeValuesRowType`
**Example usage**:
```toml
[tool.ruff.flake8-pytest-style]
parametrize-values-row-type = "list"
```
---
#### [`parametrize-values-type`](#parametrize-values-type)
Expected type for the list of values rows in `@pytest.mark.parametrize`.
The following values are supported:
* `tuple` — e.g. `@pytest.mark.parametrize('name', (1, 2, 3))`
* `list` (default) — e.g. `@pytest.mark.parametrize('name', [1, 2, 3])`
**Default value**: `list`
**Type**: `ParametrizeValuesType`
**Example usage**:
```toml
[tool.ruff.flake8-pytest-style]
parametrize-values-type = "tuple"
```
---
#### [`raises-extend-require-match-for`](#raises-extend-require-match-for)
List of additional exception names that require a match= parameter in a
`pytest.raises()` call. This extends the default list of exceptions
that require a match= parameter.
This option is useful if you want to extend the default list of
exceptions that require a match= parameter without having to specify
the entire list.
Note that this option does not remove any exceptions from the default
list.
**Default value**: `[]`
**Type**: `Vec<String>`
**Example usage**:
```toml
[tool.ruff.flake8-pytest-style]
raises-extend-require-match-for = ["requests.RequestException"]
```
---
#### [`raises-require-match-for`](#raises-require-match-for)
List of exception names that require a match= parameter in a
`pytest.raises()` call.
**Default value**: `["BaseException", "Exception", "ValueError", "OSError", "IOError", "EnvironmentError", "socket.error"]`
**Type**: `Vec<String>`
**Example usage**:
```toml
[tool.ruff.flake8-pytest-style]
raises-require-match-for = ["requests.RequestException"]
```
---
### `flake8-quotes`
#### [`avoid-escape`](#avoid-escape)
@@ -2659,6 +2905,24 @@ known-third-party = ["src"]
---
#### [`order-by-type`](#order-by-type)
Order imports by type, which is determined by case, in addition to
alphabetically.
**Default value**: `true`
**Type**: `bool`
**Example usage**:
```toml
[tool.ruff.isort]
order-by-type = true
```
---
#### [`single-line-exclusions`](#single-line-exclusions)
One or more modules to exclude from the single line rule.
@@ -2777,13 +3041,33 @@ staticmethod-decorators = ["staticmethod", "stcmthd"]
---
### `pycodestyle`
#### [`ignore-overlong-task-comments`](#ignore-overlong-task-comments)
Whether or not line-length checks (`E501`) should be triggered for
comments starting with `task-tags` (by default: ["TODO", "FIXME",
and "XXX"]).
**Default value**: `false`
**Type**: `bool`
**Example usage**:
```toml
[tool.ruff.pycodestyle]
ignore-overlong-task-comments = true
```
---
### `pydocstyle`
#### [`convention`](#convention)
Whether to use Google-style or Numpy-style conventions when detecting
docstring sections. By default, conventions will be inferred from
the available sections.
Whether to use Google-style or NumPy-style conventions or the PEP257
defaults when analyzing docstring sections.
**Default value**: `None`

8
assets/badge/v0.json Normal file
View File

@@ -0,0 +1,8 @@
{
"label": "",
"message": "Ruff",
"logoSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"128\" height=\"128\"><path d=\"M115.36 61.84 70.22 50.49 114.45 2.4a1.222 1.222 0 0 0-1.54-1.87L12.3 61.98c-.41.25-.64.72-.57 1.2.06.48.4.87.87 1.01l45.07 13.25-44.29 48.16c-.42.46-.44 1.15-.04 1.61.24.29.58.44.94.44.22 0 .45-.06.65-.19l100.78-63.41c.42-.26.64-.75.56-1.22-.08-.49-.43-.88-.91-.99z\" style=\"fill:#fcc21b\"/></svg>",
"logoWidth": 10,
"labelColor": "grey",
"color": "#E15759"
}

View File

@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8_to_ruff"
version = "0.0.201"
version = "0.0.212"
dependencies = [
"anyhow",
"clap",
@@ -1975,7 +1975,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.201"
version = "0.0.212"
dependencies = [
"anyhow",
"bincode",

View File

@@ -1,6 +1,6 @@
[package]
name = "flake8-to-ruff"
version = "0.0.201-dev.0"
version = "0.0.212-dev.0"
edition = "2021"
[lib]

View File

@@ -0,0 +1,43 @@
[flake8]
# Exclude the grpc generated code
exclude = ./manim/grpc/gen/*
max-complexity = 15
max-line-length = 88
statistics = True
# Prevents some flake8-rst-docstrings errors
rst-roles = attr,class,func,meth,mod,obj,ref,doc,exc
rst-directives = manim, SEEALSO, seealso
docstring-convention=numpy
select = A,A00,B,B9,C4,C90,D,E,F,F,PT,RST,SIM,W
# General Compatibility
extend-ignore = E203, W503, D202, D212, D213, D404
# Misc
F401, F403, F405, F841, E501, E731, E402, F811, F821,
# Plug-in: flake8-builtins
A001, A002, A003,
# Plug-in: flake8-bugbear
B006, B007, B008, B009, B010, B903, B950,
# Plug-in: flake8-simplify
SIM105, SIM106, SIM119,
# Plug-in: flake8-comprehensions
C901
# Plug-in: flake8-pytest-style
PT001, PT004, PT006, PT011, PT018, PT022, PT023,
# Plug-in: flake8-docstrings
D100, D101, D102, D103, D104, D105, D106, D107,
D200, D202, D204, D205, D209,
D301,
D400, D401, D402, D403, D405, D406, D407, D409, D411, D412, D414,
# Plug-in: flake8-rst-docstrings
RST201, RST203, RST210, RST212, RST213, RST215,
RST301, RST303,

View File

@@ -1,15 +1,18 @@
use std::collections::{BTreeSet, HashMap};
use anyhow::Result;
use ruff::checks_gen::CheckCodePrefix;
use ruff::flake8_pytest_style::types::{
ParametrizeNameType, ParametrizeValuesRowType, ParametrizeValuesType,
};
use ruff::flake8_quotes::settings::Quote;
use ruff::flake8_tidy_imports::settings::Strictness;
use ruff::pydocstyle::settings::Convention;
use ruff::registry::CheckCodePrefix;
use ruff::settings::options::Options;
use ruff::settings::pyproject::Pyproject;
use ruff::{
flake8_annotations, flake8_bugbear, flake8_errmsg, flake8_quotes, flake8_tidy_imports, mccabe,
pep8_naming, pydocstyle,
flake8_annotations, flake8_bugbear, flake8_errmsg, flake8_pytest_style, flake8_quotes,
flake8_tidy_imports, mccabe, pep8_naming, pydocstyle,
};
use crate::black::Black;
@@ -49,6 +52,19 @@ pub fn convert(
}
}
// Infer plugins, if not provided.
let plugins = plugins.unwrap_or_else(|| {
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()
});
// Check if the user has specified a `select`. If not, we'll add our own
// default `select`, and populate it based on user plugins.
let mut select = flake8
@@ -58,22 +74,7 @@ pub fn convert(
.as_ref()
.map(|value| BTreeSet::from_iter(parser::parse_prefix_codes(value)))
})
.unwrap_or_else(|| {
plugin::resolve_select(
flake8,
&plugins.unwrap_or_else(|| {
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()
}),
)
});
.unwrap_or_else(|| plugin::resolve_select(&plugins));
let mut ignore = flake8
.get("ignore")
.and_then(|value| {
@@ -88,6 +89,7 @@ pub fn convert(
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_pytest_style = flake8_pytest_style::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();
@@ -205,7 +207,8 @@ pub fn convert(
"docstring-convention" => match value.trim() {
"google" => pydocstyle.convention = Some(Convention::Google),
"numpy" => pydocstyle.convention = Some(Convention::Numpy),
"pep257" | "all" => pydocstyle.convention = None,
"pep257" => pydocstyle.convention = Some(Convention::Pep257),
"all" => pydocstyle.convention = None,
_ => eprintln!("Unexpected '{key}' value: {value}"),
},
// mccabe
@@ -222,6 +225,66 @@ pub fn convert(
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
}
}
// flake8-pytest-style
"pytest-fixture-no-parentheses" | "pytest_fixture_no_parentheses " => {
match parser::parse_bool(value.as_ref()) {
Ok(bool) => flake8_pytest_style.fixture_parentheses = Some(!bool),
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
}
}
"pytest-parametrize-names-type" | "pytest_parametrize_names_type" => {
match value.trim() {
"csv" => {
flake8_pytest_style.parametrize_names_type =
Some(ParametrizeNameType::CSV);
}
"tuple" => {
flake8_pytest_style.parametrize_names_type =
Some(ParametrizeNameType::Tuple);
}
"list" => {
flake8_pytest_style.parametrize_names_type =
Some(ParametrizeNameType::List);
}
_ => eprintln!("Unexpected '{key}' value: {value}"),
}
}
"pytest-parametrize-values-type" | "pytest_parametrize_values_type" => {
match value.trim() {
"tuple" => {
flake8_pytest_style.parametrize_values_type =
Some(ParametrizeValuesType::Tuple);
}
"list" => {
flake8_pytest_style.parametrize_values_type =
Some(ParametrizeValuesType::List);
}
_ => eprintln!("Unexpected '{key}' value: {value}"),
}
}
"pytest-parametrize-values-row-type" | "pytest_parametrize_values_row_type" => {
match value.trim() {
"tuple" => {
flake8_pytest_style.parametrize_values_row_type =
Some(ParametrizeValuesRowType::Tuple);
}
"list" => {
flake8_pytest_style.parametrize_values_row_type =
Some(ParametrizeValuesRowType::List);
}
_ => eprintln!("Unexpected '{key}' value: {value}"),
}
}
"pytest-raises-require-match-for" | "pytest_raises_require_match_for" => {
flake8_pytest_style.raises_require_match_for =
Some(parser::parse_strings(value.as_ref()));
}
"pytest-mark-no-parentheses" | "pytest_mark_no_parentheses" => {
match parser::parse_bool(value.as_ref()) {
Ok(bool) => flake8_pytest_style.mark_parentheses = Some(!bool),
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
}
}
// Unknown
_ => eprintln!("Skipping unsupported property: {key}"),
}
@@ -240,6 +303,9 @@ pub fn convert(
if flake8_errmsg != flake8_errmsg::settings::Options::default() {
options.flake8_errmsg = Some(flake8_errmsg);
}
if flake8_pytest_style != flake8_pytest_style::settings::Options::default() {
options.flake8_pytest_style = Some(flake8_pytest_style);
}
if flake8_quotes != flake8_quotes::settings::Options::default() {
options.flake8_quotes = Some(flake8_quotes);
}
@@ -278,8 +344,8 @@ mod tests {
use std::collections::HashMap;
use anyhow::Result;
use ruff::checks_gen::CheckCodePrefix;
use ruff::pydocstyle::settings::Convention;
use ruff::registry::CheckCodePrefix;
use ruff::settings::options::Options;
use ruff::settings::pyproject::Pyproject;
use ruff::{flake8_quotes, pydocstyle};
@@ -324,10 +390,13 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
task_tags: None,
update_check: None,
flake8_annotations: None,
flake8_bandit: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_pytest_style: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
@@ -335,6 +404,7 @@ mod tests {
isort: None,
mccabe: None,
pep8_naming: None,
pycodestyle: None,
pydocstyle: None,
pyupgrade: None,
});
@@ -383,10 +453,13 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
task_tags: None,
update_check: None,
flake8_annotations: None,
flake8_bandit: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_pytest_style: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
@@ -394,6 +467,7 @@ mod tests {
isort: None,
mccabe: None,
pep8_naming: None,
pycodestyle: None,
pydocstyle: None,
pyupgrade: None,
});
@@ -442,10 +516,13 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
task_tags: None,
update_check: None,
flake8_annotations: None,
flake8_bandit: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_pytest_style: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
@@ -453,6 +530,7 @@ mod tests {
isort: None,
mccabe: None,
pep8_naming: None,
pycodestyle: None,
pydocstyle: None,
pyupgrade: None,
});
@@ -501,10 +579,13 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
task_tags: None,
update_check: None,
flake8_annotations: None,
flake8_bandit: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_pytest_style: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
@@ -512,6 +593,7 @@ mod tests {
isort: None,
mccabe: None,
pep8_naming: None,
pycodestyle: None,
pydocstyle: None,
pyupgrade: None,
});
@@ -560,10 +642,13 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
task_tags: None,
update_check: None,
flake8_annotations: None,
flake8_bandit: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_pytest_style: None,
flake8_quotes: Some(flake8_quotes::settings::Options {
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
multiline_quotes: None,
@@ -576,6 +661,7 @@ mod tests {
isort: None,
mccabe: None,
pep8_naming: None,
pycodestyle: None,
pydocstyle: None,
pyupgrade: None,
});
@@ -619,42 +705,7 @@ mod tests {
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::D100,
CheckCodePrefix::D101,
CheckCodePrefix::D102,
CheckCodePrefix::D103,
CheckCodePrefix::D104,
CheckCodePrefix::D105,
CheckCodePrefix::D106,
CheckCodePrefix::D200,
CheckCodePrefix::D201,
CheckCodePrefix::D202,
CheckCodePrefix::D204,
CheckCodePrefix::D205,
CheckCodePrefix::D206,
CheckCodePrefix::D207,
CheckCodePrefix::D208,
CheckCodePrefix::D209,
CheckCodePrefix::D210,
CheckCodePrefix::D211,
CheckCodePrefix::D214,
CheckCodePrefix::D215,
CheckCodePrefix::D300,
CheckCodePrefix::D301,
CheckCodePrefix::D400,
CheckCodePrefix::D403,
CheckCodePrefix::D404,
CheckCodePrefix::D405,
CheckCodePrefix::D406,
CheckCodePrefix::D407,
CheckCodePrefix::D408,
CheckCodePrefix::D409,
CheckCodePrefix::D410,
CheckCodePrefix::D411,
CheckCodePrefix::D412,
CheckCodePrefix::D414,
CheckCodePrefix::D418,
CheckCodePrefix::D419,
CheckCodePrefix::D,
CheckCodePrefix::E,
CheckCodePrefix::F,
CheckCodePrefix::W,
@@ -663,10 +714,13 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
task_tags: None,
update_check: None,
flake8_annotations: None,
flake8_bandit: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_pytest_style: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
@@ -674,6 +728,7 @@ mod tests {
isort: None,
mccabe: None,
pep8_naming: None,
pycodestyle: None,
pydocstyle: Some(pydocstyle::settings::Options {
convention: Some(Convention::Numpy),
}),
@@ -725,10 +780,13 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
task_tags: None,
update_check: None,
flake8_annotations: None,
flake8_bandit: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_pytest_style: None,
flake8_quotes: Some(flake8_quotes::settings::Options {
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
multiline_quotes: None,
@@ -741,6 +799,7 @@ mod tests {
isort: None,
mccabe: None,
pep8_naming: None,
pycodestyle: None,
pydocstyle: None,
pyupgrade: None,
});

View File

@@ -3,8 +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::registry::{CheckCodePrefix, PREFIX_REDIRECTS};
use ruff::settings::types::PatternPrefixPair;
use rustc_hash::FxHashMap;
@@ -128,7 +127,7 @@ fn tokenize_files_to_codes_mapping(value: &str) -> Vec<Token> {
if mat.start() == 0 {
tokens.push(Token {
token_name,
src: mat.as_str().to_string().trim().to_string(),
src: mat.as_str().trim().to_string(),
});
i += mat.end();
break;
@@ -201,7 +200,7 @@ pub fn collect_per_file_ignores(
#[cfg(test)]
mod tests {
use anyhow::Result;
use ruff::checks_gen::CheckCodePrefix;
use ruff::registry::CheckCodePrefix;
use ruff::settings::types::PatternPrefixPair;
use crate::parser::{parse_files_to_codes_mapping, parse_prefix_codes, parse_strings};

View File

@@ -3,7 +3,7 @@ use std::fmt;
use std::str::FromStr;
use anyhow::anyhow;
use ruff::checks_gen::CheckCodePrefix;
use ruff::registry::CheckCodePrefix;
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Plugin {
@@ -20,6 +20,7 @@ pub enum Plugin {
Flake8ErrMsg,
Flake8ImplicitStrConcat,
Flake8Print,
Flake8PytestStyle,
Flake8Quotes,
Flake8Return,
Flake8Simplify,
@@ -48,6 +49,7 @@ impl FromStr for Plugin {
"flake8-errmsg" => Ok(Plugin::Flake8ErrMsg),
"flake8-implicit-str-concat" => Ok(Plugin::Flake8ImplicitStrConcat),
"flake8-print" => Ok(Plugin::Flake8Print),
"flake8-pytest-style" => Ok(Plugin::Flake8PytestStyle),
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
"flake8-return" => Ok(Plugin::Flake8Return),
"flake8-simplify" => Ok(Plugin::Flake8Simplify),
@@ -80,6 +82,7 @@ impl fmt::Debug for Plugin {
Plugin::Flake8ErrMsg => "flake8-errmsg",
Plugin::Flake8ImplicitStrConcat => "flake8-implicit-str-concat",
Plugin::Flake8Print => "flake8-print",
Plugin::Flake8PytestStyle => "flake8-pytest-style",
Plugin::Flake8Quotes => "flake8-quotes",
Plugin::Flake8Return => "flake8-return",
Plugin::Flake8Simplify => "flake8-simplify",
@@ -94,7 +97,7 @@ impl fmt::Debug for Plugin {
}
impl Plugin {
pub fn default(&self) -> CheckCodePrefix {
pub fn prefix(&self) -> CheckCodePrefix {
match self {
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
Plugin::Flake8Bandit => CheckCodePrefix::S,
@@ -111,6 +114,7 @@ impl Plugin {
Plugin::Flake8ErrMsg => CheckCodePrefix::EM,
Plugin::Flake8ImplicitStrConcat => CheckCodePrefix::ISC,
Plugin::Flake8Print => CheckCodePrefix::T2,
Plugin::Flake8PytestStyle => CheckCodePrefix::PT,
Plugin::Flake8Quotes => CheckCodePrefix::Q,
Plugin::Flake8Return => CheckCodePrefix::RET,
Plugin::Flake8Simplify => CheckCodePrefix::SIM,
@@ -121,53 +125,12 @@ impl Plugin {
Plugin::Pyupgrade => CheckCodePrefix::UP,
}
}
pub fn select(&self, flake8: &HashMap<String, Option<String>>) -> Vec<CheckCodePrefix> {
match self {
Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN],
Plugin::Flake8Bandit => vec![CheckCodePrefix::S],
Plugin::Flake8BlindExcept => vec![CheckCodePrefix::BLE],
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.
for key in ["docstring-convention", "docstring_convention"] {
if let Some(Some(value)) = flake8.get(key) {
match DocstringConvention::from_str(value) {
Ok(convention) => return convention.select(),
Err(e) => {
eprintln!("Unexpected '{key}' value: {value} ({e}");
return vec![];
}
}
}
}
// Default to PEP8.
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::TID],
Plugin::McCabe => vec![CheckCodePrefix::C9],
Plugin::PandasVet => vec![CheckCodePrefix::PD],
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
Plugin::Pyupgrade => vec![CheckCodePrefix::UP],
}
}
}
pub enum DocstringConvention {
All,
PEP8,
NumPy,
Pep257,
Numpy,
Google,
}
@@ -177,168 +140,14 @@ impl FromStr for DocstringConvention {
fn from_str(string: &str) -> Result<Self, Self::Err> {
match string {
"all" => Ok(DocstringConvention::All),
"pep8" => Ok(DocstringConvention::PEP8),
"numpy" => Ok(DocstringConvention::NumPy),
"pep257" => Ok(DocstringConvention::Pep257),
"numpy" => Ok(DocstringConvention::Numpy),
"google" => Ok(DocstringConvention::Google),
_ => Err(anyhow!("Unknown docstring convention: {string}")),
}
}
}
impl DocstringConvention {
fn select(&self) -> Vec<CheckCodePrefix> {
match self {
DocstringConvention::All => vec![CheckCodePrefix::D],
DocstringConvention::PEP8 => vec![
// All errors except D203, D212, D213, D214, D215, D404, D405, D406, D407, D408,
// D409, D410, D411, D413, D415, D416 and D417.
CheckCodePrefix::D100,
CheckCodePrefix::D101,
CheckCodePrefix::D102,
CheckCodePrefix::D103,
CheckCodePrefix::D104,
CheckCodePrefix::D105,
CheckCodePrefix::D106,
CheckCodePrefix::D107,
CheckCodePrefix::D200,
CheckCodePrefix::D201,
CheckCodePrefix::D202,
// CheckCodePrefix::D203,
CheckCodePrefix::D204,
CheckCodePrefix::D205,
CheckCodePrefix::D206,
CheckCodePrefix::D207,
CheckCodePrefix::D208,
CheckCodePrefix::D209,
CheckCodePrefix::D210,
CheckCodePrefix::D211,
// CheckCodePrefix::D212,
// CheckCodePrefix::D213,
// CheckCodePrefix::D214,
// CheckCodePrefix::D215,
CheckCodePrefix::D300,
CheckCodePrefix::D301,
CheckCodePrefix::D400,
CheckCodePrefix::D402,
CheckCodePrefix::D403,
// CheckCodePrefix::D404,
// CheckCodePrefix::D405,
// CheckCodePrefix::D406,
// CheckCodePrefix::D407,
// CheckCodePrefix::D408,
// CheckCodePrefix::D409,
// CheckCodePrefix::D410,
// CheckCodePrefix::D411,
CheckCodePrefix::D412,
// CheckCodePrefix::D413,
CheckCodePrefix::D414,
// CheckCodePrefix::D415,
// CheckCodePrefix::D416,
// CheckCodePrefix::D417,
CheckCodePrefix::D418,
CheckCodePrefix::D419,
],
DocstringConvention::NumPy => vec![
// All errors except D107, D203, D212, D213, D402, D413, D415, D416, and D417.
CheckCodePrefix::D100,
CheckCodePrefix::D101,
CheckCodePrefix::D102,
CheckCodePrefix::D103,
CheckCodePrefix::D104,
CheckCodePrefix::D105,
CheckCodePrefix::D106,
// CheckCodePrefix::D107,
CheckCodePrefix::D200,
CheckCodePrefix::D201,
CheckCodePrefix::D202,
// CheckCodePrefix::D203,
CheckCodePrefix::D204,
CheckCodePrefix::D205,
CheckCodePrefix::D206,
CheckCodePrefix::D207,
CheckCodePrefix::D208,
CheckCodePrefix::D209,
CheckCodePrefix::D210,
CheckCodePrefix::D211,
// CheckCodePrefix::D212,
// CheckCodePrefix::D213,
CheckCodePrefix::D214,
CheckCodePrefix::D215,
CheckCodePrefix::D300,
CheckCodePrefix::D301,
CheckCodePrefix::D400,
// CheckCodePrefix::D402,
CheckCodePrefix::D403,
CheckCodePrefix::D404,
CheckCodePrefix::D405,
CheckCodePrefix::D406,
CheckCodePrefix::D407,
CheckCodePrefix::D408,
CheckCodePrefix::D409,
CheckCodePrefix::D410,
CheckCodePrefix::D411,
CheckCodePrefix::D412,
// CheckCodePrefix::D413,
CheckCodePrefix::D414,
// CheckCodePrefix::D415,
// CheckCodePrefix::D416,
// CheckCodePrefix::D417,
CheckCodePrefix::D418,
CheckCodePrefix::D419,
],
DocstringConvention::Google => vec![
// All errors except D203, D204, D213, D215, D400, D401, D404, D406, D407, D408,
// D409 and D413.
CheckCodePrefix::D100,
CheckCodePrefix::D101,
CheckCodePrefix::D102,
CheckCodePrefix::D103,
CheckCodePrefix::D104,
CheckCodePrefix::D105,
CheckCodePrefix::D106,
CheckCodePrefix::D107,
CheckCodePrefix::D200,
CheckCodePrefix::D201,
CheckCodePrefix::D202,
// CheckCodePrefix::D203,
// CheckCodePrefix::D204,
CheckCodePrefix::D205,
CheckCodePrefix::D206,
CheckCodePrefix::D207,
CheckCodePrefix::D208,
CheckCodePrefix::D209,
CheckCodePrefix::D210,
CheckCodePrefix::D211,
CheckCodePrefix::D212,
// CheckCodePrefix::D213,
CheckCodePrefix::D214,
// CheckCodePrefix::D215,
CheckCodePrefix::D300,
CheckCodePrefix::D301,
// CheckCodePrefix::D400,
CheckCodePrefix::D402,
CheckCodePrefix::D403,
// CheckCodePrefix::D404,
CheckCodePrefix::D405,
// CheckCodePrefix::D406,
// CheckCodePrefix::D407,
// CheckCodePrefix::D408,
// CheckCodePrefix::D409,
CheckCodePrefix::D410,
CheckCodePrefix::D411,
CheckCodePrefix::D412,
// CheckCodePrefix::D413,
CheckCodePrefix::D414,
CheckCodePrefix::D415,
CheckCodePrefix::D416,
CheckCodePrefix::D417,
CheckCodePrefix::D418,
CheckCodePrefix::D419,
],
}
}
}
/// Infer the enabled plugins based on user-provided options.
///
/// For example, if the user specified a `mypy-init-return` setting, we should
@@ -394,6 +203,25 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
"eradicate-whitelist-extend" | "eradicate_whitelist_extend" => {
plugins.insert(Plugin::Flake8Eradicate);
}
// flake8-pytest-style
"pytest-fixture-no-parentheses" | "pytest_fixture_no_parentheses " => {
plugins.insert(Plugin::Flake8PytestStyle);
}
"pytest-parametrize-names-type" | "pytest_parametrize_names_type" => {
plugins.insert(Plugin::Flake8PytestStyle);
}
"pytest-parametrize-values-type" | "pytest_parametrize_values_type" => {
plugins.insert(Plugin::Flake8PytestStyle);
}
"pytest-parametrize-values-row-type" | "pytest_parametrize_values_row_type" => {
plugins.insert(Plugin::Flake8PytestStyle);
}
"pytest-raises-require-match-for" | "pytest_raises_require_match_for" => {
plugins.insert(Plugin::Flake8PytestStyle);
}
"pytest-mark-no-parentheses" | "pytest_mark_no_parentheses" => {
plugins.insert(Plugin::Flake8PytestStyle);
}
// flake8-quotes
"quotes" | "inline-quotes" | "inline_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
@@ -469,7 +297,7 @@ pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin
if prefix
.codes()
.iter()
.any(|code| plugin.default().codes().contains(code))
.any(|code| plugin.prefix().codes().contains(code))
{
return true;
}
@@ -480,18 +308,9 @@ pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin
}
/// Resolve the set of enabled `CheckCodePrefix` values for the given plugins.
pub fn resolve_select(
flake8: &HashMap<String, Option<String>>,
plugins: &[Plugin],
) -> BTreeSet<CheckCodePrefix> {
// Include default Pyflakes and pycodestyle checks.
let mut select = BTreeSet::from([CheckCodePrefix::E, CheckCodePrefix::F, CheckCodePrefix::W]);
// Add prefix codes for every plugin.
for plugin in plugins {
select.extend(plugin.select(flake8));
}
pub fn resolve_select(plugins: &[Plugin]) -> BTreeSet<CheckCodePrefix> {
let mut select = BTreeSet::from([CheckCodePrefix::F, CheckCodePrefix::E, CheckCodePrefix::W]);
select.extend(plugins.iter().map(Plugin::prefix));
select
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,22 @@
== Flake8 License (MIT) ==
Copyright (C) 2011-2013 Tarek Ziade <tarek@ziade.org>
Copyright (C) 2012-2016 Ian Cordasco <graffatcolmingov@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

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

View File

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

View File

@@ -0,0 +1,23 @@
Copyright © 2013 Florent Xicluna <florent.xicluna@gmail.com>
Licensed under the terms of the Expat License
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,25 @@
Copyright © 2006-2009 Johann C. Rocholl <johann@rocholl.net>
Copyright © 2009-2014 Florent Xicluna <florent.xicluna@gmail.com>
Copyright © 2014-2020 Ian Lee <IanLee1521@gmail.com>
Licensed under the terms of the Expat License
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,23 @@
Copyright (c) 2012 GreenSteam, <http://greensteam.dk/>
Copyright (c) 2014-2020 Amir Rachum, <http://amir.rachum.com/>
Copyright (c) 2020 Sambhav Kothari, <https://github.com/samj1912>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Nikita Sobolev
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.

View File

@@ -0,0 +1,17 @@
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.

View File

@@ -11,20 +11,12 @@
<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>"
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>

View File

@@ -98,7 +98,11 @@ export default function Editor() {
}, []);
return (
<main className={"h-full w-full flex flex-auto"}>
<main
className={
"h-full w-full flex flex-auto bg-ayu-background dark:bg-ayu-background-dark"
}
>
<Header
edit={edit}
tab={tab}

View File

@@ -1,4 +1,5 @@
import classNames from "classnames";
import RepoButton from "./RepoButton";
import ThemeButton from "./ThemeButton";
import ShareButton from "./ShareButton";
import { Theme } from "./theme";
@@ -44,8 +45,6 @@ export default function Header({
"border-b",
"border-gray-200",
"dark:border-gray-800",
"bg-ayu-background",
"dark:bg-ayu-background-dark",
)}
>
<div className="flex space-x-5">
@@ -85,14 +84,19 @@ export default function Header({
/>
Settings
</button>
</div>
<div className={"flex items-center min-w-0"}>
{version ? (
<div className={"flex items-center"}>
<div className={"hidden sm: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" />
<RepoButton />
<div className="hidden sm:block mx-6 lg:mx-4 w-px h-6 bg-gray-200 dark:bg-gray-700" />
<div className="hidden sm:block">
<ShareButton key={edit} onShare={onShare} />
</div>
<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>

View File

@@ -0,0 +1,27 @@
export default function RepoButton() {
return (
<a
className={"hover:text-ayu-accent/70 text-ayu-accent"}
href={"https://github.com/charliermarsh/ruff"}
target={"_blank"}
rel={"noreferrer"}
>
<svg
xmlns={"http://www.w3.org/2000/svg"}
width={"24"}
height={"24"}
viewBox={"0 0 16 16"}
fill={"none"}
>
<path
fillRule={"evenodd"}
clipRule={"evenodd"}
d={
"M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z"
}
fill={"currentColor"}
/>
</svg>
</a>
);
}

View File

@@ -28,7 +28,7 @@ export default function ShareButton({ onShare }: { onShare?: () => void }) {
) : (
<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"
className="relative flex-none rounded-md text-sm font-semibold leading-6 py-1.5 px-3 enabled:hover:bg-ayu-accent/70 bg-ayu-accent text-white shadow-sm dark:shadow-highlight/20 disabled:opacity-50"
disabled={!onShare || copied}
onClick={
onShare

View File

@@ -56,7 +56,9 @@ export default function SourceEditor({
.filter((check) => position.startLineNumber === check.location.row)
.filter((check) => check.fix)
.map((check) => ({
title: `Fix ${check.code}`,
title: check.fix
? `${check.code}: ${check.fix.message}` ?? `Fix ${check.code}`
: "Autofix",
id: `fix-${check.code}`,
kind: "quickfix",
edit: check.fix

View File

@@ -7,7 +7,7 @@ module.exports = {
theme: {
extend: {
colors: {
"ayu-accent": "#f07171",
"ayu-accent": "#fa8d3e",
"ayu-background": {
DEFAULT: "#f8f9fa",
dark: "#0b0e14",

View File

@@ -1,5 +1,19 @@
[build-system]
requires = ["maturin>=0.14,<0.15"]
build-backend = "maturin"
[project]
name = "ruff"
version = "0.0.212"
description = "An extremely fast Python linter, written in Rust."
authors = [
{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" },
]
maintainers = [
{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" },
]
requires-python = ">=3.7"
license = { file = "LICENSE" }
keywords = ["automation", "flake8", "pycodestyle", "pyflakes", "pylint", "clippy"]
classifiers = [
"Development Status :: 3 - Alpha",
@@ -17,30 +31,14 @@ classifiers = [
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Quality Assurance",
]
author = "Charlie Marsh"
author_email = "charlie.r.marsh@gmail.com"
description = "An extremely fast Python linter, written in Rust."
requires-python = ">=3.7"
[project.urls]
repository = "https://github.com/charliermarsh/ruff"
[build-system]
requires = ["maturin>=0.14,<0.15"]
build-backend = "maturin"
urls = { repository = "https://github.com/charliermarsh/ruff" }
[tool.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"
[tool.setuptools]
license-files = [
"LICENSE",
"licenses/*",
]

View File

@@ -0,0 +1,22 @@
import os
import stat
keyfile = "foo"
os.chmod("/etc/passwd", 0o227) # Error
os.chmod("/etc/passwd", 0o7) # Error
os.chmod("/etc/passwd", 0o664) # OK
os.chmod("/etc/passwd", 0o777) # Error
os.chmod("/etc/passwd", 0o770) # Error
os.chmod("/etc/passwd", 0o776) # Error
os.chmod("/etc/passwd", 0o760) # OK
os.chmod("~/.bashrc", 511) # Error
os.chmod("/etc/hosts", 0o777) # Error
os.chmod("/tmp/oh_hai", 0x1FF) # Error
os.chmod("/etc/passwd", stat.S_IRWXU) # OK
os.chmod(keyfile, 0o777) # Error
os.chmod(keyfile, 0o7 | 0o70 | 0o700) # Error
os.chmod(keyfile, stat.S_IRWXO | stat.S_IRWXG | stat.S_IRWXU) # Error
os.chmod("~/hidden_exec", stat.S_IXGRP) # Error
os.chmod("~/hidden_exec", stat.S_IXOTH) # OK
os.chmod("/etc/passwd", stat.S_IWOTH) # Error

View File

@@ -51,3 +51,12 @@ secret == "s3cr3t"
token == "s3cr3t"
secrete == "s3cr3t"
password == safe == "s3cr3t"
if token == "1\n2":
pass
if token == "3\t4":
pass
if token == "5\r6":
pass

View File

@@ -0,0 +1,16 @@
# ok
with open("/abc/tmp", "w") as f:
f.write("def")
with open("/tmp/abc", "w") as f:
f.write("def")
with open("/var/tmp/123", "w") as f:
f.write("def")
with open("/dev/shm/unit/test", "w") as f:
f.write("def")
# not ok by config
with open("/foo/bar", "w") as f:
f.write("def")

View File

@@ -0,0 +1,52 @@
import hashlib
from hashlib import new as hashlib_new
from hashlib import sha1 as hashlib_sha1
# Invalid
hashlib.new('md5')
hashlib.new('md4', b'test')
hashlib.new(name='md5', data=b'test')
hashlib.new('MD4', data=b'test')
hashlib.new('sha1')
hashlib.new('sha1', data=b'test')
hashlib.new('sha', data=b'test')
hashlib.new(name='SHA', data=b'test')
hashlib.sha(data=b'test')
hashlib.md5()
hashlib_new('sha1')
hashlib_sha1('sha1')
# usedforsecurity arg only available in Python 3.9+
hashlib.new('sha1', usedforsecurity=True)
# Valid
hashlib.new('sha256')
hashlib.new('SHA512')
hashlib.sha256(data=b'test')
# usedforsecurity arg only available in Python 3.9+
hashlib_new(name='sha1', usedforsecurity=False)
# usedforsecurity arg only available in Python 3.9+
hashlib_sha1(name='sha1', usedforsecurity=False)
# usedforsecurity arg only available in Python 3.9+
hashlib.md4(usedforsecurity=False)
# usedforsecurity arg only available in Python 3.9+
hashlib.new(name='sha256', usedforsecurity=False)

View File

@@ -0,0 +1,31 @@
import json
import yaml
from yaml import CSafeLoader
from yaml import SafeLoader
from yaml import SafeLoader as NewSafeLoader
def test_yaml_load():
ystr = yaml.dump({"a": 1, "b": 2, "c": 3})
y = yaml.load(ystr)
yaml.dump(y)
try:
y = yaml.load(ystr, Loader=yaml.CSafeLoader)
except AttributeError:
# CSafeLoader only exists if you build yaml with LibYAML
y = yaml.load(ystr, Loader=yaml.SafeLoader)
def test_json_load():
# no issue should be found
j = json.load("{}")
yaml.load("{}", Loader=yaml.Loader)
# no issue should be found
yaml.load("{}", SafeLoader)
yaml.load("{}", yaml.SafeLoader)
yaml.load("{}", CSafeLoader)
yaml.load("{}", yaml.CSafeLoader)
yaml.load("{}", NewSafeLoader)

View File

@@ -0,0 +1,120 @@
class Foo:
"""buzz"""
pass # PIE790
if foo:
"""foo"""
pass # PIE790
def multi_statement() -> None:
"""This is a function."""
pass; print("hello")
if foo:
pass
else:
"""bar"""
pass # PIE790
while True:
pass
else:
"""bar"""
pass # PIE790
for _ in range(10):
pass
else:
"""bar"""
pass # PIE790
async for _ in range(10):
pass
else:
"""bar"""
pass # PIE790
def foo() -> None:
"""
buzz
"""
pass # PIE790
async def foo():
"""
buzz
"""
pass # PIE790
try:
"""
buzz
"""
pass # PIE790
except ValueError:
pass
try:
bar()
except ValueError:
"""bar"""
pass # PIE790
for _ in range(10):
"""buzz"""
pass # PIE790
async for _ in range(10):
"""buzz"""
pass # PIE790
while cond:
"""buzz"""
pass # PIE790
with bar:
"""buzz"""
pass # PIE790
async with bar:
"""buzz"""
pass # PIE790
class Foo:
# bar
pass
if foo:
# foo
pass
class Error(Exception):
pass
try:
foo()
except NetworkError:
pass
def foo() -> None:
pass

View File

@@ -0,0 +1,33 @@
class Foo(BaseModel):
name = StringField()
# ....
name = StringField() # PIE794
def remove(self) -> None:
...
class Foo(BaseModel):
name: str = StringField()
# ....
name = StringField() # PIE794
def foo(self) -> None:
...
class User(BaseModel):
bar: str = StringField()
foo: bool = BooleanField()
# ...
bar = StringField() # PIE794
class User(BaseModel):
@property
def buzz(self) -> str:
...
@buzz.setter
def buzz(self, value: str | int) -> None:
...

View File

@@ -0,0 +1,20 @@
@dataclass
class Foo:
foo: List[str] = field(default_factory=lambda: []) # PIE807
class FooTable(BaseTable):
bar = fields.ListField(default=lambda: []) # PIE807
class FooTable(BaseTable):
bar = fields.ListField(lambda: []) # PIE807
@dataclass
class Foo:
foo: List[str] = field(default_factory=list)
class FooTable(BaseTable):
bar = fields.ListField(list)

View File

@@ -0,0 +1,78 @@
import pytest
from pytest import fixture
from pytest import fixture as aliased
# `import pytest`
@pytest.fixture
def no_parentheses():
return 42
@pytest.fixture()
def parentheses_no_params():
return 42
@pytest.fixture(scope="module")
def parentheses_with_params():
return 42
@pytest.fixture(
)
def parentheses_no_params_multiline():
return 42
# `from pytest import fixture`
@fixture
def imported_from_no_parentheses():
return 42
@fixture()
def imported_from_parentheses_no_params():
return 42
@fixture(scope="module")
def imported_from_parentheses_with_params():
return 42
@fixture(
)
def imported_from_parentheses_no_params_multiline():
return 42
# `from pytest import fixture as aliased`
@aliased
def aliased_no_parentheses():
return 42
@aliased()
def aliased_parentheses_no_params():
return 42
@aliased(scope="module")
def aliased_parentheses_with_params():
return 42
@aliased(
)
def aliased_parentheses_no_params_multiline():
return 42

View File

@@ -0,0 +1,21 @@
import pytest
@pytest.fixture()
def my_fixture(): # OK no args
return 0
@pytest.fixture(scope="module")
def my_fixture(): # OK only kwargs
return 0
@pytest.fixture("module")
def my_fixture(): # Error only args
return 0
@pytest.fixture("module", autouse=True)
def my_fixture(): # Error mixed
return 0

View File

@@ -0,0 +1,16 @@
import pytest
@pytest.fixture()
def ok_no_scope():
...
@pytest.fixture(scope="module")
def ok_other_scope():
...
@pytest.fixture(scope="function")
def error():
...

View File

@@ -0,0 +1,58 @@
import abc
from abc import abstractmethod
import pytest
@pytest.fixture()
def _patch_something(mocker): # OK simple
mocker.patch("some.thing")
@pytest.fixture()
def _patch_something(mocker): # OK with return
if something:
return
mocker.patch("some.thing")
@pytest.fixture()
def _activate_context(): # OK with yield
with context:
yield
class BaseTest:
@pytest.fixture()
@abc.abstractmethod
def my_fixture(): # OK abstract with import abc
raise NotImplementedError
class BaseTest:
@pytest.fixture()
@abstractmethod
def my_fixture(): # OK abstract with from import
raise NotImplementedError
@pytest.fixture()
def my_fixture(): # OK ignoring yield from
yield from some_generator()
@pytest.fixture()
def my_fixture(): # OK ignoring yield value
yield 1
@pytest.fixture()
def patch_something(mocker): # Error simple
mocker.patch("some.thing")
@pytest.fixture()
def activate_context(): # Error with yield
with context:
yield

View File

@@ -0,0 +1,57 @@
import abc
from abc import abstractmethod
import pytest
@pytest.fixture()
def my_fixture(mocker): # OK with return
return 0
@pytest.fixture()
def activate_context(): # OK with yield
with get_context() as context:
yield context
@pytest.fixture()
def _any_fixture(mocker): # Ok nested function
def nested_function():
return 1
mocker.patch("...", nested_function)
class BaseTest:
@pytest.fixture()
@abc.abstractmethod
def _my_fixture(): # OK abstract with import abc
return NotImplemented
class BaseTest:
@pytest.fixture()
@abstractmethod
def _my_fixture(): # OK abstract with from import
return NotImplemented
@pytest.fixture()
def _my_fixture(mocker): # Error with return
return 0
@pytest.fixture()
def _activate_context(): # Error with yield
with get_context() as context:
yield context
@pytest.fixture()
def _activate_context(): # Error with conditional yield from
if some_condition:
with get_context() as context:
yield context
else:
yield from other_context()

View File

@@ -0,0 +1,51 @@
import pytest
@pytest.mark.parametrize("param", [1, 2, 3])
def test_always_ok(param):
...
@pytest.mark.parametrize("param1,param2", [(1, 2), (3, 4)])
def test_csv(param1, param2):
...
@pytest.mark.parametrize(" param1, , param2 , ", [(1, 2), (3, 4)])
def test_csv_with_whitespace(param1, param2):
...
@pytest.mark.parametrize("param1,param2", [(1, 2), (3, 4)])
def test_csv_bad_quotes(param1, param2):
...
@pytest.mark.parametrize(("param1", "param2"), [(1, 2), (3, 4)])
def test_tuple(param1, param2):
...
@pytest.mark.parametrize(("param1",), [1, 2, 3])
def test_tuple_one_elem(param1, param2):
...
@pytest.mark.parametrize(["param1", "param2"], [(1, 2), (3, 4)])
def test_list(param1, param2):
...
@pytest.mark.parametrize(["param1"], [1, 2, 3])
def test_list_one_elem(param1, param2):
...
@pytest.mark.parametrize([some_expr, another_expr], [1, 2, 3])
def test_list_expressions(param1, param2):
...
@pytest.mark.parametrize([some_expr, "param2"], [1, 2, 3])
def test_list_mixed_expr_literal(param1, param2):
...

View File

@@ -0,0 +1,55 @@
import pytest
@pytest.mark.parametrize("param", (1, 2))
def test_tuple(param):
...
@pytest.mark.parametrize(
("param1", "param2"),
(
(1, 2),
(3, 4),
),
)
def test_tuple_of_tuples(param1, param2):
...
@pytest.mark.parametrize(
("param1", "param2"),
(
[1, 2],
[3, 4],
),
)
def test_tuple_of_lists(param1, param2):
...
@pytest.mark.parametrize("param", [1, 2])
def test_list(param):
...
@pytest.mark.parametrize(
("param1", "param2"),
[
(1, 2),
(3, 4),
],
)
def test_list_of_tuples(param1, param2):
...
@pytest.mark.parametrize(
("param1", "param2"),
[
[1, 2],
[3, 4],
],
)
def test_list_of_lists(param1, param2):
...

View File

@@ -0,0 +1,48 @@
# OK
mocker.patch("module.name", not_lambda)
module_mocker.patch("module.name", not_lambda)
mocker.patch.object(obj, "attr", not_lambda)
module_mocker.patch.object(obj, "attr", not_lambda)
mocker.patch("module.name", return_value=None)
module_mocker.patch("module.name", return_value=None)
mocker.patch.object(obj, "attr", return_value=None)
module_mocker.patch.object(obj, "attr", return_value=None)
mocker.patch("module.name", lambda x, y: x)
module_mocker.patch("module.name", lambda x, y: x)
mocker.patch.object(obj, "attr", lambda x, y: x)
module_mocker.patch.object(obj, "attr", lambda x, y: x)
mocker.patch("module.name", lambda *args: args)
module_mocker.patch("module.name", lambda *args: args)
mocker.patch.object(obj, "attr", lambda *args: args)
module_mocker.patch.object(obj, "attr", lambda *args: args)
mocker.patch("module.name", lambda **kwargs: kwargs)
module_mocker.patch("module.name", lambda **kwargs: kwargs)
mocker.patch.object(obj, "attr", lambda **kwargs: kwargs)
module_mocker.patch.object(obj, "attr", lambda **kwargs: kwargs)
mocker.patch("module.name", lambda x, /, y: x)
module_mocker.patch("module.name", lambda x, /, y: x)
mocker.patch.object(obj, "attr", lambda x, /, y: x)
module_mocker.patch.object(obj, "attr", lambda x, /, y: x)
# Error
mocker.patch("module.name", lambda: None)
module_mocker.patch("module.name", lambda: None)
mocker.patch.object(obj, "attr", lambda: None)
module_mocker.patch.object(obj, "attr", lambda: None)
mocker.patch("module.name", lambda x, y: None)
module_mocker.patch("module.name", lambda x, y: None)
mocker.patch.object(obj, "attr", lambda x, y: None)
module_mocker.patch.object(obj, "attr", lambda x, y: None)
mocker.patch("module.name", lambda *args, **kwargs: None)
module_mocker.patch("module.name", lambda *args, **kwargs: None)
mocker.patch.object(obj, "attr", lambda *args, **kwargs: None)
module_mocker.patch.object(obj, "attr", lambda *args, **kwargs: None)

View File

@@ -0,0 +1,9 @@
import pytest
def test_xxx():
assert 1 == 1 # OK no parameters
def test_xxx():
self.assertEqual(1, 1) # Error

View File

@@ -0,0 +1,11 @@
import pytest
def test_ok():
with pytest.raises():
pass
def test_error():
with pytest.raises(UnicodeError):
pass

View File

@@ -0,0 +1,32 @@
import socket
import pytest
def test_ok():
with pytest.raises(ValueError, match="Can't divide by 0"):
raise ValueError("Can't divide by 0")
def test_ok_different_error_from_config():
with pytest.raises(ZeroDivisionError):
raise ZeroDivisionError("Can't divide by 0")
def test_error_no_argument_given():
with pytest.raises(ValueError):
raise ValueError("Can't divide 1 by 0")
with pytest.raises(socket.error):
raise ValueError("Can't divide 1 by 0")
def test_error_match_is_empty():
with pytest.raises(ValueError, match=None):
raise ValueError("Can't divide 1 by 0")
with pytest.raises(ValueError, match=""):
raise ValueError("Can't divide 1 by 0")
with pytest.raises(ValueError, match=f""):
raise ValueError("Can't divide 1 by 0")

View File

@@ -0,0 +1,64 @@
import pytest
def test_ok():
with pytest.raises(AttributeError):
[].size
async def test_ok_trivial_with():
with pytest.raises(AttributeError):
with context_manager_under_test():
pass
with pytest.raises(AttributeError):
async with context_manager_under_test():
pass
def test_ok_complex_single_call():
with pytest.raises(AttributeError):
my_func(
[].size,
[].size,
)
def test_error_multiple_statements():
with pytest.raises(AttributeError):
len([])
[].size
async def test_error_complex_statement():
with pytest.raises(AttributeError):
if True:
[].size
with pytest.raises(AttributeError):
for i in []:
[].size
with pytest.raises(AttributeError):
async for i in []:
[].size
with pytest.raises(AttributeError):
while True:
[].size
with pytest.raises(AttributeError):
with context_manager_under_test():
[].size
with pytest.raises(AttributeError):
async with context_manager_under_test():
[].size
def test_error_try():
with pytest.raises(AttributeError):
try:
[].size
except:
raise

View File

@@ -0,0 +1,13 @@
# OK
import pytest
import pytest as pytest
from notpytest import fixture
from . import fixture
from .pytest import fixture
# Error
import pytest as other_name
from pytest import fixture
from pytest import fixture as other_name

View File

@@ -0,0 +1,25 @@
import pytest
def test_ok():
assert [0]
def test_error():
assert None
assert False
assert 0
assert 0.0
assert ""
assert f""
assert []
assert ()
assert {}
assert list()
assert set()
assert tuple()
assert dict()
assert frozenset()
assert list([])
assert set(set())
assert tuple("")

View File

@@ -0,0 +1,17 @@
import pytest
def test_xxx():
pytest.fail("this is a failure") # Test OK arg
def test_xxx():
pytest.fail(msg="this is a failure") # Test OK kwarg
def test_xxx(): # Error
pytest.fail()
pytest.fail("")
pytest.fail(f"")
pytest.fail(msg="")
pytest.fail(msg=f"")

View File

@@ -0,0 +1,19 @@
import pytest
def test_ok():
try:
something()
except Exception as e:
something_else()
with pytest.raises(ZeroDivisionError) as e:
1 / 0
assert e.value.message
def test_error():
try:
something()
except Exception as e:
assert e.message, "blah blah"

View File

@@ -0,0 +1,18 @@
import pytest
def test_ok():
assert something
assert something or something_else
assert something or something_else and something_third
assert not (something and something_else)
def test_error():
assert something and something_else
assert something and something_else and something_third
assert something and not something_else
assert something and (something_else or something_third)
assert not (something or something_else)
assert not (something or something_else or something_third)
assert not (something or something_else and something_third)

View File

@@ -0,0 +1,14 @@
def test_xxx(fixture): # Ok good param name
pass
def xxx(_param): # Ok not a test
pass
def test_xxx(_fixture): # Error arg
pass
def test_xxx(*, _fixture): # Error kwonly
pass

View File

@@ -0,0 +1,21 @@
import pytest
@pytest.fixture()
def ok_no_parameters():
return 0
@pytest.fixture
def ok_without_parens():
return 0
@pytest.yield_fixture()
def error_without_parens():
return 0
@pytest.yield_fixture
def error_with_parens():
return 0

View File

@@ -0,0 +1,58 @@
import functools
import pytest
@pytest.fixture()
def my_fixture(): # OK return
return 0
@pytest.fixture()
def my_fixture(): # OK yield
resource = acquire_resource()
yield resource
resource.release()
@pytest.fixture()
def my_fixture(): # OK other request
request = get_request()
request.addfinalizer(finalizer)
return request
def create_resource(arg, request): # OK other function
resource = Resource(arg)
request.addfinalizer(resource.release)
return resource
@pytest.fixture()
def resource_factory(request):
return functools.partial(create_resource, request=request)
@pytest.fixture()
def resource_factory(request): # OK other function
def create_resource(arg) -> Resource:
resource = Resource(arg)
request.addfinalizer(resource.release)
return resource
return create_resource
@pytest.fixture()
def my_fixture(request): # Error return
resource = acquire_resource()
request.addfinalizer(resource.release)
return resource
@pytest.fixture()
def my_fixture(request): # Error yield
resource = acquire_resource()
request.addfinalizer(resource.release)
yield resource
resource # prevent PT022

View File

@@ -0,0 +1,17 @@
import pytest
@pytest.fixture()
def ok_complex_logic():
if some_condition:
resource = acquire_resource()
yield resource
resource.release()
return
yield None
@pytest.fixture()
def error():
resource = acquire_resource()
yield resource

View File

@@ -0,0 +1,74 @@
import pytest
@pytest.mark.foo(scope="module")
def ok_with_parameters_regardless_of_config():
pass
# Without parentheses
@pytest.mark.foo
def test_something():
pass
@pytest.mark.foo
class TestClass:
def test_something():
pass
class TestClass:
@pytest.mark.foo
def test_something():
pass
class TestClass:
@pytest.mark.foo
class TestNestedClass:
def test_something():
pass
class TestClass:
class TestNestedClass:
@pytest.mark.foo
def test_something():
pass
# With parentheses
@pytest.mark.foo()
def test_something():
pass
@pytest.mark.foo()
class TestClass:
def test_something():
pass
class TestClass:
@pytest.mark.foo()
def test_something():
pass
class TestClass:
@pytest.mark.foo()
class TestNestedClass:
def test_something():
pass
class TestClass:
class TestNestedClass:
@pytest.mark.foo()
def test_something():
pass

View File

@@ -0,0 +1,35 @@
import pytest
@pytest.mark.asyncio()
async def test_something(): # Ok not fixture
pass
@pytest.mark.asyncio
async def test_something(): # Ok not fixture no parens
pass
@pytest.mark.asyncio()
@pytest.fixture()
async def my_fixture(): # Error before
return 0
@pytest.mark.asyncio
@pytest.fixture()
async def my_fixture(): # Error before no parens
return 0
@pytest.fixture()
@pytest.mark.asyncio()
async def my_fixture(): # Error after
return 0
@pytest.fixture()
@pytest.mark.asyncio
async def my_fixture(): # Error after no parens
return 0

View File

@@ -0,0 +1,18 @@
import pytest
@pytest.mark.usefixtures("a")
def test_something(): # Ok not fixture
pass
@pytest.mark.usefixtures("a")
@pytest.fixture()
def my_fixture(): # Error before
return 0
@pytest.fixture()
@pytest.mark.usefixtures("a")
def my_fixture(): # Error after
return 0

View File

@@ -0,0 +1,26 @@
import pytest
@pytest.mark.usefixtures("a")
def test_ok():
pass
@pytest.mark.foo()
def test_ok_another_mark_with_parens():
pass
@pytest.mark.foo
def test_ok_another_mark_no_parens():
pass
@pytest.mark.usefixtures()
def test_error_with_parens():
pass
@pytest.mark.usefixtures
def test_error_no_parens():
pass

View File

@@ -137,6 +137,14 @@ def x():
return a
# Considered OK, since attribute assignments can have side effects.
class X:
def x(self):
a = self.property
self.property = None
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:
@@ -238,13 +246,16 @@ def close(self):
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

View File

@@ -0,0 +1,23 @@
if isinstance(a, int) or isinstance(a, float): # SIM101
pass
if isinstance(a, (int, float)) or isinstance(a, bool): # SIM101
pass
if isinstance(a, int) or isinstance(a, float) or isinstance(b, bool): # SIM101
pass
if isinstance(b, bool) or isinstance(a, int) or isinstance(a, float): # SIM101
pass
if isinstance(a, int) or isinstance(b, bool) or isinstance(a, float): # SIM101
pass
if (isinstance(a, int) or isinstance(a, float)) and isinstance(b, bool): # SIM101
pass
if isinstance(a, int) and isinstance(b, bool) or isinstance(a, float):
pass
if isinstance(a, bool) or isinstance(b, str):
pass

View File

@@ -0,0 +1,25 @@
if a: # SIM102
if b:
c
if a:
pass
elif b: # SIM102
if c:
d
if a:
if b:
c
else:
d
if __name__ == "__main__":
if foo():
...
if a:
d
if b:
c

View File

@@ -0,0 +1,43 @@
def foo():
pass
try:
foo()
except ValueError: # SIM105
pass
try:
foo()
except (ValueError, OSError): # SIM105
pass
try:
foo()
except: # SIM105
pass
try:
foo()
except (a.Error, b.Error): # SIM105
pass
try:
foo()
except ValueError:
print('foo')
except OSError:
pass
try:
foo()
except ValueError:
pass
else:
print('bar')
try:
foo()
except ValueError:
pass
finally:
print('bar')

View File

@@ -0,0 +1,22 @@
# Bad: This example returns 3!
def foo():
try:
1 / 0
return "1"
except:
return "2"
finally:
return "3"
# Good
def foo():
return_value = None
try:
1 / 0
return_value = "1"
except:
return_value = "2"
finally:
return_value = "3" # you might also want to check if "except" happened
return return_value

View File

@@ -0,0 +1,47 @@
def f():
for x in iterable: # SIM110
if check(x):
return True
return False
def f():
for x in iterable:
if check(x):
return True
return True
def f():
for el in [1, 2, 3]:
if is_true(el):
return True
raise Exception
def f():
for x in iterable: # SIM111
if check(x):
return False
return True
def f():
for x in iterable: # SIM111
if not x.is_empty():
return False
return True
def f():
for x in iterable:
if check(x):
return False
return False
def f():
for x in iterable:
if check(x):
return "foo"
return "bar"

View File

@@ -0,0 +1,33 @@
def f():
for x in iterable: # SIM110
if check(x):
return True
return False
def f():
for el in [1, 2, 3]:
if is_true(el):
return True
raise Exception
def f():
for x in iterable: # SIM111
if check(x):
return False
return True
def f():
for x in iterable: # SIM 111
if not x.is_empty():
return False
return True
def f():
for x in iterable:
if check(x):
return "foo"
return "bar"

View File

@@ -0,0 +1,13 @@
with A() as a: # SIM117
with B() as b:
print("hello")
with A() as a:
a()
with B() as b:
print("hello")
with A() as a:
with B() as b:
print("hello")
a()

View File

@@ -1,12 +1,22 @@
key in dict.keys() # SIM118
key in obj.keys() # SIM118
foo["bar"] in dict.keys() # SIM118
foo["bar"] in obj.keys() # SIM118
foo() in dict.keys() # SIM118
foo['bar'] in obj.keys() # SIM118
for key in dict.keys(): # SIM118
foo() in obj.keys() # SIM118
for key in obj.keys(): # SIM118
pass
for key in list(dict.keys()):
for key in list(obj.keys()):
if some_property(key):
del dict[key]
del obj[key]
[k for k in obj.keys()] # SIM118
{k for k in obj.keys()} # SIM118
{k: k for k in obj.keys()} # SIM118
(k for k in obj.keys()) # SIM118

View File

@@ -0,0 +1,17 @@
if a and not a:
pass
if (a and not a) and b:
pass
if (a and not a) or b:
pass
if a:
pass
if not a:
pass
if a and not b:
pass

View File

@@ -0,0 +1,17 @@
if a or not a:
pass
if (a or not a) or b:
pass
if (a or not a) and b:
pass
if a:
pass
if not a:
pass
if a or not b:
pass

View File

@@ -0,0 +1,14 @@
if a or True: # SIM223
pass
if (a or b) or True: # SIM223
pass
if a or (b or True): # SIM223
pass
if a and True:
pass
if True:
pass

View File

@@ -0,0 +1,14 @@
if a and False: # SIM223
pass
if (a or b) and False: # SIM223
pass
if a or (b and False): # SIM223
pass
if a or False:
pass
if False:
pass

View File

@@ -0,0 +1,12 @@
# Errors
"yoda" == compare # SIM300
'yoda' == compare # SIM300
42 == age # SIM300
# OK
compare == "yoda"
age == 42
x == y
"yoda" == compare == 1
"yoda" == compare == someothervar
"yoda" == "yoda"

View File

@@ -26,3 +26,8 @@ from A import (
from D import a_long_name_to_force_multiple_lines # Comment 12
from D import another_long_name_to_force_multiple_lines # Comment 13
from E import a # Comment 1
from F import a # Comment 1
from F import b

View File

@@ -0,0 +1,11 @@
from a.prometheus.metrics import ( # type:ignore[attr-defined]
TERMINAL_CURRENTLY_RUNNING_TOTAL,
)
from b.prometheus.metrics import (
TERMINAL_CURRENTLY_RUNNING_TOTAL, # type:ignore[attr-defined]
)
from c.prometheus.metrics import TERMINAL_CURRENTLY_RUNNING_TOTAL # type:ignore[attr-defined]
from d.prometheus.metrics import TERMINAL_CURRENTLY_RUNNING_TOTAL, OTHER_RUNNING_TOTAL # type:ignore[attr-defined]

View File

@@ -0,0 +1 @@
from long_module_name import member_one, member_two, member_three, member_four, member_five

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