Developer Guide

This developer guide includes more complex interactions like contributing modular updaters. If you haven’t read Installation you should do that first.

Linting

To lint your code, you can install pre-commit and other dependencies to your environment:

$ pip install -r .github/dev-requirements.txt

And run:

$ pre-commit run --all-files

Or install as a hook:

$ pre-commit install

Developing an Updater

Each updater is required to have one file, update.py that uses the UpdaterBase class and has one function to detect. The easiest way to get this structure is to copy another updater completely, and use it as a template.

Updater Class

Your updater class is discovered based on the module folder name. The class should be the uppercase of that, with Updater as a suffix. E.g.,:

  • python_copyright_metadata -> PythonCopyrightMetadataUpdater

If you don’t follow this convention, we won’t be able to discover it and use it! You’ll also get errors and know very quickly.

Updater Metadata

You are required to have:

  • description

  • name (typically same as the folder name, but not required) must be all lowercase and only - for special characters

Here is what the header looks like of the class. Importantly, it needs a regular expression to know what files it matches:

class PythonCopyrightMetadataUpdater(UpdaterBase):

    name = "python-copyright-updater"
    description = "update __copyright__ in python files"
    file_regex = "[.]py$"

Updater Detect

Your updater class has a main function detect that must exist. Any and all other classes are largely optional (and of course encouraged to have a modular design)! The function should expect a script (license_updater.main.script.ScriptFile) to be provided, and to look at script.original and make updates to script.changed Note that we:

  • Keep track of self.count, setting it to 0 in the beginning, and incrementing it for each change.

  • Make changes directly to script.changed. Since this is a copy of the original config, this is what will be changed (and saved to file, if desired).

  • Return a boolean to indicate if changes were detected.

def detect(self, script):
    """
    An example detection function
    """
    # Set the count to 0
    self.count = 0

    # No point if we don't have jobs!
    if not script.original:
        return False

    # Do some kind of detection here
    lines = []
    for line in script.original.split('\n'):

        changed_line = self.has_header_changes(line):
        if changed_line:
            self.count += 1
            lines.append(changed_line)
            continue
        lines.append(line)

    self.changed = "\n".join(lines)
    return self.count != 0

The client will handle displaying changes and otherwise saving updates, so you do not need to The updater will also be automatically detected and registered, and included in basic testing, however you do need to add a “before” and “after” set of files, discussed next.

Testing

Each updater should have a <name>-before.<ext> and <name>-after.<ext> in license_updater/tests/data. The format is simple - it should be a text/code file (any of your choosing!) before and after running an update. The easiest way to make this is to create a “before” file manually (with updates you know need to happen) (in Python) create a client, run detect, and then write to an after file. And be sure to check that your updater worked as you would like! Here is an example (what I used for my test cases):

from license_updater.main.client import LicenseUpdater
cli = LicenseUpdater()

# Before and after files (assuming in present working directory)
before_file = "python-copyright-updater-before.py"
after_file = "python-copyright-updater-after.py"

# Run detect *only* for the updater you care about
updater = cli.detect(before_file, updaters=['pythoncopyrightupdater'])

# Write changes to new file (then check it!)
updater[before_file].write(after_file)

And then visually check it - and you should be done! These files will be used in testing, along with testing basic output and metadata for your updater. If you have an idea for an updater but don’t have bandwidth to add? Please ping @vsoch by opening an issue!