Issues
Issue Categories
DeepSource detects issues using static analysis across categories like anti-patterns, bug risks, coverage, performance issues, security flaws, style issues, and type check issues.
Anti-patterns
Anti-patterns are certain ways of writing code that result in poor design. While anti-patterns are correct code, they are not recommended as they often affect maintainability, readability, performance, and security.
Examples of anti-patterns:
In Python, the file
class is equipped with special methods that are automatically called whenever a file is opened via a with the statement (e.g. with open("file.txt", "r")
as file). These special methods ensure that the file is properly and safely opened and closed.
The code below does not use with
to open a file. This code depends on you remembering to manually close the file viaclose()
when finished. Even if you remember to call close()
, the code is still dangerous, because if an exception occurs before the call to close()
, it will not be called and memory issues or file corruption could occur.
It is a good practice to usewith
to open a file. The open
function is implemented internally as a class, which defines special built-in methods called __enter__()
and __exit__()
. These methods will automatically open and close the file for you. Python can guarantee that these special methods are always called, even if an exception occurs.
Bug risks
Bug risks are issues in code that can cause errors in code and breakages in production. A bug is a flaw in the code that produces undesired or incorrect results.
Code often has bug risks due to poor coding practices, lack of version control, miscommunication of requirements, unrealistic time schedules for development and buggy third-party tools.
Examples of bug risks:
Passing mutable lists or dictionaries as default arguments to a function can have unforeseen consequences. Usually, when you use a list or dictionary as the default argument to a function, you want the program to create a new list or dictionary every time that function is called. However, this is not what Python does. The first time the function is called, Python creates a persistent object for the list or dictionary. Every subsequent time the function is called, Python uses that same persistent object created during the first call to the function.
In the code below, the append
function is used under the assumption that a new list
with the object passed as the first argument would be returned each time that the function is called without the second argument. In reality, this is not what happens. The first time the function is called, Python creates a persistent list
. Every subsequent call to append
appends the value to that persistent list instead of creating a new one.
It is a good practice to use a sentinel value to denote an empty list
, set
or dictionary
. This means if you want the function to return a singleton list each time this function is called without the second argument, you should use a sentinel value to represent this use case and then modify the function’s body to support this scenario.
Performance issues
Performance issues are issues that impact the performance of code being executed by slowing it down. Considerable performance gains can be obtained when the appropriate functions and directives are used.
Examples of performance issues:
Checking for membership of a key in a list can potentially take n iterations to complete, where n is the number of items in the list. If possible, change the list to a set or dictionary instead because Python can search for items in a set or dictionary by directly accessing them without iterations, which is much more efficient.
The code below defines a listl
and then uses the expression if 3 in l:
to check if the number 3 exists in the list. This is inefficient. Behind the scenes, Python iterates through the list until it finds the number or reaches the end of the list.
In the modified code below, the list has been changed to a set. This is much more efficient behind the scenes, as Python can attempt to directly access the target number in the set, rather than iterate through every item in the list and compare every item to the target number.
Security issues
A bug in code that could potentially be used to compromise security is a security vulnerability issue. Using libraries and tools that are out-of-date or have known security issues can also introduce vulnerabilities in the system.
A highly dynamic language like Python that gives you many ways to change the runtime behavior of code and even dynamically execute new code is powerful but can be a security risk as well.
The three main types of security vulnerabilities based on their more extrinsic weaknesses are:
- Porous defenses
- Risky resource management
- Insecure interaction between components
Examples of security vulnerabilities:
The exec
statement enables you to execute arbitrary Python code stored in literal strings dynamically. Building a complex string of Python code and then passing that code to exec results in code that is hard to read and hard to test. Anytime the “Use of exec
” error is encountered, you should go back to the code and check if there is a clearer, more direct way to accomplish the task.
The sample code below composes a literal string containing Python code and then passes that string to exec
for execution. This is an indirect and confusing way to program in Python.
In most scenarios, you can easily refactor the code to avoid the use of exec. In the example below, the use of exec has been removed and replaced by a function.
The assert
statement is a debugging aid that tests a condition. If the condition is true, it does nothing, and your program continues to execute. But if the assert condition evaluates to false, it raises an AssertionError exception with an optional error message.
In the code below, an assert
statement is used in application logic, which is discouraged. Asserts can be turned off globally in the Python interpreter. Don’t rely on assert expressions to be executed for data validation or data processing.
Assert statements should ideally be used only in tests, and to verify invariants while debugging. Instead of using assert in production code, you could do your validation with regular if-statements and raise validation exceptions if necessary.
Style issues
Style issues are violations in the code format according to a style guide. If the code does not follow the specified code style guidelines, it fails to express its intent in the most readable way.
Code style can be boiled down to anything that is a stylistic choice in the code that has no effect on the behavior of the code
Any large code base with multiple team members should look as if only one programmer wrote it. If a team agrees on a given style it can help keep the code consistent.
Examples of style issues:
Per the PEP-8 Style Guide, all Python code should be consistently indented with 4 spaces, never tabs.
The following code mixes spaces and tabs for indentation. The print("Hello, World!")
statement is indented with a tab. The print("Goodbye, World!")
statement is indented with 4 spaces.
All Python code should be consistently indented with 4 spaces as in the code below.
In Python, there should be a whitespace after the characters,
, ;
, and :
. For instance, in the code below, there is a missing whitespace after ,
.
Documentation issues
Documentation issues are caused if certain parts of the code are left undocumented. Documentation is a collection of easy-to-understand images and written descriptions that describe what a codebase does and how it can be used.
Documentation is important because it ensures that the next time you dive into a codebase, you won’t have to take as much time to get up to speed.
Examples of documentation issues:
PEP-8 mandates that all public modules, classes, functions, and methods should have a documentation string. A documentation string is a string literal that occurs as the first statement in a module, function, class, or method definition. Such a string becomes the doc
special attribute of that object.
Ensuring that every public module, class, function, and method is documented makes it easier for other developers to maintain the code.
If a module, class, function, or method needs to be public, add a documentation string that describes the purpose or use of the object (see PEP-257 for guidelines). If the object does not need to be public then make it “private” by changing its name from xxx
to _xxx
.
The following simple, public function should be updated to include a documentation string immediately after the def line.
You might insert the documentation string: """Return the sum of x and y."""
on line 2 as shown in the code below.