Changeset 74

Show
Ignore:
Timestamp:
06/23/06 10:36:26 (7 years ago)
Author:
mk
Message:

Improved scores for documentation and installability indices.
Fixed handling of duplicates in IndexRequiredFiles?.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/mk/cheesecake/cheesecake_index.py

    r73 r74  
    354354 
    355355################################################################################ 
     356## Index that computes scores based on files and directories. 
     357################################################################################ 
     358 
     359class OneOf(object): 
     360    def __init__(self, *possibilities): 
     361        self.possibilities = possibilities 
     362    def __str__(self): 
     363        return 'one of %s' % (self.possibilities,) 
     364 
     365def WithOptionalExt(name, extensions): 
     366    """Handy way of writing Cheese rules for files with extensions. 
     367 
     368    Instead of writing: 
     369        >>> one_of = OneOf('readme', 'readme.html', 'readme.txt') 
     370 
     371    Write this: 
     372        >>> opt_ext = WithOptionalExt('readme', ['html', 'txt']) 
     373 
     374    It means the same! (representation have a meaning) 
     375        >>> str(one_of) == str(opt_ext) 
     376        True 
     377    """ 
     378    possibilities = [name] 
     379    possibilities.extend(map(lambda x: name + '.' + x, extensions)) 
     380 
     381    return OneOf(*possibilities) 
     382 
     383def Doc(name): 
     384    return WithOptionalExt(name, ['html', 'txt']) 
     385 
     386class FilesIndex(Index): 
     387    _used_rules = [] 
     388 
     389    def _compute_from_rules(self, files_list, package_dir, files_rules): 
     390        self._used_rules = [] 
     391        files_count = 0 
     392        value = 0 
     393 
     394        for filename in files_list: 
     395            if not is_empty(os.path.join(package_dir, filename)): 
     396                score = self.get_score(os.path.basename(filename), files_rules) 
     397                if score != 0: 
     398                    value += score 
     399                    files_count += 1 
     400 
     401        return files_count, value 
     402 
     403    def get_score(self, name, specs): 
     404        for entry, value in specs.iteritems(): 
     405            if self.match_filename(name, entry): 
     406                self.cheesecake.log.debug("%d points entry found: %s (%s)" % \ 
     407                                          (value, name, entry)) 
     408                return value 
     409 
     410        return 0 
     411 
     412    def match_filename(self, name, rule): 
     413        """Check if `name` matches given `rule`. 
     414        """ 
     415        def equal(x, y): 
     416            x_root, x_ext = os.path.splitext(x) 
     417            y_root, y_ext = os.path.splitext(y.lower()) 
     418            if x_root in [y_root.lower(), y_root.upper(), y_root.capitalize()] \ 
     419                   and x_ext in [y_ext.lower(), y_ext.upper()]: 
     420                return True 
     421            return False 
     422 
     423        if rule in self._used_rules: 
     424            return False 
     425 
     426        if isinstance(rule, basestring): 
     427            if equal(name, rule): 
     428                self._used_rules.append(rule) 
     429                return True 
     430        elif isinstance(rule, OneOf): 
     431            for poss in rule.possibilities: 
     432                if self.match_filename(name, poss): 
     433                    self._used_rules.append(rule) 
     434                    return True 
     435 
     436        return False 
     437 
     438################################################################################ 
    356439## Installability index. 
    357440################################################################################ 
     
    393476            self.details += " as expected" 
    394477            self.value = self.max_value 
     478 
     479        return self.value 
     480 
     481class IndexSetupPy(FilesIndex): 
     482    name = "setup.py" 
     483    max_value = 25 
     484 
     485    files_rules = { 
     486        'setup.py': 25, 
     487    } 
     488 
     489    def compute(self, files_list, package_dir): 
     490        setup_py_found, self.value = self._compute_from_rules(files_list, package_dir, self.files_rules) 
     491 
     492        if setup_py_found: 
     493            self.details = "setup.py found" 
     494        else: 
     495            self.details = "setup.py not found" 
    395496 
    396497        return self.value 
     
    459560        IndexUnpack(), 
    460561        IndexUnpackDir(), 
     562        IndexSetupPy(), 
    461563        IndexInstall(), 
    462564        IndexGeneratedFiles(), 
     
    467569################################################################################ 
    468570 
    469 def match_filename(name, rule): 
    470     """Check if `name` matches given `rule`. 
    471     """ 
    472     def equal(x, y): 
    473         x_root, x_ext = os.path.splitext(x) 
    474         y_root, y_ext = os.path.splitext(y.lower()) 
    475         if x_root in [y_root.lower(), y_root.upper(), y_root.capitalize()] \ 
    476                and x_ext in [y_ext.lower(), y_ext.upper()]: 
    477             return True 
    478         return False 
    479  
    480     if isinstance(rule, basestring): 
    481         if equal(name, rule): 
    482             return True 
    483     elif isinstance(rule, OneOf) and not rule.used: 
    484         for poss in rule.possibilities: 
    485             if match_filename(name, poss): 
    486                 rule.used = True 
    487                 return True 
    488  
    489     return False 
    490  
    491 class OneOf(object): 
    492     def __init__(self, *possibilities): 
    493         self.possibilities = possibilities 
    494         self.used = False 
    495     def __str__(self): 
    496         return 'one of %s' % (self.possibilities,) 
    497  
    498 def WithOptionalExt(name, extensions): 
    499     """Handy way of writing Cheese rules for files with extensions. 
    500  
    501     Instead of writing: 
    502         >>> one_of = OneOf('readme', 'readme.html', 'readme.txt') 
    503  
    504     Write this: 
    505         >>> opt_ext = WithOptionalExt('readme', ['html', 'txt']) 
    506  
    507     It means the same! (representation have a meaning) 
    508         >>> str(one_of) == str(opt_ext) 
    509         True 
    510     """ 
    511     possibilities = [name] 
    512     possibilities.extend(map(lambda x: name + '.' + x, extensions)) 
    513  
    514     return OneOf(*possibilities) 
    515  
    516 def Doc(name): 
    517     return WithOptionalExt(name, ['html', 'txt']) 
    518  
    519 class IndexRequiredFiles(Index): 
     571class IndexRequiredFiles(FilesIndex): 
    520572    cheese_files = { 
    521         'setup.py': 15, 
    522         Doc('readme'): 15, 
    523         OneOf(Doc('license'), Doc('copying')): 15, 
     573        Doc('readme'): 30, 
     574        OneOf(Doc('license'), Doc('copying')): 30, 
     575 
     576        OneOf(Doc('announce'), Doc('changelog')): 20, 
     577        Doc('install'): 20, 
    524578 
    525579        Doc('authors'): 10, 
    526         Doc('announce'): 10, 
    527         Doc('changelog'): 10, 
    528580        Doc('faq'): 10, 
    529         Doc('install'): 10, 
    530581        Doc('news'): 10, 
    531582        Doc('thanks'): 10, 
     
    534585 
    535586    cheese_dirs = { 
    536         'demo': 20, 
    537         OneOf('doc', 'docs'): 25, 
    538         OneOf('example', 'examples'): 20, 
    539         OneOf('test', 'tests'): 25, 
     587        OneOf('doc', 'docs'): 30, 
     588        OneOf('test', 'tests'): 30, 
     589 
     590        'demo': 10, 
     591        OneOf('example', 'examples'): 10, 
    540592    } 
    541593 
     
    543595 
    544596    def compute(self, files_list, dirs_list, package_dir): 
    545         self.value = 0 
    546         self.reset_rules(self.cheese_files.keys() + self.cheese_dirs.keys()) 
    547  
    548         files_count = 0 
    549         for filename in files_list: 
    550             if not is_empty(os.path.join(package_dir, filename)): 
    551                 score = self.get_score(os.path.basename(filename), self.cheese_files) 
    552                 if score != 0: 
    553                     self.value += score 
    554                     files_count += 1 
    555  
    556         directories_count = 0 
    557         for directory in dirs_list: 
    558             if not is_empty(os.path.join(package_dir, directory)): 
    559                 score = self.get_score(os.path.basename(directory), self.cheese_dirs) 
    560                 if score != 0: 
    561                     self.value += score 
    562                     directories_count += 1 
     597        files_count, files_value = self._compute_from_rules(files_list, package_dir, self.cheese_files) 
     598        dirs_count, dirs_value = self._compute_from_rules(dirs_list, package_dir, self.cheese_dirs) 
     599 
     600        self.value = files_value + dirs_value 
    563601 
    564602        self.details = "%d files and %d required directories found" % \ 
    565                        (files_count, directories_count) 
     603                       (files_count, dirs_count) 
    566604 
    567605        return self.value 
    568  
    569     def get_score(self, name, specs): 
    570         for entry, value in specs.iteritems(): 
    571             if match_filename(name, entry): 
    572                 self.cheesecake.log.debug("%d points entry found: %s (%s)" % \ 
    573                                           (value, name, entry)) 
    574                 return value 
    575  
    576         return 0 
    577  
    578     def reset_rules(self, rules): 
    579         if isinstance(rules, basestring): 
    580             pass 
    581         elif isiterable(rules): 
    582             for rule in rules: 
    583                 self.reset_rules(rule) 
    584         elif isinstance(rules, OneOf): 
    585             rules.used = False 
    586             self.reset_rules(rules.possibilities) 
    587606 
    588607class IndexDocstrings(Index): 
     
    611630 
    612631        # Scale the result. 
    613         self.value = int(ceil(percent * self.max_value)) 
     632        # We give 20p for 25% of formatted docstrings, 35p for 50% and 50p for 75%. 
     633        self.value = 0 
     634        if percent > 0.75: 
     635            self.value = 50 
     636        elif percent > 0.50: 
     637            self.value = 35 
     638        elif percent > 0.25: 
     639            self.value = 20 
    614640 
    615641        self.details = "found %d/%d=%.2f%% objects with formatted docstrings" %\ 
  • branches/mk/tests/unit/test_cheese_files.py

    r55 r74  
    4545        empty_filenames = ['Readme', 'INSTALL', 'setup.py'] 
    4646        self._do_it(empty_filenames, self.create_empty_files, False) 
     47 
     48 
     49class TestDoubleFiles(MockupCheesecakeTest): 
     50    def test_double_files(self): 
     51        filenames = ['Readme', 'README.txt'] 
     52 
     53        self.create_files(self.prefix_with_package_name(filenames)) 
     54        self.cheesecake.walk_pkg() 
     55        self.cheesecake.compute_cheesecake_index() 
     56 
     57        loglines = ''.join(readlines_from_file(self._mock_logfile)) 
     58 
     59        # Make sure that README was counted only once. 
     60        assert cheesefile_in_log('Readme', loglines) 
     61        assert not cheesefile_in_log('README.txt', loglines)