Source code for cheesecake_kwalitee_index.kwalitee.evaluator

"""
The main suite of tests that are run to generate the cheesecake kwalitee
index of a package.
"""

import os
import re
import subprocess
import tempfile
import shutil
import pip
import importlib
import sys

from cheesecake_kwalitee_index.utils import get_logger
from cheesecake_kwalitee_index.kwalitee.models import Score


logger = get_logger(__name__, 'stderr')


[docs]def download(package, dest): """ Download a package to a specified location. Making sure to get a source distribution. """ logger.info('Downloading %s', package) return pip.main(['install', '--target', dest, '--no-binary', ':all:', package])
[docs]def lint(dest, name): """ Run Pylint and get the overall score of a package. """ package = os.path.join(dest, name) logger.info('Lint checking %s', name) cmd = 'pylint {}'.format(package) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) proc.wait() for line in proc.stdout.read().decode().splitlines(): if line.startswith('Your code has been rated at'): score = re.search(r'(-?\d+(?:\.\d+)?)/10', line) break else: # It couldn't find our score so there must have been an error logger.error('Error while lint checking %s', package) logger.error('Stdout = \n%s', proc.stdout.read().strip()) logger.error('Stderr = \n%s', proc.stderr.read().strip()) raise RuntimeError('Linter failed with error code: {}'.format( proc.returncode)) return float(score.group(1))
[docs]def get_version_number(file_path, name): # Add the file_path to sys.path sys.path.append(file_path) # Then import the package and bind it to a variable pkg = importlib.import_module(name) try: return pkg.__version__ except AttributeError: return None
[docs]class Evaluator: """ The Evaluator is in charge of downloading a package and evaluating it against the cheesecake kwalitee index. """ def __init__(self, package, version='*'): self.package = package self.name = package.replace('-', '_') self.version = version self.score = { 'install': Score(0, 10), 'version_number': Score(0, 10), 'lint': Score(0, 100), } self.dest = tempfile.mkdtemp(prefix='cheesecake_') self.version_number = None
[docs] def evaluate_score(self): """ Run the entire test suite and get the package's score. """ logger.info('Evaluating score for %s', self.package) try: ins = self.score['install'] ins.value = self.install_package() # Stop early if we couldn't install if self.score['install'].value == 0: return self.score['version_number'].value = self.get_version() self.score['lint'].value = self.lint_test() finally: self.clean_up() # Let the tempdir be deleted and return the final score as a tuple final_score = sum(s.value for s in self.score.values()) total = sum(s.total for s in self.score.values()) return final_score, total
[docs] def get_version(self): """ Get __version__. If __version__ exists then give 10 points, otherwise zero. """ file_path = os.path.join(self.dest, self.name) version_number = get_version_number(file_path, self.name) # Save the version number for later self.version_number = version_number if version_number is not None: return 10 else: return 0
[docs] def lint_test(self): """ Pass the package through the linter. """ temp = lint(self.dest, self.name) return 10 * temp
[docs] def install_package(self): """ Try to install the package. If it installs, you get 10 points. Otherwise you get nothing. """ package_version = self.package + '==' + self.version ret = download(package_version, self.dest) if ret: return 0 else: return self.score['install'].total
[docs] def clean_up(self): """ Remove the installed directory and do any other necessary clean up. """ if self.dest is not None: logger.info('Cleaning up %s', self.dest) shutil.rmtree(self.dest) self.dest = None