Changeset 11

Show
Ignore:
Timestamp:
03/30/06 19:32:51 (3 years ago)
Author:
grig
Message:

Added ez_setup.py so that users are forced to upgrade to new version of setuptools.

Changed installation to alternate directory to use --root instead of --home.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/cheesecake/_util.py

    r8 r11  
    9494def pad_line(char="="): 
    9595    """ 
    96     Print line consisting of 'char' characters 
     96    Return line consisting of 'char' characters 
    9797    """ 
    98     print(char * (PAD_TEXT + PAD_VALUE + 1)) 
     98    msg = char * (PAD_TEXT + PAD_VALUE + 1) 
     99    return msg 
  • trunk/cheesecake/cheesecake_index.py

    r8 r11  
    2828from _util import StdoutRedirector 
    2929import logger 
    30 import config 
     30from config import get_pkg_config 
    3131from codeparser import CodeParser 
    3232 
     
    141141        self.sandbox_install_dir = "" 
    142142 
    143         self.set_defaults() 
    144         self.get_config_values() 
    145143        self.determine_pkg_name() 
    146144        self.configure_logging() 
     145        self.set_defaults() 
     146        self.get_config() 
    147147        self.init_indexes() 
    148148        self.retrieve_pkg() 
     
    196196        self.INDEX_DIR_EMPTY     = 5 
    197197        self.MAX_INDEX_DOCSTRINGS = 100 # max. percentage of modules/classes/methods/functions with docstrings 
     198        self.MAX_INDEX_UNITTESTS  = 100 # max. percentage of methods/functions that are unit tested 
    198199        self.MAX_INDEX_PYLINT     = 100 # max. pylint score 
    199200        self.cheese_files = ["readme", "install", "changelog", 
     
    207208        self.critical_cheese_dirs = ["doc", "test"] 
    208209 
    209     def get_config_values(self): 
     210    def get_config(self): 
    210211        """ 
    211212        Retrieve values from configuration file 
    212213        """ 
     214        self.config = get_pkg_config(self.short_pkg_name) 
    213215        for config_var in ["INDEX_PYPI_DOWNLOAD", "INDEX_PYPI_DISTANCE", 
    214216            "INDEX_URL_DOWNLOAD", "INDEX_UNPACK", "INDEX_UNPACK_DIR", 
     
    220222            "cheese_dirs", "critical_cheese_dirs", 
    221223            ]: 
    222             value = config.get(config_var) 
     224            value = self.config.get(config_var) 
    223225            if value: setattr(self, config_var, value) 
    224226 
     
    226228        if self.name: 
    227229            self.package = self.name 
     230            self.short_pkg_name = self.name 
    228231        elif self.package_path: 
    229232            self.package = self.get_package_from_path(self.package_path) 
     
    231234            self.package = self.get_package_from_url() 
    232235 
     236    def get_package_from_url(self): 
     237        """ 
     238        Use ``urlparse`` to obtain package path from URL 
     239        """ 
     240        (scheme,location,path,param,query,fragment_id) = urlparse(self.url) 
     241        return self.get_package_from_path(path) 
     242 
     243 
     244    def get_package_from_path(self, path): 
     245        """ 
     246        Get package name as file portion of path 
     247        """ 
     248        dir, file = os.path.split(path) 
     249        self.short_pkg_name = file 
     250        for package_type in self.package_types: 
     251            s = re.search("(.+)\.%s" % package_type, file) 
     252            if s: 
     253                self.short_pkg_name = s.group(1) 
     254                break 
     255        return file 
     256 
    233257    def configure_logging(self): 
    234258        """ 
     
    240264        log.warn and log.error go to both logfile and stdout 
    241265        """ 
    242         self.logfile = os.path.join(self.sandbox, self.package + ".log") 
     266        self.logfile = os.path.join(self.sandbox, self.short_pkg_name + ".log") 
    243267 
    244268        logger.setconsumer('logfile', open(str(self.logfile), 'w', buffering=1)) 
     
    258282        self.log.error = logger.MultipleProducer('cheesecake console') 
    259283 
    260         self.log.debug("package = ", self.package) 
     284        self.log.debug("package = ", self.short_pkg_name) 
    261285 
    262286    def init_indexes(self): 
     
    276300                                    self.INDEX_INSTALL + \ 
    277301                                    self.MAX_INDEX_DOCSTRINGS + \ 
    278                                     self.MAX_INDEX_PYLINT 
     302                                    self.MAX_INDEX_PYLINT  
     303#                                    self.MAX_INDEX_UNITTESTS 
    279304        self.max_cheesecake_index_installability = self.INDEX_PYPI_DOWNLOAD + \ 
    280305                                            self.INDEX_UNPACK + \ 
     
    283308        self.max_cheesecake_index_documentation = self.INDEX_REQUIRED_FILES + \ 
    284309                                        self.MAX_INDEX_DOCSTRINGS 
    285         self.max_cheesecake_index_codekwalitee = self.MAX_INDEX_PYLINT 
     310        self.max_cheesecake_index_codekwalitee = self.MAX_INDEX_PYLINT  
     311#                                        self.MAX_INDEX_UNITTESTS 
     312                                         
    286313        self.index = {} 
    287314        for index_type in ["file", "dir"]: 
     
    289316        for index_type in ["pypi_download", "url_download",  
    290317                           "unpack_dir", "unpack", "install", 
    291                            "docstrings", "pylint"]: 
     318                           "docstrings", "unittests", "pylint"]: 
    292319            self.index[index_type] = Index(index_type) 
    293320 
     
    321348            self.pkg_files[type] = [] 
    322349 
     350        self.object_cnt = 0  # Number of modules/functions/classes/methods in .py files found 
     351        self.docstring_cnt = 0 
     352        self.functions = [] # List of methods/functions found in .py files 
     353 
    323354    def retrieve_pkg(self): 
    324355        if self.name: 
     
    342373        """ 
    343374        dir, file = os.path.split(path) 
     375        self.short_pkg_name = file 
     376        for package_type in self.package_types: 
     377            s = re.search("(.+)\.%s" % package_type, file) 
     378            if s: 
     379                self.short_pkg_name = s.group(1) 
     380                break 
    344381        return file 
    345382 
     
    586623                    self.pkg_files["py"].append(fullpath) 
    587624                    self.log.debug("py file found: " + fullpath) 
     625                    pyfile = os.path.join(self.sandbox, fullpath) 
     626                    # Parse the file and count objects (modules/classes/functions) 
     627                    # and their associated docstrings 
     628                    code = CodeParser(pyfile, self.log.debug) 
     629                    self.object_cnt += code.object_count() 
     630                    self.docstring_cnt += code.docstring_count() 
     631                    self.functions += code.functions 
     632 
    588633                if os.path.splitext(file)[1] == ".pyc": 
    589634                    self.pkg_files["pyc"].append(fullpath) 
    590635                    self.log.debug("pyc file found: " + fullpath) 
     636 
    591637                if self.is_test_file(file, dirs_in_rootdir): 
    592638                    self.pkg_files["test"].append(fullpath) 
    593639                    self.log.debug("test file found: " + fullpath) 
     640 
    594641        len_pyc_list = len(self.pkg_files["pyc"]) 
    595642        if len_pyc_list: 
     
    623670        Return True is file is in directory rooted at "test" or "tests" 
    624671        """ 
     672        if not file.endswith(".py"): 
     673            return False 
    625674        if file in ["__init__.py"]: 
    626675            return False 
     
    700749        cwd = os.getcwd() 
    701750        os.chdir(os.path.join(self.sandbox, self.package_name)) 
    702         rc, output = run_cmd("python setup.py install --home=" + self.sandbox_install_dir) 
     751        rc, output = run_cmd("python setup.py install --root=" + self.sandbox_install_dir) 
    703752        if not rc: 
    704753            # Install succeeded 
     
    718767        Return Index object of type "docstrings" 
    719768        """ 
    720         cnt = 0 
    721         docstring_cnt = 0 
    722769        index_type = "docstrings" 
    723         for pyfile in self.pkg_files["py"]: 
    724             fullpath = os.path.join(self.sandbox, pyfile) 
    725             code = CodeParser(fullpath, self.log.debug) 
    726             cnt += code.object_count() 
    727             docstring_cnt += code.docstring_count() 
    728         if cnt: 
    729             percent = float(docstring_cnt)/float(cnt) 
     770        if self.object_cnt: 
     771            percent = float(self.docstring_cnt)/float(self.object_cnt) 
    730772        else: 
    731773            percent = 0 
    732774        index_value = int(ceil(percent*100)) 
    733         details = "found %d/%d=%.2f%% modules/classes/methods/functions with docstrings" % (docstring_cnt, cnt, percent*100) 
     775        details = "found %d/%d=%.2f%% modules/classes/methods/functions with docstrings" %\ 
     776                 (self.docstring_cnt, self.object_cnt, percent*100) 
    734777        self.index[index_type].value = index_value 
    735778        self.index[index_type].details = details 
    736779        return self.index[index_type] 
     780 
     781    def index_unittests(self): 
     782        """ 
     783        Compute unittest index as percentage of methods/functions 
     784        that are exercised in unit tests 
     785 
     786        Return Index object of type "unittests" 
     787        """ 
     788        unittest_cnt = 0 
     789        index_type = "unittests" 
     790        self.functions_tested = {} 
     791        for testfile in self.pkg_files["test"]: 
     792            fullpath = os.path.join(self.sandbox, testfile) 
     793            code = CodeParser(fullpath, self.log.debug) 
     794            func_called = code.functions_called() 
     795            self.log.debug("Functions called in unit test:") 
     796            self.log.debug(func_called) 
     797            for func in func_called: 
     798                self.functions_tested[func] = 1 
     799        self.log.debug("FUNCTIONS TO BE CHECKED WHETHER THEY ARE UNIT TESTED:") 
     800        self.log.debug(self.functions) 
     801        self.log.debug("FUNCTIONS THAT ARE UNIT TESTED:") 
     802        self.log.debug(self.functions_tested.keys()) 
     803        for funcname in self.functions: 
     804            if self.is_unit_tested(funcname): 
     805                unittest_cnt += 1 
     806                self.log.debug("%s is unit tested" % funcname) 
     807        cnt = len(self.functions) 
     808        if cnt: 
     809            percent = float(unittest_cnt)/float(cnt) 
     810        else: 
     811            percent = 0 
     812        index_value = int(ceil(percent*100)) 
     813        details = "found %d/%d=%.2f%% unit tested methods/functions" % (unittest_cnt, cnt, percent*100) 
     814        self.index[index_type].value = index_value 
     815        self.index[index_type].details = details 
     816        return self.index[index_type] 
     817 
     818    def is_unit_tested(self, funcname): 
     819        elem = funcname.split(".") 
     820        n1 = elem[-1] 
     821        n2 = "" 
     822        if len(elem) > 1: 
     823            n2 = elem[-2] + "." + elem[-1] 
     824        for key in self.functions_tested.keys(): 
     825            if key.startswith(n1) or (n2 and key.startswith(n2)): 
     826                return True 
     827        return False 
    737828 
    738829    def index_pylint(self): 
     
    805896                                         index_types, self.max_cheesecake_index_documentation) 
    806897 
    807         index_types = ["pylint"] 
     898        index_types = [  
     899                        #"unittests", 
     900                        "pylint", 
     901                        ] 
    808902        self.cheesecake_index_codekwalitee = self.process_partial_index("CODE KWALITEE",\ 
    809903                                         index_types, self.max_cheesecake_index_codekwalitee) 
    810904 
    811905        print 
    812         self.print_separator_line(
     906        self.print_separator_line("="
    813907        print pad_msg("OVERALL CHEESECAKE INDEX (ABSOLUTE)", self.cheesecake_index) 
    814908        percentage = (self.cheesecake_index * 100) / self.max_cheesecake_index 
     
    829923            partial_index_value += self.process_index(index_type) 
    830924 
    831         self.print_separator_line("-"
     925        self.print_separator_line(
    832926        print pad_msg("%s INDEX (ABSOLUTE)" % partial_index_name, partial_index_value) 
    833927        percentage = (partial_index_value * 100) / max_value 
     
    850944        return index.value 
    851945 
    852     def print_separator_line(self, char=""): 
     946    def print_separator_line(self, char="-"): 
    853947        """ 
    854948        Print line of text, unless quiet flag was given 
  • trunk/cheesecake/codeparser.py

    r8 r11  
    66    Information about the structure of a Python module 
    77 
    8     * Collects modules, classes, methods/functions and any associated docstrings 
     8    * Collects modules, classes, methods, functions and associated docstrings 
    99    * Based on mwh's docextractor.model module 
    1010    """ 
     
    1717        self.modules = [] 
    1818        self.classes = [] 
     19        self.methods = [] 
     20        self.method_func = [] 
    1921        self.functions = [] 
    2022        self.docstrings = {} 
     
    2426        self.log("Inspecting file: " + pyfile) 
    2527 
    26         system = System() 
    27         processModuleAst(parseFile(pyfile), module, system) 
    28         for obj in system.orderedallobjects: 
     28        self.system = System() 
     29        try: 
     30            processModuleAst(parseFile(pyfile), module, self.system) 
     31        except: 
     32            return 
     33 
     34        for obj in self.system.orderedallobjects: 
    2935            fullname = obj.fullName() 
    3036            if isinstance(obj, Module): 
     
    3339                self.classes.append(obj.fullName()) 
    3440            if isinstance(obj, Function): 
    35                 self.functions.append(fullname) 
     41                self.method_func.append(fullname) 
    3642            if obj.docstring: 
    3743                self.docstrings[fullname] = 1 
    3844 
     45        for method_or_func in self.method_func: 
     46            method_found = 0 
     47            for cls in self.classes: 
     48                if method_or_func.startswith(cls): 
     49                    self.methods.append(method_or_func) 
     50                    method_found = 1 
     51                    break 
     52            if not method_found: 
     53                self.functions.append(method_or_func) 
     54                 
    3955        self.log("modules: " + ",".join(self.modules)) 
    4056        self.log("classes: " + ",".join(self.classes)) 
     57        self.log("methods: " + ",".join(self.methods)) 
    4158        self.log("functions: " + ",".join(self.functions)) 
    4259 
     
    4764        * module 
    4865        * classes 
    49         * methods/functions 
     66        * methods 
     67        * functions 
    5068        """ 
    5169        module_count = len(self.modules) 
    5270        cls_count = len(self.classes) 
     71        method_count = len(self.methods) 
    5372        func_count = len(self.functions) 
    54         return module_count + cls_count + func_count 
     73        return module_count + cls_count + method_count + func_count 
    5574 
    5675    def docstring_count(self): 
     
    5978        """ 
    6079        return len(self.docstrings.keys()) 
     80 
     81    def functions_called(self): 
     82        """ 
     83        Return list of functions called by functions/methods 
     84        defined in this module 
     85        """ 
     86        return self.system.func_called.keys() 
  • trunk/cheesecake/config.py

    r9 r11  
    2121    INDEX_DIR_EMPTY     = 5, 
    2222    MAX_INDEX_DOCSTRINGS = 100, # max. percentage of modules/classes/methods/functions with docstrings 
     23    MAX_INDEX_UNITTESTS  = 100, # max. percentage of methods/functions that are unit tested 
    2324    MAX_INDEX_PYLINT     = 100, # max. pylint score 
    2425    cheese_files = ["readme", "install", "changelog", 
     
    2829    critical_cheese_files = ["readme", "license", "setup.py"], 
    2930    cheese_dirs = ["doc", "test", "example", "demo"], 
    30     critical_cheese_dirs = ["doc", "test"] 
     31    critical_cheese_dirs = ["doc", "test"], 
     32    exclude_files = [], 
     33    exclude_dirs = [], 
    3134) 
    3235 
    33 # try getting the user's home directory 
    34 homedir = "~" 
    35 homedir = os.path.expanduser(homedir) 
    36 if homedir is "~": 
    37     #raise Exception("cannot get home directory!") 
    38     # can't get it...fall back to defaults 
    39     pass 
    40 else: 
    41     # check for .cheesecake dir and create it if it's not there 
    42     cheesecake_dir = os.path.join(homedir, ".cheesecake") 
    43     if not os.path.isdir(cheesecake_dir): 
    44         os.mkdir(cheesecake_dir) 
    45     # check for config. file and create it if it's not there 
    46     cfile = os.path.join(cheesecake_dir, "my_config.py") 
    47     if not os.path.isfile(cfile): 
    48         try: 
    49             c = open(cfile, 'w') 
    50             c.write("my_config = {\n") 
    51             keys = _config.keys() 
    52             keys.sort() 
    53             for key in keys: 
    54                 c.write("\t'%s': %s,\n" % (key, _config[key])) 
    55             c.write("}\n") 
    56             c.close() 
    57         except OSError, e: 
    58             pass 
     36def get_pkg_config(package): 
     37    # try getting the user's home directory 
     38    homedir = "~" 
     39    homedir = os.path.expanduser(homedir) 
     40    if homedir is "~": 
     41        # can't get it...fall back to defaults 
     42        print "Couldn't expand ~ (home directory)" 
     43        pass 
     44    else: 
     45        # check for .cheesecake dir and create it if it's not there 
     46        cheesecake_dir = os.path.join(homedir, ".cheesecake") 
     47        if not os.path.isdir(cheesecake_dir): 
     48            os.mkdir(cheesecake_dir) 
     49        # check for config. file and create it if it's not there 
     50        cfile = os.path.join(cheesecake_dir, "%s_config.py" % package) 
     51        if not os.path.isfile(cfile): 
     52            try: 
     53                c = open(cfile, 'w') 
     54                c.write("my_config = {\n") 
     55                keys = _config.keys() 
     56                keys.sort() 
     57                for key in keys: 
     58                    c.write("\t'%s': %s,\n" % (key, _config[key])) 
     59                c.write("}\n") 
     60                c.close() 
     61            except OSError, e: 
     62                pass 
    5963 
    60     else: 
    61         # if we find the file, update _config with contents of the file 
    62         sys.path.insert(0, cheesecake_dir) 
    63         try: 
    64             from my_config import my_config 
    65             _config.update(my_config) 
    66         except ImportError, e: 
    67             pass 
     64        else: 
     65            # if we find the file, update _config with contents of the file 
     66            sys.path.insert(0, cheesecake_dir) 
     67            try: 
     68                from my_config import my_config 
     69                _config.update(my_config) 
     70            except ImportError, e: 
     71                pass 
     72    return _config 
    6873 
    6974def get(key, default=None): 
  • trunk/cheesecake/model.py

    r8 r11  
    11""" 
    2 Code borrowed from Michael Hudson's docextractor package with the author's permission. 
    3 The original code is available at <http://codespeak.net/svn/user/mwh/docextractor/> 
    4 Minor changes: 
    5  * do not print warnings to stdout 
     2-Code borrowed from Michael Hudson's docextractor package with the author's permission. 
     3-The original code is available at <http://codespeak.net/svn/user/mwh/docextractor/> 
    64""" 
     5 
    76from compiler import ast 
    87import sys 
     
    1211import sets 
    1312 
     13import compiler 
    1414from compiler.transformer import parse, parseFile 
    1515from compiler.visitor import walk 
     
    8888        # for import * statements 
    8989        self.importstargraph = {} 
     90        self.func_called = {} 
    9091 
    9192    def _push(self, cls, name, docstring): 
     
    140141        self.current = self.current.parent 
    141142 
    142     def push(self, obj): 
     143    def push(self, obj, node=None): 
    143144        self._stack.append(self.current) 
    144145        self.current = obj 
     
    158159        self._pop(self.Module) 
    159160 
    160     def pushFunction(self, name, docstring): 
     161    def pushFunction(self, name, docstring, func_called): 
     162        self.func_called.update(func_called) 
    161163        return self._push(self.Function, name, docstring) 
    162164    def popFunction(self): 
     
    260262            assert m.docstring is None 
    261263            m.docstring = node.doc 
    262             self.system.push(m
     264            self.system.push(m, node
    263265            self.default(node) 
    264266            self.system.pop(m) 
     
    327329 
    328330    def visitFunction(self, node): 
    329         func = self.system.pushFunction(node.name, node.doc) 
     331        fc = {} 
     332        get_function_calls(node, fc) 
     333        #print fc.keys() 
     334        func = self.system.pushFunction(node.name, node.doc, fc) 
    330335        # ast.Function has a pretty lame representation of 
    331336        # arguments. Let's convert it to a nice concise 
     
    357362            argnames2.append(argname) 
    358363        func.argspec = (argnames2, starargname, kwname, tuple(defaults)) 
     364        #for child in node.getChildren(): 
     365        #    if isinstance(child, compiler.ast.Stmt): 
     366        #        for c in child.getChildren(): 
     367        #            print c.__class__ 
     368        #            print c 
    359369        self.postpone(func, node.code) 
    360370        self.system.popFunction() 
    361371 
     372def get_function_calls(node, fc): 
     373    if not isinstance(node, compiler.ast.Node): 
     374        return 
     375    for child in node.getChildren(): 
     376        #print "child:", child 
     377        if isinstance(child, compiler.ast.CallFunc): 
     378                funcname = "" 
     379                attrname = "" 
     380                n = child.node 
     381                #print "n:", n 
     382                #print n.__class__ 
     383                if isinstance(n, compiler.ast.Getattr): 
     384                    expr = n.expr 
     385                    if isinstance(expr, compiler.ast.Name): 
     386                        funcname = expr.name 
     387                    attrname = n.attrname 
     388                func_called = "" 
     389                if funcname: func_called = funcname + "." 
     390                func_called += attrname 
     391                if func_called: 
     392                    fc[func_called] = 1 
     393        get_function_calls(child, fc) 
     394     
    362395def processModuleAst(ast, name, system): 
    363396    mv = ModuleVistor(system, name) 
     
    365398    while mv.morenodes: 
    366399        obj, node = mv.morenodes.pop(0) 
    367         system.push(obj
     400        system.push(obj, node
    368401        mv.visit(node) 
    369402        system.pop(obj) 
  • trunk/tests/test_code_parser.py

    r8 r11  
    1414        assert self.code1.classes == ["module1.Class1", "module1.Class2"] 
    1515 
     16    def test_methods(self): 
     17        assert self.code1.methods== ["module1.Class1.__init__", 
     18        "module1.Class1.__another_method__", 
     19        "module1.Class1.method1", 
     20        "module1.Class1.method2",  
     21        "module1.Class1.method3", 
     22        "module1.Class1.method4"] 
     23 
    1624    def test_functions(self): 
    17         assert self.code1.functions == ["module1.Class1.__init__", "module1.Class1.__another_method__",\ 
    18                             "module1.Class1.method1", "module1.Class1.method2", "module1.Class1.method3", \ 
    19                             "module1.Class1.method4", "module1.func1", "module1.func2", "module1.func3", \ 
    20                             "module1.func4", "module1.__func5__"] 
     25        assert self.code1.functions == [ 
     26        "module1.func1",  
     27        "module1.func2",  
     28        "module1.func3", 
     29        "module1.func4",  
     30        "module1.__func5__"] 
     31 
    2132    def test_count(self): 
    2233        assert self.code1.object_count() == 14 
  • trunk/tests/test_config.py

    r8 r11  
    99        homedir = os.path.expanduser(homedir) 
    1010        self.cheesecake_dir = os.path.join(homedir, ".cheesecake") 
    11         self.my_config = os.path.join(self.cheesecake_dir, "my_config.py") 
    1211 
    1312    def test_no_custom_config(self): 
     
    1514        if os.path.isdir(self.cheesecake_dir): 
    1615            shutil.rmtree(self.cheesecake_dir) 
    17         import cheesecake.config 
    18         # since the module might have been imported by another test, 
    19         # we reload it 
    20         reload(cheesecake.config
     16        from cheesecake import config 
     17        pkg_name = "my" 
     18        pkg_config_file = os.path.join(self.cheesecake_dir, "my_config.py") 
     19        config = config.get_pkg_config(pkg_name
    2120        # the .cheesecke dir and my_config.py file should have been created 
    2221        assert os.path.isdir(self.cheesecake_dir) 
    23         assert os.path.isfile(self.my_config
     22        assert os.path.isfile(pkg_config_file
    2423        # my_config should contain default values 
    2524        sys.path.insert(0, self.cheesecake_dir) 
    2625        from my_config import my_config 
    2726        for key, value in my_config.items(): 
    28             assert cheesecake.config.get(key) == value 
     27            assert config.get(key) == value