Skip to content

Remove Debugging Statements

What is the step that you should do before opening a PR, but unfortunately no pre-commit hook has done for you so far? Cleaning up all the various debugging statements that you've added while desperately curiously wondering what this code is doing.

Remove Breakpoints

Since Python 3.9, it's easy to add a new breakpoint. And it's still easy to forget about them. 😉

You can define a custom rule that removes all the calls to breakpoint() and add it to the rules section of your .sourcery.yaml config file:

rules:
  - id: remove_breakpoint
    description: Remove breakpoints from production code
    pattern: breakpoint()
    replacement: ''

Note the empty string for the replacement. This means that the breakpoint() statements will be removed.

Note: Sourcery won't apply this rule, if a code block contained only a breakpoint(), and applying this would lead to a syntax error. See also the Reference Docs about empty replacements.

Remove Old-Style Breakpoints

You can set up a similar check for the old-style breakpoints with pdb.set_trace() as well. In this case, we define 2 rules to ensure that all occurrences are caught:

rules:
  - id: remove_set_trace
    description: Remove pdb.set_trace calls from production code
    pattern: |
      import pdb
      pdb.set_trace()
    replacement: ''

  - id: remove_set_trace_no_import
    description: Remove pdb.set_trace calls from production code
    pattern: pdb.set_trace()
    replacement: ''

Note that the 2 rules can have the same description, but they both need to have a unique id.

Coming soon: In the next versions, we'll introduce syntax for optional elements, so that you can implement such checks with 1 rule.

Caveat: Same as for the 1st rule. If a code block contained only a breakpoint(), applying this rule and removing that only line will lead to a syntax error.

Flag print Statements

Of course, print statements are a valid part of many codebases. But more often than not they are remainders after a debugging session.

It might be tricky to distinguish between "real" and debugging print statements. But if your project:

  • doesn't have a command line interface at all
  • or uses a command line framework like typer or click,

you should probably flag all occurrences of print.

For this, you can define a rule that finds all calls to print with various arguments:

  - id: flag_print
    description: print statement in production code
    pattern: print(${args*}, ${kwargs*})

Note that in contrast to the previous rules, this one doesn't have a replacement field. Sourcery will only flag these code snippets, but won't replace or delete them.

The ${args*} snippet captures all arguments. This enables you to find all calls to print() with various sets of arguments. To learn more about how you can capture different numbers of repetitions, check out our Pattern Syntax Reference Docs

Replace print Statements With Logging

Another approach you might consider is to replace those print statements sprinkled all over the codebase with logging.

For this, you can define a rule with a replacement:

  - id: print_to_log
    description: Replace print statement with logging
    pattern: print(${args+})
    replacement: logger.debug(${args})

This rule uses the capture ${args+}. While ${args*} captures 0, 1 or more positional arguments, ${args+} captures only 1 or more positional arguments. This way, you can ensure not to replace a print() call without any arguments with logger.debug().

To learn more about capturing arguments and * vs +, check out our Pattern Syntax Reference Docs

You can also define tests to verify which code snippets your rule matches and which it doesn't. Here's the same print_to_log rule enhanced with some tests.

  - id: print_to_log
    description: Replace print statement with logging
    pattern: print(${args+})
    replacement: logger.debug(${args})
    tests:
      - match: print("placeholder")
      - match: print(16, 20)
      - match: print(16, 20, sep=";")
      - no-match: print()

You can learn more about tests in our Custom Rule Fields Reference

Caveat: Sourcery will replace print with logger.debug, but it won't check whether the name logger exists in that context. => After applying this rule, you might need a manual adjustment to ensure that all the affected modules have a logger defined.