ruff icon indicating copy to clipboard operation
ruff copied to clipboard

Implement flake8-bugbear

Open charliermarsh opened this issue 2 years ago ā€¢ 19 comments

This is a big set of rules but flake8-bugbear is extremely popular (~1.5M downloads per month).

charliermarsh avatar Oct 10 '22 14:10 charliermarsh

Link to bugbear warnings for reference: https://github.com/PyCQA/flake8-bugbear#list-of-warnings

B001, B002, B006, B007, B008, B017, B023, and B904 are the most useful for catching real bugs imho, out of the warnings that are not yet implemented.

B950 covers line length - it is not really necessary since ruff is already designed to work with black. However, it may be good thing to mention somewhere since black mentions configuring it explicitly.

(awesome project by the way!)

tgross35 avatar Oct 25 '22 21:10 tgross35

Cool, those are all pretty straightforward! Will prioritize them over the others.

charliermarsh avatar Oct 25 '22 22:10 charliermarsh

I'm working on B006 implementation.

harupy avatar Oct 30 '22 01:10 harupy

Quick checklist for convenience:

  • [x] B001: bare except
  • [x] B002: ++ -> +=
  • [x] B003: os.environ assigning
  • [x] B004: hasattr(x, '__call__') to test if x is callable
  • [x] B005: .strip() with multi-character strings
  • [x] B006: mutable data structures for argument defaults
  • [x] B007: Loop control variable not used within the loop body
  • [x] B008: function calls in argument defaults
  • [x] B009: getattr(x, 'attr') -> x.attr
  • [x] B010: setattr(x, 'attr', val) -> x.attr = val
  • [x] B011: assert False calls
  • [x] B012: break, continue or return inside finally blocks
  • [x] B013: length-one tuple literal is redundant
  • [x] B014: Redundant exception types in except (Exception, TypeError)
  • [x] B015: Pointless comparison
  • [x] B016: Cannot raise a literal
  • [x] B017: self.assertRaises(Exception)
  • [x] B018: Found useless expression
  • [x] B019: Use of functools.lru_cache or functools.cache on methods
  • [x] B020: Loop control variable overrides iterable it iterates
  • [x] B021: f-string used as docstring
  • [x] B022: arguments passed to contextlib.suppress
  • [x] B023: functions defined inside a loop using variables redefined in the loop
  • [x] B024: Abstract base class with no abstract method
  • [x] B025: try-except block with duplicate exceptions
  • [x] B026: Star-arg unpacking after a keyword argument is strongly discouraged
  • [x] B027: Empty method in abstract base class

Opinionated:

  • [ ] B901: return x in a generator function
  • [x] B902: invalid first argument for method. Use self for instance methods, and cls for class methods
  • [ ] B903: Use collections.namedtuple (or typing.NamedTuple) for data classes that only set attributes in an init method
  • [x] B904: Within an except clause, raise exceptions with raise ... from err or raise ... from None
  • [x] B950: Line too long, generous option

tgross35 avatar Nov 04 '22 20:11 tgross35

That's awesome, thank you for collating it!

I'm tentatively marking B001 and B950 as complete, since IIUC B001 is the same as E722, and B950 is close enough to E501 that we may not support it independently (I know they're not exactly the same).

charliermarsh avatar Nov 04 '22 20:11 charliermarsh

Just updated the list as @harupy is making awesome progress here. We're now at 50% coverage.

charliermarsh avatar Nov 07 '22 14:11 charliermarsh

@charliermarsh for B008 is there any simple way to setup extend-immutable-calls = fastapi.Depends, fastapi.params.Depends ?

grillazz avatar Nov 12 '22 17:11 grillazz

@grillazz It's rather straightforward to add. ~I'm working on it~. PR is https://github.com/charliermarsh/ruff/pull/706 šŸ™‚

edgarrmondragon avatar Nov 12 '22 19:11 edgarrmondragon

@grillazz - This is now supported thanks to @edgarrmondragon. So now you can add this to your pyproject.toml:

[tool.ruff.flake8-bugbear]
extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]

(Going out in v0.0.115 which is building now.)

charliermarsh avatar Nov 12 '22 21:11 charliermarsh

@charliermarsh and @edgarrmondragon thx. I testing and have question. In version 0.0.114 when I spec select = ["E", "F", "U", "N", "C", "B"] i get B008 Do not perform function calls in argument defaults.

Next I adding ignore = ["B008",] and lint is passing.

After upgrade to 0.0.117 lint is passing without explicitly specifying

[tool.ruff.flake8-bugbear]
extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]

Ideas ?

ps. Also I can't spec ruff = "0.0.115" only 116 and 117 working

grillazz avatar Nov 14 '22 08:11 grillazz

@grillazz

After upgrade to 0.0.117 lint is passing without explicitly specifying

Did you remove ignore = ["B008",] after upgrading to 0.0.117?

harupy avatar Nov 14 '22 11:11 harupy

@harupy yes I removed this line. Please find full pyproject.toml below

[tool.poetry]
name = "fastapi-sqlalchemy-asyncpg"
version = "0.0.2"
description = ""
authors = ["Jakub Miazek <[email protected]>"]
packages = []
license = "MIT"

[tool.poetry.dependencies]
python = "^3.10"
alembic = "*"
asyncpg = "*"
httpx = "*"
pydantic = "*"
sqlalchemy = "*"
fastapi = "*"
uvicorn = { extras = ["standard"], version = "*" }
uvloop = "*"
httptools = "*"
rich = "*"
devtools = { extras = ["pygments"], version = "*" }
black = "*"
safety = "*"
pyupgrade = "*"
ipython = "*"
pytest-cov = "*"
pytest-asyncio = "*"
ruff = "*"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.ruff]
line-length = 120

select = ["E", "F", "U", "N", "C", "B"]

# Exclude a variety of commonly ignored directories.
exclude = ["alembic",]
# Assume Python 3.10.
target-version = "py310"

[tool.ruff.flake8-quotes]
docstring-quotes = "double"

[tool.pytest.ini_options]
addopts = "-v --doctest-modules --doctest-glob=*.md --ignore=alembic"
asyncio_mode = "strict"
env_files = [".env"]

grillazz avatar Nov 14 '22 11:11 grillazz

@grillazz Can you try running ruff --no-cache ...?

harupy avatar Nov 14 '22 16:11 harupy

@grillazz +1 to the above suggestion. This is the result I get from running on https://github.com/grillazz/fastapi-sqlalchemy-asyncpg's main branch with the configuration you shared:

$ ruff .
Found 10 error(s).
app/api/nonsense.py:12:79: B008 Do not perform function call `Depends` in argument defaults
app/api/nonsense.py:21:32: B008 Do not perform function call `Depends` in argument defaults
app/api/nonsense.py:27:65: B008 Do not perform function call `Depends` in argument defaults
app/api/nonsense.py:36:32: B008 Do not perform function call `Depends` in argument defaults
app/api/shakespeare.py:15:32: B008 Do not perform function call `Depends` in argument defaults
app/api/stuff.py:16:85: B008 Do not perform function call `Depends` in argument defaults
app/api/stuff.py:30:73: B008 Do not perform function call `Depends` in argument defaults
app/api/stuff.py:39:32: B008 Do not perform function call `Depends` in argument defaults
app/api/stuff.py:45:62: B008 Do not perform function call `Depends` in argument defaults
app/api/stuff.py:54:32: B008 Do not perform function call `Depends` in argument defaults

edgarrmondragon avatar Nov 14 '22 16:11 edgarrmondragon

I may have forgotten to include the bugbear settings in the hash key. (Iā€™m on the subway but can fix in a bit, or if someone wants to PR, check out the hash trait implementation on Settings :))

charliermarsh avatar Nov 14 '22 16:11 charliermarsh

If that is the issue, it'll be fixed by https://github.com/charliermarsh/ruff/pull/739.

charliermarsh avatar Nov 14 '22 17:11 charliermarsh

Test 0.0.120 and problem solved. Thx

grillazz avatar Nov 15 '22 07:11 grillazz

@harupy is dangerously close to implementing the entire plugin ;)

charliermarsh avatar Nov 15 '22 21:11 charliermarsh

Nice to see a task list with lots of āœ… :)

harupy avatar Nov 16 '22 00:11 harupy

I think if we implement B023, we should feel comfortable closing this.

charliermarsh avatar Nov 22 '22 22:11 charliermarsh

I think if we implement B023, we should feel comfortable closing this.

Agreed! Feel free to implement it if anyone wants to :)

harupy avatar Nov 24 '22 05:11 harupy

@harupy - Sounds good, I can give it a try!

charliermarsh avatar Nov 25 '22 04:11 charliermarsh

Awesome, thank you!

harupy avatar Nov 25 '22 04:11 harupy

Is B902 covered by N804 and N805 in pep8-namiing?

harupy avatar Nov 25 '22 04:11 harupy

@harupy - Yeah I believe so.

charliermarsh avatar Nov 25 '22 04:11 charliermarsh

B905 was just added:

B905: zip() without an explicit strict= parameter set. strict=True causes the resulting iterator to raise a ValueError if the arguments are exhausted at differing lengths. The strict= argument was added in Python 3.10, so don't enable this flag for code that should work on <3.10. For more information: https://peps.python.org/pep-0618/

g-as avatar Dec 07 '22 09:12 g-as

@g-as -- Added in https://github.com/charliermarsh/ruff/pull/1122.

charliermarsh avatar Dec 07 '22 15:12 charliermarsh

@charliermarsh Are there still plans on supporting B950 ? Enforcing a line length while at the same time giving some headroom is often a pretty good compromise between sane line length and unnecessarily ugly code resulting from a hard limit.

SRv6d avatar Jan 15 '23 15:01 SRv6d

We could support it. I was a bit hesitant but we could. Can you talk me through the rationale of using B950 vs. just configuring E501 to use a 10% increase? I think they're exactly equivalent, so I'm guessing it's more of a philosophical thing?

charliermarsh avatar Jan 15 '23 19:01 charliermarsh

I wouldn't call it philosophical, it mostly comes down to personal preference and experience. I think a line length of 88 is a great middle ground, but personally find my code to often be right around that or slightly over. Reformatting those lines of code just because they are a couple of characters over the limit often looks silly and increasing the limit by 10% we are getting close to 100 which results in an entirely different looking codebase just for a couple of outliers.

SRv6d avatar Jan 15 '23 19:01 SRv6d