6 new issues added in the Python analyzer

1. Formatted string used as docstring — PTC-W0033

Formatted string literals cannot be used as docstrings, even if they do not include expressions.
If used, the formatted strings are just evaluated as an expression in the function body.

def foo():
    f"""This is intended to be the docstring for {__name__}"""
    ...

The value of foo.__doc__ would be None here.

2. Unnecessary use of getattr — PTC-W0034

This is raised when getattr is used to check if an attribute exists, without specifying a default value.
Missing a default to getattr will cause an AttributeError to be raised for non-existent properties, which is the same as when a non-existent property is accessed directly.

It is recommended to either provide a default value to be returned by getattr if the attribute is not found, or access the attribute directly as there is no additional safety in using getattr if the attribute name is known ahead of time.

3. hasattr used to check if the object is callable — PTC-W0035

It is unreliable to use hasattr(object, '__call__') to check if an object is callabe as it can give wrong results if object implements custom __getattr__ or its __call__ is itself not callabe. It is recommended to use the callable built-in instead.
Here’s an example:

In [1]: class FooBar:
    ...:     def __init__(self):
    ...:         self.foo = "Foo"
    ...:         self.bar = "Bar"
    ...:
    ...:     def __getattr__(self, key):
    ...:         return key
    ...:
In [2]: instance = FooBar()

When hasattr is used to check if instance is callable, it return True

In [3]: hasattr(instance, '__call__')
Out[3]: True

But when checked using the callable built-in, it returns False

In [4]: callable(instance)
Out[4]: False

When instance is called, here’s the traceback:

In [5]: instance()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-23-5dd8ea036ab8> in <module>
----> 1 instance()
TypeError: 'FooBar' object is not callable

4. Unnecessary else / elif used after break — PYL-R1723

break causes control flow to be disrupted, as it will exit the block.
This makes the else / elif block unnecessary here. This doesn’t mean you can not use it, but it is recommended to remove this else / elif statement for a better readability.
Bad:

def changing_denominators(p, q):
    while True:
        if q == 0:
            break
        else:
            print(p/q)
            q = q - 1

Good:

def changing_denominators(p, q):
    while True:
        if q == 0:
            break
        print(p/q)
        q = q - 1

5. Unnecessary elif / else block after continue — PYL-R1724

The else / elif block here is unnecessary because the leading if block has a continue statement. This doesn’t mean you cannot use it, but it is recommended to remove this else / elif statement for a better readability.

Bad:

def classify_number(x):
    for num in range(x):
        if x % 2 == 0:
            continue
        else:
            print(f"{num} is Odd}")

Good:

def classify_number(x):
    for num in range(x):
        if x % 2 == 0:
            continue
        print(f"{num} is Odd}")

6. Subprocess run with an ignored non-zero exit — PYL-W1510

subprocess.run uses a default of check=False, which means that a nonzero exit code will be
ignored by default, instead of raising an exception.

3 Likes