Skip to main content

pybetter

PyPI Downloads Travis CI Code coverage PyPI - Python Version License Code style: black

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 B over not A in B is 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 kwargs are 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 is or is 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/False singletons. In Python, every non-empty value is treated as True and vice versa,

    so in most cases those comparisons can be safely eliminated.

    NB: is True and is False checks 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 with statements

    Degenerate with statements can be rewritten as a single compound with statement, if following conditions are satisfied:

    • There are no statements between with statements being collapsed;
    • Neither of with statements 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!")
    
  • 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 B over not A is B is 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

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)

Source distribution for pybetter 0.4.1
File Size Uploaded
pybetter-0.4.1.tar.gz 16.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pybetter 0.4.1
File Interpreter ABI Platform
pybetter-0.4.1-py3-none-any.whl Python 3 none any Details

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

Release history Release notifications | RSS feed

This release

0.4.1 This release

2 release files

0.4.0

2 release files

0.3.7

2 release files

0.3.6

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page