It is now possible to configure the detect clones functionality for finding duplicate code.
This configuration must be added at the project level in the .sourcery.yaml
file, under the clone_detection
heading. See
here for more details on the Sourcery
configuration file.
min_lines
: The minimum number of lines each code section must have to be
picked up. The minimum value for this is 3.min_duplicates
: The minimum number of duplicate code sections there must be.
The minimum value for this is 2.identical_clones_only
: When this is set only exactly identical sections of
duplicate code will be picked up.clone_detection:
min_lines: 3
min_duplicates: 2
identical_clones_only: false
The working memory metric will now only consider the most complex element within collections or call arguments, rather than incrementing for each element.
This means that when declaring a list like this one:
hats = ["BOWLER", "TOP" + "HAT", "TRILBY"]
The working memory for this statement will now be 3 (incrementing for hats
,
TOP
and HAT
) whereas before it would have been 5. This will prevent the
phenomenon where creating dictionaries, lists or data classes would give
functions unreasonably high working memory scores. For a full discussion of the
working memory metric see our blog post
here.
swap-variable
Swap variable values with tuple assignment
temp = a
a = b
b = temp
a, b = b, a
When trying to swap the values held by two variables, we may avoid using an extra temporary variable unnecessarily by employing Python's tuple assignment. As a consequence of this refactoring, the code is not only more concise but also reflects its intention more clearly.
merge-else-if-into-elif
Merge else clause's nested if statement into elif
def interpret_response(response):
if response.status == "200":
return response.data
else:
if response.status == "404":
return "Not Found"
else:
return "Error"
def interpret_response(response):
if response.status == "200":
return response.data
elif response.status == "404":
return "Not Found"
else:
return "Error"
Flattening if statements nested within else clauses generates code that is easier to read and expand upon.
inline-variable
Inline variable that is only used once
thelist = []
for i in range(10):
k = i**2
thelist.append(k)
thelist = []
for i in range(10):
thelist.append(i**2)
Inlining variable can help to streamline the code, but can also make it less readable. Sourcery will only inline variables where doing so allows further readability changes to be made, such as converting a loop into a list comprehension.
remove-redundant-continue
Remove redundant continue
statement
mylist2 = []
for i in mylist:
if i != 2:
mylist2.append(i)
else:
continue
mylist2 = []
for i in mylist:
if i != 2:
mylist2.append(i)
If a continue
is not followed by any other statements in a for
or while
loop then it is not necessary, so can be removed. Removing unnecessary lines
declutters the code and makes it easier to understand. This refactoring will
only be triggered if it unlocks further improvements.
reintroduce-else
Lift code into else
after break in control flow
summer_hats = []
for hat in hats:
if hat in WINTER_HATS:
continue
summer_hats.append(hat)
summer_hats = []
for hat in hats:
if hat in WINTER_HATS:
continue
else:
summer_hats.append(hat)
Where the body of an if
statement ends with a break in the control flow, such
as a continue
, return
or raise
, the subsequent statements can be lifted
into the else
clause. On its own this change does not improve the code, so
Sourcery will only suggest it where it unlocks furher improvements. In the
example above once the code has been lifted into the else
the conditional can
be inverted and the continue
removed, which then lets the for
loop be
converted into a list comprehension.
remove-duplicate-key
Remove duplicate keys in declaration of sets and dicts
addresses = {*address_list1, *address_list2, *address_list1}
addresses = {*address_list2, *address_list1}
Keys of Sets and Dictionaries must be unique. Hence, repeated keys are redundant and can be removed from the declaration to increase the conciseness and clarity of the code.
For dictionaries that have repeated keys with differing values (i.e.
{key1: 'some_value', key1: 'another_value'}
), Sourcery will only remove the
key-value pairs that would be removed at run time.
flatten-nested-try
Merge nested try-statement into a single try
def testConnection(db, credentials):
try:
try:
db.connect(credentials)
except InvalidCredentials:
return "Check your credentials"
except ConnectionError:
return "Error while trying to connect"
finally:
print("Connection attempt finished")
return "Connection Successful"
def testConnection(db, credentials):
try:
db.connect(credentials)
except InvalidCredentials:
return "Check your credentials"
except ConnectionError:
return "Error while trying to connect"
finally:
print("Connection attempt finished")
return "Connection Successful"
Flattening try-except statements nested within a try-finally generates equivalent code that is easier to read and expand upon.
collection-into-set
Use set when checking membership of a collection of literals
if currency in ["EUR", "USD"]:
take_payment()
if currency in {"EUR", "USD"}:
take_payment()
When checking if a variable is one of a collection of literals it is both more
natural and more performant to use a set
rather than a list
or tuple
to
define that collection. Note that this can only be done where Sourcery can
ascertain that the value being checked is of a hashable type.
bytes