Changeset 80
- Timestamp:
- 07/06/06 18:15:29 (2 years ago)
- Files:
-
- branches/mk/cheesecake/cheesecake_index.py (modified) (28 diffs)
- branches/mk/tests/unit/_mockup_cheesecake.py (modified) (3 diffs)
- branches/mk/tests/unit/test_index_class.py (modified) (1 diff)
- branches/mk/tests/unit/test_index_installability.py (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
branches/mk/cheesecake/cheesecake_index.py
r79 r80 18 18 """ 19 19 20 import os, sys, re, shutil 20 import os 21 import re 22 import shutil 23 import sys 21 24 import tempfile 25 22 26 from optparse import OptionParser 23 27 from urllib import urlretrieve 24 28 from urlparse import urlparse 25 29 from math import ceil 30 31 import logger 26 32 27 33 from util import pad_with_dots, pad_left_spaces, pad_right_spaces, pad_msg, pad_line … … 30 36 from util import mkdirs 31 37 from util import StdoutRedirector 32 import logger33 38 from config import get_pkg_config 34 39 from codeparser import CodeParser … … 40 45 ## Helpers. 41 46 ################################################################################ 47 48 if 'sorted' not in dir(__builtins__): 49 def sorted(L): 50 new_list = L[:] 51 new_list.sort() 52 return new_list 42 53 43 54 def isiterable(obj): … … 113 124 return filter(lambda x: discover_file_type(x) == file_type, file_list) 114 125 126 def get_package_name_from_path(path): 127 """Get package name as file portion of path. 128 129 >>> get_package_name_from_path('/some/random/path/package.tar.gz') 130 'package.tar.gz' 131 >>> get_package_name_from_path('/path/underscored_name.zip') 132 'underscored_name.zip' 133 >>> get_package_name_from_path('/path/unknown.extension.txt') 134 'unknown.extension.txt' 135 """ 136 dir, filename = os.path.split(path) 137 return filename 138 139 def get_package_name_from_url(url): 140 """Use ``urlparse`` to obtain package name from URL. 141 142 >>> get_package_name_from_url('http://www.example.com/file.tar.bz2') 143 'file.tar.bz2' 144 >>> get_package_name_from_url('https://www.example.com/some/dir/file.txt') 145 'file.txt' 146 """ 147 (scheme,location,path,param,query,fragment_id) = urlparse(url) 148 return get_package_name_from_path(path) 149 150 def get_package_name_and_type(package, known_extensions): 151 """Return package name and type. 152 153 Package type must exists in known_extensions list. Otherwise None is 154 returned. 155 156 >>> extensions = ['tar.gz', 'zip'] 157 >>> get_package_name_and_type('underscored_name.zip', extensions) 158 ('underscored_name', 'zip') 159 >>> get_package_name_and_type('unknown.extension.txt', extensions) 160 """ 161 for package_type in known_extensions: 162 if package.endswith('.'+package_type): 163 # Package name is name of package without file extension (ex. twill-7.3). 164 return package[:package.rfind('.'+package_type)], package_type 165 115 166 def get_method_arguments(method): 116 167 """Return tuple of arguments for given method, excluding self. … … 261 312 details = "" 262 313 263 def __init__(self, indices=[]): 264 if not self.subindices: 314 def __init__(self, *indices): 315 # When indices are given explicitly they override the default. 316 if indices: 265 317 self.subindices = [] 266 267 # Create dictionary for fast reference. 268 self._indices_dict = make_indices_dict(self.subindices) 269 270 for index in indices: 271 self.add_subindex(index) 318 self._indices_dict = {} 319 for index in indices: 320 self.add_subindex(index) 321 else: 322 if self.subindices: 323 new_subindices = [] 324 for index in self.subindices: 325 # index must be a class subclassing from Index. 326 assert isinstance(index, type) 327 assert issubclass(index, Index) 328 new_subindices.append(index()) 329 self.subindices = new_subindices 330 else: 331 self.subindices = [] 332 # Create dictionary for fast reference. 333 self._indices_dict = make_indices_dict(self.subindices) 272 334 273 335 self._compute_arguments = get_method_arguments(self.compute) … … 303 365 return self.value 304 366 367 def decide(self, cheesecake, when): 368 """Decide if this index should be computed. 369 370 If index has children, it will automatically remove all for which 371 decide() return false. 372 """ 373 if self.subindices: 374 # Iterate over copy, as we may remove some elements. 375 for index in self.subindices[:]: 376 print "Trying %s." % index.name 377 if not getattr(index, 'decide_' + when)(cheesecake): 378 self.remove_subindex(index.name) 379 return self.subindices 380 return True 381 382 def decide_before_download(self, cheesecake): 383 return self.decide(cheesecake, 'before_download') 384 385 def decide_after_download(self, cheesecake): 386 return self.decide(cheesecake, 'after_download') 387 305 388 def _get_max_value(self): 306 389 if self.subindices: … … 310 393 311 394 max_value = property(_get_max_value) 395 396 def _get_requirements(self): 397 if self.subindices: 398 return list(self._compute_arguments) + \ 399 reduce(lambda x,y: x + y.requirements, self.subindices, []) 400 return list(self._compute_arguments) 401 402 requirements = property(_get_requirements) 312 403 313 404 def add_subindex(self, index): … … 464 555 return self.value 465 556 557 def decide_before_download(self, cheesecake): 558 return cheesecake.url 559 466 560 class IndexUnpack(Index): 467 561 max_value = 25 … … 491 585 return self.value 492 586 587 def decide_after_download(self, cheesecake): 588 return cheesecake.package_type != 'egg' 589 493 590 class IndexSetupPy(FilesIndex): 494 591 name = "setup.py" … … 508 605 509 606 return self.value 607 608 def decide_after_download(self, cheesecake): 609 return cheesecake.package_type != 'egg' 510 610 511 611 class IndexInstall(Index): … … 521 621 522 622 return self.value 623 624 def decide_before_download(self, cheesecake): 625 return not cheesecake.static_only 523 626 524 627 class IndexPyPIDownload(Index): … … 551 654 return self.value 552 655 656 def decide_before_download(self, cheesecake): 657 return cheesecake.name 658 553 659 class IndexGeneratedFiles(Index): 554 660 generated_files_penalty = -20 661 max_value = 0 555 662 556 663 def compute(self, files_list): … … 568 675 return self.value 569 676 677 def decide_after_download(self, cheesecake): 678 return cheesecake.package_type != 'egg' 679 570 680 class IndexInstallability(Index): 571 681 name = "INSTALLABILITY" 572 682 573 683 subindices = [ 574 IndexUnpack(), 575 IndexUnpackDir(), 576 IndexSetupPy(), 577 IndexInstall(), 578 IndexGeneratedFiles(), 684 IndexPyPIDownload, 685 IndexUrlDownload, 686 IndexUnpack, 687 IndexUnpackDir, 688 IndexSetupPy, 689 IndexInstall, 690 IndexGeneratedFiles, 579 691 ] 580 692 … … 662 774 663 775 subindices = [ 664 IndexRequiredFiles (),665 IndexDocstrings (),666 IndexFormattedDocstrings (),776 IndexRequiredFiles, 777 IndexDocstrings, 778 IndexFormattedDocstrings, 667 779 ] 668 780 … … 778 890 779 891 subindices = [ 780 IndexPyLint (),781 # IndexUnitTests(), TODO892 IndexPyLint, 893 #IndexUnitTests, TODO 782 894 ] 783 895 … … 795 907 name = "Cheesecake" 796 908 subindices = [ 797 IndexInstallability (),798 IndexDocumentation (),799 IndexCodeKwalitee (),909 IndexInstallability, 910 IndexDocumentation, 911 IndexCodeKwalitee, 800 912 ] 801 913 802 914 915 class Step(object): 916 """Single step during computation of package score. 917 """ 918 def __init__(self, provides): 919 self.provides = provides 920 921 def decide(self, cheesecake): 922 """Decide if step should be run. 923 924 It checks if there's at least one index from current profile that need 925 variables provided by this step. Override this method for other behaviour. 926 """ 927 for provide in self.provides: 928 if provide in cheesecake.index.requirements: 929 return True 930 return False 931 932 class StepByVariable(Step): 933 """Step which is always run if given Cheesecake instance variable is true. 934 """ 935 def __init__(self, variable_name, provides): 936 self.variable_name = variable_name 937 Step.__init__(self, provides) 938 939 def decide(self, cheesecake): 940 if getattr(cheesecake, self.variable_name, None): 941 return True 942 943 # Fallback to the default. 944 return Step.decide(self, cheesecake) 945 803 946 class Cheesecake(object): 804 """ 805 Computes 'goodness' of Python packages 947 """Computes 'goodness' of Python packages. 806 948 807 949 Generates "cheesecake index" that takes into account things like: … … 816 958 * average pylint score for all non-test and non-demo modules 817 959 """ 818 index = CheesecakeIndex() 960 961 steps = {} 962 963 package_types = { 964 "tar.gz": untar_package, 965 "tgz": untar_package, 966 "zip": unzip_package, 967 "egg": unegg_package, 968 } 819 969 820 970 def __init__(self, name="", url="", path="", sandbox=None, config=None, … … 827 977 self.package_path = path 828 978 829 if not self.name and not self.url and not self.package_path: 830 self.raise_exception("No package name, URL or path specified ... exiting") 831 979 if self.name: 980 self.package = self.name 981 elif self.url: 982 self.package = get_package_name_from_url(self.url) 983 elif self.package_path: 984 self.package = get_package_name_from_path(self.package_path) 985 else: 986 self.raise_exception("No package name, URL or path specified... exiting") 987 988 # Setup a sandbox. 832 989 self.sandbox = sandbox or tempfile.mkdtemp(prefix='cheesecake') 833 990 if not os.path.isdir(self.sandbox): … … 839 996 self.static_only = static_only 840 997 841 self.package_types = {842 "tar.gz": untar_package,843 "tgz": untar_package,844 "zip": unzip_package,845 "egg": unegg_package,846 }847 848 998 self.sandbox_pkg_file = "" 849 999 self.sandbox_pkg_dir = "" 850 1000 self.sandbox_install_dir = "" 851 1001 852 # Include indices revelant to current situation.853 if self.name:854 self.index["INSTALLABILITY"].add_subindex(IndexPyPIDownload())855 if self.url:856 self.index["INSTALLABILITY"].add_subindex(IndexUrlDownload())857 858 # Get package name.859 self.determine_pkg_name()860 861 1002 # Configure logging as soon as possible. 862 1003 self.configure_logging(logfile) 863 1004 864 # Get package, 865 self.retrieve_pkg() 1005 # Setup Cheesecake index. 1006 self.index = CheesecakeIndex() 1007 1008 self.index.decide_before_download(self) 1009 self.log.debug("Profile requirements: %s." % ', '.join(sorted(self.index.requirements))) 1010 1011 # Get the package. 1012 self.run_step('get_pkg_from_pypi') 1013 self.run_step('download_pkg') 1014 self.run_step('copy_pkg') 866 1015 867 1016 # Get package name and type. 868 self.package_name, self.package_type = self.get_package_name_and_type(self.package) 1017 name_and_type = get_package_name_and_type(self.package, self.package_types.keys()) 1018 1019 if not name_and_type: 1020 msg = "Could not determine package type for package '%s'" % self.package 1021 msg += "\nCurrently recognized types: " + ", ".join(self.package_types.keys()) 1022 self.raise_exception(msg) 1023 1024 self.package_name, self.package_type = name_and_type 869 1025 self.log.debug("Package name: " + self.package_name) 870 1026 self.log.debug("Package type: " + self.package_type) 871 1027 1028 # Make last indices decisions. 1029 self.index.decide_after_download(self) 1030 872 1031 # Unpack package and list its files. 873 self. unpack_pkg()874 self. walk_pkg()1032 self.run_step('unpack_pkg') 1033 self.run_step('walk_pkg') 875 1034 876 1035 # Install package. 877 if self.static_only: 878 self.index["INSTALLABILITY"].remove_subindex('install') 879 else: 880 self.install_pkg() 881 882 # When checking an egg exclude irrelevant indices. 883 if self.package_type == 'egg': 884 self.index["INSTALLABILITY"].remove_subindex('setup.py') 885 self.index["INSTALLABILITY"].remove_subindex('unpack_dir') 886 self.index["INSTALLABILITY"].remove_subindex('generated_files') 1036 self.run_step('install_pkg') 887 1037 888 1038 def raise_exception(self, msg): … … 927 1077 pass 928 1078 929 def determine_pkg_name(self):930 if self.name:931 self.package = self.name932 self.short_pkg_name = self.name933 elif self.package_path:934 self.package = self.get_package_from_path(self.package_path)935 else:936 self.package = self.get_package_from_url()937 938 def get_package_from_url(self):939 """Use ``urlparse`` to obtain package path from URL.940 """941 (scheme,location,path,param,query,fragment_id) = urlparse(self.url)942 return self.get_package_from_path(path)943 944 def get_package_from_path(self, path):945 """Get package name as file portion of path.946 """947 dir, file = os.path.split(path)948 self.short_pkg_name = file949 for package_type in self.package_types:950 s = re.search("(.+)\.%s" % package_type, file)951 if s:952 self.short_pkg_name = s.group(1)953 break954 return file955 956 1079 def configure_logging(self, logfile=None): 957 1080 """Default settings for logging. … … 964 1087 self.logfile = logfile 965 1088 else: 966 self.logfile = os.path.join(tempfile.gettempdir(), self. short_pkg_name + ".log")1089 self.logfile = os.path.join(tempfile.gettempdir(), self.package + ".log") 967 1090 968 1091 logger.setconsumer('logfile', open(str(self.logfile), 'w', buffering=1)) … … 980 1103 self.log.error = logger.MultipleProducer('cheesecake console') 981 1104 982 def retrieve_pkg(self): 983 if self.name: 984 self.get_pkg_from_pypi() 985 elif self.url: 986 self.download_pkg() 987 else: 988 self.copy_pkg() 989 1105 def run_step(self, step_name): 1106 """Run step if its decide() method returns True. 1107 """ 1108 step = self.steps[step_name] 1109 if step.decide(self): 1110 step_method = getattr(self, step_name) 1111 step_method() 1112 1113 steps['get_pkg_from_pypi'] = StepByVariable('name', 1114 ['download_url', 1115 'distance_from_pypi', 1116 'found_on_cheeseshop', 1117 'found_locally', 1118 'sandbox_pkg_file']) 990 1119 def get_pkg_from_pypi(self): 991 1120 """Download package using setuptools utilities. … … 1070 1199 1071 1200 self.sandbox_pkg_file = output 1072 self.package = self.get_package_from_path(output)1201 self.package = get_package_name_from_path(output) 1073 1202 self.log.info("Downloaded package %s from %s" % (self.package, self.download_url)) 1074 1203 … … 1079 1208 self.found_on_cheeseshop = True 1080 1209 1210 steps['download_pkg'] = StepByVariable('url', 1211 ['sandbox_pkg_file', 1212 'downloaded_from_url']) 1081 1213 def download_pkg(self): 1082 1214 """Use ``urllib.urlretrieve`` to download package to file in sandbox dir. … … 1099 1231 1100 1232 self.downloaded_from_url = True 1101 1233 1234 steps['copy_pkg'] = StepByVariable('package_path', 1235 ['sandbox_pkg_file']) 1102 1236 def copy_pkg(self): 1103 1237 """Copy package file to sandbox directory. … … 1109 1243 shutil.copyfile(self.package_path, self.sandbox_pkg_file) 1110 1244 1111 def get_package_name_and_type(self, package): 1112 """Return package name and type. 1113 1114 Raise an exception when package type cannot be recognized. 1115 """ 1116 for type in self.package_types.keys(): 1117 s = re.search(r"(.+)\.%s" % type, package) 1118 if s: 1119 # Package name is name of package without file extension (ex. twill-7.3). 1120 return s.group(1), type 1121 1122 msg = "Could not determine package type for package '%s'" % package 1123 msg += "\nCurrently recognized types: " + ", ".join(self.package_types.keys()) 1124 self.raise_exception(msg) 1125 1245 steps['unpack_pkg'] = Step(['original_package_name', 1246 'sandbox_pkg_dir', 1247 'unpacked', 1248 'unpack_dir']) 1126 1249 def unpack_pkg(self): 1127 1250 """Unpack the package in the sandbox directory. … … 1151 1274 self.package_name = self.unpack_dir 1152 1275 1276 steps['walk_pkg'] = Step(['dirs_list', 1277 'docstring_cnt', 1278 'docformat_cnt', 1279 'files_list', 1280 'functions', 1281 'object_cnt', 1282 'package_dir']) 1153 1283 def walk_pkg(self): 1154 1284 """Get package files and directories. … … 1196 1326 ', '.join(self.dirs_list))) 1197 1327 1328 steps['install_pkg'] = Step(['installed']) 1198 1329 def install_pkg(self): 1199 1330 """Verify that package can be installed in alternate directory. branches/mk/tests/unit/_mockup_cheesecake.py
r55 r80 6 6 import _path_cheesecake 7 7 from cheesecake.cheesecake_index import Cheesecake 8 from cheesecake.cheesecake_index import CheesecakeIndex 8 9 9 10 … … 12 13 13 14 class CheesecakeMockup(Cheesecake): 15 def run_step(self, step_name): 16 if step_name == 'install_pkg': 17 return 18 Cheesecake.run_step(self, step_name) 19 14 20 def __init__(self, sandbox, package_name, logfile): 15 21 self.name = package_name 16 22 self.package_name = package_name 23 self.package = package_name 17 24 self.sandbox = sandbox 18 25 … … 22 29 self.unpack_dir = sandbox 23 30 24 self.determine_pkg_name()25 31 self.configure_logging(logfile) 26 self. set_defaults()32 self.index = CheesecakeIndex() 27 33 28 34 def setUp(self): branches/mk/tests/unit/test_index_class.py
r75 r80 24 24 >>> index in big_index.subindices 25 25 False 26 27 Test requirements. 28 >>> class NewIndex(Index): 29 ... def compute(self, one, two, three): 30 ... pass 31 >>> new = NewIndex() 32 >>> new.requirements 33 ['one', 'two', 'three'] 34 35 Now create other index and add it to the NewIndex. 36 >>> class OtherIndex(Index): 37 ... def compute(self, four): 38 ... pass 39 >>> other = OtherIndex() 40 >>> other.requirements 41 ['four'] 42 >>> new.add_subindex(other) 43 >>> new.requirements 44 ['one', 'two', 'three', 'four'] 26 45 """ branches/mk/tests/unit/test_index_installability.py
r76 r80 11 11 from cheesecake.cheesecake_index import IndexInstall 12 12 from cheesecake.cheesecake_index import IndexUrlDownload 13 from cheesecake.cheesecake_index import IndexGeneratedFiles 13 14 14 15 … … 26 27 27 28 index = self.cheesecake.index["INSTALLABILITY"] 28 parts = [IndexUnpack, IndexUnpackDir, IndexSetupPy, IndexInstall ]29 parts = [IndexUnpack, IndexUnpackDir, IndexSetupPy, IndexInstall, IndexGeneratedFiles] 29 30 30 31 assert index.max_value == sum(map(lambda x: x.max_value, parts)) … … 37 38 38 39 index = self.cheesecake.index["INSTALLABILITY"] 39 parts = [IndexU npack, IndexUnpackDir, IndexSetupPy, IndexInstall, IndexUrlDownload]40 parts = [IndexUrlDownload, IndexUnpack, IndexUnpackDir, IndexSetupPy, IndexInstall, IndexGeneratedFiles] 40 41 41 42 assert index.max_value == sum(map(lambda x: x.max_value, parts))
