Open
Description
Some basic linting with pyflakes was introduced in #99. This needs to be extended with checks from other libraries:
- pylint
- pylint plugins. https://github.com/pyta-uoft/pyta has some custom checkers which might be useful, but the project itself isn't right for us, see Raw data API? pyta-uoft/pyta#670
- Plugins which extend pyflakes/flake8. For the latter I think plugins tend to be for flake8 so we may have to change our pyflakes code to use flake8 instead. An example of a nice looking flake8 plugin is https://pypi.org/project/flake8-bugbear/. Here are some warnings it has which interest me:
- B002: Python does not support the unary prefix increment. Writing ++n is equivalent to +(+(n)), which equals n. You meant n += 1.
- B006: Do not use mutable data structures for argument defaults. They are created during function definition time. All calls to the function reuse this one instance of that data structure, persisting changes between them.
- B007: Loop control variable not used within the loop body. If this is intended, start the name with an underscore.
- B008: Do not perform function calls in argument defaults. The call is performed only once at function definition time. All calls to your function will reuse the result of that definition-time function call. If this is intended, assign the function call to a module-level variable and use that variable as a default value.
- B009: Do not call getattr(x, 'attr'), instead use normal property access: x.attr. Missing a default to getattr will cause an AttributeError to be raised for non-existent properties. There is no additional safety in using getattr if you know the attribute name ahead of time.
- B010: Do not call setattr(x, 'attr', val), instead use normal property access: x.attr = val. There is no additional safety in using setattr if you know the attribute name ahead of time.
- B012: Use of break, continue or return inside finally blocks will silence exceptions or override return values from the try or except blocks. To silence an exception, do it explicitly in the except block. To properly use a break, continue or return refactor your code so these statements are not in the finally block.
- B013: A length-one tuple literal is redundant. Write except SomeError: instead of except (SomeError,):.
- B014: Redundant exception types in except (Exception, TypeError):. Write except Exception:, which catches exactly the same exceptions.
- Here are some other things I'd like to check. If we find them in libraries, great. I expect pylint has many of these. If not, maybe we can implement some ourselves, or open issues in other repos.
- Unreachable code, e.g. after return or break.
- Variables which might be undefined, e.g. if they were only defined in an
if
with noelse
. - Variables which shadow the same name in an outer scope. Local and global variables should have different names.
- Variables which shadow builtins, e.g.
list = [1, 2, 3]
. - Trailing commas creating a singleton tuple, e.g.
x = 1,
. This is a nasty thing for a beginner to not realise they've done. They can just add parentheses to be explicit. - Implicit concatenation of adjacent string literals, e.g.
foo("a" "b")
. Indicates they may have forgotten a comma or something else. Just add+
. - Parentheses which are really pointless, e.g.
x = (1)
. Probably meant(1,)
to make a singleton tuple. - Redefined a variable before using it. pyflakes already does this in futurecoder but only for functions and classes.
x = 1; x = 2; foo(x)
is equally pointless. - Assigned to
self
in a method.
General guidelines for things to check:
- Red flags that they may have made a mistake or forgotten something.
- Signs that they misunderstand how Python works.
Things not to check:
- Code which might be legit and there is no easy workaround. For example sometimes you don't need a loop variable, but we can still check for it and require naming the variable with an underscore, e.g.
for _ in range(10)
. But we don't want to make people put comments like# noqa
to silence warnings. - Stuff which is just a matter of appearance/style and has no bearing on how the program runs, e.g. most/all of PEP8.
- Errors which will lead to an obvious exception, e.g. completely undefined variables or syntax errors.