pybetter
Tool for fixing trivial problems with your code.
Originally intended as an example for my PyCon Belarus 2020 talk about LibCST.
Usage
Simply provide a valid Python source code file as one of the argument and it will try to fix any issues it could find.
Usage: pybetter [OPTIONS] [PATHS]...
Options:
--noop Do not make any changes to the source files.
--diff Show diff-like output of the changes made.
--select CODES Apply only improvements with the provided codes.
--exclude CODES Exclude improvements with the provided codes.
--exit-code <CODE> Exit with provided code if fixes were applied.
--help Show this message and exit.
Example
# cat test.py
def f():
return (42, "Hello, world")
# pybetter test.py
--> Processing 'test.py'...
[+] (B003) Remove parentheses from the tuple in 'return' statement.
All done!
# cat test.py
def f():
return 42, "Hello, world"
Available fixers
-
B001: Replace 'not A in B' with 'A not in B'
Usage of
A not in Bovernot A in Bis recommended both by Google and PEP-8. Both of those forms are compiled to the same bytecode, but second form has some potential of confusion for the reader.# BEFORE: if not 42 in counts: sys.exit(-1) # AFTER: if 42 not in counts: sys.exit(-1)
-
B002: Default values for
kwargsare mutable.As described in Common Gotchas section of "The Hitchhiker's Guide to Python", mutable arguments can be a tricky thing. This fixer replaces any default values that happen to be lists or dicts with None value, moving initialization from function definition into function body.
# BEFORE def p(a=[]): print(a) # AFTER def p(a=None): if a is None: a = [] print(a)
Be warned, that this fix may break code which intentionally uses mutable default arguments (e.g. caching).
-
B003: Remove parentheses from the tuple in 'return' statement.
If you are returning a tuple from the function by implicitly constructing it, then additional parentheses around it are redundant.
# BEFORE: def hello(): return ("World", 42) # AFTER: def hello(): return "World", 42
-
B004:
__all__attribute is missing.Regenerate missing
__all__attribute, filling it with the list of top-level function and class names.NB: It will ignore any names starting with
_to prevent any private members from ending up in the list.# BEFORE: def hello(): return ("World", 42) class F: pass # AFTER: def hello(): return "World", 42 class F: pass __all__ = [ "F", "hello", ]
-
B005: Replace "A == None" with "A is None"
"Comparisons to singletons like None should always be done with
isoris not, never the equality operators." (PEP8)# BEFORE: if a == None: pass # AFTER: if a is None: pass
-
B006: Remove comparisons with either 'False' or 'True'.
PEP8 recommends that conditions should be evaluated without explicit equality comparison with
True/Falsesingletons. In Python, every non-empty value is treated asTrueand vice versa,so in most cases those comparisons can be safely eliminated.
NB:
is Trueandis Falsechecks are not affected, since they can be used to explicitly check for equality with a specific singleton, instead of using abovementioned "non-empty" heuristic.# BEFORE: if a == False or b == True or c == False == True: pass # AFTER: if a or b or c: pass
-
B007: Convert f-strings without expressions into regular strings.
It is wasteful to use f-string mechanism if there are no expressions to be extrapolated.
# BEFORE: a = f"Hello, world" # AFTER: a = "Hello, world"
-
B008: Collapse nested
withstatementsDegenerate
withstatements can be rewritten as a single compoundwithstatement, if following conditions are satisfied:- There are no statements between
withstatements being collapsed; - Neither of
withstatements has any leading or inline comments.
# BEFORE: with a(): with b() as other_b: print("Hello, world!") # AFTER: with a(), b() as other_b: print("Hello, world!")
- There are no statements between
-
B009: Replace unhashable list literals in set constructors
Lists cannot be used as elements of the sets due to them being mutable and hence "unhashable". We can fix the more trivial cases of list literals being used to create a set by converting them into tuples.
# BEFORE: a = { [1, 2, 3], } b = set([[1, 2], ["a", "b"]]) c = frozenset([[1, 2], ["a", "b"]]) # AFTER: a = { (1, 2, 3) } b = set([(1, 2), ("a", "b")]) c = frozenset([(1, 2), ("a", "b")])
-
B010: Replace 'not A is B' with 'A is not B'
Usage of
A is not Bovernot A is Bis recommended both by Google and PEP-8. Both of those forms are compiled to the same bytecode, but second form has some potential of confusion for the reader. (thanks to @rs2 for submitting this!).# BEFORE: if not obj is Record: sys.exit(-1) # AFTER: if obj is not Record: sys.exit(-1)
NB: Each of the fixers can be disabled on per-line basis using flake8's "noqa" comments.
Installation
# pip install pybetter
Getting started with development
# git clone https://github.com/lensvol/pybetter
# poetry install
License
This project is licensed under the MIT License - see the LICENSE file for details
Authors
- Kirill Borisov (lensvol@gmail.com)
Metadata
Release files for pybetter 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pybetter-0.4.1.tar.gz | 16.2 kB | Without provenance | Details for pybetter-0.4.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pybetter-0.4.1-py3-none-any.whl | Python 3 | none | any | Details for pybetter-0.4.1-py3-none-any.whl |
Total release size: 35.7 kB
Release files / pybetter-0.4.1.tar.gz
| Download URL | pybetter-0.4.1.tar.gz |
|---|---|
| Size | 16.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b431cf1814935485ab1c69e3d24f1eccde4a4d10f1dadcb9ca1144902b6f9c79
|
|
BLAKE2b-256 checksum How to use checksums |
9edeb8e8af99516c1247c733e5f695a74094e3fd8574778c59149074771ff745
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.1.4 CPython/3.7.4 Darwin/21.3.0
|
Release files / pybetter-0.4.1-py3-none-any.whl
| Download URL | pybetter-0.4.1-py3-none-any.whl |
|---|---|
| Size | 19.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c7ea10c23a122922eb78f7a799c5c7b29c8f01dbd7171099891e5e4a47a2e969
|
|
BLAKE2b-256 checksum How to use checksums |
b4955d8b2992d4cad18605cf18d90e0e4a7f047ffda60f367018a717e152dd03
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.1.4 CPython/3.7.4 Darwin/21.3.0
|
Security for pybetter 0.4.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.