Search code examples
pythonpylint

How does pylint remember scores from previous runs?


In a typical pylint run, we get the following output:

Global evaluation
-----------------
Your code has been rated at 9.50/10 (previous run: 8.50/10)

Duplication
-----------

+-------------------------+------+---------+-----------+
|                         |now   |previous |difference |
+=========================+======+=========+===========+
|nb duplicated lines      |0     |0        |=          |
+-------------------------+------+---------+-----------+
|percent duplicated lines |0.000 |0.000    |=          |
+-------------------------+------+---------+-----------+

I wonder how pylint remembers scores from previous runs - In the example above, the score from previous run was 8.5.

I want to implement such a functionality in my own module, and I thought I should first find out how pylint implements the functionality.

I searched for hidden folders where this data could be stored, but found none.


Solution

  • I have a .pylintrc file in my home directory that includes the following line:

    #pickle collected data for later comparisons.
    persistent=yes
    

    So it seems pylint does use pickle for comparisons

    In the lint.py from the source code:

    def make_options():
            return (('ignore',
                     {'type' : 'csv', 'metavar' : '<file>[,<file>...]',
                      'dest' : 'black_list', 'default' : ('CVS',),
                      'help' : 'Add files or directories to the blacklist. '
                               'They should be base names, not paths.'}),
                    ('persistent',
                     {'default': True, 'type' : 'yn', 'metavar' : '<y_or_n>',
                      'level': 1,
                      'help' : 'Pickle collected data for later comparisons.'})
    

    The full lint.py source is here

    The bit that is most interesting is probably this method:

    def close(self):
            """close the whole package /module, it's time to make reports !
    
            if persistent run, pickle results for later comparison
            """
            if self.file_state.base_name is not None:
                # load previous results if any
                previous_stats = config.load_results(self.file_state.base_name)
                # XXX code below needs refactoring to be more reporter agnostic
                self.reporter.on_close(self.stats, previous_stats)
                if self.config.reports:
                    sect = self.make_reports(self.stats, previous_stats)
                    if self.config.files_output:
                        filename = 'pylint_global.' + self.reporter.extension
                        self.reporter.set_output(open(filename, 'w'))
                else:
                    sect = Section()
                if self.config.reports or self.config.output_format == 'html':
                    self.reporter.display_results(sect)
                # save results if persistent run
                if self.config.persistent:
                    config.save_results(self.stats, self.file_state.base_name)
            else:
                self.reporter.on_close(self.stats, {})
    

    You will also want to look at the config.py source

    def load_results(base):
        """try to unpickle and return data from file if it exists and is not
        corrupted
    
        return an empty dictionary if it doesn't exists
        """
        data_file = get_pdata_path(base, 1)
        try:
            with open(data_file, _PICK_LOAD) as stream:
                return pickle.load(stream)
        except:
            return {}