Refurb

Latest version: v2.0.0

Safety actively analyzes 687918 Python packages for vulnerabilities to keep your Python projects secure.

Scan your dependencies

Page 5 of 6

1.9.0

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

1.8.0

This release includes some bug fixes, new checks, as well as the ability to enable/disable checks based on category!

Categories

Checks can now have a `categories` field where they specify what categories they fit into. On the command line, you can use `--enable "category"` to enable all checks in that category. The same applies for `ignore` and `disable`. This feature is also available in config files. See [the docs](https://github.com/dosisod/refurb#enablingdisabling-checks) for more info.

Add "simplify comprehension" Check (FURB137)

This check recommends that you simplify your usage of comprehensions by:

* Removing nested layers of comprehensions (ie, `list([...])`
* Write list/set comprehensions using shorthand notation

Here are the examples from the explainer:

python
nums = [1, 1, 2, 3]

nums_times_10 = list(num * 10 for num in nums)
unique_squares = set(num ** 2 for num in nums)
number_tuple = tuple([num ** 2 for num in nums])


And the suggested improvements:

python
nums = [1, 1, 2, 3]

nums_times_10 = [num * 10 for num in nums]
unique_squares = {num ** 2 for num in nums}
number_tuple = tuple(num ** 2 for num in nums)


Add "use comprehensions" check (FURB138)

This check will find instances where you append a bunch of elements to a new list, and suggest that you use a list comprehension instead. This can result in higher performance, and in some cases can greatly improve readability.

Example from the explainer:

python
nums = [1, 2, 3, 4]
odds = []

for num in nums:
if num % 2:
odds.append(num)


Suggested improvement:

python
nums = [1, 2, 3, 4]
odds = [num for num in nums if num % 2]


Add "no multi-line lstrip()" Check (FURB139)

This check will find multi-line strings that can be written without the use of the `lstrip()` function:

python
"""
Some docstring
""".lstrip()


Suggested improvement:

python
"""\
Some docstring
"""


What's Changed
* Add ability to enable/disable errors based on category by dosisod in https://github.com/dosisod/refurb/pull/123
* Update error messages for FURB108 and FURB124: by dosisod in https://github.com/dosisod/refurb/pull/124
* Add enhancement template by dosisod in https://github.com/dosisod/refurb/pull/125
* Add "simplify comprehension" check by dosisod in https://github.com/dosisod/refurb/pull/126
* Add "use comprehensions" check by dosisod in https://github.com/dosisod/refurb/pull/127
* Bump packages by dosisod in https://github.com/dosisod/refurb/pull/128
* Fix set comprehensions being suggested to be removed for list/tuple constructions by dosisod in https://github.com/dosisod/refurb/pull/131
* Add ability to detect comparison to empty list/dict in FURB115 by dosisod in https://github.com/dosisod/refurb/pull/132
* Add more nodes to the `is_equivalent` function by dosisod in https://github.com/dosisod/refurb/pull/134
* Add "no multiline lstrip" check by dosisod in https://github.com/dosisod/refurb/pull/135
* Bump packages and version by dosisod in https://github.com/dosisod/refurb/pull/136


**Full Changelog**: https://github.com/dosisod/refurb/compare/v1.7.0...v1.8.0

1.7.0

This version includes a bug fix which has been causing some issues, as well as a new flag and a new check.

The "use min/max" check (FURB136)

This check will detect uses of ternary statements (inline-if expressions) which can be written with the builtin `min()`/`max()` functions instead. For example:

python
score1 = 90
score2 = 99

highest_score = score1 if score1 > score2 else score2


Refurb will suggest you write this as:

python
score1 = 90
score2 = 99

highest_score = max(score1, score2)


Mypy Flags

You can now add Mypy flags directly from the Refurb command line like so:


$ refurb files -- --pdb --show-traceback


The `--pdb` and `--show-traceback` flags will get forwarded to Mypy. This is primarily for development purposes, but can be useful to normal users as well (especially those who already use Mypy). See [the docs](https://github.com/dosisod/refurb#overriding-mypy-flags) for more info.

What's Changed
* Add `format()` function to FURB119 by dosisod in https://github.com/dosisod/refurb/pull/110
* Add "use min/max" check by dosisod in https://github.com/dosisod/refurb/pull/111
* Fix name of member expressions not being checked by dosisod in https://github.com/dosisod/refurb/pull/115
* Add ability to pass flags directly into Mypy by dosisod in https://github.com/dosisod/refurb/pull/118
* Bump version, update issue template by dosisod in https://github.com/dosisod/refurb/pull/120


**Full Changelog**: https://github.com/dosisod/refurb/compare/v1.6.0...v1.7.0

1.6.0

This version primarily just fixes bugs, but also includes a new check.

FURB106, the "expandtabs" check, is disabled by default now

Turns out `.replace("\t", 8 * " ")` is not the same as `.expandtabs()`: the `replace` version will replace every tab with exactly 8 spaces, but the `expandtabs` version will replace each tab with spaces up to the nearest tab stop. If your tabs are only at the start of a string, this works as expected! But, if they are in the middle of the string, this behavior might not be what you want. For this reason, it has been disabled by default. Read [the docs](https://github.com/dosisod/refurb#enablingdisabling-checks) for more information on how to enable checks.

Add the "no ignored dict items" check (FURB135)

The `items()` method on `dict` objects allow you to iterate over all the key-value pairs in a dictionary. If you only need the key or value, but not both, you shouldn't use `items()`, but instead use the specific `values()` and `keys()` methods. For example, Refurb will suggest that this:

python
books = {"Frank Herbert": "Dune"}

for author, _ in books.items():
print(author)

for _, book in books.items():
print(book)


Should be changed to this:

python
books = {"Frank Herbert": "Dune"}

for author in books:
print(author)

for book in books.values():
print(book)


What's Changed
* Add "no ignored dict items" check by dosisod in https://github.com/dosisod/refurb/pull/95
* Improve and disable the `expandtabs()` check by dosisod in https://github.com/dosisod/refurb/pull/96
* Add better node equivalence checks: by dosisod in https://github.com/dosisod/refurb/pull/98
* Bump packages by dosisod in https://github.com/dosisod/refurb/pull/105
* Add more strict type checking for `check` functions and `Error` subclasses by dosisod in https://github.com/dosisod/refurb/pull/106
* Update and improve error messages by dosisod in https://github.com/dosisod/refurb/pull/107
* Also mention another type checker Pytype. by yilei in https://github.com/dosisod/refurb/pull/108
* Add categories to checks by dosisod in https://github.com/dosisod/refurb/pull/109

New Contributors
* yilei made their first contribution in https://github.com/dosisod/refurb/pull/108

**Full Changelog**: https://github.com/dosisod/refurb/compare/v1.5.0...v1.6.0

1.5.0

Since the last release, many flags and checks have been added, and of course, lots of bugs have been fixed as well!

FURB120 is now disabled by default

Due to popular demand, FURB120 has been disabled by default. If you wish to continue using it, you will need to [enable it](https://github.com/dosisod/refurb#enablingdisabling-checks).

The `--enable-all` and `--disable-all` flags

Two new flags have been added, `--enable-all` and `--disable-all`. As the names imply, they will enable or disable all checks. The "enable all" option is good for new codebases which want to opt-in to all available checks, whereas "disable all" can be good for existing codebases which need to be incrementally cleaned up.

Read [the docs](https://github.com/dosisod/refurb#enablingdisabling-checks) for more info.

The `--python-version` flag

This flag can be used to specify what version of Python your codebase is using. This allows for Mypy to better type check your code, and also allows for more useful error messages. When specifying this flag, it must be in the form `X.Y`, such as `3.9` or `3.10`.

The "no trailing continue" check

Similar to the "no trailing return" check, this check applies to instances where the `continue` keyword is not needed:

python
for num in range(10):
print(num)

continue


Here, the `continue` is not needed, because we are already at the end of the for loop.

The "use cache decorator" check

Python 3.9 introduced the `cache` decorator, which is a shorthand for `lru_cache(maxsize=None)`. Refurb will now suggest that you change this:

python
from functools import lru_cache

lru_cache(maxsize=None)
def f(x: int) -> int:
return x + 1


To this:

python
from functools import cache

cache
def f(x: int) -> int:
return x + 1


If you are using Python 3.9+.

What's Changed
* Detect `Path` objects being passed directly to `open()` by dosisod in https://github.com/dosisod/refurb/pull/75
* Add `--disable-all` flag by dosisod in https://github.com/dosisod/refurb/pull/76
* Fix disabled checks still being loaded by dosisod in https://github.com/dosisod/refurb/pull/80
* Bump/cleanup packages by dosisod in https://github.com/dosisod/refurb/pull/81
* Fix false positive in FURB125 by dosisod in https://github.com/dosisod/refurb/pull/83
* add Python 3.11 to test matrix by jairhenrique in https://github.com/dosisod/refurb/pull/84
* Add `--python-version` flag by dosisod in https://github.com/dosisod/refurb/pull/85
* Fix `--load` being able to load a single check multiple times by dosisod in https://github.com/dosisod/refurb/pull/86
* Allow for dependency injecting settings into checks: by dosisod in https://github.com/dosisod/refurb/pull/87
* Add "no trailing continue" check by dosisod in https://github.com/dosisod/refurb/pull/88
* Disable FURB120 by default by dosisod in https://github.com/dosisod/refurb/pull/89
* Add `--enable-all` flag by dosisod in https://github.com/dosisod/refurb/pull/91
* Fix `--enable-all` not enabling checks in all situations by dosisod in https://github.com/dosisod/refurb/pull/92
* Add "use cache decorator" check for Python 3.9 and up by dosisod in https://github.com/dosisod/refurb/pull/93
* Add comparison to other tools by dosisod in https://github.com/dosisod/refurb/pull/94

New Contributors
* jairhenrique made their first contribution in https://github.com/dosisod/refurb/pull/84

**Full Changelog**: https://github.com/dosisod/refurb/compare/v1.4.0...v1.5.0

1.4.0

This version includes a bunch of bug fixes, some new flags, and a new check!

(Small) Breaking Change

Previously, the command line arguments overrode any and all settings in the config file, which probably isn't what most users would expect. Now, the command line arguments will merge with the existing config file settings (if there are any), with the command line arguments taking precedence.

The `--config-file` flag

This flag can be used to specify where the config file should be pulled from. The file can be named anything, but it must be in TOML format, and the settings must still be in the `[tool.refurb]` section. See 62 for more info.

The `--disable` flag

To complement the `--enable` flag (added in 41), the `--disable` flag has been added. This should give users more control over how checks are loaded. See [the README](https://github.com/dosisod/refurb#enablingdisabling-checks) for more info.

The "set discard" check

When removing keys from a `set()`, you don't need to check if it exists before removing it:

python
nums = set((1, 2, 3))

if 1 in nums:
nums.remove(1)


Refurb will suggests you do this instead:

python
nums = set((1, 2, 3))

nums.discard(1)


---

What's Changed
* Fix operator precedence not being upheld by dosisod in https://github.com/dosisod/refurb/pull/54
* Fix bugs in the "with suppress" check by dosisod in https://github.com/dosisod/refurb/pull/55
* Use mypy compatible method for conditional import by michaeloliverx in https://github.com/dosisod/refurb/pull/39
* Partially fix variable type related issues by dosisod in https://github.com/dosisod/refurb/pull/58
* Add more developer documentation by dosisod in https://github.com/dosisod/refurb/pull/61
* Add `--config-file` flag by dosisod in https://github.com/dosisod/refurb/pull/62
* Make command line arguments merge with config file instead of overriding it by dosisod in https://github.com/dosisod/refurb/pull/63
* Fix "use func name" check allowing non-positional arguments by dosisod in https://github.com/dosisod/refurb/pull/65
* Fix CI workflow running twice when pushing PR by dosisod in https://github.com/dosisod/refurb/pull/66
* Add bug report issue template by dosisod in https://github.com/dosisod/refurb/pull/67
* Show error message when error code doesn't have a docstring by dosisod in https://github.com/dosisod/refurb/pull/68
* Improve `refurb gen` functionality: by dosisod in https://github.com/dosisod/refurb/pull/69
* Improve "no len compare" check by dosisod in https://github.com/dosisod/refurb/pull/71
* Add ability to disable checks by dosisod in https://github.com/dosisod/refurb/pull/73
* Test generated visitor methods by jdevera in https://github.com/dosisod/refurb/pull/60
* Add "set discard" check by dosisod in https://github.com/dosisod/refurb/pull/74

New Contributors
* jdevera made their first contribution in https://github.com/dosisod/refurb/pull/60

**Full Changelog**: https://github.com/dosisod/refurb/compare/v1.3.0...v1.4.0

Page 5 of 6

© 2024 Safety CLI Cybersecurity Inc. All Rights Reserved.