Skip to content

Get Rid Of Deprecated Code

When your system relies on any 3rd party code, from time to time, you need to deal with deprecation. Addressing those issues usually doesn't seem urgent. Until you realize that you need to sort out a bunch of deprecated functions in order to get a security update.

Tackling those deprecations via rules instead of simple text replacement or editor macros provides several advantages:

  • More flexibility: Catch occurrences of the deprecated function or parameter in various formats, not just exact matches.
  • Well-documented steps. This might come handy when a replacement doesn't work as expected and you need to tweak your rules.
  • Ensure that no future code will be added using the deprecated functionality.

The examples in this guide use some functions from the standard library and pandas. You can use them as templates to deal with the deprecated stuff in your internal libraries.

Deprecating A Library

If you want to deprecate a whole library, check out our recipe Flag Dependencies to a Library.

Replacing A Renamed Function

If the deprecated function got replaced by a function with the same arguments, you can define a pattern to update all occurrences:

rules:
  - id: warn-to-warning
    description: Replace `logger.warn` with `warning`
    pattern: logger.warn(${args*})
    replacement: logger.warning(${args})

Provide Background Info About The Deprecation

You can use the optional explanation field to provide more info about when and why this function got deprecated. You can also include a link to the documentation. Or a Slack thread if it's an internal package and the deprecation was less formal :-)

rules:
  - id: warn-to-warning
    description: Replace `logger.warn` with `warning`
    pattern: logger.warn(${args*})
    replacement: logger.warning(${args})
    explanation: |
      `warn` is deprecated and functionally identical to `warning`.
      https://docs.python.org/3/library/logging.html#logging.Logger.warning

Replace A Deprecated Method

You can use a similar syntax to replace a deprecated a method call on an object:

rules:
  - id: deprecated-styler-set-na-rep
    description: The method `Styler.set_na_rep()` has been deprecated
    pattern: ${styler}.set_na_rep(${na_rep})
    replacement: ${styler}.format(na_rep=${na_rep})
    explanation: |
      See the pandas docs:
      - [Deprecate Styler.set_na_rep](https://pandas.pydata.org/pandas-docs/dev/reference/api/pandas.io.formats.style.Styler.set_na_rep.html)
      - [`Styler.format()` docs](https://pandas.pydata.org/pandas-docs/dev/reference/api/pandas.io.formats.style.Styler.format.html#pandas.io.formats.style.Styler.format)

Caveat: This pattern will match every occassion when a .set_na_rep() method is called on any object. With specific method names like set_na_rep that might be safe, but don't create such rules with method names that are used by many different types. When in doubt, create a rule without a replacement and evaluate how big the signal vs noise ratio is.

Flagging A Deprecated Parameter

Sometimes, only a parameter of a function or a method gets deprecated. In these cases, you can create a rule that spots the usages of this parameter:

rules:
  - id: deprecated-csv-prefix
    description: Argument `prefix` for `read_csv` has been deprecated
    pattern: pandas.read_csv(..., prefix=${pre}, ...)
    explanation: |
      See the [pandas docs for read_csv](https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html)
    tests:
      - match: |
          import pandas
          df = pandas.read_csv("data.csv", prefix="ab")
      - match: |
          import pandas as pd
          df = pd.read_csv("data.csv", prefix="ab")
      - no-match: |
          import pandas as pd
          df = pd.read_csv("data.csv")

Note that this will only match if the parameter is called with a keyword argument.

Replacing A Deprecated Bool Parameter

If the deprecated parameter is a bool, you often have different replacements for the True and False values. In these cases, you can create 2 rules.

For example, in pandas 1.4.0, the squeeze parameter of pandas.read_csv() got deprecated:

  • squeeze=True: "Append .squeeze("columns") to the call to read_csv to squeeze the data." (see the pandas docs for read_csv)
  • squeeze=False: False is the default value. => You can omit this argument.

You can translate this into 2 rules like this:

# 2 rules to deprecate the squeeze parameter for pandas.read_csv()
rules:
  - id: deprecated_csv_squeeze_false
    description: The parameter `squeeze` for `pandas.read_csv()` has been deprecated
    pattern: pd.read_csv(${before*}, squeeze=False, ${after*})
    replacement: pd.read_csv(${before}, ${after})
    explanation: |
      False is the default value for squeeze.
      You can just omit this argument.
  - id: deprecated-csv-squeeze-true
    description: The parameter `squeeze` for `pandas.read_csv()` has been deprecated
    pattern: pd.read_csv(${before*}, squeeze=True, ${after*})
    replacement: pd.read_csv(${before}, ${after}).squeeze("columns")
    explanation: |
      Instead of squeeze=True,
      append `.squeeze("columns")` to the call.
      See the [pandas docs for read_csv](https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html)

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

Note about imports: The rules above assume that you always import pandas with import pandas as pd. If your codebase uses multiple aliases for pandas, you need to define multiple rules.

Remove An Obsolete Step From A Workflow

Let's say before creating an account, you need to execute some validation. This used to be a separate function call, but now it has been incorporated into create_account. To avoid executing the same validations twice, you want to remove the obsolete call.

rules:
  - id: remove-separate-account-validation
    description: Remove call to validate before account creation
    pattern: |
      validate_account_data(${account_data})
      create_account(${account_data})
    replacement: create_account(${account_data})
    explanation: '`create_account` now calls the validation'

General Good Practices

  • If it's obvious how the deprecated function should be replaced, create a rule with a replacement. If the new implementation needs to be figured out by a human, create a rule without a replacement.
  • Consider your function's signature. Does it support positional arguments? Keyword arguments?
  • For more complex cases, create multiple rules. You can group them by using the same description. You can also add comments to your YAML file.
  • Use the explanation field to provide background info about the deprecation. E.g. links to documentation, relevant conversations etc.