ruff
ruff copied to clipboard
Implement flake8-bugbear
This is a big set of rules but flake8-bugbear
is extremely popular (~1.5M downloads per month).
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!)
Cool, those are all pretty straightforward! Will prioritize them over the others.
I'm working on B006 implementation.
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
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).
Just updated the list as @harupy is making awesome progress here. We're now at 50% coverage.
@charliermarsh for B008
is there any simple way to setup extend-immutable-calls = fastapi.Depends, fastapi.params.Depends
?
@grillazz It's rather straightforward to add. ~I'm working on it~. PR is https://github.com/charliermarsh/ruff/pull/706 š
@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 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
After upgrade to 0.0.117 lint is passing without explicitly specifying
Did you remove ignore = ["B008",]
after upgrading to 0.0.117?
@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 Can you try running ruff --no-cache ...
?
@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
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 :))
If that is the issue, it'll be fixed by https://github.com/charliermarsh/ruff/pull/739.
Test 0.0.120 and problem solved. Thx
@harupy is dangerously close to implementing the entire plugin ;)
Nice to see a task list with lots of ā :)
I think if we implement B023, we should feel comfortable closing this.
I think if we implement B023, we should feel comfortable closing this.
Agreed! Feel free to implement it if anyone wants to :)
@harupy - Sounds good, I can give it a try!
Awesome, thank you!
Is B902
covered by N804
and N805
in pep8-namiing?
@harupy - Yeah I believe so.
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 -- Added in https://github.com/charliermarsh/ruff/pull/1122.
@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.
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?
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.