This release includes 10 new checks, general bug fixes, and some quality-of-life improvements as well!
Add "use starmap" check (FURB140)
Often times you want to iterate over some `zip()`'d values, unpack them, and pass each of the values to function. The [`starmap`](https://docs.python.org/3/library/itertools.html#itertools.starmap) function gives you a faster and more succinct way to do just that.
The following code:
python
scores = [85, 100, 60]
passing_scores = [60, 80, 70]
def passed_test(score: int, passing_score: int) -> bool:
return score >= passing_score
passed_all_tests = all(
passed_test(score, passing_score)
for score, passing_score
in zip(scores, passing_scores)
)
Can be re-written as follows:
python
from itertools import starmap
scores = [85, 100, 60]
passing_scores = [60, 80, 70]
def passed_test(score: int, passing_score: int) -> bool:
return score >= passing_score
passed_all_tests = all(starmap(passed_test, zip(scores, passing_scores)))
Add "pathlib exists" check (FURB141)
When checking whether a file exists or not, try and use the more modern [`pathlib`](https://docs.python.org/3/library/pathlib.html) module instead of `os.path`.
This:
python
import os
if os.path.exists("filename"):
pass
Can be written like this:
python
from pathlib import Path
if Path("filename").exists():
pass
Add "no set op in for loop" check (FURB142)
When you want to add/remove a bunch of items to/from a set, don't use a for loop, call the appropriate method on the set itself.
For example, this code:
python
sentence = "hello world"
vowels = "aeiou"
letters = set(sentence)
for vowel in vowels:
letters.discard(vowel)
Can be rewritten like so:
python
sentence = "hello world"
vowels = "aeiou"
letters = set(sentence)
letters.difference_update(vowels)
or
letters -= set(vowels)
Add "no or default" check (FURB143)
Don't check an expression to see if it is falsey then assign the same falsey value to it. For example, if an expression used to be of type `int | None`, checking if the expression is falsey would make sense, since it could be `None` or `0`. But, if the expression is changed to be of type `int`, the falsey value is just `0`, so setting it to `0` if it is falsey (`0`) is redundant.
This:
python
def is_markdown_header(line: str) -> bool:
return (line or "").startswith("")
Can be written like so:
python
def is_markdown_header(line: str) -> bool:
return line.startswith("")
Add "pathlib unlink" check (FURB144)
When removing a file, use the more modern [`Path.unlink()`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.unlink) method instead of `os.remove()` or `os.unlink()`: The `pathlib` module allows for more flexibility when it comes to traversing folders, building file paths, and accessing/modifying files.
This:
python
import os
os.remove("filename")
Can be written as:
python
from pathlib import Path
Path("filename").unlink()
Add "no slice copy" check (FURB145)
Don't use a slice expression (with no bounds) to make a copy of something, use the more readable `.copy()` method instead.
For example, this:
python
nums = [3.1415, 1234]
copy = nums[:]
Can be rewritten as:
python
nums = [3.1415, 1234]
copy = nums.copy()
Add "pathlib is file" check (FURB146)
Don't use the `os.path.isfile` (or similar) functions, use the more modern `pathlib` module instead:
python
if os.path.isfile("file.txt"):
pass
Can be rewritten as:
python
if Path("file.txt").is_file():
pass
Add "pathlib join" check (FURB147)
When joining strings to make a filepath, use the more modern and flexible `Path()` object instead of `os.path.join`:
Bad:
python
with open(os.path.join("folder", "file"), "w") as f:
f.write("hello world!")
Good:
python
from pathlib import Path
with open(Path("folder", "file"), "w") as f:
f.write("hello world!")
even better ...
with Path("folder", "file").open("w") as f:
f.write("hello world!")
even better ...
Path("folder", "file").write_text("hello world!")
Add "no ignored enumerate" check (FURB148)
Don't use `enumerate()` if you are disregarding either the index or the value:
python
books = ["Ender's Game", "The Black Swan"]
for index, _ in enumerate(books):
print(index)
for _, book in enumerate(books):
print(book)
This instead should be written as:
python
books = ["Ender's Game", "The Black Swan"]
for index in range(len(books)):
print(index)
for book in books:
print(book)
Add "no is bool compare" check (FURB149)
Don't use `is` or `==` to check if a boolean is True or False, simply use the name itself:
python
failed = True
if failed is True:
print("You failed")
Should be written as:
python
failed = True
if failed:
print("You failed")
Allow for comma-separated `ignore`, `enable`, and `disable` CLI flags
Previously, if you wanted to enable/disable/ignore multiple checks at once, you would have to write:
$ refurb src --disable FURB123 --disable FURB124 --disable FURB125
Now you write it like this instead:
$ refurb src --disable FURB123,FURB124,FURB125
What's Changed
* Add "use starmap" check by dosisod in https://github.com/dosisod/refurb/pull/137
* Add ability to separate `ignore`, `enable`, and `disable` CLI flags with commas by dosisod in https://github.com/dosisod/refurb/pull/138
* Add "pathlib exists" check by dosisod in https://github.com/dosisod/refurb/pull/139
* Add "no set in for loop" check by dosisod in https://github.com/dosisod/refurb/pull/140
* Fix `.pyi` files taking precedence over `.py` files by dosisod in https://github.com/dosisod/refurb/pull/141
* Add "no or default" check by dosisod in https://github.com/dosisod/refurb/pull/142
* Add "pathlib unlink" check by dosisod in https://github.com/dosisod/refurb/pull/143
* Add "no slice copy" check by dosisod in https://github.com/dosisod/refurb/pull/145
* Add "is file" pathlib check by dosisod in https://github.com/dosisod/refurb/pull/147
* Add `noqa` comments to test files by dosisod in https://github.com/dosisod/refurb/pull/148
* Add "pathlib join" check by dosisod in https://github.com/dosisod/refurb/pull/149
* Colorize CI Output by dosisod in https://github.com/dosisod/refurb/pull/150
* Fix FURB135 false positives by dosisod in https://github.com/dosisod/refurb/pull/153
* Add "no ignored enumerate" check by dosisod in https://github.com/dosisod/refurb/pull/154
* Fix typo in no_unecessary_cast explanation by jaoxford in https://github.com/dosisod/refurb/pull/156
* Fix FURB145 emitting error for non-slice index expressions by dosisod in https://github.com/dosisod/refurb/pull/157
* Bump packages by dosisod in https://github.com/dosisod/refurb/pull/158
* Add `is` operator to FURB124 (use equal chain) by dosisod in https://github.com/dosisod/refurb/pull/159
* Add "no is bool compare" check by dosisod in https://github.com/dosisod/refurb/pull/160
* Update FURB149 to include `==` and `!=` by dosisod in https://github.com/dosisod/refurb/pull/161
New Contributors
* jaoxford made their first contribution in https://github.com/dosisod/refurb/pull/156
**Full Changelog**: https://github.com/dosisod/refurb/compare/v1.8.0...v1.9.0