Skip to content

Flag Hard-Coded Values

When your "one-off" script does its job really well, a logical next step is to reuse it for new purposes. :-) Hard-coded values tend to be the biggest hindrance in this endeavour. Especially if they are scattered accross the code and pop up at the most surprising places. Some rules can help you to detect and parametrize them.

Hard-Coded Values With A Specific Name

Let's say your script manipulates files. In the initial version the file names were hard-coded, but now you want to make it more flexible. A Sourcery custom rule to detect the hard-coded file names:

  - id: no-hard-coded-file-name
    description: Hard-coded file name
    pattern: ${var} = ${value}
    condition: |
      (var.ends_with("file")
      or var.ends_with("file_name")
      or var.ends_with("dir")
      or var.ends_with("dir_name"))
      and value.is_str_literal()
    explanation: |
      File and directory names should be read from a parameter or the config.
    tests:
      - match: input_file = "test.txt"
      - match: file_name = "placeholder.py"
      - match: working_dir = "/home/user/test"
      - no-match: other_file = input_file
      - no-match: minutes_file = f"tt_entry_{colour}.txt"
      - no-match: working_dir = Path()

To quickly see all the code this rule flagged, run the command:

sourcery review --include no-hard-coded-file-name .

Hard-Coded Values In __init__

A frequent anti-pattern is setting hard-coded values in the __init__ method. Usually, these can be replaced with a parameter that has a default value. Here's a rule flagging all string and literal hard-coded values in the __init__ methods:

  - id: no-hard-coded-value-in-init
    description: Don't set hard-coded values in `__init__`
    explanation: Consider defining a parameter with a default value
    pattern: |
      def __init__(...):
          ...
          ${var} = ${value}
          ...
    condition: |
      value.is_str_literal()
      or value.is_numeric_literal()
    tests:
      - match: |
          def __init__(self):
            nr = 42
      - match: |
          def __init__(self):
            self.nr = 42
      - match: |
          def __init__(self):
            some_init()
            self.nr = 42
            other_stuff(self)
      - match: |
          def __init__(self):
            self.description = "a special object"
      - no-match: |
          def other_func(self):
            nr = 42

What if you have a case where you really want to set a constant in the __init__ method? That's fair, this rule is a guideline, not law. Feel free to add an exception with a # sourcery skip comment:

# sourcery skip: no-hard-coded-value-in-init

Where and which hard-coded values pop up, depends a lot on your code. Use the examples above as a starting point to create your own rules and make your code more re-usable and flexible.