Interactive online tutorial: Notebook

Getting Started

Use pip install fixit to install Fixit.

Analyze Code Issues and Autofix Issues

Given an example code (example.py) like this:

[2]:
%%writefile $file_path
from typing import Dict


class C(object):
    attr = "ab" "cd" "ef" "gh"

    def method(self) -> Dict[int, str]:
        filtered_char = []
        for char in self.attr:
            if char is not "a":
                filtered_char.append(char)

        index_to_char = dict([(idx, char) for idx, char in enumerate(filtered_char)])
        return index_to_char
Writing /tmp/tmpzhdozjsb/example.py

We can run Fixit rules to check code issues:

[4]:
! python -m fixit.cli.run_rules
Scanning 1 files
Testing 33 rules

example.py:4:1
    NoInheritFromObjectRule: Inheriting from object is a no-op.  'class Foo:' is
    just fine =)
example.py:5:12
    UsePlusForStringConcatRule: Implicit string concatenation detected, please
    add '+' to be explicit. E.g. a tuple or a call ("a" "b") with a missing
    comma results in multiple strings being concatenated as one string and
    causes unexpected behaviour.
example.py:10:16
    ComparePrimitivesByEqualRule: Don't use `is` or `is not` to compare
    primitives, as they compare references. Use == or != instead.
example.py:13:25
    RewriteToComprehensionRule: It's unnecessary to use a list comprehension
    inside a call to dict since there are equivalent comprehensions for this
    type

Found 4 reports in 1 files in 0.56 seconds.

Each warning shows the violation’s position in the format of file_name:starting_line_number:starting_column_number, follwed by the lint rule name and lint message.

To fix the issues automatically:

[5]:
! python -m fixit.cli.apply_fix
Scanning 1 files
./example.py

example.py:4:1 [applied fix]
    NoInheritFromObjectRule: Inheriting from object is a no-op.  'class Foo:' is
    just fine =)
example.py:5:12 [applied fix]
    UsePlusForStringConcatRule: Implicit string concatenation detected, please
    add '+' to be explicit. E.g. a tuple or a call ("a" "b") with a missing
    comma results in multiple strings being concatenated as one string and
    causes unexpected behaviour.
example.py:10:16 [applied fix]
    ComparePrimitivesByEqualRule: Don't use `is` or `is not` to compare
    primitives, as they compare references. Use == or != instead.
example.py:13:25 [applied fix]
    RewriteToComprehensionRule: It's unnecessary to use a list comprehension
    inside a call to dict since there are equivalent comprehensions for this
    type
reformatted -

All done! ✨ 🍰 ✨
1 file reformatted.

Found 4 reports in 1 files in 3.17 seconds.

All the issues are automatically fixed!

[6]:
! git diff
diff --git a/example.py b/example.py
index aed4bb5..3f667f8 100644
--- a/example.py
+++ b/example.py
@@ -1,14 +1,14 @@
 from typing import Dict


-class C(object):
-    attr = "ab" "cd" "ef" "gh"
+class C:
+    attr = "ab" + "cd" + "ef" + "gh"

     def method(self) -> Dict[int, str]:
         filtered_char = []
         for char in self.attr:
-            if char is not "a":
+            if char != "a":
                 filtered_char.append(char)

-        index_to_char = dict([(idx, char) for idx, char in enumerate(filtered_char)])
+        index_to_char = {idx: char for idx, char in enumerate(filtered_char)}
         return index_to_char

Configuration File

A Fixit configuration file allows you to configure Fixit settings for your codebase.

  1. To initialize a configuration file populated with some defaults, run:

    python -m fixit.cli.init_config
    

This will create a .fixit.config.yaml with default settings in the current working directory.

  1. Next, you may wish to edit or add some specific settings. The available configurations are:

  • allow_list_rules: A list of rules (whether custom of from Fixit) that should be applied to the repository. Omitting this setting allows all rules to run. For example:

    allow_list_rules: [Flake8PseudoLintRule]

block_list_rules takes precendence, so if a rule is in both allow_list_rules and block_list_rules the rule will not be run.

  • block_list_patterns: A list of patterns that indicate that a file should not be linted. For example:

    block_list_patterns: ['@generated', '@nolint']
    

    will tell Fixit to skip linting any files that have @generated or @nolint in their contents.

  • block_list_rules: A list of rules (whether custom or from Fixit) that should not be applied to the repository. For example:

    block_list_rules: [NoInheritFromObjectRule]
    
  • fixture_dir: The directory in which fixture files required for unit testing are to be found. This is only necessary if you are testing rules that use a metadata cache (see AwaitAsyncCallRule for an example of such a rule). This can be an absolute path, or a path relative to repo_root (see below).

  • use_noqa: Defaults to False. Use True to support Flake8 lint suppression comment: noqa. The noqa is not recommended because a bare noqa implicitly silences all lint errors which prevent other useful lint errors to show up. We recommend use lint-fixme or lint-ignore suppression comments.

  • formatter: A list of the formatter commands to use after a lint is complete. These will be passed to the args parameter in subprocess.check_output in the order in which they appear. For example:

    formatter: [black, '-']
    

    Here, the formatter of choice would be Black and the added - tells it to read from standard input, and write to standard output so that it is compatible with Fixit’s formatting logic.

  • packages: The Python packages in which to search for lint rules. For example:

    packages: [fixit.rules, my.custom.package]
    
  • repo_root: The path to the repository root. This can be a path relative to the .fixit.config.yaml file or an absolute path. For example:

    repo_root: .
    
  • rule_config: Rule-specific configurations. For example:

    ImportConstraintsRule:
        fixit:
            rules: [["*", "allow"]]
    

    (see ImportConstraintsRule for more details on this example)

  1. A .fixit.config.yaml example with populated settings:

    block_list_patterns:
    - '@generated'
    - '@nolint'
    block_list_rules:
    - BlockListedRule
    fixture_dir: ./tests/fixtures
    formatter:
    - black
    - '-'
    packages:
    - fixit.rules
    repo_root: .
    rule_config:
        ImportConstraintsRule:
            fixit:
                rules: [["*", "allow"]]
    

Enforcing Custom Rules

After finishing up the configuration, you may wish to enforce some custom lint rules in your repository.

1. Start by creating a directory where your custom rules will live. Make sure to include an __init__.py file so that the directory is importable as a package. This can simply be an empty file. For example:

my_repo_root
    └── lint
        └── custom_rules
            └── __init__.py
  1. Include the dotted name of the package in the .fixit.config.yaml file under the packages setting:

    packages:
    - fixit.rules
    - lint.custom_rules
    
  2. See the Build a Lint Rule page for more details on how to write the logic for a custom lint rule.

Running Lint Rules

You may also want to run some rules against your repository to see all current violations.

  • To run only the pre-packaged Fixit rules against the entire repository, run:

    python -m fixit.cli.run_rules --rules fixit.rules
    
  • To run only your custom rules package against the entire repository, run:

    python -m fixit.cli.run_rules --rules <dotted_name_of_custom_package>
    
  • To run a specific rule against the entire repository, run:

    python -m fixit.cli.run_rules --rules <rule_name>
    
  • To run all the rule packages under the packages settings in the .fixit.config.yaml file against the entire repository, run:

    python -m fixit.cli.run_rules
    
  • To run all the rule packages under the packages settings in the .fixit.config.yaml file against a particular file or directory, run:

    python -m fixit.cli.run_rules <file_or_directory>
    
  • To run all the rule packages under the packages settings in the .fixit.config.yaml file against mutliple files or directories, run:

    python -m fixit.cli.run_rules <file_or_directory> <file_or_directory2> <file_or_directory3>
    

Applying Autofixes

Some rules come with provided autofix suggestions. We have provided a script to help you automatically apply these suggested fixes. To do this, run:

python -m fixit.cli.apply_fix <file_or_directory> --rules <rule_name_or_package>

This will apply one or more lint rules’ autofix to the source code in the specified file(s) or directory.

  • For more detailes on this script’s usage, run:

    python -m fixit.cli.apply_fix --help
    

Suppressing Violations

You may wish to suppress existing lint violations from the lint engine altogether. We have provided a script to help you automatically insert lint suppressions. To do this, run:

python -m fixit.cli.insert_suppressions <rule_name> <file_or_directory>

This will insert a suppression in the form of a # lint-fixme comment above lines in the source code that violate the specified rule.

  • For more detailes on this script’s usage, run:

    python -m fixit.cli.insert_suppressions --help