Package: pybetter
pybetterTool for fixing trivial problems with your code.Originally intended as an example for my PyCon Belarus 2020 talk aboutLibCST.UsageSimply 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.pydeff():return(42,"Hello, world")# pybetter test.py-->Processing'test.py'...[+](B003)Removeparenthesesfromthetuplein'return'statement.
Alldone!# cat test.pydeff():return42,"Hello, world"Available fixersB001: Replace 'not A in B' with 'A not in B'Usage ofA not in Bovernot A in Bis recommended both by Google andPEP-8. Both of those forms are compiled to the same bytecode, but second form has some potential of confusion for the reader.# BEFORE:ifnot42incounts:sys.exit(-1)# AFTER:if42notincounts:sys.exit(-1)B002: Default values forkwargsare mutable.As described inCommon Gotchassection 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 withNonevalue, moving initialization from function definition into function body.# BEFOREdefp(a=[]):print(a)# AFTERdefp(a=None):ifaisNone:a=[]print(a)Be warned, that this fix may break code whichintentionallyuses 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:defhello():return("World",42)# AFTER:defhello():return"World",42B004:__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:defhello():return("World",42)classF:pass# AFTER:defhello():return"World",42classF:pass__all__=["F","hello",]B005: Replace "A == None" with "A is None""Comparisons to singletons like None should always be done withisoris not, never the equality operators." (PEP8)# BEFORE:ifa==None:pass# AFTER:ifaisNone:passB006: Remove comparisons with either 'False' or 'True'.PEP8recommends that conditions should be evaluated without explicit equality comparison withTrue/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:ifa==Falseorb==Trueorc==False==True:pass# AFTER:ifaorborc:passB007: 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 nestedwithstatementsDegeneratewithstatements can be rewritten as a single compoundwithstatement, if following conditions are satisfied:There are no statements betweenwithstatements being collapsed;Neither ofwithstatements has any leading or inline comments.# BEFORE:witha():withb()asother_b:print("Hello, world!")# AFTER:witha(),b()asother_b:print("Hello, world!")B009: Replace unhashable list literals in set constructorsLists 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 ofA is not Bovernot A is Bis recommended both by Google andPEP-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:ifnotobjisRecord:sys.exit(-1)# AFTER:ifobjisnotRecord:sys.exit(-1)NB:Each of the fixers can be disabled on per-line basis usingflake8's "noqa" comments.Installation# pip install pybetterGetting started with development# git clone https://github.com/lensvol/pybetter# poetry installLicenseThis project is licensed under the MIT License - see theLICENSEfile for detailsAuthorsKirill Borisov(lensvol@gmail.com)