Compare commits

...

117 Commits

Author SHA1 Message Date
Micha Reiser
5d2513e5a9 POC of Black's string preview style formatting 2023-09-21 12:38:03 +02:00
Micha Reiser
272306bf5a Introduce StringContinuation data structure 2023-09-21 08:01:39 +02:00
Micha Reiser
f8f1cd5016 Introduce FormatterSettings (#7545) 2023-09-21 08:01:24 +02:00
Charlie Marsh
87a0cd219f Detect asyncio.get_running_loop calls in RUF006 (#7562)
## Summary

We can do a good enough job detecting this with our existing semantic
model.

Closes https://github.com/astral-sh/ruff/issues/3237.
2023-09-21 04:37:38 +00:00
Charlie Marsh
b685ee4749 Enable tab completion for ruff rule (#7560)
## Summary

Writing `ruff rule E1`, then tab, shows:

<img width="724" alt="Screen Shot 2023-09-20 at 9 29 09 PM"
src="https://github.com/astral-sh/ruff/assets/1309177/00297f24-8828-4485-a00e-6af1ab4e7875">

Closes https://github.com/astral-sh/ruff/issues/2812.
2023-09-20 22:05:39 -04:00
Charlie Marsh
ad893f8295 Avoid invalid fix for parenthesized values in F601 (#7559)
Closes https://github.com/astral-sh/ruff/issues/4897.
2023-09-21 01:28:11 +00:00
Charlie Marsh
621bed55c0 Add padding in PERF102 fixes (#7554)
Closes https://github.com/astral-sh/ruff/issues/7097.
2023-09-20 19:33:54 -04:00
Charlie Marsh
86faee1522 Update CONTRIBUTING.md to reflect Settings refactor (#7555)
## Summary

See:
https://github.com/astral-sh/ruff/pull/7544#issuecomment-1728457885.
2023-09-20 19:33:33 -04:00
Charlie Marsh
a0917ec658 Avoid inserting imports directly after continuation (#7553)
## Summary

This is extremely rare in practice, but common in the fuzzer issues so
worth fixing quickly.

Closes https://github.com/astral-sh/ruff/issues/7199.
2023-09-20 21:26:48 +00:00
Mahesh Saripalli
ee9ee005c5 Update flake8-self license to MIT (#7552)
## Summary

This PR updates the flake8-self license from Freely Distributable to the
MIT License to match the upstream license, which was recently updated.


f5fc507515/LICENSE
2023-09-20 17:17:55 -04:00
Charlie Marsh
5df0326bc8 Treat parameters-with-newline as empty in function formatting (#7550)
## Summary

If a function has no parameters (and no comments within the parameters'
`()`), we're supposed to wrap the return annotation _whenever_ it
breaks. However, our `empty_parameters` test didn't properly account for
the case in which the parameters include a newline (but no other
content), like:

```python
def get_dashboards_hierarchy(
) -> Dict[Type['BaseDashboard'], List[Type['BaseDashboard']]]:
    """Get hierarchy of dashboards classes.

    Returns:
        Dict of dashboards classes.
    """
    dashboards_hierarchy = {}
```

This PR fixes that detection. Instead of lexing, it now checks if the
parameters itself is empty (or if it contains comments).

Closes https://github.com/astral-sh/ruff/issues/7457.
2023-09-20 16:20:22 -04:00
Micha Reiser
6540321966 Move Settings and ResolverSettings to ruff_workspace
## Summary

## Stack Summary

This stack splits `Settings` into `FormatterSettings` and `LinterSettings` and moves it into `ruff_workspace`. This change is necessary to add the `FormatterSettings` to `Settings` without adding `ruff_python_formatter` as a dependency to `ruff_linter` (and the linter should not contain the formatter settings). 

A quick overview of our settings struct at play:

* `Options`: 1:1 representation of the options in the `pyproject.toml` or `ruff.toml`.  Used for deserialization.
* `Configuration`: Resolved `Options`, potentially merged from multiple configurations (when using `extend`). The representation is very close if not identical to the `Options`.
* `Settings`: The resolved configuration that uses a data format optimized for reading. Optional fields are initialized with their default values. Initialized by `Configuration::into_settings` .

The goal of this stack is to split `Settings` into tool-specific resolved `Settings` that are independent of each other. This comes at the advantage that the individual crates don't need to know anything about the other tools. The downside is that information gets duplicated between `Settings`. Right now the duplication is minimal (`line-length`, `tab-width`) but we may need to come up with a solution if more expensive data needs sharing.

This stack focuses on `Settings`. Splitting `Configuration` into some smaller structs is something I'll follow up on later. 

## PR Summary

This PR moves the `ResolverSettings` and `Settings` struct to `ruff_workspace`. `LinterSettings` remains in `ruff_linter` because it gets passed to lint rules, the `Checker` etc.

## Test Plan

`cargo test`
2023-09-20 17:24:28 +02:00
Micha Reiser
b34278e0cd Introduce LinterSettings
## Stack Summary

This stack splits `Settings` into `FormatterSettings` and `LinterSettings` and moves it into `ruff_workspace`. This change is necessary to add the `FormatterSettings` to `Settings` without adding `ruff_python_formatter` as a dependency to `ruff_linter` (and the linter should not contain the formatter settings). 

A quick overview of our settings struct at play:

* `Options`: 1:1 representation of the options in the `pyproject.toml` or `ruff.toml`.  Used for deserialization.
* `Configuration`: Resolved `Options`, potentially merged from multiple configurations (when using `extend`). The representation is very close if not identical to the `Options`.
* `Settings`: The resolved configuration that uses a data format optimized for reading. Optional fields are initialized with their default values. Initialized by `Configuration::into_settings` .

The goal of this stack is to split `Settings` into tool-specific resolved `Settings` that are independent of each other. This comes at the advantage that the individual crates don't need to know anything about the other tools. The downside is that information gets duplicated between `Settings`. Right now the duplication is minimal (`line-length`, `tab-width`) but we may need to come up with a solution if more expensive data needs sharing.

This stack focuses on `Settings`. Splitting `Configuration` into some smaller structs is something I'll follow up on later. 

## PR Summary

This PR extracts the linter-specific settings into a new `LinterSettings` struct and adds it as a `linter` field to the `Settings` struct. This is in preparation for moving `Settings` from `ruff_linter` to `ruff_workspace`

## Test Plan

`cargo test`
2023-09-20 17:02:34 +02:00
Micha Reiser
222f1c37b8 Add empty lines between options and move documents (#7547) 2023-09-20 16:40:25 +02:00
Micha Reiser
8f41eab0c7 Extract ResolverSettings
## Stack Summary

This stack splits `Settings` into `FormatterSettings` and `LinterSettings` and moves it into `ruff_workspace`. This change is necessary to add the `FormatterSettings` to `Settings` without adding `ruff_python_formatter` as a dependency to `ruff_linter` (and the linter should not contain the formatter settings). 

A quick overview of our settings struct at play:

* `Options`: 1:1 representation of the options in the `pyproject.toml` or `ruff.toml`.  Used for deserialization.
* `Configuration`: Resolved `Options`, potentially merged from multiple configurations (when using `extend`). The representation is very close if not identical to the `Options`.
* `Settings`: The resolved configuration that uses a data format optimized for reading. Optional fields are initialized with their default values. Initialized by `Configuration::into_settings` .

The goal of this stack is to split `Settings` into tool-specific resolved `Settings` that are independent of each other. This comes at the advantage that the individual crates don't need to know anything about the other tools. The downside is that information gets duplicated between `Settings`. Right now the duplication is minimal (`line-length`, `tab-width`) but we may need to come up with a solution if more expensive data needs sharing.

This stack focuses on `Settings`. Splitting `Configuration` into some smaller structs is something I'll follow up on later. 

## PR Summary

This PR extracts a `ResolverSettings` struct that holds all the resolver-relevant fields (uninteresting for the `Formatter` or `Linter`). This will allow us to move the `ResolverSettings` out of `ruff_linter` further up in the stack.


## Test Plan

`cargo test`

(I'll to more extensive testing at the top of this stack)
2023-09-20 16:37:49 +02:00
Micha Reiser
83daddbeb7 Rename ConfigProcessor to ConfigurationTransformer (#7536) 2023-09-20 14:17:06 +00:00
Micha Reiser
b19eec9b2a Unify Settings and AllSettings (#7532) 2023-09-20 13:56:07 +00:00
Micha Reiser
ca3c15858d Use portable hash function for CacheKey (#7533) 2023-09-20 15:19:59 +02:00
Micha Reiser
bb4f7c681a Rename format option to output-format (#7514) 2023-09-20 15:18:58 +02:00
Dhruv Manilawala
0a167dd20b Use strict sorted and union for NoQA mapping insertion (#7531)
## Summary

This PR fixes the way NoQA range is inserted to the `NoqaMapping`.

Previously, the way the mapping insertion logic worked was as follows:
1. If the range which is to be inserted _touched_ the previous range, meaning
that the end of the previous range was the same as the start of the new
range, then the new range was added in addition to the previous range.
2. Else, if the new range intersected the previous range, then the previous
   range was replaced with the new _intersection_ of the two ranges.

The problem with this logic is that it does not work for the following case:
```python
assert foo, \
    """multi-line
    string"""
```

Now, the comments cannot be added to the same line which ends with a continuation
character. So, the `NoQA` directive has to be added to the next line. But, the
next line is also a triple-quoted string, so the `NoQA` directive for that line
needs to be added to the next line. This creates a **union** pattern instead of an
**intersection** pattern.

But, only union doesn't suffice because (1) means that for the edge case where
the range touch only at the end, the union won't take place.

### Solution

1. Replace '<=' with '<' to have a _strict_ insertion case
2. Use union instead of intersection

## Test Plan

Add a new test case. Run the test suite to ensure that nothing is broken.

### Integration

1. Make a `test.py` file with the following contents:

    ```python
    assert foo, \
        """multi-line
        string"""
    ```

2. Run the following command:

    ```console
	$ cargo run --bin ruff -- check --isolated --no-cache --select=F821 test.py
	/Users/dhruv/playground/ruff/fstring.py:1:8: F821 Undefined name `foo`
    Found 1 error.
    ```

3. Use `--add-noqa`:

    ```console
	$ cargo run --bin ruff -- check --isolated --no-cache --select=F821 --add-noqa test.py
    Added 1 noqa directive.
    ```

4. Check that the NoQA directive was added in the correct position:

    ```python
    assert foo, \
        """multi-line
        string"""  # noqa: F821
    ```

5. Run the `check` command to ensure that the NoQA directive is respected:

    ```console
	$ cargo run --bin ruff -- check --isolated --no-cache --select=F821 test.py
    ```

fixes: #7530
2023-09-20 11:07:19 +00:00
dependabot[bot]
c43542896f Bump unicode-width from 0.1.10 to 0.1.11 (#7534)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-20 10:26:54 +02:00
Micha Reiser
192463c2fb Allow parenthesized content exceed configured line width (#7490) 2023-09-20 08:39:25 +02:00
Charlie Marsh
5849a75223 Rename ruff crate to ruff_linter (#7529) 2023-09-20 08:38:27 +02:00
Mathieu Kniewallner
dcbd8eacd8 feat(rules): implement flake8-bandit S507 (ssh_no_host_key_verification) (#7528)
Part of #1646.

## Summary

Implement `S507`
([`ssh_no_host_key_verification`](https://bandit.readthedocs.io/en/latest/plugins/b507_ssh_no_host_key_verification.html))
rule from `bandit`.

## Test Plan

Snapshot test from
https://github.com/PyCQA/bandit/blob/1.7.5/examples/no_host_key_verification.py,
with several additions to test for more cases (most notably passing the
parameter as a named argument).
2023-09-19 20:31:45 -04:00
Micha Reiser
297ec2c2d2 Use Settings where AllSettings isn't required (#7518) 2023-09-19 23:16:20 +02:00
Tom Kuson
511cc25fc4 [refurb] Implement unnecessary-enumerate (FURB148) (#7454)
## Summary

Implement
[`no-ignored-enumerate-items`](https://github.com/dosisod/refurb/blob/master/refurb/checks/builtin/no_ignored_enumerate.py)
as `unnecessary-enumerate` (`FURB148`).

The auto-fix considers if a `start` argument is passed to the
`enumerate()` function. If only the index is used, then the suggested
fix is to pass the `start` value to the `range()` function. So,

```python
for i, _ in enumerate(xs, 1):
    ...
```

becomes

```python
for i in range(1, len(xs)):
    ...
```

If the index is ignored and only the value is ignored, and if a start
value greater than zero is passed to `enumerate()`, the rule doesn't
produce a suggestion. I couldn't find a unanimously accepted best way to
iterate over a collection whilst skipping the first n elements. The rule
still triggers, however.

Related to #1348.

## Test Plan

`cargo test`

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2023-09-19 20:19:28 +00:00
Charlie Marsh
4c4eceee36 Add dangling comment handling for lambda expressions (#7493)
## Summary

This PR adds dangling comment handling for `lambda` expressions. In
short, comments around the `lambda` and the `:` are all considered
dangling. Comments that come between the `lambda` and the `:` may be
moved after the colon for simplicity (this is an odd position for a
comment anyway), unless they also precede the lambda parameters, in
which case they're formatted before the parameters.

Closes https://github.com/astral-sh/ruff/issues/7470.

## Test Plan

`cargo test`

No change in similarity.

Before:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99982 | 2760 | 37 |
| transformers | 0.99957 | 2587 | 398 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99929 | 648 | 16 |
| zulip | 0.99962 | 1437 | 22 |

After:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99982 | 2760 | 37 |
| transformers | 0.99957 | 2587 | 398 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99929 | 648 | 16 |
| zulip | 0.99962 | 1437 | 22 |
2023-09-19 15:23:51 -04:00
Charlie Marsh
e07670ad97 Add dangling comment handling to dictionary key-value pairs (#7495)
## Summary

This PR fixes a formatting instability by changing the comment handling
around the `:` in a dictionary to mirror that of the `:` in a lambda: we
treat comments around the `:` as dangling, then format them after the
`:`.

Closes https://github.com/astral-sh/ruff/issues/7458.

## Test Plan

`cargo test`

No change in similarity.

Before:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1631 |
| django | 0.99983 | 2760 | 36 |
| transformers | 0.99956 | 2587 | 404 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99929 | 648 | 16 |
| zulip | 0.99969 | 1437 | 21 |

After:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1631 |
| django | 0.99983 | 2760 | 36 |
| transformers | 0.99956 | 2587 | 404 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99929 | 648 | 16 |
| zulip | 0.99969 | 1437 | 21 |
2023-09-19 19:17:21 +00:00
Charlie Marsh
b792140579 Ensure that LOG007 only triggers on .exception() calls (#7524)
## Summary

Previously, this rule triggered for `logging.info("...",
exc_info=False)`.
2023-09-19 17:29:54 +00:00
Tom Kuson
36a60bd50e Add documentation to non-empty-stub-body (#7519)
## Summary

Add documentation to `non-empty-stub-body` (`PYI010`) rule. Related to
#2646.

## Test Plan

`python scripts/check_docs_formatted.py`
2023-09-19 13:21:11 -04:00
konsti
4ae463d04b Add a test for stmt assign breaking in preview mode (#7516)
In preview mode, black will consistently break the right side first.
This doesn't work yet, but we'll need the test later.
2023-09-19 16:16:40 +02:00
konsti
6dade5b9ab Tokenizer: Emit only a single bogus token (#7425)
**Summary** Instead of emitting a bogus token per char, we now only emit
on single last bogus token. This leads to much more concise output.

**Test Plan** Updated fixtures
2023-09-19 16:06:03 +02:00
Charlie Marsh
97510c888b Show --no-X variants in CLI help (#7504)
I'd really like this to render as `--preview / --no-preview`, but I
looked for a while in the Clap internals and issue tracker
(https://github.com/clap-rs/clap/issues/815) and I really can't figure
out a way to do it -- this seems like the best we can do? It's also what
they do in Orogene.

Closes https://github.com/astral-sh/ruff/issues/7486.
2023-09-19 12:27:30 +00:00
konsti
94b68f201b Fix stylist indentation with a formfeed (#7489)
**Summary** In python, a formfeed is technically undefined behaviour
(https://docs.python.org/3/reference/lexical_analysis.html#indentation):
> A formfeed character may be present at the start of the line; it will
be ignored for
> the indentation calculations above. Formfeed characters occurring
elsewhere in the
> leading whitespace have an undefined effect (for instance, they may
reset the space
> count to zero).

In practice, they just reset the indentation:


df8b3a46a7/Parser/tokenizer.c (L1819-L1821)
a41bb2733f/crates/ruff_python_parser/src/lexer.rs (L664-L667)

The stylist didn't handle formfeeds previously and would produce invalid
indents. The remedy is to cut everything before a form feed.

Checks box for
https://github.com/astral-sh/ruff/issues/7455#issuecomment-1722458825

**Test Plan** Unit test for the stylist and a regression test for the
rule
2023-09-19 12:01:16 +02:00
konsti
ef34c5cbec Update itertools to 0.11 (#7513)
Preparation for #7469.

Changelog:
https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md#0110
2023-09-19 09:53:14 +00:00
dependabot[bot]
fdbefd777c Bump syn from 2.0.33 to 2.0.37 (#7512)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-19 11:08:56 +02:00
dependabot[bot]
078547adbb Bump clap from 4.4.3 to 4.4.4 (#7511)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-19 11:08:39 +02:00
dependabot[bot]
42a0bec146 Bump schemars from 0.8.13 to 0.8.15 (#7510)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-19 11:08:22 +02:00
Micha Reiser
37b7d0f921 fix: Compiler warning about unused map_or (#7508) 2023-09-19 08:10:01 +00:00
Micha Reiser
6a4dbd622b Add optimized best_fit_parenthesize IR (#7475) 2023-09-19 06:29:05 +00:00
Charlie Marsh
28b48ab902 Avoid flagging starred expressions in UP007 (#7505)
## Summary

These can't be fixed, because fixing them would lead to invalid syntax.
So flagging them also feels misleading.

Closes https://github.com/astral-sh/ruff/issues/7452.
2023-09-19 03:37:38 +00:00
Valeriy Savchenko
4123d074bd [refurb] Implement reimplemented-starmap rule (FURB140) (#7253)
## Summary

This PR is part of a bigger effort of re-implementing `refurb` rules
#1348. It adds support for
[FURB140](https://github.com/dosisod/refurb/blob/master/refurb/checks/itertools/use_starmap.py)

## Test Plan

I included a new test + checked that all other tests pass.
2023-09-19 02:18:54 +00:00
Mathieu Kniewallner
c6ba7dfbc6 feat(rules): implement flake8-bandit S201 (flask_debug_true) (#7503)
Part of #1646.

## Summary

Implement `S201`
([`flask_debug_true`](https://bandit.readthedocs.io/en/latest/plugins/b201_flask_debug_true.html))
rule from `bandit`.

I am fairly new to Rust and Ruff's codebase, so there might be better
ways to implement the rule or write the code.

## Test Plan

Snapshot test from
https://github.com/PyCQA/bandit/blob/1.7.5/examples/flask_debug.py, with
a few additions in the "unrelated" part to test a bit more cases.
2023-09-19 00:43:28 +00:00
Micael Jarniac
40f6456add Use MkDocs' not_in_nav (#5498)
Closes #5497
Needs MkDocs 1.5 to be released.
- [x] https://github.com/mkdocs/mkdocs/milestone/15

## Summary
Uses MkDocs' `not_in_nav` config to hide spam about files in
`docs/rules/` not being in nav.
2023-09-19 00:01:43 +00:00
Micha Reiser
3e1dffab20 refactor: Use OnceCell in Memoized (#7500) 2023-09-18 19:58:55 +00:00
Micha Reiser
3336d23f48 perf: Reduce best fitting allocations (#7411) 2023-09-18 19:49:44 +00:00
Jonathan Plasse
2421805033 Avoid N802 violations for @overload methods (#7498)
Close #7479

The `@override` was already implemented

## Test Plan

Tested the code in the issue. After removing all the noqa's, only one
occurrence of `BadName()` raised a violation.
Added a fixture
2023-09-18 14:32:40 -04:00
Tom Kuson
359f50e6dc Ignore pass-statement-stub-body documentation formatting (#7497)
## Summary

Fix CI (broken in #7496).

The code snippet was formatted as Black would format a stub file, but
the CI script doesn't know that (it assumes all code snippets are
non-stub files). Easier to ignore.

Sorry for breaking CI!

## Test Plan

`python scripts/check_docs_formatted.py`
2023-09-18 14:27:20 -04:00
qdegraaf
bccba5d73f [flake8-logging] Implement LOG007: ExceptionWithoutExcInfo (#7410)
## Summary

This PR implements a new rule for `flake8-logging` plugin that checks
for uses of `logging.exception()` with `exc_info` set to `False` or a
falsy value. It suggests using `logging.error` in these cases instead.

I am unsure about the name. Open to suggestions there, went with the
most explicit name I could think of in the meantime.

Refer https://github.com/astral-sh/ruff/issues/7248

## Test Plan

Added a new fixture cases and ran `cargo test`
2023-09-18 17:46:12 +00:00
Tom Kuson
0bfdb15ecf Add documentation to pass-statement-stub-body (#7496)
## Summary

Add documentation to `pass-statement-stub-body` (`PYI009`) rule. Related
to #2646.

## Test Plan

`python scripts/check_docs_formatted.py`
2023-09-18 17:33:15 +00:00
dependabot[bot]
a902d14c31 Bump chrono from 0.4.30 to 0.4.31 (#7481) 2023-09-18 13:11:51 -04:00
Charlie Marsh
728539291f Move FormatExprDict to top of expr_dict.rs (#7494)
Put the node itself up top, and internal structs down below.
2023-09-18 11:55:18 -04:00
Dhruv Manilawala
c2bd8af59a Remove triple-quoted string ranges computation (#7476)
## Summary

This is a follow-up PR for #7435 to remove the now unused triple-quoted
string ranges from the indexer.
2023-09-18 20:57:49 +05:30
Jaap Roes
c946bf157e Extend bad-dunder-method-name to permit __html__ (#7492)
## Summary

Fixes #7478

## Test Plan

`cargo test`
2023-09-18 15:16:22 +00:00
Charlie Marsh
8ab2519717 Respect parentheses for precedence in await (#7468)
## Summary

We were using `Parenthesize::IfBreaks` universally for `await`, but
dropping parentheses can change the AST due to precedence. It turns out
that Black's rules aren't _exactly_ the same as operator precedence
(e.g., they leave parentheses around `await ([1, 2, 3])`, although they
aren't strictly required).

Closes https://github.com/astral-sh/ruff/issues/7467.

## Test Plan

`cargo test`

No change in similarity.

Before:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99982 | 2760 | 37 |
| transformers | 0.99957 | 2587 | 398 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99929 | 648 | 16 |
| zulip | 0.99962 | 1437 | 22 |

After:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99982 | 2760 | 37 |
| transformers | 0.99957 | 2587 | 398 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99929 | 648 | 16 |
| zulip | 0.99962 | 1437 | 22 |
2023-09-18 09:56:41 -04:00
konsti
c4d85d6fb6 Fix ''' ""''' formatting (#7485)
## Summary

`''' ""'''` is an edge case that was previously incorrectly formatted as
`""" """""`.

Fixes #7460

## Test Plan

Added regression test
2023-09-18 10:28:15 +00:00
dependabot[bot]
70ea49bf72 Bump test-case from 3.1.0 to 3.2.1 (#7484)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-18 11:31:41 +02:00
dependabot[bot]
8e255974bc Bump unicode-ident from 1.0.11 to 1.0.12 (#7482)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-18 11:31:06 +02:00
dependabot[bot]
d358604464 Bump serde_json from 1.0.106 to 1.0.107 (#7480)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-18 11:30:06 +02:00
dependabot[bot]
8243db74fe Bump indoc from 2.0.3 to 2.0.4 (#7483)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-18 11:29:50 +02:00
Micha Reiser
0346e781d4 Fix handling of newlines in empty files (#7473) 2023-09-18 06:08:10 +00:00
Tom Kuson
b66bfa6570 Extend bad-dunder-method-name to permit attrs dunders (#7472)
## Summary

Closes #7451.

## Test Plan

`cargo test`
2023-09-17 16:13:33 -04:00
Charlie Marsh
28273eb00b Avoid flagging starred elements in C402 (#7466)
## Summary

Rewriting these is not valid syntax.

Part of https://github.com/astral-sh/ruff/issues/7455.
2023-09-17 15:23:27 +00:00
Charlie Marsh
12acd191e1 Remove parentheses when rewriting assert calls to statements (#7464) 2023-09-17 15:18:56 +00:00
Charlie Marsh
64b929bc29 Add padding to prevent some autofix errors (#7461)
## Summary

We should really be doing this anywhere we _replace_ an expression.

See: https://github.com/astral-sh/ruff/issues/7455.
2023-09-17 15:14:16 +00:00
Micha Reiser
26ae0a6e8d Fix dangling module comments (#7456) 2023-09-17 14:56:41 +00:00
Dhruv Manilawala
959338d39d Refactor tab-indentation as a token-based rule (#7435)
## Summary

This PR updates the `W191` (`tab-indentation`) rule from a line-based to
a token-based rule.

Earlier, the rule used the `triple_quoted_string_ranges` from the
indexer to skip over any lines _inside_ a triple-quoted string. This was the only
use of the ranges. These ranges were extracted through the tokens, so instead
we can directly use the newline tokens to perform the check.

This would also mean that we can remove the `triple_quoted_string_ranges` from
the indexer but I'll hold that off until we have a better idea on #7326
but I don't think it would be a problem to remove it.

This will also fix #7379 once PEP 701 changes are merged.

## Test Plan

`cargo test`
2023-09-16 20:25:20 +00:00
Charlie Marsh
422ff82f4a Avoid extra parentheses in yield expressions (#7444)
## Summary

This is equivalent to https://github.com/astral-sh/ruff/pull/7424, but
for `yield` and `yield from` expressions. Specifically, we want to avoid
adding unnecessary extra parentheses for `yield expr` when `expr` itself
does not require parentheses.

## Test Plan

`cargo test`

No change in any of the similarity metrics.

Before:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99982 | 2760 | 37 |
| transformers | 0.99957 | 2587 | 399 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99929 | 648 | 16 |
| zulip | 0.99962 | 1437 | 22 |

After:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99982 | 2760 | 37 |
| transformers | 0.99957 | 2587 | 399 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99929 | 648 | 16 |
| zulip | 0.99962 | 1437 | 22 |
2023-09-16 14:46:56 -04:00
Charlie Marsh
8d0a5e01bd Modify comment_ranges slice in BackwardsTokenizer (#7432)
## Summary

I was kinda curious to understand this issue
(https://github.com/astral-sh/ruff/issues/7426) and just ended up
attempting to address it.

## Test Plan

`cargo test`
2023-09-16 14:04:45 -04:00
Charlie Marsh
aae02cf275 Fix broken is_expression_parenthesized call from rebase (#7442) 2023-09-16 17:22:16 +00:00
Charlie Marsh
7e2eba2592 Avoiding grouping comprehension targets separately from conditions (#7429)
## Summary

Black seems to treat comprehension targets and conditions as if they're
in a single group -- so if the comprehension expands, all conditions are
rendered on their own line, etc.

Closes https://github.com/astral-sh/ruff/issues/7421.

## Test Plan

`cargo test`

No change in any of the similarity metrics.

Before:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99982 | 2760 | 37 |
| transformers | 0.99957 | 2587 | 399 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99923 | 648 | 18 |
| zulip | 0.99962 | 1437 | 22 |

After:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99982 | 2760 | 37 |
| transformers | 0.99957 | 2587 | 399 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99923 | 648 | 18 |
| zulip | 0.99962 | 1437 | 22 |
2023-09-16 17:19:34 +00:00
Charlie Marsh
22770fb4be Avoid extra parentheses in await expressions (#7424)
## Summary

This PR aligns the await parenthesizing with the unary case, which is:
if the value is already parenthesized, avoid parenthesizing; otherwise,
only parenthesize if the _value_ needs parenthesizing.

Closes https://github.com/astral-sh/ruff/issues/7420.

## Test Plan

`cargo test`

No change in similarity.

Before:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99982 | 2760 | 37 |
| transformers | 0.99957 | 2587 | 399 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99923 | 648 | 18 |
| zulip | 0.99962 | 1437 | 22 |

After:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99982 | 2760 | 37 |
| transformers | 0.99957 | 2587 | 399 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99923 | 648 | 18 |
| zulip | 0.99962 | 1437 | 22 |
2023-09-16 13:10:35 -04:00
Charlie Marsh
1880cceac1 Avoid extra parentheses in unary expressions (#7428)
## Summary

This PR applies a similar fix to unary expressions as in
https://github.com/astral-sh/ruff/pull/7424. Specifically, we only need
to parenthesize the entire operator if the operand itself doesn't have
parentheses, and requires parentheses.

Closes https://github.com/astral-sh/ruff/issues/7423.

## Test Plan

`cargo test`

No change in similarity.

Before:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99982 | 2760 | 37 |
| transformers | 0.99957 | 2587 | 399 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99923 | 648 | 18 |
| zulip | 0.99962 | 1437 | 22 |

After:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99982 | 2760 | 37 |
| transformers | 0.99957 | 2587 | 399 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99923 | 648 | 18 |
| zulip | 0.99962 | 1437 | 22 |
2023-09-16 13:07:38 -04:00
Dhruv Manilawala
0d1fb823d6 [flake8-logging] Implement LOG002: invalid-get-logger-argument (#7399)
## Summary

This PR implements a new rule for `flake8-logging` plugin that checks
for
`logging.getLogger` calls with either `__file__` or `__cached__` as the
first
argument and generates a suggested fix to use `__name__` instead.

Refer: #7248

## Test Plan

Add test cases and `cargo test`
2023-09-16 12:21:30 -04:00
Micha Reiser
c907317199 Fix build (#7437) 2023-09-16 14:50:36 +00:00
Micha Reiser
916dd5b7fa fix: Use BestFit layout for subscript (#7409) 2023-09-16 16:21:45 +02:00
konsti
2cbe1733c8 Use CommentRanges in backwards lexing (#7360)
## Summary

The tokenizer was split into a forward and a backwards tokenizer. The
backwards tokenizer uses the same names as the forwards ones (e.g.
`next_token`). The backwards tokenizer gets the comment ranges that we
already built to skip comments.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2023-09-16 03:21:45 +00:00
Charlie Marsh
1f6e1485f9 Change playground to use pages deploy (#7430)
Fixes a deprecation warning.
2023-09-16 03:11:34 +00:00
Charlie Marsh
9b43162cc4 Move documentation to docs.astral.sh/ruff (#7419)
## Summary

We're planning to move the documentation from
[https://beta.ruff.rs/docs](https://beta.ruff.rs/docs) to
[https://docs.astral.sh/ruff](https://docs.astral.sh/ruff), for a few
reasons:

1. We want to remove the `beta` from the domain, as Ruff is no longer
considered beta software.
2. We want to migrate to a structure that could accommodate multiple
future tools living under one domain.

The docs are actually already live at
[https://docs.astral.sh/ruff](https://docs.astral.sh/ruff), but later
today, I'll add a permanent redirect from the previous to the new
domain. **All existing links will continue to work, now and in
perpetuity.**

This PR contains the code changes necessary for the updated
documentation. As part of this effort, I moved the playground and
documentation from my personal Cloudflare account to our team Cloudflare
account (hence the new `--project-name` references). After merging, I'll
also update the secrets on this repo.
2023-09-15 22:49:42 -04:00
Charlie Marsh
cc9e84c144 Format trailing operator comments as dangling (#7427)
## Summary

Given a trailing operator comment in a unary expression, like:

```python
if (
  not  # comment
  a):
    ...
```

We were attaching these to the operand (`a`), but formatting them in the
unary operator via special handling. Parents shouldn't format the
comments of their children, so this instead attaches them as dangling
comments on the unary expression. (No intended change in formatting.)
2023-09-15 20:34:09 -04:00
Zanie Blue
f4d50a2aec Fix release validation step (#7417)
Proof of concept at #7416
Fixes `main` branch check added in #7279 (see
[failure](https://github.com/astral-sh/ruff/actions/runs/6201772425/job/16839150669))
Removes the meaningless "SHA consistency" check (since we literally
check out the SHA now)
2023-09-15 20:18:25 +00:00
Zanie Blue
0c030b5bf3 Bump version to 0.0.290 (#7413)
See also:
- https://github.com/astral-sh/astral-sh/pull/41
- https://github.com/astral-sh/ruff-pre-commit/pull/51
2023-09-15 13:51:46 -05:00
Zanie Blue
1b082ce67e Add maximum length for line-length to JSON schema (#7412)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
Adds the maximum of 320 for the line-length setting to the JSON schema
for better integration with IDEs.

Related https://github.com/astral-sh/ruff/pull/6873

## Test Plan

<!-- How was it tested? -->
2023-09-15 18:10:06 +00:00
dependabot[bot]
f936d319cc Bump toml from 0.7.6 to 0.7.8 (#7405)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-15 09:14:58 +00:00
dependabot[bot]
85d8b6228f Bump chrono from 0.4.28 to 0.4.30 (#7406)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-15 09:00:17 +00:00
dependabot[bot]
7594dadc1d Bump syn from 2.0.29 to 2.0.33 (#7404)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-15 08:49:52 +00:00
dependabot[bot]
de37fbfac9 Bump serde-wasm-bindgen from 0.5.0 to 0.6.0 (#7403)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-15 08:47:42 +00:00
dependabot[bot]
4e2769a16c Bump mimalloc from 0.1.38 to 0.1.39 (#7402)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-15 08:46:55 +00:00
Manuel Jacob
75b5c314e3 Change CWE reference in documentation for S607 rule (#7398)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

The previous reference was “CWE-78: Improper Neutralization of Special
Elements used in an OS Command ('OS Command Injection')”, which
describes another issue. The new reference is “CWE-426: Untrusted Search
Path”, which describes exactly the problem that this rule should warn
about.

## Test Plan

The change was not tested, as it only changes two numbers in the
documentation.
2023-09-14 23:12:54 -05:00
Charlie Marsh
450fb9b99a [flake8-logging] Implement LOG001: direct-logger-instantiation (#7397)
## Summary

See: https://github.com/astral-sh/ruff/issues/7248.
2023-09-14 23:07:10 -04:00
Charlie Marsh
6163c99551 Mark PERF403 as a preview rule (#7396) 2023-09-15 01:57:48 +00:00
qdegraaf
3112202a5b [flake8-logging] Add flake8_logging boilerplate and first rule LOG009 (#7249)
## Summary

Adds `LOG009` from
[flake8-logging](https://github.com/adamchainz/flake8-logging). Also
adds the boilerplate for a new plugin

Checks for usages of undocumented `logging.WARN` constant and suggests
replacement with `logging.WARNING`.

## Test Plan

`cargo test` with fresh fixture

## Issue links

Refers: https://github.com/astral-sh/ruff/issues/7248
2023-09-15 01:41:32 +00:00
Charlie Marsh
64ea00048b Add known problems to UP040 documentation (#7395) 2023-09-15 01:31:20 +00:00
qdegraaf
067a4acd54 [perflint] Add PERF403 (#6132)
## Summary

Adds `PERF403` mirroring `W8403` from
https://github.com/tonybaloney/perflint

## Test Plan

Fixtures were added based on perflint tests

## Issue Links

Refers: https://github.com/astral-sh/ruff/issues/4789
2023-09-15 01:23:51 +00:00
Nathan Whitaker
c88376f468 Add support for bounds, constraints, and explicit variance on generic type variables to UP040 (#6749)
## Summary

Extends UP040 to support moving type variables with
bounds/constraints/variance that are used in type aliases to use PEP-695
syntax.

Part of #4617.

## Test Plan

The existing tests added by #6314 already cover the relevant cases.
2023-09-14 21:11:24 -04:00
Charlie Marsh
9b7c29853d Reflect conversion reason in UP012 messages (#7393)
Closes https://github.com/astral-sh/ruff/issues/7254.
2023-09-14 20:08:58 +00:00
Charlie Marsh
3e21d32b79 Avoid flagging single-quoted docstrings with continuations for multi-line rules (#7392)
Rules like D209 and D205 are only intended to apply to multi-line
docstrings. If a docstring is single-quoted, but extends via a
continuation, it should be excluded (it'll be flagged by another rule
anyway). Closes https://github.com/astral-sh/ruff/issues/7058.
2023-09-14 18:58:39 +00:00
Charlie Marsh
f9e3ea23ba Show rule codes in shell tab completion (#7375)
## Summary

I noticed that we have a custom parser for rule selectors, but it wasn't
actually being used? This PR adds it back to our Clap setup and changes
the parser to only show full categories and individual rules when
tab-completing:

<img width="1792" alt="Screen Shot 2023-09-13 at 9 13 38 PM"
src="https://github.com/astral-sh/ruff/assets/1309177/028b18d2-8c92-49c1-b781-f24c9ae310f7">

<img width="1792" alt="Screen Shot 2023-09-13 at 9 13 40 PM"
src="https://github.com/astral-sh/ruff/assets/1309177/fd598da5-78fb-412d-a69e-2a0963d479cd">

<img width="1792" alt="Screen Shot 2023-09-13 at 9 13 58 PM"
src="https://github.com/astral-sh/ruff/assets/1309177/7c482b90-6e54-425c-ae23-fb50496a177a">

The previous implementation showed all codes, which I found too noisy:

<img width="1792" alt="Screen Shot 2023-09-13 at 8 57 09 PM"
src="https://github.com/astral-sh/ruff/assets/1309177/db370a0e-2a9f-4acd-b1e3-224a1f8e9ce5">
2023-09-14 18:37:23 +00:00
Charlie Marsh
6856d0b44b Use dot references in docs for methods (#7391)
## Summary

This matches the convention used in the Python documentation.
2023-09-14 14:35:34 -04:00
Charlie Marsh
21539f1663 Allow NURSERY in JSON Schema (#7374)
## Summary

At some point, we removed these so that they wouldn't be autocompleted
for users, since we wanted to discourage usage of `ALL`. But given that
they're valid values, I think that was a bad idea -- it leads to an even
more confusing experience whereby JSON Schema validators tell you that
you have an error, when you don't.

Closes https://github.com/astral-sh/ruff/issues/7261.
2023-09-14 18:09:35 +00:00
Zanie Blue
b9bb6bf780 Remove the PREVIEW rule selector (#7389)
The rule selector is not useful because `--select PREVIEW` only targets
Ruff developers and `--ignore PREVIEW` has no effect due to its low
specificity. We may restore it later if useful.
2023-09-14 12:31:59 -05:00
Charlie Marsh
d39eae2713 Extend C416 to catch tuple unpacking (#7363)
Closes https://github.com/astral-sh/ruff/issues/7307.
2023-09-14 15:53:15 +00:00
Charlie Marsh
5d21b9c22e Catch panics in formatter (#7377)
## Summary

This PR ensures that we catch and render panics in the formatter
identically to other kinds of errors. It also improves the consistency
in error rendering throughout and makes a few stylistic changes to the
messages.

Closes https://github.com/astral-sh/ruff/issues/7247.

## Test Plan

I created a file `foo.py` with a syntax error, and a file `bar.py` with
an intentional panic.

<img width="1624" alt="Screen Shot 2023-09-13 at 10 25 22 PM"
src="https://github.com/astral-sh/ruff/assets/1309177/605c2839-ad02-4376-a2e9-d5a593ab660f">

<img width="1624" alt="Screen Shot 2023-09-13 at 10 25 24 PM"
src="https://github.com/astral-sh/ruff/assets/1309177/b1381909-157c-48cb-9630-d0bbfcb1b640">
2023-09-14 11:44:16 -04:00
Charlie Marsh
ec2f229a45 Remove ExprContext from ComparableExpr (#7362)
`ComparableExpr` includes the `ExprContext` field on an expression, so,
e.g., the two tuples in `(a, b) = (a, b)` won't be considered equal.
Similarly, the tuples in `[(a, b) for (a, b) in c]` _also_ wouldn't be
considered equal. I find this behavior surprising, since
`ComparableExpr` is intended to allow you to compare two ASTs, but
`ExprContext` is really encoding information about the broader context
for the expression.
2023-09-14 15:40:02 +00:00
Charlie Marsh
34c1cb7d11 Treat parenthesized power operands as non-simple (#7371)
Closes https://github.com/astral-sh/ruff/issues/7318.
2023-09-14 15:36:21 +00:00
dependabot[bot]
2ddea7c657 Bump regex from 1.9.4 to 1.9.5 (#7384)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-14 14:58:24 +00:00
dependabot[bot]
45eabdd2c3 Bump shlex from 1.1.0 to 1.2.0 (#7381)
Bumps [shlex](https://github.com/comex/rust-shlex) from 1.1.0 to 1.2.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/comex/rust-shlex/blob/master/CHANGELOG.md">shlex's
changelog</a>.</em></p>
<blockquote>
<h1>1.2.0</h1>
<ul>
<li>Adds <code>bytes</code> module to support operating directly on byte
strings.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/comex/rust-shlex/commits">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=shlex&package-manager=cargo&previous-version=1.1.0&new-version=1.2.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2023-09-14 09:40:05 -05:00
Micha Reiser
675c86c175 fix: Group fluent subscript (#7386) 2023-09-14 13:04:14 +02:00
dependabot[bot]
1f8e2b8f14 Bump proc-macro2 from 1.0.66 to 1.0.67 (#7383)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-14 09:49:57 +00:00
Micha Reiser
58b3040342 chore: Upgrade notify (#7338) 2023-09-14 09:47:55 +00:00
dependabot[bot]
6a9b8aede1 Bump thiserror from 1.0.47 to 1.0.48 (#7385)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-14 09:21:47 +00:00
dependabot[bot]
f48126ad00 Bump clap from 4.4.1 to 4.4.3 (#7382)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-14 09:13:12 +00:00
Charlie Marsh
11287f944f Avoid re-parenthesizing call chains whose inner values are parenthesized (#7373)
## Summary

Given a statement like:

```python
result = (
    f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
    + 1
)()
```

When we go to parenthesize the target of the assignment, we use
`maybe_parenthesize_expression` with `Parenthesize::IfBreaks`. This then
checks if the call on the right-hand side needs to be parenthesized, the
implementation of which looks like:

```rust
impl NeedsParentheses for ExprCall {
    fn needs_parentheses(
        &self,
        _parent: AnyNodeRef,
        context: &PyFormatContext,
    ) -> OptionalParentheses {
        if CallChainLayout::from_expression(self.into(), context.source())
            == CallChainLayout::Fluent
        {
            OptionalParentheses::Multiline
        } else if context.comments().has_dangling(self) {
            OptionalParentheses::Always
        } else {
            self.func.needs_parentheses(self.into(), context)
        }
    }
}
```

Checking for `self.func.needs_parentheses(self.into(), context)` is
problematic, since, as in the example above, `self.func` may _already_
be parenthesized -- in which case, we _don't_ want to parenthesize the
entire expression. If we do, we end up with this non-ideal formatting:

```python
result = (
    (
        f(
            111111111111111111111111111111111111111111111111111111111111111111111111111111111
        )
        + 1
    )()
)
```

This PR modifies the `NeedsParentheses` implementations for call chain
expressions to return `Never` if the inner expression has its own
parentheses, in which case, the formatting implementations for those
expressions will preserve them anyway.

Closes https://github.com/astral-sh/ruff/issues/7370.

## Test Plan

Zulip improves a bit, everything else is unchanged.

Before:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99981 | 2760 | 40 |
| transformers | 0.99944 | 2587 | 413 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99834 | 648 | 20 |
| zulip | 0.99956 | 1437 | 23 |

After:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99981 | 2760 | 40 |
| transformers | 0.99944 | 2587 | 413 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99834 | 648 | 20 |
| **zulip** | **0.99962** | **1437** | **22** |
2023-09-14 05:05:37 -04:00
Micha Reiser
a65efcf459 fix: Don't omit optional parentheses for subscripts (#7380) 2023-09-14 08:43:53 +00:00
Jelle van der Waa
04183b0299 [pylint] Implement too-many-public-methods rule (PLR0904) (#6179)
Implement
https://pylint.pycqa.org/en/latest/user_guide/messages/refactor/too-many-public-methods.html

Confusingly the rule page mentions a max of 7 while in practice it is
20.
https://github.com/search?q=repo%3Apylint-dev%2Fpylint+max-public-methods&type=code

## Summary

Implement pylint's R0904

## Test Plan

Unit tests.
2023-09-14 00:52:26 +00:00
James Braza
36fa1fe359 Docs linking error tutorial with error suppression (#7014)
Documents takeaway from
https://github.com/astral-sh/ruff/discussions/7011#discussioncomment-6869239.
2023-09-13 20:22:18 -04:00
Charlie Marsh
6e625bd93d Invert reverse argument regardless of whether it's a boolean (#7372)
## Summary

When fixing `reversed(sorted(x, reverse=False))`, we rewrite as
`sorted(x, reverse=True)`. However, if the `reverse` argument isn't
`True` or `False`, we leave it as-is, which is incorrect.

Now, given `reversed(sorted(x, reverse=y))`, we rewrite as `sorted(x,
reverse=not y)`.
2023-09-13 20:12:35 -04:00
4636 changed files with 118890 additions and 114036 deletions

4
.gitattributes vendored
View File

@@ -1,7 +1,7 @@
* text=auto eol=lf
crates/ruff/resources/test/fixtures/isort/line_ending_crlf.py text eol=crlf
crates/ruff/resources/test/fixtures/pycodestyle/W605_1.py text eol=crlf
crates/ruff_linter/resources/test/fixtures/isort/line_ending_crlf.py text eol=crlf
crates/ruff_linter/resources/test/fixtures/pycodestyle/W605_1.py text eol=crlf
ruff.schema.json linguist-generated=true text=auto eol=lf
*.md.snap linguist-language=Markdown

2
.github/CODEOWNERS vendored
View File

@@ -6,4 +6,4 @@
# - Order is important. The last matching pattern has the most precedence.
# Jupyter
/crates/ruff/src/jupyter/ @dhruvmanila
/crates/ruff_linter/src/jupyter/ @dhruvmanila

View File

@@ -52,4 +52,4 @@ jobs:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
# `github.head_ref` is only set during pull requests and for manual runs or tags we use `main` to deploy to production
command: pages deploy site --project-name=ruff-docs --branch ${{ github.head_ref || 'main' }} --commit-hash ${GITHUB_SHA}
command: pages deploy site --project-name=astral-docs --branch ${{ github.head_ref || 'main' }} --commit-hash ${GITHUB_SHA}

View File

@@ -44,4 +44,4 @@ jobs:
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
command: pages publish playground/dist --project-name=ruff --branch ${GITHUB_HEAD_REF} --commit-hash ${GITHUB_SHA}
command: pages deploy playground/dist --project-name=ruff-playground --branch ${GITHUB_HEAD_REF} --commit-hash ${GITHUB_SHA}

View File

@@ -419,18 +419,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.sha }}
- name: Check tag consistency
run: |
version=$(grep "version = " pyproject.toml | sed -e 's/version = "\(.*\)"/\1/g')
if [ "${{ inputs.tag }}" != "${version}" ]; then
echo "The input tag does not match the version from pyproject.toml:" >&2
echo "${{ inputs.tag }}" >&2
echo "${version}" >&2
exit 1
else
echo "Releasing ${version}"
fi
ref: main # We checkout the main branch to check for the commit
- name: Check main branch
if: ${{ inputs.sha }}
run: |
@@ -440,17 +429,18 @@ jobs:
echo "The specified sha is not on the main branch" >&2
exit 1
fi
- name: Check SHA consistency
if: ${{ inputs.sha }}
- name: Check tag consistency
run: |
git_sha=$(git rev-parse HEAD)
if [ "${{ inputs.sha }}" != "${git_sha}" ]; then
echo "The specified sha does not match the git checkout" >&2
echo "${{ inputs.sha }}" >&2
echo "${git_sha}" >&2
# Switch to the commit we want to release
git checkout ${{ inputs.sha }}
version=$(grep "version = " pyproject.toml | sed -e 's/version = "\(.*\)"/\1/g')
if [ "${{ inputs.tag }}" != "${version}" ]; then
echo "The input tag does not match the version from pyproject.toml:" >&2
echo "${{ inputs.tag }}" >&2
echo "${version}" >&2
exit 1
else
echo "Releasing ${git_sha}"
echo "Releasing ${version}"
fi
upload-release:

2
.gitignore vendored
View File

@@ -1,5 +1,5 @@
# Benchmarking cpython (CONTRIBUTING.md)
crates/ruff/resources/test/cpython
crates/ruff_linter/resources/test/cpython
# generate_mkdocs.py
mkdocs.generated.yml
# check_ecosystem.py

View File

@@ -2,8 +2,8 @@ fail_fast: true
exclude: |
(?x)^(
crates/ruff/resources/.*|
crates/ruff/src/rules/.*/snapshots/.*|
crates/ruff_linter/resources/.*|
crates/ruff_linter/src/rules/.*/snapshots/.*|
crates/ruff_cli/resources/.*|
crates/ruff_python_formatter/resources/.*|
crates/ruff_python_formatter/tests/snapshots/.*|
@@ -50,7 +50,7 @@ repos:
require_serial: true
exclude: |
(?x)^(
crates/ruff/resources/.*|
crates/ruff_linter/resources/.*|
crates/ruff_python_formatter/resources/.*
)$

View File

@@ -299,4 +299,4 @@ default.
`pyproject.toml` files are now resolved hierarchically, such that for each Python file, we find
the first `pyproject.toml` file in its path, and use that to determine its lint settings.
See the [documentation](https://beta.ruff.rs/docs/configuration/#python-file-discovery) for more.
See the [documentation](https://docs.astral.sh/ruff/configuration/#python-file-discovery) for more.

View File

@@ -112,11 +112,11 @@ Ruff is structured as a monorepo with a [flat crate structure](https://matklad.g
such that all crates are contained in a flat `crates` directory.
The vast majority of the code, including all lint rules, lives in the `ruff` crate (located at
`crates/ruff`). As a contributor, that's the crate that'll be most relevant to you.
`crates/ruff_linter`). As a contributor, that's the crate that'll be most relevant to you.
At time of writing, the repository includes the following crates:
- `crates/ruff`: library crate containing all lint rules and the core logic for running them.
- `crates/ruff_linter`: library crate containing all lint rules and the core logic for running them.
If you're working on a rule, this is the crate for you.
- `crates/ruff_benchmark`: binary crate for running micro-benchmarks.
- `crates/ruff_cache`: library crate for caching lint results.
@@ -153,7 +153,7 @@ At a high level, the steps involved in adding a new lint rule are as follows:
1. Determine a name for the new rule as per our [rule naming convention](#rule-naming-convention)
(e.g., `AssertFalse`, as in, "allow `assert False`").
1. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/assert_false.rs`).
1. Create a file for your rule (e.g., `crates/ruff_linter/src/rules/flake8_bugbear/rules/assert_false.rs`).
1. In that file, define a violation struct (e.g., `pub struct AssertFalse`). You can grep for
`#[violation]` to see examples.
@@ -162,21 +162,21 @@ At a high level, the steps involved in adding a new lint rule are as follows:
(e.g., `pub(crate) fn assert_false`) based on whatever inputs are required for the rule (e.g.,
an `ast::StmtAssert` node).
1. Define the logic for invoking the diagnostic in `crates/ruff/src/checkers/ast/analyze` (for
AST-based rules), `crates/ruff/src/checkers/tokens.rs` (for token-based rules),
`crates/ruff/src/checkers/physical_lines.rs` (for text-based rules),
`crates/ruff/src/checkers/filesystem.rs` (for filesystem-based rules), etc. For AST-based rules,
1. Define the logic for invoking the diagnostic in `crates/ruff_linter/src/checkers/ast/analyze` (for
AST-based rules), `crates/ruff_linter/src/checkers/tokens.rs` (for token-based rules),
`crates/ruff_linter/src/checkers/physical_lines.rs` (for text-based rules),
`crates/ruff_linter/src/checkers/filesystem.rs` (for filesystem-based rules), etc. For AST-based rules,
you'll likely want to modify `analyze/statement.rs` (if your rule is based on analyzing
statements, like imports) or `analyze/expression.rs` (if your rule is based on analyzing
expressions, like function calls).
1. Map the violation struct to a rule code in `crates/ruff/src/codes.rs` (e.g., `B011`).
1. Map the violation struct to a rule code in `crates/ruff_linter/src/codes.rs` (e.g., `B011`).
1. Add proper [testing](#rule-testing-fixtures-and-snapshots) for your rule.
1. Update the generated files (documentation and generated code).
To trigger the violation, you'll likely want to augment the logic in `crates/ruff/src/checkers/ast.rs`
To trigger the violation, you'll likely want to augment the logic in `crates/ruff_linter/src/checkers/ast.rs`
to call your new function at the appropriate time and with the appropriate inputs. The `Checker`
defined therein is a Python AST visitor, which iterates over the AST, building up a semantic model,
and calling out to lint rule analyzer functions as it goes.
@@ -221,7 +221,7 @@ Ruff's output for each fixture, which you can then commit alongside your changes
Once you've completed the code for the rule itself, you can define tests with the following steps:
1. Add a Python file to `crates/ruff/resources/test/fixtures/[linter]` that contains the code you
1. Add a Python file to `crates/ruff_linter/resources/test/fixtures/[linter]` that contains the code you
want to test. The file name should match the rule name (e.g., `E402.py`), and it should include
examples of both violations and non-violations.
@@ -230,16 +230,16 @@ Once you've completed the code for the rule itself, you can define tests with th
For example, if you're adding a new rule named `E402`, you would run:
```shell
cargo run -p ruff_cli -- check crates/ruff/resources/test/fixtures/pycodestyle/E402.py --no-cache --select E402
cargo run -p ruff_cli -- check crates/ruff_linter/resources/test/fixtures/pycodestyle/E402.py --no-cache --select E402
```
**Note:** Only a subset of rules are enabled by default. When testing a new rule, ensure that
you activate it by adding `--select ${rule_code}` to the command.
1. Add the test to the relevant `crates/ruff/src/rules/[linter]/mod.rs` file. If you're contributing
1. Add the test to the relevant `crates/ruff_linter/src/rules/[linter]/mod.rs` file. If you're contributing
a rule to a pre-existing set, you should be able to find a similar example to pattern-match
against. If you're adding a new linter, you'll need to create a new `mod.rs` file (see,
e.g., `crates/ruff/src/rules/flake8_bugbear/mod.rs`)
e.g., `crates/ruff_linter/src/rules/flake8_bugbear/mod.rs`)
1. Run `cargo test`. Your test will fail, but you'll be prompted to follow-up
with `cargo insta review`. Run `cargo insta review`, review and accept the generated snapshot,
@@ -251,25 +251,24 @@ Once you've completed the code for the rule itself, you can define tests with th
Ruff's user-facing settings live in a few different places.
First, the command-line options are defined via the `Cli` struct in `crates/ruff/src/cli.rs`.
First, the command-line options are defined via the `Args` struct in `crates/ruff_cli/src/args.rs`.
Second, the `pyproject.toml` options are defined in `crates/ruff/src/settings/options.rs` (via the
`Options` struct), `crates/ruff/src/settings/configuration.rs` (via the `Configuration` struct), and
`crates/ruff/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.
Second, the `pyproject.toml` options are defined in `crates/ruff_workspace/src/options.rs` (via the
`Options` struct), `crates/ruff_workspace/src/configuration.rs` (via the `Configuration` struct),
and `crates/ruff_workspace/src/settings.rs` (via the `Settings` struct), which then includes
the `LinterSettings` struct as a field.
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.
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
`arg.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.,
`crates/ruff/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
lives in `crates/ruff/src/flake8_to_ruff/converter.rs`.
`Settings` in `crates/ruff_linter/src/flake8_unused_arguments/settings.rs` coupled with
`Flake8UnusedArgumentsOptions` in `crates/ruff_workspace/src/options.rs`).
Finally, regenerate the documentation and generated code with `cargo dev generate-all`.
@@ -362,46 +361,46 @@ First, clone [CPython](https://github.com/python/cpython). It's a large and dive
which makes it a good target for benchmarking.
```shell
git clone --branch 3.10 https://github.com/python/cpython.git crates/ruff/resources/test/cpython
git clone --branch 3.10 https://github.com/python/cpython.git crates/ruff_linter/resources/test/cpython
```
To benchmark the release build:
```shell
cargo build --release && hyperfine --warmup 10 \
"./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache -e" \
"./target/release/ruff ./crates/ruff/resources/test/cpython/ -e"
"./target/release/ruff ./crates/ruff_linter/resources/test/cpython/ --no-cache -e" \
"./target/release/ruff ./crates/ruff_linter/resources/test/cpython/ -e"
Benchmark 1: ./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache
Benchmark 1: ./target/release/ruff ./crates/ruff_linter/resources/test/cpython/ --no-cache
Time (mean ± σ): 293.8 ms ± 3.2 ms [User: 2384.6 ms, System: 90.3 ms]
Range (min … max): 289.9 ms … 301.6 ms 10 runs
Benchmark 2: ./target/release/ruff ./crates/ruff/resources/test/cpython/
Benchmark 2: ./target/release/ruff ./crates/ruff_linter/resources/test/cpython/
Time (mean ± σ): 48.0 ms ± 3.1 ms [User: 65.2 ms, System: 124.7 ms]
Range (min … max): 45.0 ms … 66.7 ms 62 runs
Summary
'./target/release/ruff ./crates/ruff/resources/test/cpython/' ran
6.12 ± 0.41 times faster than './target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache'
'./target/release/ruff ./crates/ruff_linter/resources/test/cpython/' ran
6.12 ± 0.41 times faster than './target/release/ruff ./crates/ruff_linter/resources/test/cpython/ --no-cache'
```
To benchmark against the ecosystem's existing tools:
```shell
hyperfine --ignore-failure --warmup 5 \
"./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache" \
"pyflakes crates/ruff/resources/test/cpython" \
"./target/release/ruff ./crates/ruff_linter/resources/test/cpython/ --no-cache" \
"pyflakes crates/ruff_linter/resources/test/cpython" \
"autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython" \
"pycodestyle crates/ruff/resources/test/cpython" \
"flake8 crates/ruff/resources/test/cpython"
"pycodestyle crates/ruff_linter/resources/test/cpython" \
"flake8 crates/ruff_linter/resources/test/cpython"
Benchmark 1: ./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache
Benchmark 1: ./target/release/ruff ./crates/ruff_linter/resources/test/cpython/ --no-cache
Time (mean ± σ): 294.3 ms ± 3.3 ms [User: 2467.5 ms, System: 89.6 ms]
Range (min … max): 291.1 ms … 302.8 ms 10 runs
Warning: Ignoring non-zero exit code.
Benchmark 2: pyflakes crates/ruff/resources/test/cpython
Benchmark 2: pyflakes crates/ruff_linter/resources/test/cpython
Time (mean ± σ): 15.786 s ± 0.143 s [User: 15.560 s, System: 0.214 s]
Range (min … max): 15.640 s … 16.157 s 10 runs
@@ -411,31 +410,31 @@ Benchmark 3: autoflake --recursive --expand-star-imports --remove-all-unused-imp
Time (mean ± σ): 6.175 s ± 0.169 s [User: 54.102 s, System: 1.057 s]
Range (min … max): 5.950 s … 6.391 s 10 runs
Benchmark 4: pycodestyle crates/ruff/resources/test/cpython
Benchmark 4: pycodestyle crates/ruff_linter/resources/test/cpython
Time (mean ± σ): 46.921 s ± 0.508 s [User: 46.699 s, System: 0.202 s]
Range (min … max): 46.171 s … 47.863 s 10 runs
Warning: Ignoring non-zero exit code.
Benchmark 5: flake8 crates/ruff/resources/test/cpython
Benchmark 5: flake8 crates/ruff_linter/resources/test/cpython
Time (mean ± σ): 12.260 s ± 0.321 s [User: 102.934 s, System: 1.230 s]
Range (min … max): 11.848 s … 12.933 s 10 runs
Warning: Ignoring non-zero exit code.
Summary
'./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache' ran
'./target/release/ruff ./crates/ruff_linter/resources/test/cpython/ --no-cache' ran
20.98 ± 0.62 times faster than 'autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython'
41.66 ± 1.18 times faster than 'flake8 crates/ruff/resources/test/cpython'
53.64 ± 0.77 times faster than 'pyflakes crates/ruff/resources/test/cpython'
159.43 ± 2.48 times faster than 'pycodestyle crates/ruff/resources/test/cpython'
41.66 ± 1.18 times faster than 'flake8 crates/ruff_linter/resources/test/cpython'
53.64 ± 0.77 times faster than 'pyflakes crates/ruff_linter/resources/test/cpython'
159.43 ± 2.48 times faster than 'pycodestyle crates/ruff_linter/resources/test/cpython'
```
To benchmark a subset of rules, e.g. `LineTooLong` and `DocLineTooLong`:
```shell
cargo build --release && hyperfine --warmup 10 \
"./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache -e --select W505,E501"
"./target/release/ruff ./crates/ruff_linter/resources/test/cpython/ --no-cache -e --select W505,E501"
```
You can run `poetry install` from `./scripts/benchmarks` to create a working environment for the
@@ -468,10 +467,10 @@ rm Lib/test/bad_coding.py \
Lib/test/test_typing.py
```
Then, from `crates/ruff/resources/test/cpython`, run: `time pylint -j 0 -E $(git ls-files '*.py')`. This
Then, from `crates/ruff_linter/resources/test/cpython`, run: `time pylint -j 0 -E $(git ls-files '*.py')`. This
will execute Pylint with maximum parallelism and only report errors.
To benchmark Pyupgrade, run the following from `crates/ruff/resources/test/cpython`:
To benchmark Pyupgrade, run the following from `crates/ruff_linter/resources/test/cpython`:
```shell
hyperfine --ignore-failure --warmup 5 --prepare "git reset --hard HEAD" \
@@ -719,8 +718,8 @@ Module {
- `cargo dev generate-cli-help`, `cargo dev generate-docs` and `cargo dev generate-json-schema`:
Update just `docs/configuration.md`, `docs/rules` and `ruff.schema.json` respectively.
- `cargo dev generate-options`: Generate a markdown-compatible table of all `pyproject.toml`
options. Used for <https://beta.ruff.rs/docs/settings/>
- `cargo dev generate-rules-table`: Generate a markdown-compatible table of all rules. Used for <https://beta.ruff.rs/docs/rules/>
options. Used for <https://docs.astral.sh/ruff/settings/>.
- `cargo dev generate-rules-table`: Generate a markdown-compatible table of all rules. Used for <https://docs.astral.sh/ruff/rules/>.
- `cargo dev round-trip <python file or jupyter notebook>`: Read a Python file or Jupyter Notebook,
parse it, serialize the parsed representation and write it back. Used to check how good our
representation is so that fixes don't rewrite irrelevant parts of a file.
@@ -778,7 +777,7 @@ To understand Ruff's import categorization system, we first need to define two c
- "Package root": The top-most directory defining the Python package that includes a given Python
file. To find the package root for a given Python file, traverse up its parent directories until
you reach a parent directory that doesn't contain an `__init__.py` file (and isn't marked as
a [namespace package](https://beta.ruff.rs/docs/settings/#namespace-packages)); take the directory
a [namespace package](https://docs.astral.sh/ruff/settings/#namespace-packages)); take the directory
just before that, i.e., the first directory in the package.
For example, given:
@@ -867,7 +866,7 @@ There are three ways in which an import can be categorized as "first-party":
package (e.g., `from foo import bar` or `import foo.bar`), they'll be classified as first-party
automatically. This check is as simple as comparing the first segment of the current file's
module path to the first segment of the import.
1. **Source roots**: Ruff supports a `[src](https://beta.ruff.rs/docs/settings/#src)` setting, which
1. **Source roots**: Ruff supports a `[src](https://docs.astral.sh/ruff/settings/#src)` setting, which
sets the directories to scan when identifying first-party imports. The algorithm is
straightforward: given an import, like `import foo`, iterate over the directories enumerated in
the `src` setting and, for each directory, check for the existence of a subdirectory `foo` or a

567
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,8 +5,8 @@ resolver = "2"
[workspace.package]
edition = "2021"
rust-version = "1.71"
homepage = "https://beta.ruff.rs/docs"
documentation = "https://beta.ruff.rs/docs"
homepage = "https://docs.astral.sh/ruff"
documentation = "https://docs.astral.sh/ruff"
repository = "https://github.com/astral-sh/ruff"
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
license = "MIT"
@@ -14,8 +14,8 @@ license = "MIT"
[workspace.dependencies]
anyhow = { version = "1.0.69" }
bitflags = { version = "2.3.1" }
chrono = { version = "0.4.23", default-features = false, features = ["clock"] }
clap = { version = "4.1.8", features = ["derive"] }
chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
clap = { version = "4.4.4", features = ["derive"] }
colored = { version = "2.0.0" }
filetime = { version = "0.2.20" }
glob = { version = "0.3.1" }
@@ -23,35 +23,35 @@ globset = { version = "0.4.10" }
ignore = { version = "0.4.20" }
insta = { version = "1.31.0", feature = ["filters", "glob"] }
is-macro = { version = "0.3.0" }
itertools = { version = "0.10.5" }
itertools = { version = "0.11.0" }
log = { version = "0.4.17" }
memchr = "2.6.3"
num-bigint = { version = "0.4.3" }
num-traits = { version = "0.2.15" }
once_cell = { version = "1.17.1" }
path-absolutize = { version = "3.1.1" }
proc-macro2 = { version = "1.0.51" }
proc-macro2 = { version = "1.0.67" }
quote = { version = "1.0.23" }
regex = { version = "1.7.1" }
regex = { version = "1.9.5" }
rustc-hash = { version = "1.1.0" }
schemars = { version = "0.8.12" }
schemars = { version = "0.8.15" }
serde = { version = "1.0.152", features = ["derive"] }
serde_json = { version = "1.0.106" }
serde_json = { version = "1.0.107" }
shellexpand = { version = "3.0.0" }
similar = { version = "2.2.1", features = ["inline"] }
smallvec = { version = "1.10.0" }
static_assertions = "1.1.0"
strum = { version = "0.25.0", features = ["strum_macros"] }
strum_macros = { version = "0.25.2" }
syn = { version = "2.0.15" }
test-case = { version = "3.0.0" }
thiserror = { version = "1.0.43" }
toml = { version = "0.7.2" }
syn = { version = "2.0.37" }
test-case = { version = "3.2.1" }
thiserror = { version = "1.0.48" }
toml = { version = "0.7.8" }
tracing = "0.1.37"
tracing-indicatif = "0.3.4"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
unicode-ident = "1.0.11"
unicode-width = "0.1.10"
unicode-ident = "1.0.12"
unicode-width = "0.1.11"
uuid = { version = "1.4.1", features = ["v4", "fast-rng", "macro-diagnostics", "js"] }
wsl = { version = "0.1.0" }

47
LICENSE
View File

@@ -1194,7 +1194,27 @@ are:
- flake8-self, licensed as follows:
"""
Freely Distributable
MIT License
Copyright (c) 2023 Korijn van Golen
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-django, licensed under the GPL license.
@@ -1224,6 +1244,31 @@ are:
SOFTWARE.
"""
- flake8-logging, licensed as follows:
"""
MIT License
Copyright (c) 2023 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.
"""
- Pyright, licensed as follows:
"""
MIT License

View File

@@ -8,7 +8,7 @@
[![image](https://img.shields.io/pypi/pyversions/ruff.svg)](https://pypi.python.org/pypi/ruff)
[![Actions status](https://github.com/astral-sh/ruff/workflows/CI/badge.svg)](https://github.com/astral-sh/ruff/actions)
[**Discord**](https://discord.gg/c9MhzV8aU5) | [**Docs**](https://beta.ruff.rs/docs/) | [**Playground**](https://play.ruff.rs/)
[**Discord**](https://discord.gg/c9MhzV8aU5) | [**Docs**](https://docs.astral.sh/ruff/) | [**Playground**](https://play.ruff.rs/)
An extremely fast Python linter, written in Rust.
@@ -30,13 +30,13 @@ An extremely fast Python linter, written in Rust.
- 🤝 Python 3.11 compatibility
- 📦 Built-in caching, to avoid re-analyzing unchanged files
- 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
- 📏 Over [600 built-in rules](https://beta.ruff.rs/docs/rules/)
- ⚖️ [Near-parity](https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-flake8) with the
- 📏 Over [600 built-in rules](https://docs.astral.sh/ruff/rules/)
- ⚖️ [Near-parity](https://docs.astral.sh/ruff/faq/#how-does-ruff-compare-to-flake8) with the
built-in Flake8 rule set
- 🔌 Native re-implementations of dozens of Flake8 plugins, like flake8-bugbear
- ⌨️ First-party [editor integrations](https://beta.ruff.rs/docs/editor-integrations/) for
- ⌨️ First-party [editor integrations](https://docs.astral.sh/ruff/editor-integrations/) for
[VS Code](https://github.com/astral-sh/ruff-vscode) and [more](https://github.com/astral-sh/ruff-lsp)
- 🌎 Monorepo-friendly, with [hierarchical and cascading configuration](https://beta.ruff.rs/docs/configuration/#pyprojecttoml-discovery)
- 🌎 Monorepo-friendly, with [hierarchical and cascading configuration](https://docs.astral.sh/ruff/configuration/#pyprojecttoml-discovery)
Ruff aims to be orders of magnitude faster than alternative tools while integrating more
functionality behind a single, common interface.
@@ -98,7 +98,7 @@ developer of [Zulip](https://github.com/zulip/zulip):
## Table of Contents
For more, see the [documentation](https://beta.ruff.rs/docs/).
For more, see the [documentation](https://docs.astral.sh/ruff/).
1. [Getting Started](#getting-started)
1. [Configuration](#configuration)
@@ -111,7 +111,7 @@ For more, see the [documentation](https://beta.ruff.rs/docs/).
## Getting Started
For more, see the [documentation](https://beta.ruff.rs/docs/).
For more, see the [documentation](https://docs.astral.sh/ruff/).
### Installation
@@ -122,7 +122,7 @@ pip install ruff
```
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
and with [a variety of other package managers](https://beta.ruff.rs/docs/installation/).
and with [a variety of other package managers](https://docs.astral.sh/ruff/installation/).
### Usage
@@ -140,7 +140,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.0.289
rev: v0.0.290
hooks:
- id: ruff
```
@@ -165,7 +165,7 @@ jobs:
### Configuration
Ruff can be configured through a `pyproject.toml`, `ruff.toml`, or `.ruff.toml` file (see:
[_Configuration_](https://beta.ruff.rs/docs/configuration/), or [_Settings_](https://beta.ruff.rs/docs/settings/)
[_Configuration_](https://docs.astral.sh/ruff/configuration/), or [_Settings_](https://docs.astral.sh/ruff/settings/)
for a complete list of all configuration options).
If left unspecified, the default configuration is equivalent to:
@@ -238,7 +238,7 @@ isort, pyupgrade, and others. Regardless of the rule's origin, Ruff re-implement
Rust as a first-party feature.
By default, Ruff enables Flake8's `E` and `F` rules. Ruff supports all rules from the `F` category,
and a [subset](https://beta.ruff.rs/docs/rules/#error-e) of the `E` category, omitting those
and a [subset](https://docs.astral.sh/ruff/rules/#error-e) of the `E` category, omitting those
stylistic rules made obsolete by the use of an autoformatter, like
[Black](https://github.com/psf/black).
@@ -274,6 +274,7 @@ quality tools, including:
- [flake8-gettext](https://pypi.org/project/flake8-gettext/)
- [flake8-implicit-str-concat](https://pypi.org/project/flake8-implicit-str-concat/)
- [flake8-import-conventions](https://github.com/joaopalmeiro/flake8-import-conventions)
- [flake8-logging](https://pypi.org/project/flake8-logging/)
- [flake8-logging-format](https://pypi.org/project/flake8-logging-format/)
- [flake8-no-pep420](https://pypi.org/project/flake8-no-pep420)
- [flake8-pie](https://pypi.org/project/flake8-pie/)
@@ -303,12 +304,12 @@ quality tools, including:
- [tryceratops](https://pypi.org/project/tryceratops/)
- [yesqa](https://pypi.org/project/yesqa/)
For a complete enumeration of the supported rules, see [_Rules_](https://beta.ruff.rs/docs/rules/).
For a complete enumeration of the supported rules, see [_Rules_](https://docs.astral.sh/ruff/rules/).
## Contributing
Contributions are welcome and highly appreciated. To get started, check out the
[**contributing guidelines**](https://beta.ruff.rs/docs/contributing/).
[**contributing guidelines**](https://docs.astral.sh/ruff/contributing/).
You can also join us on [**Discord**](https://discord.gg/c9MhzV8aU5).

View File

@@ -1,6 +1,6 @@
[package]
name = "flake8-to-ruff"
version = "0.0.289"
version = "0.0.290"
description = """
Convert Flake8 configuration files to Ruff configuration files.
"""
@@ -13,7 +13,7 @@ repository = { workspace = true }
license = { workspace = true }
[dependencies]
ruff = { path = "../ruff", default-features = false }
ruff_linter = { path = "../ruff_linter", default-features = false }
ruff_workspace = { path = "../ruff_workspace" }
anyhow = { workspace = true }

View File

@@ -86,7 +86,7 @@ flake8-to-ruff path/to/.flake8 --plugin flake8-builtins --plugin flake8-quotes
configuration options that don't exist in Flake8.)
1. Ruff will omit any rule codes that are unimplemented or unsupported by Ruff, including rule
codes from unsupported plugins. (See the
[documentation](https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-flake8) for the complete
[documentation](https://docs.astral.sh/ruff/faq/#how-does-ruff-compare-to-flake8) for the complete
list of supported plugins.)
## License

View File

@@ -1,7 +1,7 @@
//! Extract Black configuration settings from a pyproject.toml.
use ruff::line_width::LineLength;
use ruff::settings::types::PythonVersion;
use ruff_linter::line_width::LineLength;
use ruff_linter::settings::types::PythonVersion;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]

View File

@@ -3,17 +3,17 @@ use std::str::FromStr;
use itertools::Itertools;
use ruff::line_width::LineLength;
use ruff::registry::Linter;
use ruff::rule_selector::RuleSelector;
use ruff::rules::flake8_pytest_style::types::{
use ruff_linter::line_width::LineLength;
use ruff_linter::registry::Linter;
use ruff_linter::rule_selector::RuleSelector;
use ruff_linter::rules::flake8_pytest_style::types::{
ParametrizeNameType, ParametrizeValuesRowType, ParametrizeValuesType,
};
use ruff::rules::flake8_quotes::settings::Quote;
use ruff::rules::flake8_tidy_imports::settings::Strictness;
use ruff::rules::pydocstyle::settings::Convention;
use ruff::settings::types::PythonVersion;
use ruff::warn_user;
use ruff_linter::rules::flake8_quotes::settings::Quote;
use ruff_linter::rules::flake8_tidy_imports::settings::Strictness;
use ruff_linter::rules::pydocstyle::settings::Convention;
use ruff_linter::settings::types::PythonVersion;
use ruff_linter::warn_user;
use ruff_workspace::options::{
Flake8AnnotationsOptions, Flake8BugbearOptions, Flake8BuiltinsOptions, Flake8ErrMsgOptions,
Flake8PytestStyleOptions, Flake8QuotesOptions, Flake8TidyImportsOptions, McCabeOptions,
@@ -458,12 +458,12 @@ mod tests {
use pep440_rs::VersionSpecifiers;
use pretty_assertions::assert_eq;
use ruff::line_width::LineLength;
use ruff::registry::Linter;
use ruff::rule_selector::RuleSelector;
use ruff::rules::flake8_quotes;
use ruff::rules::pydocstyle::settings::Convention;
use ruff::settings::types::PythonVersion;
use ruff_linter::line_width::LineLength;
use ruff_linter::registry::Linter;
use ruff_linter::rule_selector::RuleSelector;
use ruff_linter::rules::flake8_quotes;
use ruff_linter::rules::pydocstyle::settings::Convention;
use ruff_linter::settings::types::PythonVersion;
use ruff_workspace::options::{Flake8QuotesOptions, Options, PydocstyleOptions};
use ruff_workspace::pyproject::Pyproject;

View File

@@ -19,7 +19,7 @@ use crate::converter::convert;
use crate::external_config::ExternalConfig;
use crate::plugin::Plugin;
use crate::pyproject::parse;
use ruff::logging::{set_up_logging, LogLevel};
use ruff_linter::logging::{set_up_logging, LogLevel};
#[derive(Parser)]
#[command(

View File

@@ -3,10 +3,11 @@ use std::str::FromStr;
use anyhow::{bail, Result};
use once_cell::sync::Lazy;
use regex::Regex;
use ruff::settings::types::PatternPrefixPair;
use ruff::{warn_user, RuleSelector};
use rustc_hash::FxHashMap;
use ruff_linter::settings::types::PatternPrefixPair;
use ruff_linter::{warn_user, RuleSelector};
static COMMA_SEPARATED_LIST_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"[,\s]").unwrap());
/// Parse a comma-separated list of `RuleSelector` values (e.g.,
@@ -192,11 +193,11 @@ pub(crate) fn collect_per_file_ignores(
#[cfg(test)]
mod tests {
use anyhow::Result;
use ruff::RuleSelector;
use ruff::codes;
use ruff::registry::Linter;
use ruff::settings::types::PatternPrefixPair;
use ruff_linter::codes;
use ruff_linter::registry::Linter;
use ruff_linter::settings::types::PatternPrefixPair;
use ruff_linter::RuleSelector;
use super::{parse_files_to_codes_mapping, parse_prefix_codes, parse_strings};

View File

@@ -3,9 +3,9 @@ use std::fmt;
use std::str::FromStr;
use anyhow::anyhow;
use ruff::registry::Linter;
use ruff::settings::types::PreviewMode;
use ruff::RuleSelector;
use ruff_linter::registry::Linter;
use ruff_linter::settings::types::PreviewMode;
use ruff_linter::RuleSelector;
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Plugin {

View File

@@ -1,15 +0,0 @@
type("")
type(b"")
type(0)
type(0.0)
type(0j)
# OK
type(arg)(" ")
# OK
y = x.dtype.type(0.0)
# OK
type = lambda *args, **kwargs: None
type("")

View File

@@ -1,96 +0,0 @@
# project
An example multi-package Python project used to test setting resolution and other complex
behaviors.
## Expected behavior
Running from the repo root should pick up and enforce the appropriate settings for each package:
```console
∴ cargo run -p ruff_cli -- check crates/ruff/resources/test/project/
crates/ruff/resources/test/project/examples/.dotfiles/script.py:1:1: I001 [*] Import block is un-sorted or un-formatted
crates/ruff/resources/test/project/examples/.dotfiles/script.py:1:8: F401 [*] `numpy` imported but unused
crates/ruff/resources/test/project/examples/.dotfiles/script.py:2:17: F401 [*] `app.app_file` imported but unused
crates/ruff/resources/test/project/examples/docs/docs/file.py:1:1: I001 [*] Import block is un-sorted or un-formatted
crates/ruff/resources/test/project/examples/docs/docs/file.py:8:5: F841 [*] Local variable `x` is assigned to but never used
crates/ruff/resources/test/project/project/file.py:1:8: F401 [*] `os` imported but unused
crates/ruff/resources/test/project/project/import_file.py:1:1: I001 [*] Import block is un-sorted or un-formatted
Found 7 errors.
[*] 7 potentially fixable with the --fix option.
```
Running from the project directory itself should exhibit the same behavior:
```console
∴ (cd crates/ruff/resources/test/project/ && cargo run -p ruff_cli -- check .)
examples/.dotfiles/script.py:1:1: I001 [*] Import block is un-sorted or un-formatted
examples/.dotfiles/script.py:1:8: F401 [*] `numpy` imported but unused
examples/.dotfiles/script.py:2:17: F401 [*] `app.app_file` imported but unused
examples/docs/docs/file.py:1:1: I001 [*] Import block is un-sorted or un-formatted
examples/docs/docs/file.py:8:5: F841 [*] Local variable `x` is assigned to but never used
project/file.py:1:8: F401 [*] `os` imported but unused
project/import_file.py:1:1: I001 [*] Import block is un-sorted or un-formatted
Found 7 errors.
[*] 7 potentially fixable with the --fix option.
```
Running from the sub-package directory should exhibit the same behavior, but omit the top-level
files:
```console
∴ (cd crates/ruff/resources/test/project/examples/docs && cargo run -p ruff_cli -- check .)
docs/file.py:1:1: I001 [*] Import block is un-sorted or un-formatted
docs/file.py:8:5: F841 [*] Local variable `x` is assigned to but never used
Found 2 errors.
[*] 2 potentially fixable with the --fix option.
```
`--config` should force Ruff to use the specified `pyproject.toml` for all files, and resolve
file paths from the current working directory:
```console
∴ (cargo run -p ruff_cli -- check --config=crates/ruff/resources/test/project/pyproject.toml crates/ruff/resources/test/project/)
crates/ruff/resources/test/project/examples/.dotfiles/script.py:1:8: F401 [*] `numpy` imported but unused
crates/ruff/resources/test/project/examples/.dotfiles/script.py:2:17: F401 [*] `app.app_file` imported but unused
crates/ruff/resources/test/project/examples/docs/docs/concepts/file.py:1:8: F401 [*] `os` imported but unused
crates/ruff/resources/test/project/examples/docs/docs/file.py:1:1: I001 [*] Import block is un-sorted or un-formatted
crates/ruff/resources/test/project/examples/docs/docs/file.py:1:8: F401 [*] `os` imported but unused
crates/ruff/resources/test/project/examples/docs/docs/file.py:3:8: F401 [*] `numpy` imported but unused
crates/ruff/resources/test/project/examples/docs/docs/file.py:4:27: F401 [*] `docs.concepts.file` imported but unused
crates/ruff/resources/test/project/examples/excluded/script.py:1:8: F401 [*] `os` imported but unused
crates/ruff/resources/test/project/project/file.py:1:8: F401 [*] `os` imported but unused
Found 9 errors.
[*] 9 potentially fixable with the --fix option.
```
Running from a parent directory should "ignore" the `exclude` (hence, `concepts/file.py` gets
included in the output):
```console
∴ (cd crates/ruff/resources/test/project/examples && cargo run -p ruff_cli -- check --config=docs/ruff.toml .)
docs/docs/concepts/file.py:5:5: F841 [*] Local variable `x` is assigned to but never used
docs/docs/file.py:1:1: I001 [*] Import block is un-sorted or un-formatted
docs/docs/file.py:8:5: F841 [*] Local variable `x` is assigned to but never used
excluded/script.py:5:5: F841 [*] Local variable `x` is assigned to but never used
Found 4 errors.
[*] 4 potentially fixable with the --fix option.
```
Passing an excluded directory directly should report errors in the contained files:
```console
∴ cargo run -p ruff_cli -- check crates/ruff/resources/test/project/examples/excluded/
crates/ruff/resources/test/project/examples/excluded/script.py:1:8: F401 [*] `os` imported but unused
Found 1 error.
[*] 1 potentially fixable with the --fix option.
```
Unless we `--force-exclude`:
```console
∴ cargo run -p ruff_cli -- check crates/ruff/resources/test/project/examples/excluded/ --force-exclude
warning: No Python files found under the given path(s)
∴ cargo run -p ruff_cli -- check crates/ruff/resources/test/project/examples/excluded/script.py --force-exclude
warning: No Python files found under the given path(s)
```

File diff suppressed because it is too large Load Diff

View File

@@ -1,242 +0,0 @@
//! `NoQA` enforcement and validation.
use std::path::Path;
use itertools::Itertools;
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use ruff_diagnostics::{Diagnostic, Edit, Fix};
use ruff_source_file::Locator;
use crate::noqa;
use crate::noqa::{Directive, FileExemption, NoqaDirectives, NoqaMapping};
use crate::registry::{AsRule, Rule};
use crate::rule_redirects::get_redirect_target;
use crate::rules::ruff::rules::{UnusedCodes, UnusedNOQA};
use crate::settings::Settings;
pub(crate) fn check_noqa(
diagnostics: &mut Vec<Diagnostic>,
path: &Path,
locator: &Locator,
comment_ranges: &[TextRange],
noqa_line_for: &NoqaMapping,
analyze_directives: bool,
settings: &Settings,
) -> Vec<usize> {
// Identify any codes that are globally exempted (within the current file).
let exemption = FileExemption::try_extract(locator.contents(), comment_ranges, path, locator);
// Extract all `noqa` directives.
let mut noqa_directives = NoqaDirectives::from_commented_ranges(comment_ranges, path, locator);
// Indices of diagnostics that were ignored by a `noqa` directive.
let mut ignored_diagnostics = vec![];
// Remove any ignored diagnostics.
'outer: for (index, diagnostic) in diagnostics.iter().enumerate() {
if matches!(diagnostic.kind.rule(), Rule::BlanketNOQA) {
continue;
}
match &exemption {
Some(FileExemption::All) => {
// If the file is exempted, ignore all diagnostics.
ignored_diagnostics.push(index);
continue;
}
Some(FileExemption::Codes(codes)) => {
// If the diagnostic is ignored by a global exemption, ignore it.
if codes.contains(&diagnostic.kind.rule().noqa_code()) {
ignored_diagnostics.push(index);
continue;
}
}
None => {}
}
let noqa_offsets = diagnostic
.parent
.into_iter()
.chain(std::iter::once(diagnostic.start()))
.map(|position| noqa_line_for.resolve(position))
.unique();
for noqa_offset in noqa_offsets {
if let Some(directive_line) = noqa_directives.find_line_with_directive_mut(noqa_offset)
{
let suppressed = match &directive_line.directive {
Directive::All(_) => {
directive_line
.matches
.push(diagnostic.kind.rule().noqa_code());
ignored_diagnostics.push(index);
true
}
Directive::Codes(directive) => {
if noqa::includes(diagnostic.kind.rule(), directive.codes()) {
directive_line
.matches
.push(diagnostic.kind.rule().noqa_code());
ignored_diagnostics.push(index);
true
} else {
false
}
}
};
if suppressed {
continue 'outer;
}
}
}
}
// Enforce that the noqa directive was actually used (RUF100), unless RUF100 was itself
// suppressed.
if settings.rules.enabled(Rule::UnusedNOQA)
&& analyze_directives
&& !exemption.is_some_and(|exemption| match exemption {
FileExemption::All => true,
FileExemption::Codes(codes) => codes.contains(&Rule::UnusedNOQA.noqa_code()),
})
{
for line in noqa_directives.lines() {
match &line.directive {
Directive::All(directive) => {
if line.matches.is_empty() {
let mut diagnostic =
Diagnostic::new(UnusedNOQA { codes: None }, directive.range());
if settings.rules.should_fix(diagnostic.kind.rule()) {
diagnostic
.set_fix(Fix::suggested(delete_noqa(directive.range(), locator)));
}
diagnostics.push(diagnostic);
}
}
Directive::Codes(directive) => {
let mut disabled_codes = vec![];
let mut unknown_codes = vec![];
let mut unmatched_codes = vec![];
let mut valid_codes = vec![];
let mut self_ignore = false;
for code in directive.codes() {
let code = get_redirect_target(code).unwrap_or(code);
if Rule::UnusedNOQA.noqa_code() == code {
self_ignore = true;
break;
}
if line.matches.iter().any(|match_| *match_ == code)
|| settings.external.contains(code)
{
valid_codes.push(code);
} else {
if let Ok(rule) = Rule::from_code(code) {
if settings.rules.enabled(rule) {
unmatched_codes.push(code);
} else {
disabled_codes.push(code);
}
} else {
unknown_codes.push(code);
}
}
}
if self_ignore {
continue;
}
if !(disabled_codes.is_empty()
&& unknown_codes.is_empty()
&& unmatched_codes.is_empty())
{
let mut diagnostic = Diagnostic::new(
UnusedNOQA {
codes: Some(UnusedCodes {
disabled: disabled_codes
.iter()
.map(|code| (*code).to_string())
.collect(),
unknown: unknown_codes
.iter()
.map(|code| (*code).to_string())
.collect(),
unmatched: unmatched_codes
.iter()
.map(|code| (*code).to_string())
.collect(),
}),
},
directive.range(),
);
if settings.rules.should_fix(diagnostic.kind.rule()) {
if valid_codes.is_empty() {
diagnostic.set_fix(Fix::suggested(delete_noqa(
directive.range(),
locator,
)));
} else {
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
format!("# noqa: {}", valid_codes.join(", ")),
directive.range(),
)));
}
}
diagnostics.push(diagnostic);
}
}
}
}
}
ignored_diagnostics.sort_unstable();
ignored_diagnostics
}
/// Generate a [`Edit`] to delete a `noqa` directive.
fn delete_noqa(range: TextRange, locator: &Locator) -> Edit {
let line_range = locator.line_range(range.start());
// Compute the leading space.
let prefix = locator.slice(TextRange::new(line_range.start(), range.start()));
let leading_space = prefix
.rfind(|c: char| !c.is_whitespace())
.map_or(prefix.len(), |i| prefix.len() - i - 1);
let leading_space_len = TextSize::try_from(leading_space).unwrap();
// Compute the trailing space.
let suffix = locator.slice(TextRange::new(range.end(), line_range.end()));
let trailing_space = suffix
.find(|c: char| !c.is_whitespace())
.map_or(suffix.len(), |i| i);
let trailing_space_len = TextSize::try_from(trailing_space).unwrap();
// Ex) `# noqa`
if line_range
== TextRange::new(
range.start() - leading_space_len,
range.end() + trailing_space_len,
)
{
let full_line_end = locator.full_line_end(line_range.end());
Edit::deletion(line_range.start(), full_line_end)
}
// Ex) `x = 1 # noqa`
else if range.end() + trailing_space_len == line_range.end() {
Edit::deletion(range.start() - leading_space_len, line_range.end())
}
// Ex) `x = 1 # noqa # type: ignore`
else if locator.contents()[usize::from(range.end() + trailing_space_len)..].starts_with('#') {
Edit::deletion(range.start(), range.end() + trailing_space_len)
}
// Ex) `x = 1 # noqa here`
else {
Edit::deletion(
range.start() + "# ".text_len(),
range.end() + trailing_space_len,
)
}
}

View File

@@ -1,5 +0,0 @@
---
source: crates/ruff/src/comments/shebang.rs
expression: "ShebangDirective::try_extract(source)"
---
None

View File

@@ -1,5 +0,0 @@
---
source: crates/ruff/src/comments/shebang.rs
expression: "ShebangDirective::try_extract(source)"
---
None

View File

@@ -1,9 +0,0 @@
---
source: crates/ruff/src/comments/shebang.rs
expression: "ShebangDirective::try_extract(source)"
---
Some(
ShebangDirective(
"/usr/bin/env python",
),
)

View File

@@ -1,9 +0,0 @@
---
source: crates/ruff/src/comments/shebang.rs
expression: "ShebangDirective::try_extract(source)"
---
Some(
ShebangDirective(
"/usr/bin/env python # trailing comment",
),
)

View File

@@ -1,5 +0,0 @@
---
source: crates/ruff/src/comments/shebang.rs
expression: "ShebangDirective::try_extract(source)"
---
None

View File

@@ -1,52 +0,0 @@
use libcst_native::{Expression, NameOrAttribute, ParenthesizableWhitespace, SimpleWhitespace};
fn compose_call_path_inner<'a>(expr: &'a Expression, parts: &mut Vec<&'a str>) {
match expr {
Expression::Call(expr) => {
compose_call_path_inner(&expr.func, parts);
}
Expression::Attribute(expr) => {
compose_call_path_inner(&expr.value, parts);
parts.push(expr.attr.value);
}
Expression::Name(expr) => {
parts.push(expr.value);
}
_ => {}
}
}
pub(crate) fn compose_call_path(expr: &Expression) -> Option<String> {
let mut segments = vec![];
compose_call_path_inner(expr, &mut segments);
if segments.is_empty() {
None
} else {
Some(segments.join("."))
}
}
pub(crate) fn compose_module_path(module: &NameOrAttribute) -> String {
match module {
NameOrAttribute::N(name) => name.value.to_string(),
NameOrAttribute::A(attr) => {
let name = attr.attr.value;
let prefix = compose_call_path(&attr.value);
prefix.map_or_else(|| name.to_string(), |prefix| format!("{prefix}.{name}"))
}
}
}
/// Return a [`ParenthesizableWhitespace`] containing a single space.
pub(crate) fn space() -> ParenthesizableWhitespace<'static> {
ParenthesizableWhitespace::SimpleWhitespace(SimpleWhitespace(" "))
}
/// Ensure that a [`ParenthesizableWhitespace`] contains at least one space.
pub(crate) fn or_space(whitespace: ParenthesizableWhitespace) -> ParenthesizableWhitespace {
if whitespace == ParenthesizableWhitespace::default() {
space()
} else {
whitespace
}
}

View File

@@ -1,73 +0,0 @@
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
use ruff_python_ast::Expr;
use ruff_python_semantic::Definition;
use ruff_text_size::{Ranged, TextRange};
pub(crate) mod extraction;
pub(crate) mod google;
pub(crate) mod numpy;
pub(crate) mod sections;
pub(crate) mod styles;
#[derive(Debug)]
pub(crate) struct Docstring<'a> {
pub(crate) definition: &'a Definition<'a>,
pub(crate) expr: &'a Expr,
/// The content of the docstring, including the leading and trailing quotes.
pub(crate) contents: &'a str,
/// The range of the docstring body (without the quotes). The range is relative to [`Self::contents`].
pub(crate) body_range: TextRange,
pub(crate) indentation: &'a str,
}
impl<'a> Docstring<'a> {
pub(crate) fn body(&self) -> DocstringBody {
DocstringBody { docstring: self }
}
pub(crate) fn leading_quote(&self) -> &'a str {
&self.contents[TextRange::up_to(self.body_range.start())]
}
}
impl Ranged for Docstring<'_> {
fn range(&self) -> TextRange {
self.expr.range()
}
}
#[derive(Copy, Clone)]
pub(crate) struct DocstringBody<'a> {
docstring: &'a Docstring<'a>,
}
impl<'a> DocstringBody<'a> {
pub(crate) fn as_str(self) -> &'a str {
&self.docstring.contents[self.docstring.body_range]
}
}
impl Ranged for DocstringBody<'_> {
fn range(&self) -> TextRange {
self.docstring.body_range + self.docstring.start()
}
}
impl Deref for DocstringBody<'_> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl Debug for DocstringBody<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DocstringBody")
.field("text", &self.as_str())
.field("range", &self.range())
.finish()
}
}

View File

@@ -1,8 +0,0 @@
---
source: crates/ruff/src/message/azure.rs
expression: content
---
##vso[task.logissue type=error;sourcepath=fib.py;linenumber=1;columnnumber=8;code=F401;]`os` imported but unused
##vso[task.logissue type=error;sourcepath=fib.py;linenumber=6;columnnumber=5;code=F841;]Local variable `x` is assigned to but never used
##vso[task.logissue type=error;sourcepath=undef.py;linenumber=1;columnnumber=4;code=F821;]Undefined name `a`

View File

@@ -1,8 +0,0 @@
---
source: crates/ruff/src/message/github.rs
expression: content
---
::error title=Ruff (F401),file=fib.py,line=1,col=8,endLine=1,endColumn=10::fib.py:1:8: F401 `os` imported but unused
::error title=Ruff (F841),file=fib.py,line=6,col=5,endLine=6,endColumn=6::fib.py:6:5: F841 Local variable `x` is assigned to but never used
::error title=Ruff (F821),file=undef.py,line=1,col=4,endLine=1,endColumn=5::undef.py:1:4: F821 Undefined name `a`

View File

@@ -1,42 +0,0 @@
---
source: crates/ruff/src/message/gitlab.rs
expression: redact_fingerprint(&content)
---
[
{
"description": "(F401) `os` imported but unused",
"fingerprint": "<redacted>",
"location": {
"lines": {
"begin": 1,
"end": 1
},
"path": "fib.py"
},
"severity": "major"
},
{
"description": "(F841) Local variable `x` is assigned to but never used",
"fingerprint": "<redacted>",
"location": {
"lines": {
"begin": 6,
"end": 6
},
"path": "fib.py"
},
"severity": "major"
},
{
"description": "(F821) Undefined name `a`",
"fingerprint": "<redacted>",
"location": {
"lines": {
"begin": 1,
"end": 1
},
"path": "undef.py"
},
"severity": "major"
}
]

View File

@@ -1,12 +0,0 @@
---
source: crates/ruff/src/message/grouped.rs
expression: content
---
fib.py:
1:8 F401 `os` imported but unused
6:5 F841 Local variable `x` is assigned to but never used
undef.py:
1:4 F821 Undefined name `a`

View File

@@ -1,31 +0,0 @@
---
source: crates/ruff/src/message/grouped.rs
expression: content
---
fib.py:
1:8 F401 [*] `os` imported but unused
|
1 | import os
| ^^ F401
|
= help: Remove unused import: `os`
6:5 F841 [*] Local variable `x` is assigned to but never used
|
4 | def fibonacci(n):
5 | """Compute the nth number in the Fibonacci sequence."""
6 | x = 1
| ^ F841
7 | if n == 0:
8 | return 0
|
= help: Remove assignment to unused variable `x`
undef.py:
1:4 F821 Undefined name `a`
|
1 | if a == 1: pass
| ^ F821
|

View File

@@ -1,31 +0,0 @@
---
source: crates/ruff/src/message/grouped.rs
expression: content
---
fib.py:
1:8 F401 `os` imported but unused
|
1 | import os
| ^^ F401
|
= help: Remove unused import: `os`
6:5 F841 Local variable `x` is assigned to but never used
|
4 | def fibonacci(n):
5 | """Compute the nth number in the Fibonacci sequence."""
6 | x = 1
| ^ F841
7 | if n == 0:
8 | return 0
|
= help: Remove assignment to unused variable `x`
undef.py:
1:4 F821 Undefined name `a`
|
1 | if a == 1: pass
| ^ F821
|

View File

@@ -1,86 +0,0 @@
---
source: crates/ruff/src/message/json.rs
expression: content
---
[
{
"code": "F401",
"end_location": {
"column": 10,
"row": 1
},
"filename": "fib.py",
"fix": {
"applicability": "Suggested",
"edits": [
{
"content": "",
"end_location": {
"column": 1,
"row": 2
},
"location": {
"column": 1,
"row": 1
}
}
],
"message": "Remove unused import: `os`"
},
"location": {
"column": 8,
"row": 1
},
"message": "`os` imported but unused",
"noqa_row": 1,
"url": "https://beta.ruff.rs/docs/rules/unused-import"
},
{
"code": "F841",
"end_location": {
"column": 6,
"row": 6
},
"filename": "fib.py",
"fix": {
"applicability": "Suggested",
"edits": [
{
"content": "",
"end_location": {
"column": 10,
"row": 6
},
"location": {
"column": 5,
"row": 6
}
}
],
"message": "Remove assignment to unused variable `x`"
},
"location": {
"column": 5,
"row": 6
},
"message": "Local variable `x` is assigned to but never used",
"noqa_row": 6,
"url": "https://beta.ruff.rs/docs/rules/unused-variable"
},
{
"code": "F821",
"end_location": {
"column": 5,
"row": 1
},
"filename": "undef.py",
"fix": null,
"location": {
"column": 4,
"row": 1
},
"message": "Undefined name `a`",
"noqa_row": 1,
"url": "https://beta.ruff.rs/docs/rules/undefined-name"
}
]

View File

@@ -1,8 +0,0 @@
---
source: crates/ruff/src/message/json_lines.rs
expression: content
---
{"code":"F401","end_location":{"column":10,"row":1},"filename":"fib.py","fix":{"applicability":"Suggested","edits":[{"content":"","end_location":{"column":1,"row":2},"location":{"column":1,"row":1}}],"message":"Remove unused import: `os`"},"location":{"column":8,"row":1},"message":"`os` imported but unused","noqa_row":1,"url":"https://beta.ruff.rs/docs/rules/unused-import"}
{"code":"F841","end_location":{"column":6,"row":6},"filename":"fib.py","fix":{"applicability":"Suggested","edits":[{"content":"","end_location":{"column":10,"row":6},"location":{"column":5,"row":6}}],"message":"Remove assignment to unused variable `x`"},"location":{"column":5,"row":6},"message":"Local variable `x` is assigned to but never used","noqa_row":6,"url":"https://beta.ruff.rs/docs/rules/unused-variable"}
{"code":"F821","end_location":{"column":5,"row":1},"filename":"undef.py","fix":null,"location":{"column":4,"row":1},"message":"Undefined name `a`","noqa_row":1,"url":"https://beta.ruff.rs/docs/rules/undefined-name"}

View File

@@ -1,21 +0,0 @@
---
source: crates/ruff/src/message/junit.rs
expression: content
---
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="ruff" tests="3" failures="3" errors="0">
<testsuite name="fib.py" tests="2" disabled="0" errors="0" failures="2" package="org.ruff">
<testcase name="org.ruff.F401" classname="fib" line="1" column="8">
<failure message="`os` imported but unused">line 1, col 8, `os` imported but unused</failure>
</testcase>
<testcase name="org.ruff.F841" classname="fib" line="6" column="5">
<failure message="Local variable `x` is assigned to but never used">line 6, col 5, Local variable `x` is assigned to but never used</failure>
</testcase>
</testsuite>
<testsuite name="undef.py" tests="1" disabled="0" errors="0" failures="1" package="org.ruff">
<testcase name="org.ruff.F821" classname="undef" line="1" column="4">
<failure message="Undefined name `a`">line 1, col 4, Undefined name `a`</failure>
</testcase>
</testsuite>
</testsuites>

View File

@@ -1,8 +0,0 @@
---
source: crates/ruff/src/message/pylint.rs
expression: content
---
fib.py:1: [F401] `os` imported but unused
fib.py:6: [F841] Local variable `x` is assigned to but never used
undef.py:1: [F821] Undefined name `a`

View File

@@ -1,29 +0,0 @@
---
source: crates/ruff/src/message/text.rs
expression: content
---
fib.py:1:8: F401 `os` imported but unused
|
1 | import os
| ^^ F401
|
= help: Remove unused import: `os`
fib.py:6:5: F841 Local variable `x` is assigned to but never used
|
4 | def fibonacci(n):
5 | """Compute the nth number in the Fibonacci sequence."""
6 | x = 1
| ^ F841
7 | if n == 0:
8 | return 0
|
= help: Remove assignment to unused variable `x`
undef.py:1:4: F821 Undefined name `a`
|
1 | if a == 1: pass
| ^ F821
|

View File

@@ -1,29 +0,0 @@
---
source: crates/ruff/src/message/text.rs
expression: content
---
fib.py:1:8: F401 [*] `os` imported but unused
|
1 | import os
| ^^ F401
|
= help: Remove unused import: `os`
fib.py:6:5: F841 [*] Local variable `x` is assigned to but never used
|
4 | def fibonacci(n):
5 | """Compute the nth number in the Fibonacci sequence."""
6 | x = 1
| ^ F841
7 | if n == 0:
8 | return 0
|
= help: Remove assignment to unused variable `x`
undef.py:1:4: F821 Undefined name `a`
|
1 | if a == 1: pass
| ^ F821
|

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +0,0 @@
//! Airflow-specific rules.
pub(crate) mod rules;
#[cfg(test)]
mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::registry::Rule;
use crate::test::test_path;
use crate::{assert_messages, settings};
#[test_case(Rule::AirflowVariableNameTaskIdMismatch, Path::new("AIR001.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path(
Path::new("airflow").join(path).as_path(),
&settings::Settings::for_rule(rule_code),
)?;
assert_messages!(snapshot, diagnostics);
Ok(())
}
}

View File

@@ -1,22 +0,0 @@
---
source: crates/ruff/src/rules/airflow/mod.rs
---
AIR001.py:11:1: AIR001 Task variable name should match the `task_id`: "my_task"
|
9 | my_task_2 = PythonOperator(callable=my_callable, task_id="my_task_2")
10 |
11 | incorrect_name = PythonOperator(task_id="my_task")
| ^^^^^^^^^^^^^^ AIR001
12 | incorrect_name_2 = PythonOperator(callable=my_callable, task_id="my_task_2")
|
AIR001.py:12:1: AIR001 Task variable name should match the `task_id`: "my_task_2"
|
11 | incorrect_name = PythonOperator(task_id="my_task")
12 | incorrect_name_2 = PythonOperator(callable=my_callable, task_id="my_task_2")
| ^^^^^^^^^^^^^^^^ AIR001
13 |
14 | from my_module import MyClass
|

View File

@@ -1,26 +0,0 @@
//! Rules from [eradicate](https://pypi.org/project/eradicate/).
pub(crate) mod detection;
pub(crate) mod rules;
#[cfg(test)]
mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::registry::Rule;
use crate::test::test_path;
use crate::{assert_messages, settings};
#[test_case(Rule::CommentedOutCode, Path::new("ERA001.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path(
Path::new("eradicate").join(path).as_path(),
&settings::Settings::for_rule(rule_code),
)?;
assert_messages!(snapshot, diagnostics);
Ok(())
}
}

View File

@@ -1,151 +0,0 @@
---
source: crates/ruff/src/rules/eradicate/mod.rs
---
ERA001.py:1:1: ERA001 [*] Found commented-out code
|
1 | #import os
| ^^^^^^^^^^ ERA001
2 | # from foo import junk
3 | #a = 3
|
= help: Remove commented-out code
Possible fix
1 |-#import os
2 1 | # from foo import junk
3 2 | #a = 3
4 3 | a = 4
ERA001.py:2:1: ERA001 [*] Found commented-out code
|
1 | #import os
2 | # from foo import junk
| ^^^^^^^^^^^^^^^^^^^^^^ ERA001
3 | #a = 3
4 | a = 4
|
= help: Remove commented-out code
Possible fix
1 1 | #import os
2 |-# from foo import junk
3 2 | #a = 3
4 3 | a = 4
5 4 | #foo(1, 2, 3)
ERA001.py:3:1: ERA001 [*] Found commented-out code
|
1 | #import os
2 | # from foo import junk
3 | #a = 3
| ^^^^^^ ERA001
4 | a = 4
5 | #foo(1, 2, 3)
|
= help: Remove commented-out code
Possible fix
1 1 | #import os
2 2 | # from foo import junk
3 |-#a = 3
4 3 | a = 4
5 4 | #foo(1, 2, 3)
6 5 |
ERA001.py:5:1: ERA001 [*] Found commented-out code
|
3 | #a = 3
4 | a = 4
5 | #foo(1, 2, 3)
| ^^^^^^^^^^^^^ ERA001
6 |
7 | def foo(x, y, z):
|
= help: Remove commented-out code
Possible fix
2 2 | # from foo import junk
3 3 | #a = 3
4 4 | a = 4
5 |-#foo(1, 2, 3)
6 5 |
7 6 | def foo(x, y, z):
8 7 | content = 1 # print('hello')
ERA001.py:13:5: ERA001 [*] Found commented-out code
|
11 | # This is a real comment.
12 | # # This is a (nested) comment.
13 | #return True
| ^^^^^^^^^^^^ ERA001
14 | return False
|
= help: Remove commented-out code
Possible fix
10 10 |
11 11 | # This is a real comment.
12 12 | # # This is a (nested) comment.
13 |- #return True
14 13 | return False
15 14 |
16 15 | #import os # noqa: ERA001
ERA001.py:21:5: ERA001 [*] Found commented-out code
|
19 | class A():
20 | pass
21 | # b = c
| ^^^^^^^ ERA001
|
= help: Remove commented-out code
Possible fix
18 18 |
19 19 | class A():
20 20 | pass
21 |- # b = c
22 21 |
23 22 |
24 23 | dictionary = {
ERA001.py:26:5: ERA001 [*] Found commented-out code
|
24 | dictionary = {
25 | # "key1": 123, # noqa: ERA001
26 | # "key2": 456,
| ^^^^^^^^^^^^^^ ERA001
27 | # "key3": 789, # test
28 | }
|
= help: Remove commented-out code
Possible fix
23 23 |
24 24 | dictionary = {
25 25 | # "key1": 123, # noqa: ERA001
26 |- # "key2": 456,
27 26 | # "key3": 789, # test
28 27 | }
29 28 |
ERA001.py:27:5: ERA001 [*] Found commented-out code
|
25 | # "key1": 123, # noqa: ERA001
26 | # "key2": 456,
27 | # "key3": 789, # test
| ^^^^^^^^^^^^^^^^^^^^^^ ERA001
28 | }
|
= help: Remove commented-out code
Possible fix
24 24 | dictionary = {
25 25 | # "key1": 123, # noqa: ERA001
26 26 | # "key2": 456,
27 |- # "key3": 789, # test
28 27 | }
29 28 |
30 29 | #import os # noqa

View File

@@ -1,35 +0,0 @@
//! Rules from [flake8-2020](https://pypi.org/project/flake8-2020/).
mod helpers;
pub(crate) mod rules;
#[cfg(test)]
mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::registry::Rule;
use crate::test::test_path;
use crate::{assert_messages, settings};
#[test_case(Rule::SysVersionSlice3, Path::new("YTT101.py"))]
#[test_case(Rule::SysVersion2, Path::new("YTT102.py"))]
#[test_case(Rule::SysVersionCmpStr3, Path::new("YTT103.py"))]
#[test_case(Rule::SysVersionInfo0Eq3, Path::new("YTT201.py"))]
#[test_case(Rule::SixPY3, Path::new("YTT202.py"))]
#[test_case(Rule::SysVersionInfo1CmpInt, Path::new("YTT203.py"))]
#[test_case(Rule::SysVersionInfoMinorCmpInt, Path::new("YTT204.py"))]
#[test_case(Rule::SysVersion0, Path::new("YTT301.py"))]
#[test_case(Rule::SysVersionCmpStr10, Path::new("YTT302.py"))]
#[test_case(Rule::SysVersionSlice1, Path::new("YTT303.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path(
Path::new("flake8_2020").join(path).as_path(),
&settings::Settings::for_rule(rule_code),
)?;
assert_messages!(snapshot, diagnostics);
Ok(())
}
}

View File

@@ -1,32 +0,0 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
---
YTT101.py:6:7: YTT101 `sys.version[:3]` referenced (python3.10), use `sys.version_info`
|
4 | print(sys.version)
5 |
6 | print(sys.version[:3])
| ^^^^^^^^^^^ YTT101
7 | print(version[:3])
8 | print(v[:3])
|
YTT101.py:7:7: YTT101 `sys.version[:3]` referenced (python3.10), use `sys.version_info`
|
6 | print(sys.version[:3])
7 | print(version[:3])
| ^^^^^^^ YTT101
8 | print(v[:3])
|
YTT101.py:8:7: YTT101 `sys.version[:3]` referenced (python3.10), use `sys.version_info`
|
6 | print(sys.version[:3])
7 | print(version[:3])
8 | print(v[:3])
| ^ YTT101
9 |
10 | # the tool is timid and only flags certain numeric slices
|

View File

@@ -1,20 +0,0 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
---
YTT102.py:4:12: YTT102 `sys.version[2]` referenced (python3.10), use `sys.version_info`
|
2 | from sys import version
3 |
4 | py_minor = sys.version[2]
| ^^^^^^^^^^^ YTT102
5 | py_minor = version[2]
|
YTT102.py:5:12: YTT102 `sys.version[2]` referenced (python3.10), use `sys.version_info`
|
4 | py_minor = sys.version[2]
5 | py_minor = version[2]
| ^^^^^^^ YTT102
|

View File

@@ -1,50 +0,0 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
---
YTT103.py:4:1: YTT103 `sys.version` compared to string (python3.10), use `sys.version_info`
|
2 | from sys import version
3 |
4 | version < "3.5"
| ^^^^^^^ YTT103
5 | sys.version < "3.5"
6 | sys.version <= "3.5"
|
YTT103.py:5:1: YTT103 `sys.version` compared to string (python3.10), use `sys.version_info`
|
4 | version < "3.5"
5 | sys.version < "3.5"
| ^^^^^^^^^^^ YTT103
6 | sys.version <= "3.5"
7 | sys.version > "3.5"
|
YTT103.py:6:1: YTT103 `sys.version` compared to string (python3.10), use `sys.version_info`
|
4 | version < "3.5"
5 | sys.version < "3.5"
6 | sys.version <= "3.5"
| ^^^^^^^^^^^ YTT103
7 | sys.version > "3.5"
8 | sys.version >= "3.5"
|
YTT103.py:7:1: YTT103 `sys.version` compared to string (python3.10), use `sys.version_info`
|
5 | sys.version < "3.5"
6 | sys.version <= "3.5"
7 | sys.version > "3.5"
| ^^^^^^^^^^^ YTT103
8 | sys.version >= "3.5"
|
YTT103.py:8:1: YTT103 `sys.version` compared to string (python3.10), use `sys.version_info`
|
6 | sys.version <= "3.5"
7 | sys.version > "3.5"
8 | sys.version >= "3.5"
| ^^^^^^^^^^^ YTT103
|

View File

@@ -1,40 +0,0 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
---
YTT201.py:7:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=`
|
5 | PY3 = sys.version_info[0] >= 3
6 |
7 | PY3 = sys.version_info[0] == 3
| ^^^^^^^^^^^^^^^^^^^ YTT201
8 | PY3 = version_info[0] == 3
9 | PY2 = sys.version_info[0] != 3
|
YTT201.py:8:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=`
|
7 | PY3 = sys.version_info[0] == 3
8 | PY3 = version_info[0] == 3
| ^^^^^^^^^^^^^^^ YTT201
9 | PY2 = sys.version_info[0] != 3
10 | PY2 = version_info[0] != 3
|
YTT201.py:9:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=`
|
7 | PY3 = sys.version_info[0] == 3
8 | PY3 = version_info[0] == 3
9 | PY2 = sys.version_info[0] != 3
| ^^^^^^^^^^^^^^^^^^^ YTT201
10 | PY2 = version_info[0] != 3
|
YTT201.py:10:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=`
|
8 | PY3 = version_info[0] == 3
9 | PY2 = sys.version_info[0] != 3
10 | PY2 = version_info[0] != 3
| ^^^^^^^^^^^^^^^ YTT201
|

View File

@@ -1,23 +0,0 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
---
YTT202.py:4:4: YTT202 `six.PY3` referenced (python4), use `not six.PY2`
|
2 | from six import PY3
3 |
4 | if six.PY3:
| ^^^^^^^ YTT202
5 | print("3")
6 | if PY3:
|
YTT202.py:6:4: YTT202 `six.PY3` referenced (python4), use `not six.PY2`
|
4 | if six.PY3:
5 | print("3")
6 | if PY3:
| ^^^ YTT202
7 | print("3")
|

View File

@@ -1,20 +0,0 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
---
YTT203.py:4:1: YTT203 `sys.version_info[1]` compared to integer (python4), compare `sys.version_info` to tuple
|
2 | from sys import version_info
3 |
4 | sys.version_info[1] >= 5
| ^^^^^^^^^^^^^^^^^^^ YTT203
5 | version_info[1] < 6
|
YTT203.py:5:1: YTT203 `sys.version_info[1]` compared to integer (python4), compare `sys.version_info` to tuple
|
4 | sys.version_info[1] >= 5
5 | version_info[1] < 6
| ^^^^^^^^^^^^^^^ YTT203
|

View File

@@ -1,20 +0,0 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
---
YTT204.py:4:1: YTT204 `sys.version_info.minor` compared to integer (python4), compare `sys.version_info` to tuple
|
2 | from sys import version_info
3 |
4 | sys.version_info.minor <= 7
| ^^^^^^^^^^^^^^^^^^^^^^ YTT204
5 | version_info.minor > 8
|
YTT204.py:5:1: YTT204 `sys.version_info.minor` compared to integer (python4), compare `sys.version_info` to tuple
|
4 | sys.version_info.minor <= 7
5 | version_info.minor > 8
| ^^^^^^^^^^^^^^^^^^ YTT204
|

View File

@@ -1,20 +0,0 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
---
YTT301.py:4:12: YTT301 `sys.version[0]` referenced (python10), use `sys.version_info`
|
2 | from sys import version
3 |
4 | py_major = sys.version[0]
| ^^^^^^^^^^^ YTT301
5 | py_major = version[0]
|
YTT301.py:5:12: YTT301 `sys.version[0]` referenced (python10), use `sys.version_info`
|
4 | py_major = sys.version[0]
5 | py_major = version[0]
| ^^^^^^^ YTT301
|

View File

@@ -1,50 +0,0 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
---
YTT302.py:4:1: YTT302 `sys.version` compared to string (python10), use `sys.version_info`
|
2 | from sys import version
3 |
4 | version < "3"
| ^^^^^^^ YTT302
5 | sys.version < "3"
6 | sys.version <= "3"
|
YTT302.py:5:1: YTT302 `sys.version` compared to string (python10), use `sys.version_info`
|
4 | version < "3"
5 | sys.version < "3"
| ^^^^^^^^^^^ YTT302
6 | sys.version <= "3"
7 | sys.version > "3"
|
YTT302.py:6:1: YTT302 `sys.version` compared to string (python10), use `sys.version_info`
|
4 | version < "3"
5 | sys.version < "3"
6 | sys.version <= "3"
| ^^^^^^^^^^^ YTT302
7 | sys.version > "3"
8 | sys.version >= "3"
|
YTT302.py:7:1: YTT302 `sys.version` compared to string (python10), use `sys.version_info`
|
5 | sys.version < "3"
6 | sys.version <= "3"
7 | sys.version > "3"
| ^^^^^^^^^^^ YTT302
8 | sys.version >= "3"
|
YTT302.py:8:1: YTT302 `sys.version` compared to string (python10), use `sys.version_info`
|
6 | sys.version <= "3"
7 | sys.version > "3"
8 | sys.version >= "3"
| ^^^^^^^^^^^ YTT302
|

View File

@@ -1,20 +0,0 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
---
YTT303.py:4:7: YTT303 `sys.version[:1]` referenced (python10), use `sys.version_info`
|
2 | from sys import version
3 |
4 | print(sys.version[:1])
| ^^^^^^^^^^^ YTT303
5 | print(version[:1])
|
YTT303.py:5:7: YTT303 `sys.version[:1]` referenced (python10), use `sys.version_info`
|
4 | print(sys.version[:1])
5 | print(version[:1])
| ^^^^^^^ YTT303
|

View File

@@ -1,202 +0,0 @@
//! Rules from [flake8-annotations](https://pypi.org/project/flake8-annotations/).
pub(crate) mod helpers;
pub(crate) mod rules;
pub mod settings;
#[cfg(test)]
mod tests {
use std::path::Path;
use anyhow::Result;
use crate::assert_messages;
use crate::registry::Rule;
use crate::settings::Settings;
use crate::test::test_path;
#[test]
fn defaults() -> Result<()> {
let diagnostics = test_path(
Path::new("flake8_annotations/annotation_presence.py"),
&Settings {
..Settings::for_rules(vec![
Rule::MissingTypeFunctionArgument,
Rule::MissingTypeArgs,
Rule::MissingTypeKwargs,
Rule::MissingTypeSelf,
Rule::MissingTypeCls,
Rule::MissingReturnTypeUndocumentedPublicFunction,
Rule::MissingReturnTypePrivateFunction,
Rule::MissingReturnTypeSpecialMethod,
Rule::MissingReturnTypeStaticMethod,
Rule::MissingReturnTypeClassMethod,
Rule::AnyType,
])
},
)?;
assert_messages!(diagnostics);
Ok(())
}
#[test]
fn ignore_fully_untyped() -> Result<()> {
let diagnostics = test_path(
Path::new("flake8_annotations/ignore_fully_untyped.py"),
&Settings {
flake8_annotations: super::settings::Settings {
ignore_fully_untyped: true,
..Default::default()
},
..Settings::for_rules(vec![
Rule::MissingTypeFunctionArgument,
Rule::MissingTypeArgs,
Rule::MissingTypeKwargs,
Rule::MissingTypeSelf,
Rule::MissingTypeCls,
Rule::MissingReturnTypeUndocumentedPublicFunction,
Rule::MissingReturnTypePrivateFunction,
Rule::MissingReturnTypeSpecialMethod,
Rule::MissingReturnTypeStaticMethod,
Rule::MissingReturnTypeClassMethod,
Rule::AnyType,
])
},
)?;
assert_messages!(diagnostics);
Ok(())
}
#[test]
fn suppress_dummy_args() -> Result<()> {
let diagnostics = test_path(
Path::new("flake8_annotations/suppress_dummy_args.py"),
&Settings {
flake8_annotations: super::settings::Settings {
suppress_dummy_args: true,
..Default::default()
},
..Settings::for_rules(vec![
Rule::MissingTypeFunctionArgument,
Rule::MissingTypeArgs,
Rule::MissingTypeKwargs,
Rule::MissingTypeSelf,
Rule::MissingTypeCls,
])
},
)?;
assert_messages!(diagnostics);
Ok(())
}
#[test]
fn mypy_init_return() -> Result<()> {
let diagnostics = test_path(
Path::new("flake8_annotations/mypy_init_return.py"),
&Settings {
flake8_annotations: super::settings::Settings {
mypy_init_return: true,
..Default::default()
},
..Settings::for_rules(vec![
Rule::MissingReturnTypeUndocumentedPublicFunction,
Rule::MissingReturnTypePrivateFunction,
Rule::MissingReturnTypeSpecialMethod,
Rule::MissingReturnTypeStaticMethod,
Rule::MissingReturnTypeClassMethod,
])
},
)?;
assert_messages!(diagnostics);
Ok(())
}
#[test]
fn suppress_none_returning() -> Result<()> {
let diagnostics = test_path(
Path::new("flake8_annotations/suppress_none_returning.py"),
&Settings {
flake8_annotations: super::settings::Settings {
suppress_none_returning: true,
..Default::default()
},
..Settings::for_rules(vec![
Rule::MissingTypeFunctionArgument,
Rule::MissingTypeArgs,
Rule::MissingTypeKwargs,
Rule::MissingTypeSelf,
Rule::MissingTypeCls,
Rule::MissingReturnTypeUndocumentedPublicFunction,
Rule::MissingReturnTypePrivateFunction,
Rule::MissingReturnTypeSpecialMethod,
Rule::MissingReturnTypeStaticMethod,
Rule::MissingReturnTypeClassMethod,
Rule::AnyType,
])
},
)?;
assert_messages!(diagnostics);
Ok(())
}
#[test]
fn allow_star_arg_any() -> Result<()> {
let diagnostics = test_path(
Path::new("flake8_annotations/allow_star_arg_any.py"),
&Settings {
flake8_annotations: super::settings::Settings {
allow_star_arg_any: true,
..Default::default()
},
..Settings::for_rules(vec![Rule::AnyType])
},
)?;
assert_messages!(diagnostics);
Ok(())
}
#[test]
fn allow_overload() -> Result<()> {
let diagnostics = test_path(
Path::new("flake8_annotations/allow_overload.py"),
&Settings {
..Settings::for_rules(vec![
Rule::MissingReturnTypeUndocumentedPublicFunction,
Rule::MissingReturnTypePrivateFunction,
Rule::MissingReturnTypeSpecialMethod,
Rule::MissingReturnTypeStaticMethod,
Rule::MissingReturnTypeClassMethod,
])
},
)?;
assert_messages!(diagnostics);
Ok(())
}
#[test]
fn allow_nested_overload() -> Result<()> {
let diagnostics = test_path(
Path::new("flake8_annotations/allow_nested_overload.py"),
&Settings {
..Settings::for_rules(vec![
Rule::MissingReturnTypeUndocumentedPublicFunction,
Rule::MissingReturnTypePrivateFunction,
Rule::MissingReturnTypeSpecialMethod,
Rule::MissingReturnTypeStaticMethod,
Rule::MissingReturnTypeClassMethod,
])
},
)?;
assert_messages!(diagnostics);
Ok(())
}
#[test]
fn simple_magic_methods() -> Result<()> {
let diagnostics = test_path(
Path::new("flake8_annotations/simple_magic_methods.py"),
&Settings::for_rule(Rule::MissingReturnTypeSpecialMethod),
)?;
assert_messages!(diagnostics);
Ok(())
}
}

View File

@@ -1,4 +0,0 @@
---
source: crates/ruff/src/rules/flake8_annotations/mod.rs
---

View File

@@ -1,12 +0,0 @@
---
source: crates/ruff/src/rules/flake8_annotations/mod.rs
---
allow_overload.py:29:9: ANN201 Missing return type annotation for public function `bar`
|
28 | class X:
29 | def bar(i):
| ^^^ ANN201
30 | return i
|

View File

@@ -1,36 +0,0 @@
---
source: crates/ruff/src/rules/flake8_annotations/mod.rs
---
allow_star_arg_any.py:10:12: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
9 | # ANN401
10 | def foo(a: Any, *args: str, **kwargs: str) -> int:
| ^^^ ANN401
11 | pass
|
allow_star_arg_any.py:15:47: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `foo`
|
14 | # ANN401
15 | def foo(a: int, *args: str, **kwargs: str) -> Any:
| ^^^ ANN401
16 | pass
|
allow_star_arg_any.py:40:29: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
39 | # ANN401
40 | def foo_method(self, a: Any, *params: str, **options: str) -> int:
| ^^^ ANN401
41 | pass
|
allow_star_arg_any.py:44:67: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `foo_method`
|
43 | # ANN401
44 | def foo_method(self, a: int, *params: str, **options: str) -> Any:
| ^^^ ANN401
45 | pass
|

View File

@@ -1,263 +0,0 @@
---
source: crates/ruff/src/rules/flake8_annotations/mod.rs
---
annotation_presence.py:5:5: ANN201 Missing return type annotation for public function `foo`
|
4 | # Error
5 | def foo(a, b):
| ^^^ ANN201
6 | pass
|
annotation_presence.py:5:9: ANN001 Missing type annotation for function argument `a`
|
4 | # Error
5 | def foo(a, b):
| ^ ANN001
6 | pass
|
annotation_presence.py:5:12: ANN001 Missing type annotation for function argument `b`
|
4 | # Error
5 | def foo(a, b):
| ^ ANN001
6 | pass
|
annotation_presence.py:10:5: ANN201 Missing return type annotation for public function `foo`
|
9 | # Error
10 | def foo(a: int, b):
| ^^^ ANN201
11 | pass
|
annotation_presence.py:10:17: ANN001 Missing type annotation for function argument `b`
|
9 | # Error
10 | def foo(a: int, b):
| ^ ANN001
11 | pass
|
annotation_presence.py:15:17: ANN001 Missing type annotation for function argument `b`
|
14 | # Error
15 | def foo(a: int, b) -> int:
| ^ ANN001
16 | pass
|
annotation_presence.py:20:5: ANN201 Missing return type annotation for public function `foo`
|
19 | # Error
20 | def foo(a: int, b: int):
| ^^^ ANN201
21 | pass
|
annotation_presence.py:25:5: ANN201 Missing return type annotation for public function `foo`
|
24 | # Error
25 | def foo():
| ^^^ ANN201
26 | pass
|
annotation_presence.py:45:12: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
44 | # ANN401
45 | def foo(a: Any, *args: str, **kwargs: str) -> int:
| ^^^ ANN401
46 | pass
|
annotation_presence.py:50:47: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `foo`
|
49 | # ANN401
50 | def foo(a: int, *args: str, **kwargs: str) -> Any:
| ^^^ ANN401
51 | pass
|
annotation_presence.py:55:24: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `*args`
|
54 | # ANN401
55 | def foo(a: int, *args: Any, **kwargs: Any) -> int:
| ^^^ ANN401
56 | pass
|
annotation_presence.py:55:39: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `**kwargs`
|
54 | # ANN401
55 | def foo(a: int, *args: Any, **kwargs: Any) -> int:
| ^^^ ANN401
56 | pass
|
annotation_presence.py:60:24: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `*args`
|
59 | # ANN401
60 | def foo(a: int, *args: Any, **kwargs: str) -> int:
| ^^^ ANN401
61 | pass
|
annotation_presence.py:65:39: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `**kwargs`
|
64 | # ANN401
65 | def foo(a: int, *args: str, **kwargs: Any) -> int:
| ^^^ ANN401
66 | pass
|
annotation_presence.py:75:13: ANN101 Missing type annotation for `self` in method
|
74 | # ANN101
75 | def foo(self, a: int, b: int) -> int:
| ^^^^ ANN101
76 | pass
|
annotation_presence.py:79:29: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
78 | # ANN401
79 | def foo(self: "Foo", a: Any, *params: str, **options: str) -> int:
| ^^^ ANN401
80 | pass
|
annotation_presence.py:83:67: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `foo`
|
82 | # ANN401
83 | def foo(self: "Foo", a: int, *params: str, **options: str) -> Any:
| ^^^ ANN401
84 | pass
|
annotation_presence.py:87:43: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `*params`
|
86 | # ANN401
87 | def foo(self: "Foo", a: int, *params: Any, **options: Any) -> int:
| ^^^ ANN401
88 | pass
|
annotation_presence.py:87:59: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `**options`
|
86 | # ANN401
87 | def foo(self: "Foo", a: int, *params: Any, **options: Any) -> int:
| ^^^ ANN401
88 | pass
|
annotation_presence.py:91:43: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `*params`
|
90 | # ANN401
91 | def foo(self: "Foo", a: int, *params: Any, **options: str) -> int:
| ^^^ ANN401
92 | pass
|
annotation_presence.py:95:59: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `**options`
|
94 | # ANN401
95 | def foo(self: "Foo", a: int, *params: str, **options: Any) -> int:
| ^^^ ANN401
96 | pass
|
annotation_presence.py:130:13: ANN102 Missing type annotation for `cls` in classmethod
|
128 | # ANN102
129 | @classmethod
130 | def foo(cls, a: int, b: int) -> int:
| ^^^ ANN102
131 | pass
|
annotation_presence.py:134:13: ANN101 Missing type annotation for `self` in method
|
133 | # ANN101
134 | def foo(self, /, a: int, b: int) -> int:
| ^^^^ ANN101
135 | pass
|
annotation_presence.py:149:10: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
148 | # ANN401
149 | def f(a: Any | int) -> None: ...
| ^^^^^^^^^ ANN401
150 | def f(a: int | Any) -> None: ...
151 | def f(a: Union[str, bytes, Any]) -> None: ...
|
annotation_presence.py:150:10: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
148 | # ANN401
149 | def f(a: Any | int) -> None: ...
150 | def f(a: int | Any) -> None: ...
| ^^^^^^^^^ ANN401
151 | def f(a: Union[str, bytes, Any]) -> None: ...
152 | def f(a: Optional[Any]) -> None: ...
|
annotation_presence.py:151:10: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
149 | def f(a: Any | int) -> None: ...
150 | def f(a: int | Any) -> None: ...
151 | def f(a: Union[str, bytes, Any]) -> None: ...
| ^^^^^^^^^^^^^^^^^^^^^^ ANN401
152 | def f(a: Optional[Any]) -> None: ...
153 | def f(a: Annotated[Any, ...]) -> None: ...
|
annotation_presence.py:152:10: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
150 | def f(a: int | Any) -> None: ...
151 | def f(a: Union[str, bytes, Any]) -> None: ...
152 | def f(a: Optional[Any]) -> None: ...
| ^^^^^^^^^^^^^ ANN401
153 | def f(a: Annotated[Any, ...]) -> None: ...
154 | def f(a: "Union[str, bytes, Any]") -> None: ...
|
annotation_presence.py:153:10: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
151 | def f(a: Union[str, bytes, Any]) -> None: ...
152 | def f(a: Optional[Any]) -> None: ...
153 | def f(a: Annotated[Any, ...]) -> None: ...
| ^^^^^^^^^^^^^^^^^^^ ANN401
154 | def f(a: "Union[str, bytes, Any]") -> None: ...
|
annotation_presence.py:154:10: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
152 | def f(a: Optional[Any]) -> None: ...
153 | def f(a: Annotated[Any, ...]) -> None: ...
154 | def f(a: "Union[str, bytes, Any]") -> None: ...
| ^^^^^^^^^^^^^^^^^^^^^^^^ ANN401
|
annotation_presence.py:159:9: ANN204 [*] Missing return type annotation for special method `__init__`
|
157 | class Foo:
158 | @decorator()
159 | def __init__(self: "Foo", foo: int):
| ^^^^^^^^ ANN204
160 | ...
|
= help: Add `None` return type
Suggested fix
156 156 |
157 157 | class Foo:
158 158 | @decorator()
159 |- def __init__(self: "Foo", foo: int):
159 |+ def __init__(self: "Foo", foo: int) -> None:
160 160 | ...

View File

@@ -1,41 +0,0 @@
---
source: crates/ruff/src/rules/flake8_annotations/mod.rs
---
ignore_fully_untyped.py:24:5: ANN201 Missing return type annotation for public function `error_partially_typed_1`
|
24 | def error_partially_typed_1(a: int, b):
| ^^^^^^^^^^^^^^^^^^^^^^^ ANN201
25 | pass
|
ignore_fully_untyped.py:24:37: ANN001 Missing type annotation for function argument `b`
|
24 | def error_partially_typed_1(a: int, b):
| ^ ANN001
25 | pass
|
ignore_fully_untyped.py:28:37: ANN001 Missing type annotation for function argument `b`
|
28 | def error_partially_typed_2(a: int, b) -> int:
| ^ ANN001
29 | pass
|
ignore_fully_untyped.py:32:5: ANN201 Missing return type annotation for public function `error_partially_typed_3`
|
32 | def error_partially_typed_3(a: int, b: int):
| ^^^^^^^^^^^^^^^^^^^^^^^ ANN201
33 | pass
|
ignore_fully_untyped.py:43:9: ANN201 Missing return type annotation for public function `error_typed_self`
|
41 | pass
42 |
43 | def error_typed_self(self: X):
| ^^^^^^^^^^^^^^^^ ANN201
44 | pass
|

View File

@@ -1,70 +0,0 @@
---
source: crates/ruff/src/rules/flake8_annotations/mod.rs
---
mypy_init_return.py:5:9: ANN204 [*] Missing return type annotation for special method `__init__`
|
3 | # Error
4 | class Foo:
5 | def __init__(self):
| ^^^^^^^^ ANN204
6 | ...
|
= help: Add `None` return type
Suggested fix
2 2 |
3 3 | # Error
4 4 | class Foo:
5 |- def __init__(self):
5 |+ def __init__(self) -> None:
6 6 | ...
7 7 |
8 8 |
mypy_init_return.py:11:9: ANN204 [*] Missing return type annotation for special method `__init__`
|
9 | # Error
10 | class Foo:
11 | def __init__(self, foo):
| ^^^^^^^^ ANN204
12 | ...
|
= help: Add `None` return type
Suggested fix
8 8 |
9 9 | # Error
10 10 | class Foo:
11 |- def __init__(self, foo):
11 |+ def __init__(self, foo) -> None:
12 12 | ...
13 13 |
14 14 |
mypy_init_return.py:40:5: ANN202 Missing return type annotation for private function `__init__`
|
39 | # Error
40 | def __init__(self, foo: int):
| ^^^^^^^^ ANN202
41 | ...
|
mypy_init_return.py:47:9: ANN204 [*] Missing return type annotation for special method `__init__`
|
45 | # of a vararg falsely indicated that the function has a typed argument.
46 | class Foo:
47 | def __init__(self, *arg):
| ^^^^^^^^ ANN204
48 | ...
|
= help: Add `None` return type
Suggested fix
44 44 | # Error used to be ok for a moment since the mere presence
45 45 | # of a vararg falsely indicated that the function has a typed argument.
46 46 | class Foo:
47 |- def __init__(self, *arg):
47 |+ def __init__(self, *arg) -> None:
48 48 | ...

View File

@@ -1,279 +0,0 @@
---
source: crates/ruff/src/rules/flake8_annotations/mod.rs
---
simple_magic_methods.py:2:9: ANN204 [*] Missing return type annotation for special method `__str__`
|
1 | class Foo:
2 | def __str__(self):
| ^^^^^^^ ANN204
3 | ...
|
= help: Add `None` return type
Suggested fix
1 1 | class Foo:
2 |- def __str__(self):
2 |+ def __str__(self) -> str:
3 3 | ...
4 4 |
5 5 | def __repr__(self):
simple_magic_methods.py:5:9: ANN204 [*] Missing return type annotation for special method `__repr__`
|
3 | ...
4 |
5 | def __repr__(self):
| ^^^^^^^^ ANN204
6 | ...
|
= help: Add `None` return type
Suggested fix
2 2 | def __str__(self):
3 3 | ...
4 4 |
5 |- def __repr__(self):
5 |+ def __repr__(self) -> str:
6 6 | ...
7 7 |
8 8 | def __len__(self):
simple_magic_methods.py:8:9: ANN204 [*] Missing return type annotation for special method `__len__`
|
6 | ...
7 |
8 | def __len__(self):
| ^^^^^^^ ANN204
9 | ...
|
= help: Add `None` return type
Suggested fix
5 5 | def __repr__(self):
6 6 | ...
7 7 |
8 |- def __len__(self):
8 |+ def __len__(self) -> int:
9 9 | ...
10 10 |
11 11 | def __length_hint__(self):
simple_magic_methods.py:11:9: ANN204 [*] Missing return type annotation for special method `__length_hint__`
|
9 | ...
10 |
11 | def __length_hint__(self):
| ^^^^^^^^^^^^^^^ ANN204
12 | ...
|
= help: Add `None` return type
Suggested fix
8 8 | def __len__(self):
9 9 | ...
10 10 |
11 |- def __length_hint__(self):
11 |+ def __length_hint__(self) -> int:
12 12 | ...
13 13 |
14 14 | def __init__(self):
simple_magic_methods.py:14:9: ANN204 [*] Missing return type annotation for special method `__init__`
|
12 | ...
13 |
14 | def __init__(self):
| ^^^^^^^^ ANN204
15 | ...
|
= help: Add `None` return type
Suggested fix
11 11 | def __length_hint__(self):
12 12 | ...
13 13 |
14 |- def __init__(self):
14 |+ def __init__(self) -> None:
15 15 | ...
16 16 |
17 17 | def __del__(self):
simple_magic_methods.py:17:9: ANN204 [*] Missing return type annotation for special method `__del__`
|
15 | ...
16 |
17 | def __del__(self):
| ^^^^^^^ ANN204
18 | ...
|
= help: Add `None` return type
Suggested fix
14 14 | def __init__(self):
15 15 | ...
16 16 |
17 |- def __del__(self):
17 |+ def __del__(self) -> None:
18 18 | ...
19 19 |
20 20 | def __bool__(self):
simple_magic_methods.py:20:9: ANN204 [*] Missing return type annotation for special method `__bool__`
|
18 | ...
19 |
20 | def __bool__(self):
| ^^^^^^^^ ANN204
21 | ...
|
= help: Add `None` return type
Suggested fix
17 17 | def __del__(self):
18 18 | ...
19 19 |
20 |- def __bool__(self):
20 |+ def __bool__(self) -> bool:
21 21 | ...
22 22 |
23 23 | def __bytes__(self):
simple_magic_methods.py:23:9: ANN204 [*] Missing return type annotation for special method `__bytes__`
|
21 | ...
22 |
23 | def __bytes__(self):
| ^^^^^^^^^ ANN204
24 | ...
|
= help: Add `None` return type
Suggested fix
20 20 | def __bool__(self):
21 21 | ...
22 22 |
23 |- def __bytes__(self):
23 |+ def __bytes__(self) -> bytes:
24 24 | ...
25 25 |
26 26 | def __format__(self, format_spec):
simple_magic_methods.py:26:9: ANN204 [*] Missing return type annotation for special method `__format__`
|
24 | ...
25 |
26 | def __format__(self, format_spec):
| ^^^^^^^^^^ ANN204
27 | ...
|
= help: Add `None` return type
Suggested fix
23 23 | def __bytes__(self):
24 24 | ...
25 25 |
26 |- def __format__(self, format_spec):
26 |+ def __format__(self, format_spec) -> str:
27 27 | ...
28 28 |
29 29 | def __contains__(self, item):
simple_magic_methods.py:29:9: ANN204 [*] Missing return type annotation for special method `__contains__`
|
27 | ...
28 |
29 | def __contains__(self, item):
| ^^^^^^^^^^^^ ANN204
30 | ...
|
= help: Add `None` return type
Suggested fix
26 26 | def __format__(self, format_spec):
27 27 | ...
28 28 |
29 |- def __contains__(self, item):
29 |+ def __contains__(self, item) -> bool:
30 30 | ...
31 31 |
32 32 | def __complex__(self):
simple_magic_methods.py:32:9: ANN204 [*] Missing return type annotation for special method `__complex__`
|
30 | ...
31 |
32 | def __complex__(self):
| ^^^^^^^^^^^ ANN204
33 | ...
|
= help: Add `None` return type
Suggested fix
29 29 | def __contains__(self, item):
30 30 | ...
31 31 |
32 |- def __complex__(self):
32 |+ def __complex__(self) -> complex:
33 33 | ...
34 34 |
35 35 | def __int__(self):
simple_magic_methods.py:35:9: ANN204 [*] Missing return type annotation for special method `__int__`
|
33 | ...
34 |
35 | def __int__(self):
| ^^^^^^^ ANN204
36 | ...
|
= help: Add `None` return type
Suggested fix
32 32 | def __complex__(self):
33 33 | ...
34 34 |
35 |- def __int__(self):
35 |+ def __int__(self) -> int:
36 36 | ...
37 37 |
38 38 | def __float__(self):
simple_magic_methods.py:38:9: ANN204 [*] Missing return type annotation for special method `__float__`
|
36 | ...
37 |
38 | def __float__(self):
| ^^^^^^^^^ ANN204
39 | ...
|
= help: Add `None` return type
Suggested fix
35 35 | def __int__(self):
36 36 | ...
37 37 |
38 |- def __float__(self):
38 |+ def __float__(self) -> float:
39 39 | ...
40 40 |
41 41 | def __index__(self):
simple_magic_methods.py:41:9: ANN204 [*] Missing return type annotation for special method `__index__`
|
39 | ...
40 |
41 | def __index__(self):
| ^^^^^^^^^ ANN204
42 | ...
|
= help: Add `None` return type
Suggested fix
38 38 | def __float__(self):
39 39 | ...
40 40 |
41 |- def __index__(self):
41 |+ def __index__(self) -> int:
42 42 | ...

View File

@@ -1,4 +0,0 @@
---
source: crates/ruff/src/rules/flake8_annotations/mod.rs
---

View File

@@ -1,29 +0,0 @@
---
source: crates/ruff/src/rules/flake8_annotations/mod.rs
---
suppress_none_returning.py:45:5: ANN201 Missing return type annotation for public function `foo`
|
44 | # Error
45 | def foo():
| ^^^ ANN201
46 | return True
|
suppress_none_returning.py:50:5: ANN201 Missing return type annotation for public function `foo`
|
49 | # Error
50 | def foo():
| ^^^ ANN201
51 | a = 2 + 2
52 | if a == 4:
|
suppress_none_returning.py:59:9: ANN001 Missing type annotation for function argument `a`
|
58 | # Error (on the argument, but not the return type)
59 | def foo(a):
| ^ ANN001
60 | a = 2 + 2
|

View File

@@ -1,28 +0,0 @@
//! Rules from [flake8-async](https://pypi.org/project/flake8-async/).
pub(crate) mod rules;
#[cfg(test)]
mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::assert_messages;
use crate::registry::Rule;
use crate::settings::Settings;
use crate::test::test_path;
#[test_case(Rule::BlockingHttpCallInAsyncFunction, Path::new("ASYNC100.py"))]
#[test_case(Rule::OpenSleepOrSubprocessInAsyncFunction, Path::new("ASYNC101.py"))]
#[test_case(Rule::BlockingOsCallInAsyncFunction, Path::new("ASYNC102.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path(
Path::new("flake8_async").join(path).as_path(),
&Settings::for_rule(rule_code),
)?;
assert_messages!(snapshot, diagnostics);
Ok(())
}
}

View File

@@ -1,39 +0,0 @@
---
source: crates/ruff/src/rules/flake8_async/mod.rs
---
ASYNC100.py:7:5: ASYNC100 Async functions should not call blocking HTTP methods
|
6 | async def foo():
7 | urllib.request.urlopen("http://example.com/foo/bar").read()
| ^^^^^^^^^^^^^^^^^^^^^^ ASYNC100
|
ASYNC100.py:11:5: ASYNC100 Async functions should not call blocking HTTP methods
|
10 | async def foo():
11 | requests.get()
| ^^^^^^^^^^^^ ASYNC100
|
ASYNC100.py:15:5: ASYNC100 Async functions should not call blocking HTTP methods
|
14 | async def foo():
15 | httpx.get()
| ^^^^^^^^^ ASYNC100
|
ASYNC100.py:19:5: ASYNC100 Async functions should not call blocking HTTP methods
|
18 | async def foo():
19 | requests.post()
| ^^^^^^^^^^^^^ ASYNC100
|
ASYNC100.py:23:5: ASYNC100 Async functions should not call blocking HTTP methods
|
22 | async def foo():
23 | httpx.post()
| ^^^^^^^^^^ ASYNC100
|

View File

@@ -1,46 +0,0 @@
---
source: crates/ruff/src/rules/flake8_async/mod.rs
---
ASYNC101.py:7:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods
|
6 | async def foo():
7 | open("foo")
| ^^^^ ASYNC101
|
ASYNC101.py:11:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods
|
10 | async def foo():
11 | time.sleep(1)
| ^^^^^^^^^^ ASYNC101
|
ASYNC101.py:15:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods
|
14 | async def foo():
15 | subprocess.run("foo")
| ^^^^^^^^^^^^^^ ASYNC101
|
ASYNC101.py:19:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods
|
18 | async def foo():
19 | subprocess.call("foo")
| ^^^^^^^^^^^^^^^ ASYNC101
|
ASYNC101.py:27:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods
|
26 | async def foo():
27 | os.wait4(10)
| ^^^^^^^^ ASYNC101
|
ASYNC101.py:31:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods
|
30 | async def foo():
31 | os.wait(12)
| ^^^^^^^ ASYNC101
|

View File

@@ -1,18 +0,0 @@
---
source: crates/ruff/src/rules/flake8_async/mod.rs
---
ASYNC102.py:5:5: ASYNC102 Async functions should not call synchronous `os` methods
|
4 | async def foo():
5 | os.popen()
| ^^^^^^^^ ASYNC102
|
ASYNC102.py:9:5: ASYNC102 Async functions should not call synchronous `os` methods
|
8 | async def foo():
9 | os.spawnl()
| ^^^^^^^^^ ASYNC102
|

View File

@@ -1,94 +0,0 @@
//! Rules from [flake8-bandit](https://pypi.org/project/flake8-bandit/).
mod helpers;
pub(crate) mod rules;
pub mod settings;
#[cfg(test)]
mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::assert_messages;
use crate::registry::Rule;
use crate::settings::Settings;
use crate::test::test_path;
#[test_case(Rule::Assert, Path::new("S101.py"))]
#[test_case(Rule::BadFilePermissions, Path::new("S103.py"))]
#[test_case(Rule::CallWithShellEqualsTrue, Path::new("S604.py"))]
#[test_case(Rule::ExecBuiltin, Path::new("S102.py"))]
#[test_case(Rule::HardcodedBindAllInterfaces, Path::new("S104.py"))]
#[test_case(Rule::HardcodedPasswordDefault, Path::new("S107.py"))]
#[test_case(Rule::HardcodedPasswordFuncArg, Path::new("S106.py"))]
#[test_case(Rule::HardcodedPasswordString, Path::new("S105.py"))]
#[test_case(Rule::HardcodedSQLExpression, Path::new("S608.py"))]
#[test_case(Rule::HardcodedTempFile, Path::new("S108.py"))]
#[test_case(Rule::HashlibInsecureHashFunction, Path::new("S324.py"))]
#[test_case(Rule::Jinja2AutoescapeFalse, Path::new("S701.py"))]
#[test_case(Rule::LoggingConfigInsecureListen, Path::new("S612.py"))]
#[test_case(Rule::ParamikoCall, Path::new("S601.py"))]
#[test_case(Rule::RequestWithNoCertValidation, Path::new("S501.py"))]
#[test_case(Rule::RequestWithoutTimeout, Path::new("S113.py"))]
#[test_case(Rule::SnmpInsecureVersion, Path::new("S508.py"))]
#[test_case(Rule::SnmpWeakCryptography, Path::new("S509.py"))]
#[test_case(Rule::StartProcessWithAShell, Path::new("S605.py"))]
#[test_case(Rule::StartProcessWithNoShell, Path::new("S606.py"))]
#[test_case(Rule::StartProcessWithPartialPath, Path::new("S607.py"))]
#[test_case(Rule::SubprocessPopenWithShellEqualsTrue, Path::new("S602.py"))]
#[test_case(Rule::SubprocessWithoutShellEqualsTrue, Path::new("S603.py"))]
#[test_case(Rule::SuspiciousPickleUsage, Path::new("S301.py"))]
#[test_case(Rule::SuspiciousEvalUsage, Path::new("S307.py"))]
#[test_case(Rule::SuspiciousTelnetUsage, Path::new("S312.py"))]
#[test_case(Rule::TryExceptContinue, Path::new("S112.py"))]
#[test_case(Rule::TryExceptPass, Path::new("S110.py"))]
#[test_case(Rule::UnixCommandWildcardInjection, Path::new("S609.py"))]
#[test_case(Rule::UnsafeYAMLLoad, Path::new("S506.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path(
Path::new("flake8_bandit").join(path).as_path(),
&Settings::for_rule(rule_code),
)?;
assert_messages!(snapshot, diagnostics);
Ok(())
}
#[test]
fn check_hardcoded_tmp_additional_dirs() -> Result<()> {
let diagnostics = test_path(
Path::new("flake8_bandit/S108.py"),
&Settings {
flake8_bandit: super::settings::Settings {
hardcoded_tmp_directory: vec![
"/tmp".to_string(),
"/var/tmp".to_string(),
"/dev/shm".to_string(),
"/foo".to_string(),
],
check_typed_exception: false,
},
..Settings::for_rule(Rule::HardcodedTempFile)
},
)?;
assert_messages!("S108_extend", diagnostics);
Ok(())
}
#[test]
fn check_typed_exception() -> Result<()> {
let diagnostics = test_path(
Path::new("flake8_bandit/S110.py"),
&Settings {
flake8_bandit: super::settings::Settings {
check_typed_exception: true,
..Default::default()
},
..Settings::for_rule(Rule::TryExceptPass)
},
)?;
assert_messages!("S110_typed", diagnostics);
Ok(())
}
}

View File

@@ -1,45 +0,0 @@
pub(crate) use assert_used::*;
pub(crate) use bad_file_permissions::*;
pub(crate) use exec_used::*;
pub(crate) use hardcoded_bind_all_interfaces::*;
pub(crate) use hardcoded_password_default::*;
pub(crate) use hardcoded_password_func_arg::*;
pub(crate) use hardcoded_password_string::*;
pub(crate) use hardcoded_sql_expression::*;
pub(crate) use hardcoded_tmp_directory::*;
pub(crate) use hashlib_insecure_hash_functions::*;
pub(crate) use jinja2_autoescape_false::*;
pub(crate) use logging_config_insecure_listen::*;
pub(crate) use paramiko_calls::*;
pub(crate) use request_with_no_cert_validation::*;
pub(crate) use request_without_timeout::*;
pub(crate) use shell_injection::*;
pub(crate) use snmp_insecure_version::*;
pub(crate) use snmp_weak_cryptography::*;
pub(crate) use suspicious_function_call::*;
pub(crate) use try_except_continue::*;
pub(crate) use try_except_pass::*;
pub(crate) use unsafe_yaml_load::*;
mod assert_used;
mod bad_file_permissions;
mod exec_used;
mod hardcoded_bind_all_interfaces;
mod hardcoded_password_default;
mod hardcoded_password_func_arg;
mod hardcoded_password_string;
mod hardcoded_sql_expression;
mod hardcoded_tmp_directory;
mod hashlib_insecure_hash_functions;
mod jinja2_autoescape_false;
mod logging_config_insecure_listen;
mod paramiko_calls;
mod request_with_no_cert_validation;
mod request_without_timeout;
mod shell_injection;
mod snmp_insecure_version;
mod snmp_weak_cryptography;
mod suspicious_function_call;
mod try_except_continue;
mod try_except_pass;
mod unsafe_yaml_load;

View File

@@ -1,27 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S101.py:1:1: S101 Use of `assert` detected
|
1 | assert True # S101
| ^^^^^^ S101
|
S101.py:6:5: S101 Use of `assert` detected
|
4 | def fn():
5 | x = 1
6 | assert x == 1 # S101
| ^^^^^^ S101
7 | assert x == 2 # S101
|
S101.py:7:5: S101 Use of `assert` detected
|
5 | x = 1
6 | assert x == 1 # S101
7 | assert x == 2 # S101
| ^^^^^^ S101
|

View File

@@ -1,22 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S102.py:3:5: S102 Use of `exec` detected
|
1 | def fn():
2 | # Error
3 | exec('x = 2')
| ^^^^ S102
4 |
5 | exec('y = 3')
|
S102.py:5:1: S102 Use of `exec` detected
|
3 | exec('x = 2')
4 |
5 | exec('y = 3')
| ^^^^ S102
|

View File

@@ -1,131 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S103.py:6:25: S103 `os.chmod` setting a permissive mask `0o227` on file or directory
|
4 | keyfile = "foo"
5 |
6 | os.chmod("/etc/passwd", 0o227) # Error
| ^^^^^ S103
7 | os.chmod("/etc/passwd", 0o7) # Error
8 | os.chmod("/etc/passwd", 0o664) # OK
|
S103.py:7:25: S103 `os.chmod` setting a permissive mask `0o7` on file or directory
|
6 | os.chmod("/etc/passwd", 0o227) # Error
7 | os.chmod("/etc/passwd", 0o7) # Error
| ^^^ S103
8 | os.chmod("/etc/passwd", 0o664) # OK
9 | os.chmod("/etc/passwd", 0o777) # Error
|
S103.py:9:25: S103 `os.chmod` setting a permissive mask `0o777` on file or directory
|
7 | os.chmod("/etc/passwd", 0o7) # Error
8 | os.chmod("/etc/passwd", 0o664) # OK
9 | os.chmod("/etc/passwd", 0o777) # Error
| ^^^^^ S103
10 | os.chmod("/etc/passwd", 0o770) # Error
11 | os.chmod("/etc/passwd", 0o776) # Error
|
S103.py:10:25: S103 `os.chmod` setting a permissive mask `0o770` on file or directory
|
8 | os.chmod("/etc/passwd", 0o664) # OK
9 | os.chmod("/etc/passwd", 0o777) # Error
10 | os.chmod("/etc/passwd", 0o770) # Error
| ^^^^^ S103
11 | os.chmod("/etc/passwd", 0o776) # Error
12 | os.chmod("/etc/passwd", 0o760) # OK
|
S103.py:11:25: S103 `os.chmod` setting a permissive mask `0o776` on file or directory
|
9 | os.chmod("/etc/passwd", 0o777) # Error
10 | os.chmod("/etc/passwd", 0o770) # Error
11 | os.chmod("/etc/passwd", 0o776) # Error
| ^^^^^ S103
12 | os.chmod("/etc/passwd", 0o760) # OK
13 | os.chmod("~/.bashrc", 511) # Error
|
S103.py:13:23: S103 `os.chmod` setting a permissive mask `0o777` on file or directory
|
11 | os.chmod("/etc/passwd", 0o776) # Error
12 | os.chmod("/etc/passwd", 0o760) # OK
13 | os.chmod("~/.bashrc", 511) # Error
| ^^^ S103
14 | os.chmod("/etc/hosts", 0o777) # Error
15 | os.chmod("/tmp/oh_hai", 0x1FF) # Error
|
S103.py:14:24: S103 `os.chmod` setting a permissive mask `0o777` on file or directory
|
12 | os.chmod("/etc/passwd", 0o760) # OK
13 | os.chmod("~/.bashrc", 511) # Error
14 | os.chmod("/etc/hosts", 0o777) # Error
| ^^^^^ S103
15 | os.chmod("/tmp/oh_hai", 0x1FF) # Error
16 | os.chmod("/etc/passwd", stat.S_IRWXU) # OK
|
S103.py:15:25: S103 `os.chmod` setting a permissive mask `0o777` on file or directory
|
13 | os.chmod("~/.bashrc", 511) # Error
14 | os.chmod("/etc/hosts", 0o777) # Error
15 | os.chmod("/tmp/oh_hai", 0x1FF) # Error
| ^^^^^ S103
16 | os.chmod("/etc/passwd", stat.S_IRWXU) # OK
17 | os.chmod(keyfile, 0o777) # Error
|
S103.py:17:19: S103 `os.chmod` setting a permissive mask `0o777` on file or directory
|
15 | os.chmod("/tmp/oh_hai", 0x1FF) # Error
16 | os.chmod("/etc/passwd", stat.S_IRWXU) # OK
17 | os.chmod(keyfile, 0o777) # Error
| ^^^^^ S103
18 | os.chmod(keyfile, 0o7 | 0o70 | 0o700) # Error
19 | os.chmod(keyfile, stat.S_IRWXO | stat.S_IRWXG | stat.S_IRWXU) # Error
|
S103.py:18:19: S103 `os.chmod` setting a permissive mask `0o777` on file or directory
|
16 | os.chmod("/etc/passwd", stat.S_IRWXU) # OK
17 | os.chmod(keyfile, 0o777) # Error
18 | os.chmod(keyfile, 0o7 | 0o70 | 0o700) # Error
| ^^^^^^^^^^^^^^^^^^ S103
19 | os.chmod(keyfile, stat.S_IRWXO | stat.S_IRWXG | stat.S_IRWXU) # Error
20 | os.chmod("~/hidden_exec", stat.S_IXGRP) # Error
|
S103.py:19:19: S103 `os.chmod` setting a permissive mask `0o777` on file or directory
|
17 | os.chmod(keyfile, 0o777) # Error
18 | os.chmod(keyfile, 0o7 | 0o70 | 0o700) # Error
19 | os.chmod(keyfile, stat.S_IRWXO | stat.S_IRWXG | stat.S_IRWXU) # Error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S103
20 | os.chmod("~/hidden_exec", stat.S_IXGRP) # Error
21 | os.chmod("~/hidden_exec", stat.S_IXOTH) # OK
|
S103.py:20:27: S103 `os.chmod` setting a permissive mask `0o10` on file or directory
|
18 | os.chmod(keyfile, 0o7 | 0o70 | 0o700) # Error
19 | os.chmod(keyfile, stat.S_IRWXO | stat.S_IRWXG | stat.S_IRWXU) # Error
20 | os.chmod("~/hidden_exec", stat.S_IXGRP) # Error
| ^^^^^^^^^^^^ S103
21 | os.chmod("~/hidden_exec", stat.S_IXOTH) # OK
22 | os.chmod("/etc/passwd", stat.S_IWOTH) # Error
|
S103.py:22:25: S103 `os.chmod` setting a permissive mask `0o2` on file or directory
|
20 | os.chmod("~/hidden_exec", stat.S_IXGRP) # Error
21 | os.chmod("~/hidden_exec", stat.S_IXOTH) # OK
22 | os.chmod("/etc/passwd", stat.S_IWOTH) # Error
| ^^^^^^^^^^^^ S103
|

View File

@@ -1,35 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S104.py:9:1: S104 Possible binding to all interfaces
|
8 | # Error
9 | "0.0.0.0"
| ^^^^^^^^^ S104
10 | '0.0.0.0'
|
S104.py:10:1: S104 Possible binding to all interfaces
|
8 | # Error
9 | "0.0.0.0"
10 | '0.0.0.0'
| ^^^^^^^^^ S104
|
S104.py:14:6: S104 Possible binding to all interfaces
|
13 | # Error
14 | func("0.0.0.0")
| ^^^^^^^^^ S104
|
S104.py:18:9: S104 Possible binding to all interfaces
|
17 | def my_func():
18 | x = "0.0.0.0"
| ^^^^^^^^^ S104
19 | print(x)
|

View File

@@ -1,377 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S105.py:13:12: S105 Possible hardcoded password assigned to: "password"
|
12 | # Errors
13 | password = "s3cr3t"
| ^^^^^^^^ S105
14 | _pass = "s3cr3t"
15 | passwd = "s3cr3t"
|
S105.py:14:9: S105 Possible hardcoded password assigned to: "_pass"
|
12 | # Errors
13 | password = "s3cr3t"
14 | _pass = "s3cr3t"
| ^^^^^^^^ S105
15 | passwd = "s3cr3t"
16 | pwd = "s3cr3t"
|
S105.py:15:10: S105 Possible hardcoded password assigned to: "passwd"
|
13 | password = "s3cr3t"
14 | _pass = "s3cr3t"
15 | passwd = "s3cr3t"
| ^^^^^^^^ S105
16 | pwd = "s3cr3t"
17 | secret = "s3cr3t"
|
S105.py:16:7: S105 Possible hardcoded password assigned to: "pwd"
|
14 | _pass = "s3cr3t"
15 | passwd = "s3cr3t"
16 | pwd = "s3cr3t"
| ^^^^^^^^ S105
17 | secret = "s3cr3t"
18 | token = "s3cr3t"
|
S105.py:17:10: S105 Possible hardcoded password assigned to: "secret"
|
15 | passwd = "s3cr3t"
16 | pwd = "s3cr3t"
17 | secret = "s3cr3t"
| ^^^^^^^^ S105
18 | token = "s3cr3t"
19 | secrete = "s3cr3t"
|
S105.py:18:9: S105 Possible hardcoded password assigned to: "token"
|
16 | pwd = "s3cr3t"
17 | secret = "s3cr3t"
18 | token = "s3cr3t"
| ^^^^^^^^ S105
19 | secrete = "s3cr3t"
20 | safe = password = "s3cr3t"
|
S105.py:19:11: S105 Possible hardcoded password assigned to: "secrete"
|
17 | secret = "s3cr3t"
18 | token = "s3cr3t"
19 | secrete = "s3cr3t"
| ^^^^^^^^ S105
20 | safe = password = "s3cr3t"
21 | password = safe = "s3cr3t"
|
S105.py:20:19: S105 Possible hardcoded password assigned to: "password"
|
18 | token = "s3cr3t"
19 | secrete = "s3cr3t"
20 | safe = password = "s3cr3t"
| ^^^^^^^^ S105
21 | password = safe = "s3cr3t"
22 | PASSWORD = "s3cr3t"
|
S105.py:21:19: S105 Possible hardcoded password assigned to: "password"
|
19 | secrete = "s3cr3t"
20 | safe = password = "s3cr3t"
21 | password = safe = "s3cr3t"
| ^^^^^^^^ S105
22 | PASSWORD = "s3cr3t"
23 | PassWord = "s3cr3t"
|
S105.py:22:12: S105 Possible hardcoded password assigned to: "PASSWORD"
|
20 | safe = password = "s3cr3t"
21 | password = safe = "s3cr3t"
22 | PASSWORD = "s3cr3t"
| ^^^^^^^^ S105
23 | PassWord = "s3cr3t"
|
S105.py:23:12: S105 Possible hardcoded password assigned to: "PassWord"
|
21 | password = safe = "s3cr3t"
22 | PASSWORD = "s3cr3t"
23 | PassWord = "s3cr3t"
| ^^^^^^^^ S105
24 |
25 | d["password"] = "s3cr3t"
|
S105.py:25:17: S105 Possible hardcoded password assigned to: "password"
|
23 | PassWord = "s3cr3t"
24 |
25 | d["password"] = "s3cr3t"
| ^^^^^^^^ S105
26 | d["pass"] = "s3cr3t"
27 | d["passwd"] = "s3cr3t"
|
S105.py:26:13: S105 Possible hardcoded password assigned to: "pass"
|
25 | d["password"] = "s3cr3t"
26 | d["pass"] = "s3cr3t"
| ^^^^^^^^ S105
27 | d["passwd"] = "s3cr3t"
28 | d["pwd"] = "s3cr3t"
|
S105.py:27:15: S105 Possible hardcoded password assigned to: "passwd"
|
25 | d["password"] = "s3cr3t"
26 | d["pass"] = "s3cr3t"
27 | d["passwd"] = "s3cr3t"
| ^^^^^^^^ S105
28 | d["pwd"] = "s3cr3t"
29 | d["secret"] = "s3cr3t"
|
S105.py:28:12: S105 Possible hardcoded password assigned to: "pwd"
|
26 | d["pass"] = "s3cr3t"
27 | d["passwd"] = "s3cr3t"
28 | d["pwd"] = "s3cr3t"
| ^^^^^^^^ S105
29 | d["secret"] = "s3cr3t"
30 | d["token"] = "s3cr3t"
|
S105.py:29:15: S105 Possible hardcoded password assigned to: "secret"
|
27 | d["passwd"] = "s3cr3t"
28 | d["pwd"] = "s3cr3t"
29 | d["secret"] = "s3cr3t"
| ^^^^^^^^ S105
30 | d["token"] = "s3cr3t"
31 | d["secrete"] = "s3cr3t"
|
S105.py:30:14: S105 Possible hardcoded password assigned to: "token"
|
28 | d["pwd"] = "s3cr3t"
29 | d["secret"] = "s3cr3t"
30 | d["token"] = "s3cr3t"
| ^^^^^^^^ S105
31 | d["secrete"] = "s3cr3t"
32 | safe = d["password"] = "s3cr3t"
|
S105.py:31:16: S105 Possible hardcoded password assigned to: "secrete"
|
29 | d["secret"] = "s3cr3t"
30 | d["token"] = "s3cr3t"
31 | d["secrete"] = "s3cr3t"
| ^^^^^^^^ S105
32 | safe = d["password"] = "s3cr3t"
33 | d["password"] = safe = "s3cr3t"
|
S105.py:32:24: S105 Possible hardcoded password assigned to: "password"
|
30 | d["token"] = "s3cr3t"
31 | d["secrete"] = "s3cr3t"
32 | safe = d["password"] = "s3cr3t"
| ^^^^^^^^ S105
33 | d["password"] = safe = "s3cr3t"
|
S105.py:33:24: S105 Possible hardcoded password assigned to: "password"
|
31 | d["secrete"] = "s3cr3t"
32 | safe = d["password"] = "s3cr3t"
33 | d["password"] = safe = "s3cr3t"
| ^^^^^^^^ S105
|
S105.py:37:16: S105 Possible hardcoded password assigned to: "password"
|
36 | class MyClass:
37 | password = "s3cr3t"
| ^^^^^^^^ S105
38 | safe = password
|
S105.py:41:20: S105 Possible hardcoded password assigned to: "password"
|
41 | MyClass.password = "s3cr3t"
| ^^^^^^^^ S105
42 | MyClass._pass = "s3cr3t"
43 | MyClass.passwd = "s3cr3t"
|
S105.py:42:17: S105 Possible hardcoded password assigned to: "_pass"
|
41 | MyClass.password = "s3cr3t"
42 | MyClass._pass = "s3cr3t"
| ^^^^^^^^ S105
43 | MyClass.passwd = "s3cr3t"
44 | MyClass.pwd = "s3cr3t"
|
S105.py:43:18: S105 Possible hardcoded password assigned to: "passwd"
|
41 | MyClass.password = "s3cr3t"
42 | MyClass._pass = "s3cr3t"
43 | MyClass.passwd = "s3cr3t"
| ^^^^^^^^ S105
44 | MyClass.pwd = "s3cr3t"
45 | MyClass.secret = "s3cr3t"
|
S105.py:44:15: S105 Possible hardcoded password assigned to: "pwd"
|
42 | MyClass._pass = "s3cr3t"
43 | MyClass.passwd = "s3cr3t"
44 | MyClass.pwd = "s3cr3t"
| ^^^^^^^^ S105
45 | MyClass.secret = "s3cr3t"
46 | MyClass.token = "s3cr3t"
|
S105.py:45:18: S105 Possible hardcoded password assigned to: "secret"
|
43 | MyClass.passwd = "s3cr3t"
44 | MyClass.pwd = "s3cr3t"
45 | MyClass.secret = "s3cr3t"
| ^^^^^^^^ S105
46 | MyClass.token = "s3cr3t"
47 | MyClass.secrete = "s3cr3t"
|
S105.py:46:17: S105 Possible hardcoded password assigned to: "token"
|
44 | MyClass.pwd = "s3cr3t"
45 | MyClass.secret = "s3cr3t"
46 | MyClass.token = "s3cr3t"
| ^^^^^^^^ S105
47 | MyClass.secrete = "s3cr3t"
|
S105.py:47:19: S105 Possible hardcoded password assigned to: "secrete"
|
45 | MyClass.secret = "s3cr3t"
46 | MyClass.token = "s3cr3t"
47 | MyClass.secrete = "s3cr3t"
| ^^^^^^^^ S105
48 |
49 | password == "s3cr3t"
|
S105.py:49:13: S105 Possible hardcoded password assigned to: "password"
|
47 | MyClass.secrete = "s3cr3t"
48 |
49 | password == "s3cr3t"
| ^^^^^^^^ S105
50 | _pass == "s3cr3t"
51 | passwd == "s3cr3t"
|
S105.py:50:10: S105 Possible hardcoded password assigned to: "_pass"
|
49 | password == "s3cr3t"
50 | _pass == "s3cr3t"
| ^^^^^^^^ S105
51 | passwd == "s3cr3t"
52 | pwd == "s3cr3t"
|
S105.py:51:11: S105 Possible hardcoded password assigned to: "passwd"
|
49 | password == "s3cr3t"
50 | _pass == "s3cr3t"
51 | passwd == "s3cr3t"
| ^^^^^^^^ S105
52 | pwd == "s3cr3t"
53 | secret == "s3cr3t"
|
S105.py:52:8: S105 Possible hardcoded password assigned to: "pwd"
|
50 | _pass == "s3cr3t"
51 | passwd == "s3cr3t"
52 | pwd == "s3cr3t"
| ^^^^^^^^ S105
53 | secret == "s3cr3t"
54 | token == "s3cr3t"
|
S105.py:53:11: S105 Possible hardcoded password assigned to: "secret"
|
51 | passwd == "s3cr3t"
52 | pwd == "s3cr3t"
53 | secret == "s3cr3t"
| ^^^^^^^^ S105
54 | token == "s3cr3t"
55 | secrete == "s3cr3t"
|
S105.py:54:10: S105 Possible hardcoded password assigned to: "token"
|
52 | pwd == "s3cr3t"
53 | secret == "s3cr3t"
54 | token == "s3cr3t"
| ^^^^^^^^ S105
55 | secrete == "s3cr3t"
56 | password == safe == "s3cr3t"
|
S105.py:55:12: S105 Possible hardcoded password assigned to: "secrete"
|
53 | secret == "s3cr3t"
54 | token == "s3cr3t"
55 | secrete == "s3cr3t"
| ^^^^^^^^ S105
56 | password == safe == "s3cr3t"
|
S105.py:56:21: S105 Possible hardcoded password assigned to: "password"
|
54 | token == "s3cr3t"
55 | secrete == "s3cr3t"
56 | password == safe == "s3cr3t"
| ^^^^^^^^ S105
57 |
58 | if token == "1\n2":
|
S105.py:58:13: S105 Possible hardcoded password assigned to: "token"
|
56 | password == safe == "s3cr3t"
57 |
58 | if token == "1\n2":
| ^^^^^^ S105
59 | pass
|
S105.py:61:13: S105 Possible hardcoded password assigned to: "token"
|
59 | pass
60 |
61 | if token == "3\t4":
| ^^^^^^ S105
62 | pass
|
S105.py:64:13: S105 Possible hardcoded password assigned to: "token"
|
62 | pass
63 |
64 | if token == "5\r6":
| ^^^^^^ S105
65 | pass
|

View File

@@ -1,11 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S106.py:14:9: S106 Possible hardcoded password assigned to argument: "password"
|
13 | # Error
14 | func(1, password="s3cr3t")
| ^^^^^^^^^^^^^^^^^ S106
|

View File

@@ -1,39 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S107.py:5:29: S107 Possible hardcoded password assigned to function default: "password"
|
5 | def default(first, password="default"):
| ^^^^^^^^^ S107
6 | pass
|
S107.py:13:45: S107 Possible hardcoded password assigned to function default: "password"
|
13 | def default_posonly(first, /, pos, password="posonly"):
| ^^^^^^^^^ S107
14 | pass
|
S107.py:21:39: S107 Possible hardcoded password assigned to function default: "password"
|
21 | def default_kwonly(first, *, password="kwonly"):
| ^^^^^^^^ S107
22 | pass
|
S107.py:29:39: S107 Possible hardcoded password assigned to function default: "secret"
|
29 | def default_all(first, /, pos, secret="posonly", *, password="kwonly"):
| ^^^^^^^^^ S107
30 | pass
|
S107.py:29:62: S107 Possible hardcoded password assigned to function default: "password"
|
29 | def default_all(first, /, pos, secret="posonly", *, password="kwonly"):
| ^^^^^^^^ S107
30 | pass
|

View File

@@ -1,31 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S108.py:5:11: S108 Probable insecure usage of temporary file or directory: "/tmp/abc"
|
3 | f.write("def")
4 |
5 | with open("/tmp/abc", "w") as f:
| ^^^^^^^^^^ S108
6 | f.write("def")
|
S108.py:8:11: S108 Probable insecure usage of temporary file or directory: "/var/tmp/123"
|
6 | f.write("def")
7 |
8 | with open("/var/tmp/123", "w") as f:
| ^^^^^^^^^^^^^^ S108
9 | f.write("def")
|
S108.py:11:11: S108 Probable insecure usage of temporary file or directory: "/dev/shm/unit/test"
|
9 | f.write("def")
10 |
11 | with open("/dev/shm/unit/test", "w") as f:
| ^^^^^^^^^^^^^^^^^^^^ S108
12 | f.write("def")
|

View File

@@ -1,39 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S108.py:5:11: S108 Probable insecure usage of temporary file or directory: "/tmp/abc"
|
3 | f.write("def")
4 |
5 | with open("/tmp/abc", "w") as f:
| ^^^^^^^^^^ S108
6 | f.write("def")
|
S108.py:8:11: S108 Probable insecure usage of temporary file or directory: "/var/tmp/123"
|
6 | f.write("def")
7 |
8 | with open("/var/tmp/123", "w") as f:
| ^^^^^^^^^^^^^^ S108
9 | f.write("def")
|
S108.py:11:11: S108 Probable insecure usage of temporary file or directory: "/dev/shm/unit/test"
|
9 | f.write("def")
10 |
11 | with open("/dev/shm/unit/test", "w") as f:
| ^^^^^^^^^^^^^^^^^^^^ S108
12 | f.write("def")
|
S108.py:15:11: S108 Probable insecure usage of temporary file or directory: "/foo/bar"
|
14 | # not ok by config
15 | with open("/foo/bar", "w") as f:
| ^^^^^^^^^^ S108
16 | f.write("def")
|

View File

@@ -1,26 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S110.py:3:1: S110 `try`-`except`-`pass` detected, consider logging the exception
|
1 | try:
2 | pass
3 | / except Exception:
4 | | pass
| |________^ S110
5 |
6 | try:
|
S110.py:8:1: S110 `try`-`except`-`pass` detected, consider logging the exception
|
6 | try:
7 | pass
8 | / except:
9 | | pass
| |________^ S110
10 |
11 | try:
|

View File

@@ -1,35 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S110.py:3:1: S110 `try`-`except`-`pass` detected, consider logging the exception
|
1 | try:
2 | pass
3 | / except Exception:
4 | | pass
| |________^ S110
5 |
6 | try:
|
S110.py:8:1: S110 `try`-`except`-`pass` detected, consider logging the exception
|
6 | try:
7 | pass
8 | / except:
9 | | pass
| |________^ S110
10 |
11 | try:
|
S110.py:13:1: S110 `try`-`except`-`pass` detected, consider logging the exception
|
11 | try:
12 | pass
13 | / except ValueError:
14 | | pass
| |________^ S110
|

View File

@@ -1,48 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S112.py:3:1: S112 `try`-`except`-`continue` detected, consider logging the exception
|
1 | try:
2 | pass
3 | / except Exception:
4 | | continue
| |____________^ S112
5 |
6 | try:
|
S112.py:8:1: S112 `try`-`except`-`continue` detected, consider logging the exception
|
6 | try:
7 | pass
8 | / except:
9 | | continue
| |____________^ S112
10 |
11 | try:
|
S112.py:13:1: S112 `try`-`except`-`continue` detected, consider logging the exception
|
11 | try:
12 | pass
13 | / except (Exception,):
14 | | continue
| |____________^ S112
15 |
16 | try:
|
S112.py:18:1: S112 `try`-`except`-`continue` detected, consider logging the exception
|
16 | try:
17 | pass
18 | / except (Exception, ValueError):
19 | | continue
| |____________^ S112
20 |
21 | try:
|

View File

@@ -1,142 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S113.py:3:1: S113 Probable use of requests call without timeout
|
1 | import requests
2 |
3 | requests.get('https://gmail.com')
| ^^^^^^^^^^^^ S113
4 | requests.get('https://gmail.com', timeout=None)
5 | requests.get('https://gmail.com', timeout=5)
|
S113.py:4:35: S113 Probable use of requests call with timeout set to `None`
|
3 | requests.get('https://gmail.com')
4 | requests.get('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
5 | requests.get('https://gmail.com', timeout=5)
6 | requests.post('https://gmail.com')
|
S113.py:6:1: S113 Probable use of requests call without timeout
|
4 | requests.get('https://gmail.com', timeout=None)
5 | requests.get('https://gmail.com', timeout=5)
6 | requests.post('https://gmail.com')
| ^^^^^^^^^^^^^ S113
7 | requests.post('https://gmail.com', timeout=None)
8 | requests.post('https://gmail.com', timeout=5)
|
S113.py:7:36: S113 Probable use of requests call with timeout set to `None`
|
5 | requests.get('https://gmail.com', timeout=5)
6 | requests.post('https://gmail.com')
7 | requests.post('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
8 | requests.post('https://gmail.com', timeout=5)
9 | requests.put('https://gmail.com')
|
S113.py:9:1: S113 Probable use of requests call without timeout
|
7 | requests.post('https://gmail.com', timeout=None)
8 | requests.post('https://gmail.com', timeout=5)
9 | requests.put('https://gmail.com')
| ^^^^^^^^^^^^ S113
10 | requests.put('https://gmail.com', timeout=None)
11 | requests.put('https://gmail.com', timeout=5)
|
S113.py:10:35: S113 Probable use of requests call with timeout set to `None`
|
8 | requests.post('https://gmail.com', timeout=5)
9 | requests.put('https://gmail.com')
10 | requests.put('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
11 | requests.put('https://gmail.com', timeout=5)
12 | requests.delete('https://gmail.com')
|
S113.py:12:1: S113 Probable use of requests call without timeout
|
10 | requests.put('https://gmail.com', timeout=None)
11 | requests.put('https://gmail.com', timeout=5)
12 | requests.delete('https://gmail.com')
| ^^^^^^^^^^^^^^^ S113
13 | requests.delete('https://gmail.com', timeout=None)
14 | requests.delete('https://gmail.com', timeout=5)
|
S113.py:13:38: S113 Probable use of requests call with timeout set to `None`
|
11 | requests.put('https://gmail.com', timeout=5)
12 | requests.delete('https://gmail.com')
13 | requests.delete('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
14 | requests.delete('https://gmail.com', timeout=5)
15 | requests.patch('https://gmail.com')
|
S113.py:15:1: S113 Probable use of requests call without timeout
|
13 | requests.delete('https://gmail.com', timeout=None)
14 | requests.delete('https://gmail.com', timeout=5)
15 | requests.patch('https://gmail.com')
| ^^^^^^^^^^^^^^ S113
16 | requests.patch('https://gmail.com', timeout=None)
17 | requests.patch('https://gmail.com', timeout=5)
|
S113.py:16:37: S113 Probable use of requests call with timeout set to `None`
|
14 | requests.delete('https://gmail.com', timeout=5)
15 | requests.patch('https://gmail.com')
16 | requests.patch('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
17 | requests.patch('https://gmail.com', timeout=5)
18 | requests.options('https://gmail.com')
|
S113.py:18:1: S113 Probable use of requests call without timeout
|
16 | requests.patch('https://gmail.com', timeout=None)
17 | requests.patch('https://gmail.com', timeout=5)
18 | requests.options('https://gmail.com')
| ^^^^^^^^^^^^^^^^ S113
19 | requests.options('https://gmail.com', timeout=None)
20 | requests.options('https://gmail.com', timeout=5)
|
S113.py:19:39: S113 Probable use of requests call with timeout set to `None`
|
17 | requests.patch('https://gmail.com', timeout=5)
18 | requests.options('https://gmail.com')
19 | requests.options('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
20 | requests.options('https://gmail.com', timeout=5)
21 | requests.head('https://gmail.com')
|
S113.py:21:1: S113 Probable use of requests call without timeout
|
19 | requests.options('https://gmail.com', timeout=None)
20 | requests.options('https://gmail.com', timeout=5)
21 | requests.head('https://gmail.com')
| ^^^^^^^^^^^^^ S113
22 | requests.head('https://gmail.com', timeout=None)
23 | requests.head('https://gmail.com', timeout=5)
|
S113.py:22:36: S113 Probable use of requests call with timeout set to `None`
|
20 | requests.options('https://gmail.com', timeout=5)
21 | requests.head('https://gmail.com')
22 | requests.head('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
23 | requests.head('https://gmail.com', timeout=5)
|

View File

@@ -1,12 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S301.py:3:1: S301 `pickle` and modules that wrap it can be unsafe when used to deserialize untrusted data, possible security issue
|
1 | import pickle
2 |
3 | pickle.loads()
| ^^^^^^^^^^^^^^ S301
|

View File

@@ -1,20 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S307.py:3:7: S307 Use of possibly insecure function; consider using `ast.literal_eval`
|
1 | import os
2 |
3 | print(eval("1+1")) # S307
| ^^^^^^^^^^^ S307
4 | print(eval("os.getcwd()")) # S307
|
S307.py:4:7: S307 Use of possibly insecure function; consider using `ast.literal_eval`
|
3 | print(eval("1+1")) # S307
4 | print(eval("os.getcwd()")) # S307
| ^^^^^^^^^^^^^^^^^^^ S307
|

View File

@@ -1,12 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S312.py:3:1: S312 Telnet-related functions are being called. Telnet is considered insecure. Use SSH or some other encrypted protocol.
|
1 | from telnetlib import Telnet
2 |
3 | Telnet("localhost", 23)
| ^^^^^^^^^^^^^^^^^^^^^^^ S312
|

View File

@@ -1,133 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S324.py:7:13: S324 Probable use of insecure hash functions in `hashlib`: `md5`
|
5 | # Invalid
6 |
7 | hashlib.new('md5')
| ^^^^^ S324
8 |
9 | hashlib.new('md4', b'test')
|
S324.py:9:13: S324 Probable use of insecure hash functions in `hashlib`: `md4`
|
7 | hashlib.new('md5')
8 |
9 | hashlib.new('md4', b'test')
| ^^^^^ S324
10 |
11 | hashlib.new(name='md5', data=b'test')
|
S324.py:11:18: S324 Probable use of insecure hash functions in `hashlib`: `md5`
|
9 | hashlib.new('md4', b'test')
10 |
11 | hashlib.new(name='md5', data=b'test')
| ^^^^^ S324
12 |
13 | hashlib.new('MD4', data=b'test')
|
S324.py:13:13: S324 Probable use of insecure hash functions in `hashlib`: `MD4`
|
11 | hashlib.new(name='md5', data=b'test')
12 |
13 | hashlib.new('MD4', data=b'test')
| ^^^^^ S324
14 |
15 | hashlib.new('sha1')
|
S324.py:15:13: S324 Probable use of insecure hash functions in `hashlib`: `sha1`
|
13 | hashlib.new('MD4', data=b'test')
14 |
15 | hashlib.new('sha1')
| ^^^^^^ S324
16 |
17 | hashlib.new('sha1', data=b'test')
|
S324.py:17:13: S324 Probable use of insecure hash functions in `hashlib`: `sha1`
|
15 | hashlib.new('sha1')
16 |
17 | hashlib.new('sha1', data=b'test')
| ^^^^^^ S324
18 |
19 | hashlib.new('sha', data=b'test')
|
S324.py:19:13: S324 Probable use of insecure hash functions in `hashlib`: `sha`
|
17 | hashlib.new('sha1', data=b'test')
18 |
19 | hashlib.new('sha', data=b'test')
| ^^^^^ S324
20 |
21 | hashlib.new(name='SHA', data=b'test')
|
S324.py:21:18: S324 Probable use of insecure hash functions in `hashlib`: `SHA`
|
19 | hashlib.new('sha', data=b'test')
20 |
21 | hashlib.new(name='SHA', data=b'test')
| ^^^^^ S324
22 |
23 | hashlib.sha(data=b'test')
|
S324.py:23:1: S324 Probable use of insecure hash functions in `hashlib`: `sha`
|
21 | hashlib.new(name='SHA', data=b'test')
22 |
23 | hashlib.sha(data=b'test')
| ^^^^^^^^^^^ S324
24 |
25 | hashlib.md5()
|
S324.py:25:1: S324 Probable use of insecure hash functions in `hashlib`: `md5`
|
23 | hashlib.sha(data=b'test')
24 |
25 | hashlib.md5()
| ^^^^^^^^^^^ S324
26 |
27 | hashlib_new('sha1')
|
S324.py:27:13: S324 Probable use of insecure hash functions in `hashlib`: `sha1`
|
25 | hashlib.md5()
26 |
27 | hashlib_new('sha1')
| ^^^^^^ S324
28 |
29 | hashlib_sha1('sha1')
|
S324.py:29:1: S324 Probable use of insecure hash functions in `hashlib`: `sha1`
|
27 | hashlib_new('sha1')
28 |
29 | hashlib_sha1('sha1')
| ^^^^^^^^^^^^ S324
30 |
31 | # usedforsecurity arg only available in Python 3.9+
|
S324.py:32:13: S324 Probable use of insecure hash functions in `hashlib`: `sha1`
|
31 | # usedforsecurity arg only available in Python 3.9+
32 | hashlib.new('sha1', usedforsecurity=True)
| ^^^^^^ S324
33 |
34 | # Valid
|

View File

@@ -1,180 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S501.py:5:47: S501 Probable use of `requests` call with `verify=False` disabling SSL certificate checks
|
4 | requests.get('https://gmail.com', timeout=30, verify=True)
5 | requests.get('https://gmail.com', timeout=30, verify=False)
| ^^^^^^^^^^^^ S501
6 | requests.post('https://gmail.com', timeout=30, verify=True)
7 | requests.post('https://gmail.com', timeout=30, verify=False)
|
S501.py:7:48: S501 Probable use of `requests` call with `verify=False` disabling SSL certificate checks
|
5 | requests.get('https://gmail.com', timeout=30, verify=False)
6 | requests.post('https://gmail.com', timeout=30, verify=True)
7 | requests.post('https://gmail.com', timeout=30, verify=False)
| ^^^^^^^^^^^^ S501
8 | requests.put('https://gmail.com', timeout=30, verify=True)
9 | requests.put('https://gmail.com', timeout=30, verify=False)
|
S501.py:9:47: S501 Probable use of `requests` call with `verify=False` disabling SSL certificate checks
|
7 | requests.post('https://gmail.com', timeout=30, verify=False)
8 | requests.put('https://gmail.com', timeout=30, verify=True)
9 | requests.put('https://gmail.com', timeout=30, verify=False)
| ^^^^^^^^^^^^ S501
10 | requests.delete('https://gmail.com', timeout=30, verify=True)
11 | requests.delete('https://gmail.com', timeout=30, verify=False)
|
S501.py:11:50: S501 Probable use of `requests` call with `verify=False` disabling SSL certificate checks
|
9 | requests.put('https://gmail.com', timeout=30, verify=False)
10 | requests.delete('https://gmail.com', timeout=30, verify=True)
11 | requests.delete('https://gmail.com', timeout=30, verify=False)
| ^^^^^^^^^^^^ S501
12 | requests.patch('https://gmail.com', timeout=30, verify=True)
13 | requests.patch('https://gmail.com', timeout=30, verify=False)
|
S501.py:13:49: S501 Probable use of `requests` call with `verify=False` disabling SSL certificate checks
|
11 | requests.delete('https://gmail.com', timeout=30, verify=False)
12 | requests.patch('https://gmail.com', timeout=30, verify=True)
13 | requests.patch('https://gmail.com', timeout=30, verify=False)
| ^^^^^^^^^^^^ S501
14 | requests.options('https://gmail.com', timeout=30, verify=True)
15 | requests.options('https://gmail.com', timeout=30, verify=False)
|
S501.py:15:51: S501 Probable use of `requests` call with `verify=False` disabling SSL certificate checks
|
13 | requests.patch('https://gmail.com', timeout=30, verify=False)
14 | requests.options('https://gmail.com', timeout=30, verify=True)
15 | requests.options('https://gmail.com', timeout=30, verify=False)
| ^^^^^^^^^^^^ S501
16 | requests.head('https://gmail.com', timeout=30, verify=True)
17 | requests.head('https://gmail.com', timeout=30, verify=False)
|
S501.py:17:48: S501 Probable use of `requests` call with `verify=False` disabling SSL certificate checks
|
15 | requests.options('https://gmail.com', timeout=30, verify=False)
16 | requests.head('https://gmail.com', timeout=30, verify=True)
17 | requests.head('https://gmail.com', timeout=30, verify=False)
| ^^^^^^^^^^^^ S501
18 |
19 | httpx.request('GET', 'https://gmail.com', verify=True)
|
S501.py:20:43: S501 Probable use of `httpx` call with `verify=False` disabling SSL certificate checks
|
19 | httpx.request('GET', 'https://gmail.com', verify=True)
20 | httpx.request('GET', 'https://gmail.com', verify=False)
| ^^^^^^^^^^^^ S501
21 | httpx.get('https://gmail.com', verify=True)
22 | httpx.get('https://gmail.com', verify=False)
|
S501.py:22:32: S501 Probable use of `httpx` call with `verify=False` disabling SSL certificate checks
|
20 | httpx.request('GET', 'https://gmail.com', verify=False)
21 | httpx.get('https://gmail.com', verify=True)
22 | httpx.get('https://gmail.com', verify=False)
| ^^^^^^^^^^^^ S501
23 | httpx.options('https://gmail.com', verify=True)
24 | httpx.options('https://gmail.com', verify=False)
|
S501.py:24:36: S501 Probable use of `httpx` call with `verify=False` disabling SSL certificate checks
|
22 | httpx.get('https://gmail.com', verify=False)
23 | httpx.options('https://gmail.com', verify=True)
24 | httpx.options('https://gmail.com', verify=False)
| ^^^^^^^^^^^^ S501
25 | httpx.head('https://gmail.com', verify=True)
26 | httpx.head('https://gmail.com', verify=False)
|
S501.py:26:33: S501 Probable use of `httpx` call with `verify=False` disabling SSL certificate checks
|
24 | httpx.options('https://gmail.com', verify=False)
25 | httpx.head('https://gmail.com', verify=True)
26 | httpx.head('https://gmail.com', verify=False)
| ^^^^^^^^^^^^ S501
27 | httpx.post('https://gmail.com', verify=True)
28 | httpx.post('https://gmail.com', verify=False)
|
S501.py:28:33: S501 Probable use of `httpx` call with `verify=False` disabling SSL certificate checks
|
26 | httpx.head('https://gmail.com', verify=False)
27 | httpx.post('https://gmail.com', verify=True)
28 | httpx.post('https://gmail.com', verify=False)
| ^^^^^^^^^^^^ S501
29 | httpx.put('https://gmail.com', verify=True)
30 | httpx.put('https://gmail.com', verify=False)
|
S501.py:30:32: S501 Probable use of `httpx` call with `verify=False` disabling SSL certificate checks
|
28 | httpx.post('https://gmail.com', verify=False)
29 | httpx.put('https://gmail.com', verify=True)
30 | httpx.put('https://gmail.com', verify=False)
| ^^^^^^^^^^^^ S501
31 | httpx.patch('https://gmail.com', verify=True)
32 | httpx.patch('https://gmail.com', verify=False)
|
S501.py:32:34: S501 Probable use of `httpx` call with `verify=False` disabling SSL certificate checks
|
30 | httpx.put('https://gmail.com', verify=False)
31 | httpx.patch('https://gmail.com', verify=True)
32 | httpx.patch('https://gmail.com', verify=False)
| ^^^^^^^^^^^^ S501
33 | httpx.delete('https://gmail.com', verify=True)
34 | httpx.delete('https://gmail.com', verify=False)
|
S501.py:34:35: S501 Probable use of `httpx` call with `verify=False` disabling SSL certificate checks
|
32 | httpx.patch('https://gmail.com', verify=False)
33 | httpx.delete('https://gmail.com', verify=True)
34 | httpx.delete('https://gmail.com', verify=False)
| ^^^^^^^^^^^^ S501
35 | httpx.stream('https://gmail.com', verify=True)
36 | httpx.stream('https://gmail.com', verify=False)
|
S501.py:36:35: S501 Probable use of `httpx` call with `verify=False` disabling SSL certificate checks
|
34 | httpx.delete('https://gmail.com', verify=False)
35 | httpx.stream('https://gmail.com', verify=True)
36 | httpx.stream('https://gmail.com', verify=False)
| ^^^^^^^^^^^^ S501
37 | httpx.Client()
38 | httpx.Client(verify=False)
|
S501.py:38:14: S501 Probable use of `httpx` call with `verify=False` disabling SSL certificate checks
|
36 | httpx.stream('https://gmail.com', verify=False)
37 | httpx.Client()
38 | httpx.Client(verify=False)
| ^^^^^^^^^^^^ S501
39 | httpx.AsyncClient()
40 | httpx.AsyncClient(verify=False)
|
S501.py:40:19: S501 Probable use of `httpx` call with `verify=False` disabling SSL certificate checks
|
38 | httpx.Client(verify=False)
39 | httpx.AsyncClient()
40 | httpx.AsyncClient(verify=False)
| ^^^^^^^^^^^^ S501
|

View File

@@ -1,22 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S506.py:10:9: S506 Probable use of unsafe `yaml.load`. Allows instantiation of arbitrary objects. Consider `yaml.safe_load`.
|
8 | def test_yaml_load():
9 | ystr = yaml.dump({"a": 1, "b": 2, "c": 3})
10 | y = yaml.load(ystr)
| ^^^^^^^^^ S506
11 | yaml.dump(y)
12 | try:
|
S506.py:24:24: S506 Probable use of unsafe loader `Loader` with `yaml.load`. Allows instantiation of arbitrary objects. Consider `yaml.safe_load`.
|
24 | yaml.load("{}", Loader=yaml.Loader)
| ^^^^^^^^^^^ S506
25 |
26 | # no issue should be found
|

View File

@@ -1,22 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S508.py:3:25: S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
1 | from pysnmp.hlapi import CommunityData
2 |
3 | CommunityData("public", mpModel=0) # S508
| ^^^^^^^^^ S508
4 | CommunityData("public", mpModel=1) # S508
|
S508.py:4:25: S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
3 | CommunityData("public", mpModel=0) # S508
4 | CommunityData("public", mpModel=1) # S508
| ^^^^^^^^^ S508
5 |
6 | CommunityData("public", mpModel=2) # OK
|

View File

@@ -1,20 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S509.py:4:12: S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure.
|
4 | insecure = UsmUserData("securityName") # S509
| ^^^^^^^^^^^ S509
5 | auth_no_priv = UsmUserData("securityName", "authName") # S509
|
S509.py:5:16: S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure.
|
4 | insecure = UsmUserData("securityName") # S509
5 | auth_no_priv = UsmUserData("securityName", "authName") # S509
| ^^^^^^^^^^^ S509
6 |
7 | less_insecure = UsmUserData("securityName", "authName", "privName") # OK
|

View File

@@ -1,12 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S601.py:3:1: S601 Possible shell injection via Paramiko call; check inputs are properly sanitized
|
1 | import paramiko
2 |
3 | paramiko.exec_command('something; really; unsafe')
| ^^^^^^^^^^^^^^^^^^^^^ S601
|

View File

@@ -1,117 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S602.py:4:15: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
3 | # Check different Popen wrappers are checked.
4 | Popen("true", shell=True)
| ^^^^^^^^^^ S602
5 | call("true", shell=True)
6 | check_call("true", shell=True)
|
S602.py:5:14: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
3 | # Check different Popen wrappers are checked.
4 | Popen("true", shell=True)
5 | call("true", shell=True)
| ^^^^^^^^^^ S602
6 | check_call("true", shell=True)
7 | check_output("true", shell=True)
|
S602.py:6:20: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
4 | Popen("true", shell=True)
5 | call("true", shell=True)
6 | check_call("true", shell=True)
| ^^^^^^^^^^ S602
7 | check_output("true", shell=True)
8 | run("true", shell=True)
|
S602.py:7:22: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
5 | call("true", shell=True)
6 | check_call("true", shell=True)
7 | check_output("true", shell=True)
| ^^^^^^^^^^ S602
8 | run("true", shell=True)
|
S602.py:8:13: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
6 | check_call("true", shell=True)
7 | check_output("true", shell=True)
8 | run("true", shell=True)
| ^^^^^^^^^^ S602
9 |
10 | # Check values that truthy values are treated as true.
|
S602.py:11:15: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
10 | # Check values that truthy values are treated as true.
11 | Popen("true", shell=1)
| ^^^^^^^ S602
12 | Popen("true", shell=[1])
13 | Popen("true", shell={1: 1})
|
S602.py:12:15: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
10 | # Check values that truthy values are treated as true.
11 | Popen("true", shell=1)
12 | Popen("true", shell=[1])
| ^^^^^^^^^ S602
13 | Popen("true", shell={1: 1})
14 | Popen("true", shell=(1,))
|
S602.py:13:15: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
11 | Popen("true", shell=1)
12 | Popen("true", shell=[1])
13 | Popen("true", shell={1: 1})
| ^^^^^^^^^^^^ S602
14 | Popen("true", shell=(1,))
|
S602.py:14:15: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
12 | Popen("true", shell=[1])
13 | Popen("true", shell={1: 1})
14 | Popen("true", shell=(1,))
| ^^^^^^^^^^ S602
15 |
16 | # Check command argument looks unsafe.
|
S602.py:18:19: S602 `subprocess` call with `shell=True` identified, security issue
|
16 | # Check command argument looks unsafe.
17 | var_string = "true"
18 | Popen(var_string, shell=True)
| ^^^^^^^^^^ S602
19 | Popen([var_string], shell=True)
20 | Popen([var_string, ""], shell=True)
|
S602.py:19:21: S602 `subprocess` call with `shell=True` identified, security issue
|
17 | var_string = "true"
18 | Popen(var_string, shell=True)
19 | Popen([var_string], shell=True)
| ^^^^^^^^^^ S602
20 | Popen([var_string, ""], shell=True)
|
S602.py:20:25: S602 `subprocess` call with `shell=True` identified, security issue
|
18 | Popen(var_string, shell=True)
19 | Popen([var_string], shell=True)
20 | Popen([var_string, ""], shell=True)
| ^^^^^^^^^^ S602
|

View File

@@ -1,106 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S603.py:4:15: S603 `subprocess` call: check for execution of untrusted input
|
3 | # Different Popen wrappers are checked.
4 | Popen("true", shell=False)
| ^^^^^^^^^^^ S603
5 | call("true", shell=False)
6 | check_call("true", shell=False)
|
S603.py:5:14: S603 `subprocess` call: check for execution of untrusted input
|
3 | # Different Popen wrappers are checked.
4 | Popen("true", shell=False)
5 | call("true", shell=False)
| ^^^^^^^^^^^ S603
6 | check_call("true", shell=False)
7 | check_output("true", shell=False)
|
S603.py:6:20: S603 `subprocess` call: check for execution of untrusted input
|
4 | Popen("true", shell=False)
5 | call("true", shell=False)
6 | check_call("true", shell=False)
| ^^^^^^^^^^^ S603
7 | check_output("true", shell=False)
8 | run("true", shell=False)
|
S603.py:7:22: S603 `subprocess` call: check for execution of untrusted input
|
5 | call("true", shell=False)
6 | check_call("true", shell=False)
7 | check_output("true", shell=False)
| ^^^^^^^^^^^ S603
8 | run("true", shell=False)
|
S603.py:8:13: S603 `subprocess` call: check for execution of untrusted input
|
6 | check_call("true", shell=False)
7 | check_output("true", shell=False)
8 | run("true", shell=False)
| ^^^^^^^^^^^ S603
9 |
10 | # Values that falsey values are treated as false.
|
S603.py:11:15: S603 `subprocess` call: check for execution of untrusted input
|
10 | # Values that falsey values are treated as false.
11 | Popen("true", shell=0)
| ^^^^^^^ S603
12 | Popen("true", shell=[])
13 | Popen("true", shell={})
|
S603.py:12:15: S603 `subprocess` call: check for execution of untrusted input
|
10 | # Values that falsey values are treated as false.
11 | Popen("true", shell=0)
12 | Popen("true", shell=[])
| ^^^^^^^^ S603
13 | Popen("true", shell={})
14 | Popen("true", shell=None)
|
S603.py:13:15: S603 `subprocess` call: check for execution of untrusted input
|
11 | Popen("true", shell=0)
12 | Popen("true", shell=[])
13 | Popen("true", shell={})
| ^^^^^^^^ S603
14 | Popen("true", shell=None)
|
S603.py:14:15: S603 `subprocess` call: check for execution of untrusted input
|
12 | Popen("true", shell=[])
13 | Popen("true", shell={})
14 | Popen("true", shell=None)
| ^^^^^^^^^^ S603
15 |
16 | # Unknown values are treated as falsey.
|
S603.py:17:15: S603 `subprocess` call: check for execution of untrusted input
|
16 | # Unknown values are treated as falsey.
17 | Popen("true", shell=True if True else False)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S603
18 |
19 | # No value is also caught.
|
S603.py:20:7: S603 `subprocess` call: check for execution of untrusted input
|
19 | # No value is also caught.
20 | Popen("true")
| ^^^^^^ S603
|

View File

@@ -1,10 +0,0 @@
---
source: crates/ruff/src/rules/flake8_bandit/mod.rs
---
S604.py:5:5: S604 Function call with `shell=True` parameter identified, security issue
|
5 | foo(shell=True)
| ^^^^^^^^^^ S604
|

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