Detect clones configuration, new refactorings and bug fixes
Detect clones configuration
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
Working memory metric improvements
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.
New refactorings
Swap Variable
Sourcery refactoring id: swap-variable
Description:
Swap variable values with tuple assignment
Before:
temp = a
a = b
b = temp
After:
a, b = b, a
Explanation:
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
Sourcery refactoring id: merge-else-if-into-elif
Description:
Merge else clause’s nested if statement into elif
Before:
def interpret_response(response):
if response.status == "200":
return response.data
else:
if response.status == "404":
return "Not Found"
else:
return "Error"
After:
def interpret_response(response):
if response.status == "200":
return response.data
elif response.status == "404":
return "Not Found"
else:
return "Error"
Explanation:
Flattening if statements nested within else clauses generates code that is easier to read and expand upon.
Inline variable
Sourcery refactoring id: inline-variable
Description:
Inline variable that is only used once
Before:
thelist = []
for i in range(10):
k = i**2
thelist.append(k)
After:
thelist = []
for i in range(10):
thelist.append(i**2)
Explanation:
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
Sourcery refactoring id: remove-redundant-continue
Description:
Remove redundant continue
statement
Before:
mylist2 = []
for i in mylist:
if i != 2:
mylist2.append(i)
else:
continue
After:
mylist2 = []
for i in mylist:
if i != 2:
mylist2.append(i)
Explanation:
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
Sourcery refactoring id: reintroduce-else
Description:
Lift code into else
after break in control flow
Before:
summer_hats = []
for hat in hats:
if hat in WINTER_HATS:
continue
summer_hats.append(hat)
After:
summer_hats = []
for hat in hats:
if hat in WINTER_HATS:
continue
else:
summer_hats.append(hat)
Explanation:
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
Sourcery refactoring id: remove-duplicate-key
Description:
Remove duplicate keys in declaration of sets and dicts
Before:
addresses = {*address_list1, *address_list2, *address_list1}
After:
addresses = {*address_list2, *address_list1}
Explanation:
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
Sourcery refactoring id: flatten-nested-try
Description:
Merge nested try-statement into a single try
Before:
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"
After:
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"
Explanation:
Flattening try-except statements nested within a try-finally generates equivalent code that is easier to read and expand upon.
Collection into Set
Sourcery refactoring id: collection-into-set
Description:
Use set when checking membership of a collection of literals
Before:
if currency in ["EUR", "USD"]:
take_payment()
After:
if currency in {"EUR", "USD"}:
take_payment()
Explanation:
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.
Enhancements
- Sourcery will now suggest refactorings which involve the removal of comments. This allows us to make more suggestions and be more impactful on your code. These suggestions are flagged and the comments that are removed are clearly shown when hovering over.
- Simplify generators contained in calls to
bytes
- Adjust return-identity proposal so that it does not propose in situations where a series of checks are being made.
- Flip Compare now works for all constant types
- Merge comparison refactoring extended to work with sets and tuples
- Ensure switch proposal is not suggested where the if conditions end with breaks of control flow
- Refinements to where the else-after-guard proposal is suggested
Bug fixes
- Fixed issue with Sourcery incorrectly removing brackets after a splat operator